Skip to content

Commit 9b9c84a

Browse files
authored
Merge pull request #8972 from akyrtzi/index-external-symbol-namespacing
[index] Use a #pragma to apply module namespacing for exported ObjC header and match Swift namespaced USRs with the clang side
2 parents f83d1a7 + 73c3a18 commit 9b9c84a

File tree

12 files changed

+242
-95
lines changed

12 files changed

+242
-95
lines changed

lib/AST/USRGeneration.cpp

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,27 @@ bool ide::printDeclTypeUSR(const ValueDecl *D, raw_ostream &OS) {
4646
}
4747

4848
static bool printObjCUSRFragment(const ValueDecl *D, StringRef ObjCName,
49-
raw_ostream &OS) {
49+
const ExtensionDecl *ExtContextD,
50+
raw_ostream &OS) {
5051
if (!D)
5152
return true;
5253

54+
// The Swift module name that the decl originated from. If the decl is
55+
// originating from ObjC code (ObjC module or the bridging header) then this
56+
// will be empty.
57+
StringRef ModuleName;
58+
if (!D->hasClangNode())
59+
ModuleName = D->getModuleContext()->getNameStr();
60+
5361
if (isa<ClassDecl>(D)) {
54-
clang::index::generateUSRForObjCClass(ObjCName, OS);
62+
StringRef extContextName;
63+
if (ExtContextD) {
64+
extContextName = ExtContextD->getModuleContext()->getNameStr();
65+
}
66+
clang::index::generateUSRForObjCClass(ObjCName, OS,
67+
ModuleName, extContextName);
5568
} else if (isa<ProtocolDecl>(D)) {
56-
clang::index::generateUSRForObjCProtocol(ObjCName, OS);
69+
clang::index::generateUSRForObjCProtocol(ObjCName, OS, ModuleName);
5770
} else if (isa<VarDecl>(D)) {
5871
clang::index::generateUSRForObjCProperty(ObjCName, D->isStatic(), OS);
5972
} else if (isa<ConstructorDecl>(D)) {
@@ -63,18 +76,33 @@ static bool printObjCUSRFragment(const ValueDecl *D, StringRef ObjCName,
6376
} else if (isa<AbstractFunctionDecl>(D)) {
6477
clang::index::generateUSRForObjCMethod(ObjCName, D->isInstanceMember(), OS);
6578
} else if (isa<EnumDecl>(D)) {
66-
OS << "@E@" << ObjCName; // FIXME: expose clang API to handle enum names
79+
clang::index::generateUSRForGlobalEnum(ObjCName, OS, ModuleName);
6780
} else if (isa<EnumElementDecl>(D)) {
68-
OS << "@" << ObjCName;
81+
clang::index::generateUSRForEnumConstant(ObjCName, OS);
6982
} else {
7083
llvm_unreachable("Unexpected value decl");
7184
}
7285
return false;
7386
}
7487

88+
static bool printObjCUSRContext(const Decl *D, raw_ostream &OS) {
89+
OS << clang::index::getUSRSpacePrefix();
90+
auto *DC = D->getDeclContext();
91+
if (auto *Parent = DC->getAsNominalTypeOrNominalTypeExtensionContext()) {
92+
auto *extContextD = dyn_cast<ExtensionDecl>(DC);
93+
auto ObjCName = objc_translation::getObjCNameForSwiftDecl(Parent);
94+
if (printObjCUSRFragment(Parent, ObjCName.first.str(), extContextD, OS))
95+
return true;
96+
}
97+
return false;
98+
}
99+
75100
static bool printObjCUSRForAccessor(const AbstractStorageDecl *ASD,
76101
AccessorKind Kind,
77102
raw_ostream &OS) {
103+
if (printObjCUSRContext(ASD, OS))
104+
return true;
105+
78106
ObjCSelector Selector;
79107
switch (Kind) {
80108
case swift::AccessorKind::IsGetter:
@@ -94,23 +122,19 @@ static bool printObjCUSRForAccessor(const AbstractStorageDecl *ASD,
94122
}
95123

96124
static bool printObjCUSR(const ValueDecl *D, raw_ostream &OS) {
97-
OS << clang::index::getUSRSpacePrefix();
98-
99-
if (auto *Parent = D->getDeclContext()->
100-
getAsNominalTypeOrNominalTypeExtensionContext()) {
101-
auto ObjCName = objc_translation::getObjCNameForSwiftDecl(Parent);
102-
if (printObjCUSRFragment(Parent, ObjCName.first.str(), OS))
103-
return true;
104-
}
125+
if (printObjCUSRContext(D, OS))
126+
return true;
127+
auto *extContextD = dyn_cast<ExtensionDecl>(D->getDeclContext());
105128

106129
auto ObjCName = objc_translation::getObjCNameForSwiftDecl(D);
107130

108131
if (!ObjCName.first.empty())
109-
return printObjCUSRFragment(D, ObjCName.first.str(), OS);
132+
return printObjCUSRFragment(D, ObjCName.first.str(), extContextD, OS);
110133

111134
assert(ObjCName.second);
112135
llvm::SmallString<128> Buf;
113-
return printObjCUSRFragment(D, ObjCName.second.getString(Buf), OS);
136+
return printObjCUSRFragment(D, ObjCName.second.getString(Buf),
137+
extContextD, OS);
114138
}
115139

116140
static bool shouldUseObjCUSR(const Decl *D) {

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2330,6 +2330,25 @@ class ModuleWriter {
23302330
"#if !defined(__has_feature)\n"
23312331
"# define __has_feature(x) 0\n"
23322332
"#endif\n"
2333+
"#if !defined(__has_warning)\n"
2334+
"# define __has_warning(x) 0\n"
2335+
"#endif\n"
2336+
"\n"
2337+
"#if __has_attribute(external_source_symbol)\n"
2338+
"# define SWIFT_STRINGIFY(str) #str\n"
2339+
"# define SWIFT_MODULE_NAMESPACE_PUSH(module_name) "
2340+
"_Pragma(SWIFT_STRINGIFY(clang attribute "
2341+
"push(__attribute__((external_source_symbol(language=\"Swift\", "
2342+
"defined_in=module_name, generated_declaration))), "
2343+
"apply_to=any(function, enum, objc_interface, objc_category, "
2344+
"objc_protocol))))\n"
2345+
"# define SWIFT_MODULE_NAMESPACE_POP "
2346+
"_Pragma(\"clang attribute pop\")\n"
2347+
"#else\n"
2348+
"# define SWIFT_MODULE_NAMESPACE_PUSH(module_name)\n"
2349+
"# define SWIFT_MODULE_NAMESPACE_POP\n"
2350+
"#endif\n"
2351+
"\n"
23332352
"#if __has_include(<swift/objc-prologue.h>)\n"
23342353
"# include <swift/objc-prologue.h>\n"
23352354
"#endif\n"
@@ -2682,8 +2701,15 @@ class ModuleWriter {
26822701
out <<
26832702
"#pragma clang diagnostic ignored \"-Wproperty-attribute-mismatch\"\n"
26842703
"#pragma clang diagnostic ignored \"-Wduplicate-method-arg\"\n"
2704+
"#if __has_warning(\"-Wpragma-clang-attribute\")\n"
2705+
"# pragma clang diagnostic ignored \"-Wpragma-clang-attribute\"\n"
2706+
"#endif\n"
2707+
"#pragma clang diagnostic ignored \"-Wunknown-pragmas\"\n"
2708+
"\n"
2709+
"SWIFT_MODULE_NAMESPACE_PUSH(\"" << M.getNameStr() << "\")\n"
26852710
<< os.str()
2686-
<< "#pragma clang diagnostic pop\n";
2711+
<< "SWIFT_MODULE_NAMESPACE_POP\n"
2712+
"#pragma clang diagnostic pop\n";
26872713
return false;
26882714
}
26892715
};

test/IDE/comment_to_xml.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//===--- Check that we convert comments to XML correctly.
22

33
// RUN: %target-swift-frontend -typecheck -verify -disable-objc-attr-requires-foundation-module %S/../Inputs/comment_to_something_conversion.swift
4-
// RUN: %target-swift-ide-test -print-comments -source-filename %S/../Inputs/comment_to_something_conversion.swift -comments-xml-schema %S/../../bindings/xml/comment-xml-schema.rng > %t.txt
4+
// RUN: %target-swift-ide-test -module-name comment_to_xml -print-comments -source-filename %S/../Inputs/comment_to_something_conversion.swift -comments-xml-schema %S/../../bindings/xml/comment-xml-schema.rng > %t.txt
55
// RUN: %FileCheck %S/../Inputs/comment_to_something_conversion.swift < %t.txt
66
// RUN: %FileCheck %s -check-prefix=WRONG < %t.txt
77

test/IDE/print_usrs.swift

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -156,36 +156,36 @@ class Observers {
156156
}
157157
}
158158

159-
// CHECK: [[@LINE+2]]:7 c:objc(cs)ObjCClass1{{$}}
159+
// CHECK: [[@LINE+2]]:7 c:@M@swift_ide_test@objc(cs)ObjCClass1{{$}}
160160
@objc
161161
class ObjCClass1 {
162-
// CHECK: [[@LINE+1]]:7 c:objc(cs)ObjCClass1(py)instanceVar{{$}}
162+
// CHECK: [[@LINE+1]]:7 c:@M@swift_ide_test@objc(cs)ObjCClass1(py)instanceVar{{$}}
163163
var instanceVar: Int = 1
164-
// CHECK: [[@LINE+1]]:14 c:objc(cs)ObjCClass1(cpy)typeVar{{$}}
164+
// CHECK: [[@LINE+1]]:14 c:@M@swift_ide_test@objc(cs)ObjCClass1(cpy)typeVar{{$}}
165165
static var typeVar: Int = 1
166166

167-
// CHECK: [[@LINE+1]]:7 c:objc(cs)ObjCClass1(py)computed{{$}}
167+
// CHECK: [[@LINE+1]]:7 c:@M@swift_ide_test@objc(cs)ObjCClass1(py)computed{{$}}
168168
var computed: Int {
169-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(im)computed{{$}}
169+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)computed{{$}}
170170
get { return 1}
171-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(im)setComputed:{{$}}
171+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)setComputed:{{$}}
172172
set {}
173173
}
174174

175-
// CHECK: [[@LINE+1]]:13 c:objc(cs)ObjCClass1(cpy)typeComputed{{$}}
175+
// CHECK: [[@LINE+1]]:13 c:@M@swift_ide_test@objc(cs)ObjCClass1(cpy)typeComputed{{$}}
176176
class var typeComputed: Int {
177-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(cm)typeComputed{{$}}
177+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(cm)typeComputed{{$}}
178178
get { return 1 }
179-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(cm)setTypeComputed:{{$}}
179+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(cm)setTypeComputed:{{$}}
180180
set {}
181181
}
182182

183-
// CHECK: [[@LINE+1]]:3 c:objc(cs)ObjCClass1(im)initWithX:{{$}}
183+
// CHECK: [[@LINE+1]]:3 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)initWithX:{{$}}
184184
init(x: Int) {}
185-
// CHECK: [[@LINE+1]]:3 c:objc(cs)ObjCClass1(im)init{{$}}
185+
// CHECK: [[@LINE+1]]:3 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)init{{$}}
186186
init() {}
187187

188-
// CHECK: [[@LINE+1]]:8 c:objc(cs)ObjCClass1(im)instanceFunc1:{{$}}
188+
// CHECK: [[@LINE+1]]:8 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)instanceFunc1:{{$}}
189189
func instanceFunc1(_ a: Int) {
190190

191191
// CHECK: [[@LINE+1]]:16 s:14swift_ide_test10ObjCClass1C13instanceFunc1ySiF9LocalEnumL_O
@@ -194,28 +194,28 @@ class ObjCClass1 {
194194
case someCase
195195
}
196196
}
197-
// CHECK: [[@LINE+1]]:14 c:objc(cs)ObjCClass1(cm)staticFunc1:{{$}}
197+
// CHECK: [[@LINE+1]]:14 c:@M@swift_ide_test@objc(cs)ObjCClass1(cm)staticFunc1:{{$}}
198198
class func staticFunc1(_ a: Int) {}
199199

200200
// CHECK: [[@LINE+2]]:10 s:14swift_ide_test10ObjCClass1C9subscriptS2ici{{$}}
201201
// CHECK: [[@LINE+1]]:20 s:14swift_ide_test10ObjCClass1C1xL_Siv{{$}}
202202
public subscript(x: Int) -> Int {
203203

204-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(im)objectAtIndexedSubscript:{{$}}
204+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)objectAtIndexedSubscript:{{$}}
205205
get { return 1 }
206206

207-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(im)setObject:atIndexedSubscript:{{$}}
207+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)setObject:atIndexedSubscript:{{$}}
208208
set {}
209209
}
210210

