Skip to content

Commit bf0bcfa

Browse files
committed
[IDE] Only consider synthesized extensions when in the same module
Only declarations in the same module as synthesized extension's target are placed within a synthesized extension. We should thus not add "::SYNTHESIZED::" to the USR if the given declaration is in a different and. As an example, `Foundation` adds a method `components(separatedBy:)` to `String` through an extension on `StringProtocol`. But since it is within `Foundation` and not `Swift` it will *not* be in a synthesized extension of `String` or `StringProtocol`. So it should not have "::SYNTHESIZED::" added and should also not being in the `String` group. Resolves rdar://71355632.
1 parent b1b6cae commit bf0bcfa

File tree

4 files changed

+102
-29
lines changed

4 files changed

+102
-29
lines changed

lib/AST/RawComment.cpp

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -197,50 +197,34 @@ RawComment Decl::getRawComment(bool SerializedOK) const {
197197
llvm_unreachable("invalid file kind");
198198
}
199199

200-
static const Decl* getGroupDecl(const Decl *D) {
201-
auto GroupD = D;
202-
203-
// Extensions always exist in the same group with the nominal.
204-
if (auto ED = dyn_cast_or_null<ExtensionDecl>(D->getDeclContext()->
205-
getInnermostTypeContext())) {
206-
if (auto ExtNominal = ED->getExtendedNominal())
207-
GroupD = ExtNominal;
208-
}
209-
return GroupD;
210-
}
211-
212200
Optional<StringRef> Decl::getGroupName() const {
213201
if (hasClangNode())
214202
return None;
215-
if (auto GroupD = getGroupDecl(this)) {
203+
if (auto *Unit =
204+
dyn_cast<FileUnit>(getDeclContext()->getModuleScopeContext())) {
216205
// We can only get group information from deserialized module files.
217-
if (auto *Unit =
218-
dyn_cast<FileUnit>(GroupD->getDeclContext()->getModuleScopeContext())) {
219-
return Unit->getGroupNameForDecl(GroupD);
220-
}
206+
return Unit->getGroupNameForDecl(this);
221207
}
222208
return None;
223209
}
224210

225211
Optional<StringRef> Decl::getSourceFileName() const {
226212
if (hasClangNode())
227213
return None;
228-
if (auto GroupD = getGroupDecl(this)) {
214+
if (auto *Unit =
215+
dyn_cast<FileUnit>(getDeclContext()->getModuleScopeContext())) {
229216
// We can only get group information from deserialized module files.
230-
if (auto *Unit =
231-
dyn_cast<FileUnit>(GroupD->getDeclContext()->getModuleScopeContext())) {
232-
return Unit->getSourceFileNameForDecl(GroupD);
233-
}
217+
return Unit->getSourceFileNameForDecl(this);
234218
}
235219
return None;
236220
}
237221

238222
Optional<unsigned> Decl::getSourceOrder() const {
239223
if (hasClangNode())
240224
return None;
241-
// We can only get source orders from deserialized module files.
242225
if (auto *Unit =
243226
dyn_cast<FileUnit>(this->getDeclContext()->getModuleScopeContext())) {
227+
// We can only get source orders from deserialized module files.
244228
return Unit->getSourceOrderForDecl(this);
245229
}
246230
return None;

lib/IDE/IDETypeChecking.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -538,12 +538,16 @@ SynthesizedExtensionAnalyzer::SynthesizedExtensionAnalyzer(
538538

539539
SynthesizedExtensionAnalyzer::~SynthesizedExtensionAnalyzer() {delete &Impl;}
540540

541-
bool SynthesizedExtensionAnalyzer::
542-
isInSynthesizedExtension(const ValueDecl *VD) {
541+
bool SynthesizedExtensionAnalyzer::isInSynthesizedExtension(
542+
const ValueDecl *VD) {
543543
if (auto Ext = dyn_cast_or_null<ExtensionDecl>(VD->getDeclContext()->
544544
getInnermostTypeContext())) {
545-
return Impl.InfoMap.count(Ext) != 0 &&
546-
Impl.InfoMap.find(Ext)->second.IsSynthesized;
545+
auto It = Impl.InfoMap.find(Ext);
546+
if (It != Impl.InfoMap.end() && It->second.IsSynthesized) {
547+
// A synthesized extension will only be created if the underlying type
548+
// is in the same module
549+
return VD->getModuleContext() == Impl.Target->getModuleContext();
550+
}
547551
}
548552
return false;
549553
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: mkdir -p %t/mods
3+
// RUN: split-file --leading-lines %s %t
4+
5+
// RUN: %target-swift-frontend -module-name Foo -emit-module -emit-module-path %t/mods/Foo.swiftmodule -emit-module-doc -emit-module-doc-path %t/mods/Foo.swiftdoc -group-info-path %t/group.json %t/Foo.swift
6+
// RUN: %target-swift-frontend -module-name Bar -emit-module -emit-module-path %t/mods/Bar.swiftmodule -emit-module-doc -emit-module-doc-path %t/mods/Bar.swiftdoc -I%t/mods %t/Bar.swift
7+
8+
//--- group.json
9+
{
10+
"TestGroup": [
11+
"Foo.swift",
12+
]
13+
}
14+
15+
//--- Foo.swift
16+
public protocol FooProto {
17+
associatedtype T
18+
}
19+
20+
public extension FooProto {
21+
func fooExt() {}
22+
}
23+
24+
public extension FooProto {
25+
func fooExt2() {}
26+
}
27+
28+
public extension FooProto where T == Int {
29+
func fooIntExt() {}
30+
}
31+
32+
public struct FooStruct: FooProto {
33+
public typealias T = Int
34+
public func foo() {}
35+
}
36+
37+
//--- Bar.swift
38+
import Foo
39+
40+
public extension FooProto {
41+
func barExt() {}
42+
}
43+
44+
public extension FooProto where T == Int {
45+
func barIntExt() {}
46+
}
47+
48+
//--- Baz.swift
49+
import Foo
50+
import Bar
51+
52+
// The generated interface for Foo will contain all the extensions as well
53+
// as the "synthesized extension" on FooStruct itself, ie. the extension
54+
// functions are added to FooStruct as if they were written there in the source.
55+
//
56+
// We prefer jumping to these declarations rather than the one in the extension,
57+
// but also have to be careful not to attempt to do so for extensions outside
58+
// of the original module - these will *not* have "synthesized extensions"
59+
// in their generated interface.
60+
func test(f: FooStruct) {
61+
// RUN: %sourcekitd-test -req=cursor -pos=%(line+1):5 %t/Baz.swift -- %t/Baz.swift -I %t/mods -target %target-triple | %FileCheck --check-prefix=CHECK-EXT %t/Baz.swift
62+
f.fooExt()
63+
// CHECK-EXT: 3Foo0A5ProtoPAAE6fooExtyyF::SYNTHESIZED::s:3Foo0A6StructV
64+
// CHECK-EXT: <Group>TestGroup</Group>
65+
66+
// RUN: %sourcekitd-test -req=cursor -pos=%(line+1):5 %t/Baz.swift -- %t/Baz.swift -I %t/mods -target %target-triple | %FileCheck --check-prefix=CHECK-EXT2 %t/Baz.swift
67+
f.fooExt2()
68+
// CHECK-EXT2: s:3Foo0A5ProtoPAAE7fooExt2yyF::SYNTHESIZED::s:3Foo0A6StructV
69+
// CHECK-EXT2: <Group>TestGroup</Group>
70+
71+
// RUN: %sourcekitd-test -req=cursor -pos=%(line+1):5 %t/Baz.swift -- %t/Baz.swift -I %t/mods -target %target-triple | %FileCheck --check-prefix=CHECK-INTEXT %t/Baz.swift
72+
f.fooIntExt()
73+
// CHECK-INTEXT: s:3Foo0A5ProtoPAASi1TRtzrlE9fooIntExtyyF::SYNTHESIZED::s:3Foo0A6StructV
74+
// CHECK-INTEXT: <Group>TestGroup</Group>
75+
76+
// RUN: %sourcekitd-test -req=cursor -pos=%(line+1):5 %t/Baz.swift -- %t/Baz.swift -I %t/mods -target %target-triple | %FileCheck --check-prefix=CHECK-BAREXT %t/Baz.swift
77+
f.barExt()
78+
// CHECK-BAREXT: s:3Foo0A5ProtoP3BarE6barExtyyF
79+
// CHECK-BAREXT-NOT: <Group>TestGroup</Group>
80+
81+
// RUN: %sourcekitd-test -req=cursor -pos=%(line+1):5 %t/Baz.swift -- %t/Baz.swift -I %t/mods -target %target-triple | %FileCheck --check-prefix=CHECK-BARINTEXT %t/Baz.swift
82+
f.barIntExt()
83+
// CHECK-BARINTEXT: s:3Foo0A5ProtoP3BarSi1TRtzrlE9barIntExtyyF
84+
// CHECK-BARINTEXT-NOT: <Group>TestGroup</Group>
85+
}

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ struct DeclInfo {
785785
const ValueDecl *OriginalProperty = nullptr;
786786
bool Unavailable = true;
787787
Type BaseType;
788+
/// Whether the \c VD is in a synthesized extension of \c BaseType
788789
bool InSynthesizedExtension = false;
789790

790791
DeclInfo(const ValueDecl *VD, Type ContainerType, bool IsRef, bool IsDynamic,
@@ -810,9 +811,8 @@ struct DeclInfo {
810811
}
811812

812813
BaseType = findBaseTypeForReplacingArchetype(VD, ContainerType);
813-
InSynthesizedExtension = false;
814814
if (BaseType) {
815-
if (auto Target = BaseType->getAnyNominal()) {
815+
if (auto *Target = BaseType->getAnyNominal()) {
816816
SynthesizedExtensionAnalyzer Analyzer(
817817
Target, PrintOptions::printModuleInterface(
818818
Invoc.getFrontendOptions().PrintFullConvention));

0 commit comments

Comments
 (0)