From 7b32de27c63a2f6bdb6602538294fa72d0f1196e Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 28 Oct 2024 10:47:08 -0700 Subject: [PATCH 1/6] ModuleObjcMessageTrace: also include the source file name where an ObjcMethod is referenced. rdar://138776708 --- lib/FrontendTool/LoadedModuleTrace.cpp | 10 +++++++++- test/IDE/objc_send_collector.swift | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/FrontendTool/LoadedModuleTrace.cpp b/lib/FrontendTool/LoadedModuleTrace.cpp index 406d31fe2e9b4..e570b584e47f2 100644 --- a/lib/FrontendTool/LoadedModuleTrace.cpp +++ b/lib/FrontendTool/LoadedModuleTrace.cpp @@ -21,6 +21,7 @@ #include "swift/Basic/Assertions.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/JSONSerialization.h" +#include "swift/Basic/SourceManager.h" #include "swift/Frontend/FrontendOptions.h" #include "swift/IDE/SourceEntityWalker.h" @@ -816,6 +817,7 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, } class ObjcMethodReferenceCollector: public SourceEntityWalker { + StringRef visitingFilePath; llvm::DenseSet results; bool visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, @@ -828,6 +830,10 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { return true; } public: + void setFileBeforeVisiting(SourceFile *SF) { + assert(SF && "need to visit actual source files"); + visitingFilePath = SF->getFilename(); + } void serializeAsJson(llvm::raw_ostream &OS) { llvm::json::OStream out(OS, /*IndentSize=*/4); out.array([&] { @@ -845,7 +851,8 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { out.attribute("type", pName); } out.attribute("method", clangD->getNameAsString()); - out.attribute("location", Loc.printToString(SM)); + out.attribute("declared_at", Loc.printToString(SM)); + out.attribute("referenced_at", visitingFilePath); }); } }); @@ -879,6 +886,7 @@ bool swift::emitObjCMessageSendTraceIfNeeded(ModuleDecl *mainModule, ObjcMethodReferenceCollector collector; for (auto *FU : mainModule->getFiles()) { if (auto *SF = dyn_cast(FU)) { + collector.setFileBeforeVisiting(SF); collector.walk(*SF); } } diff --git a/test/IDE/objc_send_collector.swift b/test/IDE/objc_send_collector.swift index fb2e8e8c7ba5c..b66c0a3e9c05a 100644 --- a/test/IDE/objc_send_collector.swift +++ b/test/IDE/objc_send_collector.swift @@ -15,4 +15,5 @@ public func testProperties(_ x: FooClassBase) { // CHECK-DAG: fooBaseInstanceFunc0 // CHECK-DAG: fooBaseInstanceFunc1 // CHECK-DAG: "type": "FooClassBase" -// CHECK-DAG: SOURCE_DIR/test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h +// CHECK-DAG: "declared_at": "SOURCE_DIR/test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h +// CHECK-DAG: "referenced_at": "SOURCE_DIR/test/IDE/objc_send_collector.swift" From b1fa3e65b5162e5c224fafea91d0bf96b079ed98 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 28 Oct 2024 15:58:51 -0700 Subject: [PATCH 2/6] ModuleObjcMessageTrace: indicate whether a method is instance method or class method --- lib/FrontendTool/LoadedModuleTrace.cpp | 11 ++++++++++- test/IDE/objc_send_collector.swift | 6 ++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/FrontendTool/LoadedModuleTrace.cpp b/lib/FrontendTool/LoadedModuleTrace.cpp index e570b584e47f2..56f431e54ffb8 100644 --- a/lib/FrontendTool/LoadedModuleTrace.cpp +++ b/lib/FrontendTool/LoadedModuleTrace.cpp @@ -829,6 +829,15 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { } return true; } + static StringRef selectMethodKey(const clang::ObjCMethodDecl* clangD) { + assert(clangD); + if (clangD->isInstanceMethod()) + return "instance_method"; + else if (clangD->isClassMethod()) + return "class_method"; + else + return "method"; + } public: void setFileBeforeVisiting(SourceFile *SF) { assert(SF && "need to visit actual source files"); @@ -850,7 +859,7 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { if (!pName.empty()) out.attribute("type", pName); } - out.attribute("method", clangD->getNameAsString()); + out.attribute(selectMethodKey(clangD), clangD->getNameAsString()); out.attribute("declared_at", Loc.printToString(SM)); out.attribute("referenced_at", visitingFilePath); }); diff --git a/test/IDE/objc_send_collector.swift b/test/IDE/objc_send_collector.swift index b66c0a3e9c05a..131c9663789dc 100644 --- a/test/IDE/objc_send_collector.swift +++ b/test/IDE/objc_send_collector.swift @@ -10,10 +10,12 @@ import Foo public func testProperties(_ x: FooClassBase) { _ = x.fooBaseInstanceFunc0() x.fooBaseInstanceFunc1(1.2) + _ = FooClassBase.fooBaseClassFunc0() } -// CHECK-DAG: fooBaseInstanceFunc0 -// CHECK-DAG: fooBaseInstanceFunc1 +// CHECK-DAG: "instance_method": "fooBaseInstanceFunc0" +// CHECK-DAG: "instance_method": "fooBaseInstanceFunc1:" +// CHECK-DAG: "class_method": "fooBaseClassFunc0" // CHECK-DAG: "type": "FooClassBase" // CHECK-DAG: "declared_at": "SOURCE_DIR/test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h // CHECK-DAG: "referenced_at": "SOURCE_DIR/test/IDE/objc_send_collector.swift" From 1b025bacc5eff250e19b646c46dd7f5f6b420cf4 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 28 Oct 2024 16:16:02 -0700 Subject: [PATCH 3/6] ModuleObjcMessageTrace: specify whether the owning context is an interface or category --- lib/FrontendTool/LoadedModuleTrace.cpp | 10 +++++++++- test/IDE/objc_send_collector.swift | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/FrontendTool/LoadedModuleTrace.cpp b/lib/FrontendTool/LoadedModuleTrace.cpp index 56f431e54ffb8..0757023cb730c 100644 --- a/lib/FrontendTool/LoadedModuleTrace.cpp +++ b/lib/FrontendTool/LoadedModuleTrace.cpp @@ -838,6 +838,14 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { else return "method"; } + static StringRef selectMethodOwnerKey(const clang::NamedDecl* clangD) { + assert(clangD); + if (isa(clangD)) + return "interface_type"; + if (isa(clangD)) + return "category_type"; + return "type"; + } public: void setFileBeforeVisiting(SourceFile *SF) { assert(SF && "need to visit actual source files"); @@ -857,7 +865,7 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { ->getParent())) { auto pName = parent->getName(); if (!pName.empty()) - out.attribute("type", pName); + out.attribute(selectMethodOwnerKey(parent), pName); } out.attribute(selectMethodKey(clangD), clangD->getNameAsString()); out.attribute("declared_at", Loc.printToString(SM)); diff --git a/test/IDE/objc_send_collector.swift b/test/IDE/objc_send_collector.swift index 131c9663789dc..e45d59b63ed9a 100644 --- a/test/IDE/objc_send_collector.swift +++ b/test/IDE/objc_send_collector.swift @@ -16,6 +16,6 @@ public func testProperties(_ x: FooClassBase) { // CHECK-DAG: "instance_method": "fooBaseInstanceFunc0" // CHECK-DAG: "instance_method": "fooBaseInstanceFunc1:" // CHECK-DAG: "class_method": "fooBaseClassFunc0" -// CHECK-DAG: "type": "FooClassBase" +// CHECK-DAG: "interface_type": "FooClassBase" // CHECK-DAG: "declared_at": "SOURCE_DIR/test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h // CHECK-DAG: "referenced_at": "SOURCE_DIR/test/IDE/objc_send_collector.swift" From 84ba7897079f52d3c07dbd74d81c749e6a6541e8 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 28 Oct 2024 16:49:50 -0700 Subject: [PATCH 4/6] ModuleObjcMessageTrace: include target and target variant info and a format version number --- lib/FrontendTool/LoadedModuleTrace.cpp | 54 +++++++++++++++++--------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/lib/FrontendTool/LoadedModuleTrace.cpp b/lib/FrontendTool/LoadedModuleTrace.cpp index 0757023cb730c..09074ed1aa611 100644 --- a/lib/FrontendTool/LoadedModuleTrace.cpp +++ b/lib/FrontendTool/LoadedModuleTrace.cpp @@ -816,7 +816,11 @@ bool swift::emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, return false; } +const static unsigned OBJC_METHOD_TRACE_FILE_FORMAT_VERSION = 1; + class ObjcMethodReferenceCollector: public SourceEntityWalker { + std::string target; + std::string targetVariant; StringRef visitingFilePath; llvm::DenseSet results; bool visitDeclReference(ValueDecl *D, CharSourceRange Range, @@ -847,31 +851,43 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { return "type"; } public: + ObjcMethodReferenceCollector(ModuleDecl *MD) { + auto &Opts = MD->getASTContext().LangOpts; + target = Opts.Target.str(); + targetVariant = Opts.TargetVariant.has_value() ? + Opts.TargetVariant->str() : ""; + } void setFileBeforeVisiting(SourceFile *SF) { assert(SF && "need to visit actual source files"); visitingFilePath = SF->getFilename(); } void serializeAsJson(llvm::raw_ostream &OS) { llvm::json::OStream out(OS, /*IndentSize=*/4); - out.array([&] { - for (const clang::ObjCMethodDecl* clangD: results) { - auto &SM = clangD->getASTContext().getSourceManager(); - clang::SourceLocation Loc = clangD->getLocation(); - if (!Loc.isValid()) { - continue; - } - out.object([&] { - if (auto *parent = dyn_cast_or_null(clangD - ->getParent())) { - auto pName = parent->getName(); - if (!pName.empty()) - out.attribute(selectMethodOwnerKey(parent), pName); + out.object([&] { + out.attribute("format-vesion", OBJC_METHOD_TRACE_FILE_FORMAT_VERSION); + out.attribute("target", target); + if (!targetVariant.empty()) + out.attribute("target-variant", targetVariant); + out.attributeArray("references", [&] { + for (const clang::ObjCMethodDecl* clangD: results) { + auto &SM = clangD->getASTContext().getSourceManager(); + clang::SourceLocation Loc = clangD->getLocation(); + if (!Loc.isValid()) { + continue; } - out.attribute(selectMethodKey(clangD), clangD->getNameAsString()); - out.attribute("declared_at", Loc.printToString(SM)); - out.attribute("referenced_at", visitingFilePath); - }); - } + out.object([&] { + if (auto *parent = dyn_cast_or_null(clangD + ->getParent())) { + auto pName = parent->getName(); + if (!pName.empty()) + out.attribute(selectMethodOwnerKey(parent), pName); + } + out.attribute(selectMethodKey(clangD), clangD->getNameAsString()); + out.attribute("declared_at", Loc.printToString(SM)); + out.attribute("referenced_at", visitingFilePath); + }); + } + }); }); } }; @@ -900,7 +916,7 @@ bool swift::emitObjCMessageSendTraceIfNeeded(ModuleDecl *mainModule, } // Write the contents of the buffer. llvm::raw_fd_ostream out(tmpFD, /*shouldClose=*/true); - ObjcMethodReferenceCollector collector; + ObjcMethodReferenceCollector collector(mainModule); for (auto *FU : mainModule->getFiles()) { if (auto *SF = dyn_cast(FU)) { collector.setFileBeforeVisiting(SF); From 58e40166fb716d4b527d77af9265af3c5a01dde0 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 28 Oct 2024 18:04:03 -0700 Subject: [PATCH 5/6] ModuleObjcMessageTrace: consolidate all source code file paths to a side table --- lib/FrontendTool/LoadedModuleTrace.cpp | 16 +++++++++++++--- test/IDE/objc_send_collector.swift | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/FrontendTool/LoadedModuleTrace.cpp b/lib/FrontendTool/LoadedModuleTrace.cpp index 09074ed1aa611..1ea916305c8f1 100644 --- a/lib/FrontendTool/LoadedModuleTrace.cpp +++ b/lib/FrontendTool/LoadedModuleTrace.cpp @@ -821,7 +821,8 @@ const static unsigned OBJC_METHOD_TRACE_FILE_FORMAT_VERSION = 1; class ObjcMethodReferenceCollector: public SourceEntityWalker { std::string target; std::string targetVariant; - StringRef visitingFilePath; + SmallVector FilePaths; + unsigned CurrentFileID; llvm::DenseSet results; bool visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, @@ -859,7 +860,8 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { } void setFileBeforeVisiting(SourceFile *SF) { assert(SF && "need to visit actual source files"); - visitingFilePath = SF->getFilename(); + FilePaths.push_back(SF->getFilename()); + CurrentFileID = FilePaths.size(); } void serializeAsJson(llvm::raw_ostream &OS) { llvm::json::OStream out(OS, /*IndentSize=*/4); @@ -884,7 +886,15 @@ class ObjcMethodReferenceCollector: public SourceEntityWalker { } out.attribute(selectMethodKey(clangD), clangD->getNameAsString()); out.attribute("declared_at", Loc.printToString(SM)); - out.attribute("referenced_at", visitingFilePath); + out.attribute("referenced_at_file_id", CurrentFileID); + }); + } + }); + out.attributeArray("fileMap", [&]{ + for (unsigned I = 0, N = FilePaths.size(); I != N; I ++) { + out.object([&] { + out.attribute("file_id", I + 1); + out.attribute("file_path", FilePaths[I]); }); } }); diff --git a/test/IDE/objc_send_collector.swift b/test/IDE/objc_send_collector.swift index e45d59b63ed9a..c5bad9045d424 100644 --- a/test/IDE/objc_send_collector.swift +++ b/test/IDE/objc_send_collector.swift @@ -18,4 +18,6 @@ public func testProperties(_ x: FooClassBase) { // CHECK-DAG: "class_method": "fooBaseClassFunc0" // CHECK-DAG: "interface_type": "FooClassBase" // CHECK-DAG: "declared_at": "SOURCE_DIR/test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h -// CHECK-DAG: "referenced_at": "SOURCE_DIR/test/IDE/objc_send_collector.swift" +// CHECK-DAG: "referenced_at_file_id": 1 +// CHECK-DAG: "file_id": 1, +// CHECK-DAG: "file_path": "SOURCE_DIR/test/IDE/objc_send_collector.swift" From 3fc9e273f387b539d3e45dcdd9fe569641458686 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Tue, 29 Oct 2024 13:42:22 -0700 Subject: [PATCH 6/6] ModuleObjcMessageTrace: use a more generalized directory name. NFC --- lib/FrontendTool/LoadedModuleTrace.cpp | 2 +- test/IDE/objc_send_collector.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/FrontendTool/LoadedModuleTrace.cpp b/lib/FrontendTool/LoadedModuleTrace.cpp index 1ea916305c8f1..411047912135e 100644 --- a/lib/FrontendTool/LoadedModuleTrace.cpp +++ b/lib/FrontendTool/LoadedModuleTrace.cpp @@ -914,7 +914,7 @@ bool swift::emitObjCMessageSendTraceIfNeeded(ModuleDecl *mainModule, return false; llvm::SmallString<128> tracePath {loadedModuleTracePath}; llvm::sys::path::remove_filename(tracePath); - llvm::sys::path::append(tracePath, ".SWIFT_OBJC_MESSAGE_TRACE"); + llvm::sys::path::append(tracePath, ".SWIFT_FINE_DEPENDENCY_TRACE"); if (!llvm::sys::fs::exists(tracePath)) { if (llvm::sys::fs::create_directory(tracePath)) return false; diff --git a/test/IDE/objc_send_collector.swift b/test/IDE/objc_send_collector.swift index c5bad9045d424..7f47adaadb0bf 100644 --- a/test/IDE/objc_send_collector.swift +++ b/test/IDE/objc_send_collector.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -I %t/lib/swift -typecheck %s -module-name main -swift-version 5 -F %S/Inputs/mock-sdk -emit-loaded-module-trace-path %t/.MODULE_TRACE -// RUN: cat %t/.SWIFT_OBJC_MESSAGE_TRACE/* | %FileCheck %s +// RUN: cat %t/.SWIFT_FINE_DEPENDENCY_TRACE/* | %FileCheck %s // REQUIRES: objc_interop