211211
// CHECK: [[@LINE+2]]:10 s:14swift_ide_test10ObjCClass1C9subscriptSiACci{{$}}
212212
// CHECK: [[@LINE+1]]:20 s:14swift_ide_test10ObjCClass1C1xL_ACv{{$}}
213213
public subscript(x: ObjCClass1) -> Int {
214214

215-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(im)objectForKeyedSubscript:{{$}}
215+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)objectForKeyedSubscript:{{$}}
216216
get { return 1 }
217217

218-
// CHECK: [[@LINE+1]]:5 c:objc(cs)ObjCClass1(im)setObject:forKeyedSubscript:{{$}}
218+
// CHECK: [[@LINE+1]]:5 c:@M@swift_ide_test@objc(cs)ObjCClass1(im)setObject:forKeyedSubscript:{{$}}
219219
set {}
220220
}
221221

@@ -224,22 +224,22 @@ class ObjCClass1 {
224224
@objc class Nested {}
225225
}
226226

227-
// CHECK: [[@LINE+1]]:23 c:objc(pl)ObjCProto{{$}}
227+
// CHECK: [[@LINE+1]]:23 c:@M@swift_ide_test@objc(pl)ObjCProto{{$}}
228228
@objc public protocol ObjCProto {
229229

230-
// CHECK: [[@LINE+1]]:8 c:objc(pl)ObjCProto(im)protoMeth{{$}}
230+
// CHECK: [[@LINE+1]]:8 c:@M@swift_ide_test@objc(pl)ObjCProto(im)protoMeth{{$}}
231231
func protoMeth()
232232
}
233233

