Skip to content

Commit 6a510c1

Browse files
authored
Merge pull request swiftlang#33410 from CodaFi/extensional-angst
Implement Richer Diagnostics for Cross-File Synthesis Failures
2 parents ad4aba8 + 3a3f92a commit 6a510c1

File tree

10 files changed

+71
-15
lines changed

10 files changed

+71
-15
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,8 +2765,9 @@ ERROR(cannot_synthesize_init_in_extension_of_nonfinal,none,
27652765
"be satisfied by a 'required' initializer in the class definition",
27662766
(Type, DeclName))
27672767
ERROR(cannot_synthesize_in_crossfile_extension,none,
2768-
"implementation of %0 cannot be automatically synthesized in an extension "
2769-
"in a different file to the type", (Type))
2768+
"extension outside of file declaring %0 %1 prevents automatic synthesis "
2769+
"of %2 for protocol %3",
2770+
(DescriptiveDeclKind, DeclName, DeclName, Type))
27702771

27712772
ERROR(broken_additive_arithmetic_requirement,none,
27722773
"AdditiveArithmetic protocol is broken: unexpected requirement", ())

lib/Sema/DerivedConformances.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "TypeChecker.h"
14+
#include "swift/AST/ASTPrinter.h"
1415
#include "swift/AST/Decl.h"
1516
#include "swift/AST/Stmt.h"
1617
#include "swift/AST/Expr.h"
@@ -490,8 +491,27 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext(
490491
Nominal->getModuleScopeContext() !=
491492
getConformanceContext()->getModuleScopeContext()) {
492493
ConformanceDecl->diagnose(diag::cannot_synthesize_in_crossfile_extension,
494+
Nominal->getDescriptiveKind(), Nominal->getName(),
495+
synthesizing->getName(),
493496
getProtocolType());
494497
Nominal->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
498+
499+
// In editor mode, try to insert a stub.
500+
if (Context.LangOpts.DiagnosticsEditorMode) {
501+
auto Extension = cast<ExtensionDecl>(getConformanceContext());
502+
auto FixitLocation = Extension->getBraces().Start;
503+
llvm::SmallString<128> Text;
504+
{
505+
llvm::raw_svector_ostream SS(Text);
506+
swift::printRequirementStub(synthesizing, Nominal,
507+
Nominal->getDeclaredType(),
508+
Extension->getStartLoc(), SS);
509+
if (!Text.empty()) {
510+
ConformanceDecl->diagnose(diag::missing_witnesses_general)
511+
.fixItInsertAfter(FixitLocation, Text.str());
512+
}
513+
}
514+
}
495515
return true;
496516
}
497517

localization/diagnostics/en.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6556,8 +6556,8 @@
65566556
65576557
- id: cannot_synthesize_in_crossfile_extension
65586558
msg: >-
6559-
implementation of %0 cannot be automatically synthesized in an extension
6560-
in a different file to the type
6559+
extension outside of file declaring %0 %1 prevents automatic synthesis
6560+
of %2 for protocol %3
65616561
65626562
- id: broken_additive_arithmetic_requirement
65636563
msg: >-

