Skip to content

Commit 0cc4676

Browse files
[SymbolGraphGen] distinguish between headers of the same name in different modules (#82112)
Resolves rdar://152676102 In Objective-C, it's reasonable to sort extensions of your dependency's types into headers that match the name of that type. However, this runs into a bug in SymbolGraphGen when it comes time to generate Swift symbol graphs for that Objective-C code: At the moment, it only differentiates between modules based on their base name, regardless of their parent modules (if any). This causes these extensions to be incorrectly sorted into the _extending module's_ symbol graph, rather than in an extension symbol graph where it can be displayed with the _extended module_. When processed with Swift-DocC, it would cause these symbols to disappear. This PR updates the `areModulesEqual` function used by the `SymbolGraphASTWalker` to consider the fully-qualified module name for comparisons, rather than just the module's base name, causing situations like the test's `Dependency.DependencyClass` and `HeaderCollision.DependencyClass` to be properly distinguished from each other.
1 parent 034bfa1 commit 0cc4676

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

lib/SymbolGraphGen/SymbolGraphASTWalker.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ using namespace symbolgraphgen;
2828

2929
namespace {
3030

31+
/// Get the fully-qualified module name of the given `ModuleDecl` as a `std::string`.
32+
///
33+
/// For example, if `M` is a submodule of `ParentModule` named `SubModule`,
34+
/// this function would return `"ParentModule.SubModule"`.
35+
std::string getFullModuleName(const ModuleDecl *M) {
36+
if (!M) return {};
37+
38+
std::string S;
39+
llvm::raw_string_ostream OS(S);
40+
41+
M->getReverseFullModuleName().printForward(OS);
42+
43+
return S;
44+
}
45+
3146
/// Compare the two \c ModuleDecl instances to see whether they are the same.
3247
///
3348
/// This does a by-name comparison to consider a module's underlying Clang module to be equivalent
@@ -36,7 +51,7 @@ namespace {
3651
/// If the `isClangEqual` argument is set to `false`, the modules must also be from the same
3752
/// compiler, i.e. a Swift module and its underlying Clang module would be considered not equal.
3853
bool areModulesEqual(const ModuleDecl *lhs, const ModuleDecl *rhs, bool isClangEqual = true) {
39-
if (lhs->getNameStr() != rhs->getNameStr())
54+
if (getFullModuleName(lhs) != getFullModuleName(rhs))
4055
return false;
4156

4257
if (!isClangEqual && (lhs->isNonSwiftModule() != rhs->isNonSwiftModule()))
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-symbolgraph-extract -sdk %clang-importer-sdk -module-name HeaderCollision -F %t -output-dir %t -pretty-print -v
5+
// RUN: %FileCheck %s --input-file %t/HeaderCollision.symbols.json --check-prefix MODULE
6+
// RUN: %FileCheck %s --input-file %t/[email protected] --check-prefix EXTENSION
7+
8+
// REQUIRES: objc_interop
9+
10+
// Ensure that extensions to a dependency's types, declared in a header with the same name as the
11+
// dependency's type's header, correctly get sorted into an extension header rather than the module
12+
// itself.
13+
14+
// MODULE: "symbols": []
15+
// EXTENSION: "precise": "c:objc(cs)DependencyClass(im)addNumber:to:"
16+
17+
//--- Dependency.framework/Modules/module.modulemap
18+
framework module Dependency {
19+
umbrella header "Dependency.h"
20+
export *
21+
module * { export * }
22+
}
23+
24+
//--- Dependency.framework/Headers/Dependency.h
25+
#import <Dependency/DependencyClass.h>
26+
27+
//--- Dependency.framework/Headers/DependencyClass.h
28+
@import Foundation;
29+
30+
@class DependencyClass;
31+
32+
@interface DependencyClass : NSObject
33+
34+
@end
35+
36+
//--- HeaderCollision.framework/Modules/module.modulemap
37+
framework module HeaderCollision {
38+
umbrella header "HeaderCollision.h"
39+
export *
40+
module * { export * }
41+
}
42+
43+
//--- HeaderCollision.framework/Headers/HeaderCollision.h
44+
#import <HeaderCollision/DependencyClass.h>
45+
46+
//--- HeaderCollision.framework/Headers/DependencyClass.h
47+
#import <Dependency/DependencyClass.h>
48+
49+
@interface DependencyClass (HeaderCollisionClassAdditions)
50+
51+
- (NSInteger)addNumber:(NSInteger)x to:(NSInteger)y;
52+
53+
@end

0 commit comments

Comments
 (0)