234-
// CHECK: [[@LINE+1]]:12 c:@E@ObjCEnum{{$}}
234+
// CHECK: [[@LINE+1]]:12 c:@M@swift_ide_test@E@ObjCEnum{{$}}
235235
@objc enum ObjCEnum : Int {
236236

237-
// CHECK: [[@LINE+1]]:8 c:@E@ObjCEnum@ObjCEnumAmazingCase{{$}}
237+
// CHECK: [[@LINE+1]]:8 c:@M@swift_ide_test@E@ObjCEnum@ObjCEnumAmazingCase{{$}}
238238
case amazingCase
239239
}
240240

241241
extension ObjCClass1 {
242-
// CHECK: [[@LINE+1]]:15 c:objc(cs)ObjCClass1(im)objcExtMethodWithX:{{$}}
242+
// CHECK: [[@LINE+1]]:15 c:@CM@swift_ide_test@objc(cs)ObjCClass1(im)objcExtMethodWithX:{{$}}
243243
public func objcExtMethod(x: Int) {}
244244
}
245245

test/Index/Inputs/cross_language.m

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// The USR check definitions are in cross_language.swift.
2+
3+
#include "objc_header.h"
4+
5+
@interface MyCls1(ext_in_objc)
6+
// CHECK: [[@LINE-1]]:12 | class/Swift | MyCls1 | [[MyCls1_USR]] |
7+
// CHECK: [[@LINE-2]]:19 | extension/ObjC | ext_in_objc | c:@M@cross_language@objc(cy)MyCls1@ext_in_objc |
8+
-(void)someMethFromObjC;
9+
// CHECK: [[@LINE-1]]:8 | instance-method/ObjC | someMethFromObjC | [[someMethFromObjC_USR:.*]] | -[ext_in_objc someMethFromObjC]
10+
@end
11+
12+
void test1() {
13+
MyCls1 *o = [[MyCls1 alloc] init];
14+
// CHECK: [[@LINE-1]]:3 | class/Swift | MyCls1 | [[MyCls1_USR]] |
15+
// CHECK: [[@LINE-2]]:31 | instance-method/Swift | init | c:@M@cross_language@objc(cs)MyCls1(im)init |
16+
// CHECK: [[@LINE-3]]:17 | class/Swift | MyCls1 | [[MyCls1_USR]] |
17+
[o someMeth];
18+
// CHECK: [[@LINE-1]]:6 | instance-method/Swift | someMeth | [[MyCls1_someMeth_USR]] |
19+
[o someExtMeth];
20+
// CHECK: [[@LINE-1]]:6 | instance-method/Swift | someExtMeth | [[MyCls1_someExtMeth_USR]] |
21+
[o someMethFromObjC];
22+
// CHECK: [[@LINE-1]]:6 | instance-method/ObjC | someMethFromObjC | [[someMethFromObjC_USR]] |
23+
24+
MyCls2 *o2 = [[MyCls2 alloc] initWithInt:0];
25+
// CHECK: [[@LINE-1]]:32 | instance-method/Swift | initWithInt: | [[MyCls2_initwithInt_USR]] |
26+
27+
SomeObjCClass *oo;
28+
// CHECK: [[@LINE-1]]:3 | class/ObjC | SomeObjCClass | [[SomeObjCClass_USR]] |
29+
[oo someSwiftExtMeth];
30+
// CHECK: [[@LINE-1]]:7 | instance-method/Swift | someSwiftExtMeth | [[SomeObjCClass_someSwiftExtMeth_USR]] |
31+
32+
id<MyProt> p;
33+
// CHECK: [[@LINE-1]]:6 | protocol/Swift | MyProt | [[MyProt_USR]] |
34+
[p someProtMeth];
35+
// CHECK: [[@LINE-1]]:6 | instance-method/Swift | someProtMeth | [[MyProt_someProtMeth_USR]] |
36+
37+
MyEnum myenm = MyEnumSomeEnumConst;
38+
// CHECK: [[@LINE-1]]:3 | enum/Swift | MyEnum | [[MyEnum_USR]] |
39+
// CHECK: [[@LINE-2]]:18 | enumerator/Swift | MyEnumSomeEnumConst | [[MyEnum_someEnumConst_USR]] |
40+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@interface SomeObjCClass
2+
@end

test/Index/cross_language.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// REQUIRES: objc_interop
2+
3+
// RUN: mkdir -p %t
4+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -module-name cross_language -import-objc-header %S/Inputs/cross_language_bridge_head.h -D SWIFT_CODE -print-indexed-symbols -source-filename %s > %t.idx.out
5+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -module-name cross_language -import-objc-header %S/Inputs/cross_language_bridge_head.h -D SWIFT_CODE -emit-objc-header-path %t/objc_header.h
6+
7+
// This concatenates the swift and objc file into one so we can CHECK them together. This ensures that the USRs are in-sync between swift and clang.
8+
9+
// RUN: cat %s > %t/combined.m
10+
// RUN: cat %S/Inputs/cross_language.m >> %t/combined.m
11+
// RUN: c-index-test core -print-source-symbols -- %t/combined.m -I %t -isysroot %S/../Inputs/clang-importer-sdk >> %t.idx.out
12+
// RUN: %FileCheck %t/combined.m -input-file %t.idx.out
13+
14+
#if SWIFT_CODE
15+
16+
import Foundation
17+
18+
@objc public class MyCls1 : NSObject {
19+
// CHECK: [[@LINE-1]]:20 | class/Swift | MyCls1 | [[MyCls1_USR:.*]] | Def
20+
@objc public func someMeth() {}
21+
// CHECK: [[@LINE-1]]:21 | instance-method/Swift | someMeth() | [[MyCls1_someMeth_USR:.*]] | Def
22+
}
23+
24+
@objc public class MyCls2 : NSObject {
25+
@objc public init(withInt: Int) {}
26+
// CHECK: [[@LINE-1]]:16 | constructor/Swift | init(withInt:) | [[MyCls2_initwithInt_USR:.*]] | Def
27+
}
28+
29+
@objc public protocol MyProt {
30+
// CHECK: [[@LINE-1]]:23 | protocol/Swift | MyProt | [[MyProt_USR:.*]] | Def
31+
func someProtMeth()
32+
// CHECK: [[@LINE-1]]:8 | instance-method/Swift | someProtMeth() | [[MyProt_someProtMeth_USR:.*]] | Def
33+
}
34+
35+
public extension MyCls1 {
36+
// CHECK: [[@LINE-1]]:18 | extension/ext-class/Swift | MyCls1 | s:e:c:@CM@cross_language@objc(cs)MyCls1(im)someExtMeth |
37+
// CHECK: [[@LINE-2]]:18 | class/Swift | MyCls1 | [[MyCls1_USR]] |
38+
@objc public func someExtMeth() {}
39+
// CHECK: [[@LINE-1]]:21 | instance-method/Swift | someExtMeth() | [[MyCls1_someExtMeth_USR:.*]] | Def
40+
}
41+
42+
public extension SomeObjCClass {
43+
// CHECK: [[@LINE-1]]:18 | class/Swift | SomeObjCClass | [[SomeObjCClass_USR:.*]] | Ref
44+
@objc public func someSwiftExtMeth() {}
45+
// CHECK: [[@LINE-1]]:21 | instance-method/Swift | someSwiftExtMeth() | [[SomeObjCClass_someSwiftExtMeth_USR:.*]] | Def
46+
}
47+
48+
@objc public enum MyEnum : Int {
49+
// CHECK: [[@LINE-1]]:19 | enum/Swift | MyEnum | [[MyEnum_USR:.*]] | Def
50+
case someEnumConst = 1
51+
// CHECK: [[@LINE-1]]:8 | enumerator/Swift | someEnumConst | [[MyEnum_someEnumConst_USR:.*]] | Def
52+
}
53+
54+
#endif

0 commit comments

Comments
 (0)