-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SourceKit] Allow module refs to be indexed #19243
Conversation
@akyrtzi Here it is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are some directions:
- For the module USR, update the clang checkout and use
generateFullUSRForTopLevelModuleName(StringRef ModName, raw_ostream &OS)
which comes fromclang/Index/USRGeneration.h
. This will allow us to use same USR and track module references across both Swift and ObjC. - For testing, primarily use
swift-ide-test
, and follow the examples fromtest/Index
directory. Testing core functionality (e.g. how are symbols indexed) should occur viaswift-ide-test
, and SourceKit testing should be used mainly for verifying that the information goes through the SourceKit responses as expected. From your PR I think the modifications to existing SourceKit tests are sufficient to verify that the information goes through, you don't need to add additional SourceKit test cases. - For testing, I recommend to extend your test case with also:
- Test that module references in
import func
type of imports also get handled - Test that module references in clang submodules (like
import SomeClangModule.SubModule
) are handled properly. You may findgenerateFullUSRForModule(const clang::Module *Mod, raw_ostream &OS)
useful for this
- Test that module references in
I had to add a few more methods to treat the clang modules - |
lib/Index/IndexSymbol.cpp
Outdated
@@ -134,10 +134,18 @@ static SymbolKind getVarSymbolKind(const VarDecl *VD) { | |||
return SymbolKind::Variable; | |||
} | |||
|
|||
SymbolInfo index::getSymbolInfoForModule(ModuleEntity Mod) { | |||
SymbolInfo info{ SymbolKind::Unknown, SymbolSubKind::None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we give different parameters to clang modules?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use the language kind to differentiate, SymbolLanguage::Swift
for Swift modules and generic SymbolLanguage::C
for clang modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
include/swift/AST/USRGeneration.h
Outdated
@@ -13,6 +13,7 @@ | |||
#ifndef SWIFT_AST_USRGENERATION_H | |||
#define SWIFT_AST_USRGENERATION_H | |||
|
|||
#include "swift/AST/Module.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally try to keep headers lean, meaning don't include other headers if you can just forward declare the symbols you need instead.
Here you included swift/AST/Module.h
so you can reference ModuleEntity
, but you can just forward declare it as class ModuleEntity;
.
You'd need to include the header itself in the cases where the full definition of ModuleEntity
is needed, for example if you use it inside an inline function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah forgive me, I didn't know you could do that in C++. Changes applied
include/swift/Index/IndexSymbol.h
Outdated
@@ -13,6 +13,7 @@ | |||
#ifndef SWIFT_INDEX_INDEXSYMBOL_H | |||
#define SWIFT_INDEX_INDEXSYMBOL_H | |||
|
|||
#include "swift/AST/Module.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forward declare here as well.
lib/AST/Module.cpp
Outdated
@@ -365,6 +365,13 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx) | |||
setAccess(AccessLevel::Public); | |||
} | |||
|
|||
bool ModuleDecl::isClangModule() const { | |||
if (!getFiles().empty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be using ModuleDecl::findUnderlyingClangModule()
instead ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was the isClangModule
logic at https://github.com/apple/swift/blob/c94bc1d7dbde329242cf68f856284095e07d9f1f/tools/swift-ide-test/swift-ide-test.cpp#L2436 duplicated? I took it from there, but changing it to use findUnderlyingClangModule
works as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was the
isClangModule
logic duplicated?
Quite possible, utility code in testing tools generally gets less scrutiny than additions in libAST.
lib/AST/USRGeneration.cpp
Outdated
@@ -205,6 +203,17 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator, const ValueDecl* D) | |||
llvm::SmallString<128> Buffer; | |||
llvm::raw_svector_ostream OS(Buffer); | |||
|
|||
if (auto ModuleD = dyn_cast<ModuleDecl>(D)) { | |||
StringRef moduleName = ModuleD->getName().str(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit better to use ModuleD->getNameStr()
include/swift/AST/Module.h
Outdated
@@ -264,7 +264,6 @@ class ModuleDecl : public DeclContext, public TypeDecl { | |||
return { Files.begin(), Files.size() }; | |||
} | |||
|
|||
bool isClangModule() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for being unclear, I actually liked that you added isClangModule()
method, I was mainly suggesting to look into implementing this in terms of calling findUnderlyingModule()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries! I've misread it
lib/AST/Module.cpp
Outdated
bool ModuleDecl::isClangModule() const { | ||
if (findUnderlyingClangModule()) | ||
return true; | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I mainly did this instead of return findUnderlyingClangModule()
because I thought the latter might be confusing in terms of the actual return type)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In such situations I usually write it as:
return findUnderlyingClangModule() != nullptr;
include/swift/AST/Module.h
Outdated
@@ -1290,6 +1289,7 @@ class ModuleEntity { | |||
bool isBuiltinModule() const; | |||
const ModuleDecl *getAsSwiftModule() const; | |||
const clang::Module *getAsClangModule() const; | |||
void *getOpaqueValue() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend to make this an inline method (move the definition inline). The definition is simple and inlining it would be good for performance.
test/Index/index_module_refs.swift
Outdated
import ClangModuleB | ||
// CHECK: [[@LINE-1]]:8 | module/C | ClangModuleB | c:@M@ClangModuleB | Ref | rel: 0 | ||
import ClangModuleC.Sub1 | ||
// CHECK: [[@LINE-1]]:8 | module/C | ClangModuleC | c:@M@ClangModuleC | Ref | rel: 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a nice improvement you can make for how test checking works in this file:
- Match the USR and add it as a FileCheck variable. So instead of having
c:@M@ClangModuleC
make it[[ClangModuleC_USR:c:@M@ClangModuleC]]
- Later in the file, instead of repeating the same USR to match (
c:@M@ClangModuleC
) you match with the variable you defined earlier ([[ClangModuleC_USR]]
). - Do the same for the other USRs that get repeated, like for
SwiftModuleC
.
The benefit of this is that it reduces the duplication of the USR string. Later on, if for some reason we decide to change how the USRs for modules are formed, there will be much fewer places in the test cases that would be necessary to correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing feature. Thanks for the tip!
test/Index/index_module_refs.swift
Outdated
func myMethod() { | ||
_ = SwiftModuleC.MyGeneric<SwiftModuleC.MyType, MyType>() | ||
// CHECK: [[@LINE-1]]:9 | module/Swift | SwiftModuleC | c:@M@SwiftModuleC | Ref | rel: 0 | ||
// CHECK: [[@LINE-2]]:22 | struct/Swift | MyGeneric | s:12SwiftModuleC9MyGenericV | Ref,RelCont | rel: 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will be a bit better to keep the test case focused on testing that module references are indexed. Meaning we don't need to check for MyGeneric
and MyType
here, other test cases should be responsible to make sure that we index these properly.
Essentially I'd recommend to remove the check lines for those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since some of the references rely on getParentDecl()
, I was concerned that a change to this feature could potentially break the reference that comes right after it - perhaps I should make a separate test file for this interaction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair, it'd be fine to have the additional checks but at least reduce how strict the CHECK string needs to be, for example if we only want to check that the struct
reference shows up we could have it like this:
// CHECK: [[@LINE-2]]:22 | struct/Swift | MyGeneric | {{.*}} | Ref
So essentially we care that MyGeneric
reference shows up but we don't care to match its USR.
include/swift/AST/Module.h
Outdated
@@ -1288,6 +1289,12 @@ class ModuleEntity { | |||
bool isSystemModule() const; | |||
bool isBuiltinModule() const; | |||
const ModuleDecl *getAsSwiftModule() const; | |||
const clang::Module *getAsClangModule() const; | |||
|
|||
inline void *getOpaqueValue() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inline
is redundant, it's the fact that you have the definition present that makes it inlinable.
test/Index/index_module_refs.swift
Outdated
// CHECK: [[@LINE-1]]:11 | module/Swift | SwiftModuleC | [[SwiftModuleC_USR]] | Ref | rel: 0 | ||
func myMethod() { | ||
_ = SwiftModuleC.MyGeneric<SwiftModuleC.MyType, MyType>() | ||
// CHECK: [[@LINE-1]]:9 | module/Swift | SwiftModuleC | [[SwiftModuleC_USR]] | Ref | rel: 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One I think I noticed is that while the other type references have | Ref,RelCont | rel: 1
(a 'RelCont' relation with the containing method), the module references do not have that.
I wouldn't say it is super important to have the containing relation, but could you take a look to see if it is simple to add, so that the module references do not diverge ?
If you see other test cases, we check for relations by using CHECK-NEXT
to match the relations that are printed immediately after the symbol occurrence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this rule applies only to AbstractFunctionDecl
? It seems that merely mimicking the generic Decl behaviour does the trick:
auto Parent = getParentDecl();
if (Parent && isa<AbstractFunctionDecl>(Parent))
addRelation(Info, (unsigned)SymbolRole::RelationContainedBy, Parent);
module/Swift | SwiftModuleC | [[SwiftModuleC_USR]] | Ref,RelCont | rel: 1
RelCont | instance-method/Swift | myMethod() | [[myMethod_USR:.*]]
@@ -0,0 +1,29 @@ | |||
// RUN: %target-swift-ide-test -print-indexed-symbols -enable-source-import -source-filename %s -I %S/Store/Inputs | %FileCheck %s | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's shaping up great! One final (I think) suggestion about the test case; add a line like this:
import NonExistingModuleName // make sure there's no problem with invalid imports
as per comment this would be to guard that there's no issue occurring with invalid imports, like some crash/assertion hit while trying to index its module reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense! I added // CHECK-NOT: {{.*}} | NonExistingModuleName
to it to guarantee it doesn't even show up, but if it has performance implications we can leave it out.
@swift-ci smoke test |
Thanks for your patience and work on this! It will be critically useful for providing global rename functionality for frameworks at some point. |
Thank -you- for the tips and assistance! I promise the next PRs will be far less painful... |
I think test fails on linux because |
Also add |
Done |
@swift-ci smoke test |
Pitch
This allows Swift to index explicit reference to modules, such as
Swift.String
.Resolves SR-8677.