Skip to content

Commit 1f67146

Browse files
authored
[Diagnostics] Emit fix-its for 'did you mean to override init(from:)/encode(to:)' diagnostics (swiftlang#29259)
1 parent 09bfd94 commit 1f67146

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,45 @@ static Optional<std::string> buildDefaultInitializerString(DeclContext *dc,
932932
llvm_unreachable("Unhandled PatternKind in switch.");
933933
}
934934

935+
/// Create a fix-it string for the 'decodable_suggest_overriding_init_here' and
936+
/// optionally, the 'codable_suggest_overriding_init_here' diagnostics.
937+
static std::string getFixItStringForDecodable(ClassDecl *CD,
938+
bool includeEncodeTo) {
939+
auto &ctx = CD->getASTContext();
940+
SourceLoc indentationLoc = CD->getBraces().End;
941+
StringRef extraIndentation;
942+
StringRef indentation = Lexer::getIndentationForLine(
943+
ctx.SourceMgr, indentationLoc, &extraIndentation);
944+
std::string fixItStringToReturn;
945+
{
946+
llvm::raw_string_ostream out(fixItStringToReturn);
947+
ExtraIndentStreamPrinter printer(out, indentation);
948+
949+
printer.printNewline();
950+
printer << "override init(from decoder: Decoder) throws";
951+
952+
// Add a dummy body.
953+
auto printDummyBody = [&]() {
954+
printer << " {";
955+
printer.printNewline();
956+
printer << extraIndentation << getCodePlaceholder();
957+
printer.printNewline();
958+
printer << "}";
959+
};
960+
961+
printDummyBody();
962+
963+
if (includeEncodeTo) {
964+
printer.printNewline();
965+
printer.printNewline();
966+
printer << "override func encode(to encoder: Encoder) throws";
967+
printDummyBody();
968+
}
969+
}
970+
971+
return fixItStringToReturn;
972+
}
973+
935974
/// Diagnose a class that does not have any initializers.
936975
static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) {
937976
ASTContext &C = classDecl->getASTContext();
@@ -950,7 +989,6 @@ static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) {
950989
// It is helpful to suggest here that the user may have forgotten to override
951990
// init(from:) (and encode(to:), if applicable) in a note, before we start
952991
// listing the members that prevented initializer synthesis.
953-
// TODO: Add a fixit along with this suggestion.
954992
if (auto *superclassDecl = classDecl->getSuperclassDecl()) {
955993
auto *decodableProto = C.getProtocol(KnownProtocolKind::Decodable);
956994
auto superclassType = superclassDecl->getDeclaredInterfaceType();
@@ -975,6 +1013,7 @@ static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) {
9751013
diagDest = result.front().getValueDecl();
9761014

9771015
auto diagName = diag::decodable_suggest_overriding_init_here;
1016+
auto shouldEmitFixItForEncodeTo = false;
9781017

9791018
// This is also a bit of a hack, but the best place we've got at the
9801019
// moment to suggest this.
@@ -992,11 +1031,18 @@ static void diagnoseClassWithoutInitializers(ClassDecl *classDecl) {
9921031
// The direct lookup here won't see an encode(to:) if it is inherited
9931032
// from the superclass.
9941033
auto encodeTo = DeclName(C, C.Id_encode, C.Id_to);
995-
if (classDecl->lookupDirect(encodeTo).empty())
1034+
if (classDecl->lookupDirect(encodeTo).empty()) {
9961035
diagName = diag::codable_suggest_overriding_init_here;
1036+
shouldEmitFixItForEncodeTo = true;
1037+
}
9971038
}
9981039

999-
C.Diags.diagnose(diagDest, diagName);
1040+
auto insertionLoc =
1041+
Lexer::getLocForEndOfLine(C.SourceMgr, classDecl->getBraces().Start);
1042+
auto fixItString =
1043+
getFixItStringForDecodable(classDecl, shouldEmitFixItForEncodeTo);
1044+
C.Diags.diagnose(diagDest, diagName)
1045+
.fixItInsert(insertionLoc, fixItString);
10001046
}
10011047
}
10021048

test/decl/protocol/special/coding/class_codable_inheritance_diagnostics.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class DecodableSuper : Decodable {
6262
}
6363

6464
class DecodableSubWithoutInitialValue : DecodableSuper { // expected-error {{class 'DecodableSubWithoutInitialValue' has no initializers}}
65-
// expected-note@-1 {{did you mean to override 'init(from:)'?}}
65+
// expected-note@-1 {{did you mean to override 'init(from:)'?}}{{1-1=\noverride init(from decoder: Decoder) throws {\n <#code#>\n\}}}
6666
var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}}
6767
}
6868

@@ -77,14 +77,14 @@ class CodableSuper : Codable {
7777
}
7878

7979
class CodableSubWithoutInitialValue : CodableSuper { // expected-error {{class 'CodableSubWithoutInitialValue' has no initializers}}
80-
// expected-note@-1 {{did you mean to override 'init(from:)' and 'encode(to:)'?}}
80+
// expected-note@-1 {{did you mean to override 'init(from:)' and 'encode(to:)'?}}{{1-1=\noverride init(from decoder: Decoder) throws {\n <#code#>\n\}\n\noverride func encode(to encoder: Encoder) throws {\n <#code#>\n\}}}
8181
var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}}
8282
}
8383

8484
// We should only mention encode(to:) in the diagnostic if the subclass does not
8585
// override it.
8686
class EncodableSubWithoutInitialValue : CodableSuper { // expected-error {{class 'EncodableSubWithoutInitialValue' has no initializers}}
87-
// expected-note@-1 {{did you mean to override 'init(from:)'?}}
87+
// expected-note@-1 {{did you mean to override 'init(from:)'?}}{{1-1=\noverride init(from decoder: Decoder) throws {\n <#code#>\n\}}}
8888
var value2: Int // expected-note {{stored property 'value2' without initial value prevents synthesized initializers}}
8989

9090
override func encode(to: Encoder) throws {}

0 commit comments

Comments
 (0)