test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,10 @@ class WrappedProperties: Differentiable {
570570

571571
// Test derived conformances in disallowed contexts.
572572

573-
// expected-error @+1 2 {{implementation of 'Differentiable' cannot be automatically synthesized in an extension in a different file to the type}}
574573
extension OtherFileNonconforming: Differentiable {}
574+
// expected-error @-1 {{extension outside of file declaring class 'OtherFileNonconforming' prevents automatic synthesis of 'move(along:)' for protocol 'Differentiable'}}
575+
// expected-error @-2 {{extension outside of file declaring class 'OtherFileNonconforming' prevents automatic synthesis of 'zeroTangentVectorInitializer' for protocol 'Differentiable'}}
575576

576-
// expected-error @+1 2 {{implementation of 'Differentiable' cannot be automatically synthesized in an extension in a different file to the type}}
577577
extension GenericOtherFileNonconforming: Differentiable {}
578+
// expected-error @-1 {{extension outside of file declaring generic class 'GenericOtherFileNonconforming' prevents automatic synthesis of 'move(along:)' for protocol 'Differentiable'}}
579+
// expected-error @-2 {{extension outside of file declaring generic class 'GenericOtherFileNonconforming' prevents automatic synthesis of 'zeroTangentVectorInitializer' for protocol 'Differentiable'}}

test/AutoDiff/Sema/DerivedConformances/struct_additive_arithmetic.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,12 @@ where T: AdditiveArithmetic {}
113113

114114
// Test derived conformances in disallowed contexts.
115115

116-
// expected-error @+1 3 {{implementation of 'AdditiveArithmetic' cannot be automatically synthesized in an extension in a different file to the type}}
117116
extension OtherFileNonconforming: AdditiveArithmetic {}
117+
// expected-error @-1 {{extension outside of file declaring struct 'OtherFileNonconforming' prevents automatic synthesis of 'zero' for protocol 'AdditiveArithmetic'}}
118+
// expected-error @-2 {{extension outside of file declaring struct 'OtherFileNonconforming' prevents automatic synthesis of '+' for protocol 'AdditiveArithmetic'}}
119+
// expected-error @-3 {{extension outside of file declaring struct 'OtherFileNonconforming' prevents automatic synthesis of '-' for protocol 'AdditiveArithmetic'}}
118120

119-
// expected-error @+1 3 {{implementation of 'AdditiveArithmetic' cannot be automatically synthesized in an extension in a different file to the type}}
120121
extension GenericOtherFileNonconforming: AdditiveArithmetic {}
122+
// expected-error @-1 {{extension outside of file declaring generic struct 'GenericOtherFileNonconforming' prevents automatic synthesis of 'zero' for protocol 'AdditiveArithmetic'}}
123+
// expected-error @-2 {{extension outside of file declaring generic struct 'GenericOtherFileNonconforming' prevents automatic synthesis of '+' for protocol 'AdditiveArithmetic'}}
124+
// expected-error @-3 {{extension outside of file declaring generic struct 'GenericOtherFileNonconforming' prevents automatic synthesis of '-' for protocol 'AdditiveArithmetic'}}

test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,10 @@ struct WrappedProperties: Differentiable {
383383

384384
// Verify that cross-file derived conformances are disallowed.
385385

386-
// expected-error @+1 2 {{implementation of 'Differentiable' cannot be automatically synthesized in an extension in a different file to the type}}
387386
extension OtherFileNonconforming: Differentiable {}
387+
// expected-error @-1 {{extension outside of file declaring struct 'OtherFileNonconforming' prevents automatic synthesis of 'move(along:)' for protocol 'Differentiable'}}
388+
// expected-error @-2 {{extension outside of file declaring struct 'OtherFileNonconforming' prevents automatic synthesis of 'zeroTangentVectorInitializer' for protocol 'Differentiable'}}
388389

389-
// expected-error @+1 2 {{implementation of 'Differentiable' cannot be automatically synthesized in an extension in a different file to the type}}
390390
extension GenericOtherFileNonconforming: Differentiable {}
391+
// expected-error @-1 {{extension outside of file declaring generic struct 'GenericOtherFileNonconforming' prevents automatic synthesis of 'move(along:)' for protocol 'Differentiable'}}
392+
// expected-error @-2 {{extension outside of file declaring generic struct 'GenericOtherFileNonconforming' prevents automatic synthesis of 'zeroTangentVectorInitializer' for protocol 'Differentiable'}}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
public enum Enum { case one } // expected-note {{type declared here}}
2+
public enum GenericEnum<T> { case one(Int) } // expected-note {{type declared here}}
3+
4+
public struct Struct {} // expected-note {{type declared here}}
5+
public struct GenericStruct<T> {} // expected-note {{type declared here}}
6+

test/Sema/enum_conformance_synthesis.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ enum Complex2 {
224224
}
225225
extension Complex2 : Hashable {}
226226
extension Complex2 : CaseIterable {} // expected-error {{type 'Complex2' does not conform to protocol 'CaseIterable'}}
227-
extension FromOtherFile: CaseIterable {} // expected-error {{cannot be automatically synthesized in an extension in a different file to the type}}
227+
extension FromOtherFile: CaseIterable {} // expected-error {{extension outside of file declaring enum 'FromOtherFile' prevents automatic synthesis of 'allCases' for protocol 'CaseIterable'}}
228228
extension CaseIterableAcrossFiles: CaseIterable {
229229
public static var allCases: [CaseIterableAcrossFiles] {
230230
return [ .A ]
@@ -248,7 +248,7 @@ extension OtherFileNonconforming: Hashable {
248248
func hash(into hasher: inout Hasher) {}
249249
}
250250
// ...but synthesis in a type defined in another file doesn't work yet.
251-
extension YetOtherFileNonconforming: Equatable {} // expected-error {{cannot be automatically synthesized in an extension in a different file to the type}}
251+
extension YetOtherFileNonconforming: Equatable {} // expected-error {{extension outside of file declaring enum 'YetOtherFileNonconforming' prevents automatic synthesis of '==' for protocol 'Equatable'}}
252252
extension YetOtherFileNonconforming: CaseIterable {} // expected-error {{does not conform}}
253253

254254
// Verify that an indirect enum doesn't emit any errors as long as its "leaves"
@@ -319,7 +319,7 @@ extension UnusedGenericDeriveExtension: Hashable {}
319319
// Cross-file synthesis is disallowed for conditional cases just as it is for
320320
// non-conditional ones.
321321
extension GenericOtherFileNonconforming: Equatable where T: Equatable {}
322-
// expected-error@-1{{implementation of 'Equatable' cannot be automatically synthesized in an extension in a different file to the type}}
322+
// expected-error@-1{{extension outside of file declaring generic enum 'GenericOtherFileNonconforming' prevents automatic synthesis of '==' for protocol 'Equatable'}}
323323

324324
// rdar://problem/41852654
325325

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -emit-library -module-name Types %S/Inputs/fixits-derived-conformances-multifile.swift -o %t/%target-library-name(Types)
3+
// RUN: %swift -typecheck -target %target-triple -I %t -diagnostics-editor-mode -verify %s
4+
5+
import Types
6+
7+
extension GenericEnum: Equatable { }
8+
// expected-error@-1 {{extension outside of file declaring generic enum 'GenericEnum' prevents automatic synthesis of '==' for protocol 'Equatable'}}
9+
// expected-note@-2 {{do you want to add protocol stubs?}}{{35-35=\n public static func == (lhs: GenericEnum, rhs: GenericEnum) -> Bool {\n <#code#>\n \}\n}}
10+
11+
extension Struct: Equatable { }
12+
// expected-error@-1 {{extension outside of file declaring struct 'Struct' prevents automatic synthesis of '==' for protocol 'Equatable'}}
13+
// expected-note@-2 {{do you want to add protocol stubs?}}{{30-30=\n public static func == (lhs: Struct, rhs: Struct) -> Bool {\n <#code#>\n \}\n}}
14+
extension GenericStruct: Equatable { }
15+
// expected-error@-1 {{extension outside of file declaring generic struct 'GenericStruct' prevents automatic synthesis of '==' for protocol 'Equatable'}}
16+
// expected-note@-2 {{do you want to add protocol stubs?}}{{37-37=\n public static func == (lhs: GenericStruct, rhs: GenericStruct) -> Bool {\n <#code#>\n \}\n}}
17+
18+
extension Enum: CaseIterable { }
19+
// expected-error@-1 {{extension outside of file declaring enum 'Enum' prevents automatic synthesis of 'allCases' for protocol 'CaseIterable'}}
20+
// expected-note@-2 {{do you want to add protocol stubs?}}{{31-31=\n public static var allCases: [Enum]\n}}
21+

test/Sema/struct_equatable_hashable.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ extension OtherFileNonconforming: Hashable {
196196
func hash(into hasher: inout Hasher) {}
197197
}
198198
// ...but synthesis in a type defined in another file doesn't work yet.
199-
extension YetOtherFileNonconforming: Equatable {} // expected-error {{cannot be automatically synthesized in an extension in a different file to the type}}
199+
extension YetOtherFileNonconforming: Equatable {} // expected-error {{extension outside of file declaring struct 'YetOtherFileNonconforming' prevents automatic synthesis of '==' for protocol 'Equatable'}}
200200

201201
// Verify that we can add Hashable conformance in an extension by only
202202
// implementing hash(into:)
@@ -253,7 +253,7 @@ extension UnusedGenericDeriveExtension: Hashable {}
253253

254254
// Cross-file synthesis is still disallowed for conditional cases
255255
extension GenericOtherFileNonconforming: Equatable where T: Equatable {}
256-
// expected-error@-1{{implementation of 'Equatable' cannot be automatically synthesized in an extension in a different file to the type}}
256+
// expected-error@-1{{extension outside of file declaring generic struct 'GenericOtherFileNonconforming' prevents automatic synthesis of '==' for protocol 'Equatable'}}
257257

258258
// rdar://problem/41852654
259259

0 commit comments

Comments
 (0)