Skip to content

Commit 7f959fd

Browse files
committed
[Typechecker] Emit fix-its for witness mismatches
1 parent 6b9133b commit 7f959fd

File tree

2 files changed

+114
-39
lines changed

2 files changed

+114
-39
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,9 +2072,14 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
20722072
(unsigned)match.MissingRequirement->getKind());
20732073
break;
20742074

2075-
case MatchKind::ThrowsConflict:
2076-
diags.diagnose(match.Witness, diag::protocol_witness_throws_conflict);
2075+
case MatchKind::ThrowsConflict: {
2076+
auto witness = match.Witness;
2077+
auto diag = diags.diagnose(witness, diag::protocol_witness_throws_conflict);
2078+
assert(isa<FuncDecl>(witness));
2079+
auto FD = cast<FuncDecl>(witness);
2080+
diag.fixItRemove(FD->getThrowsLoc());
20772081
break;
2082+
}
20782083

20792084
case MatchKind::OptionalityConflict: {
20802085
auto &adjustments = match.OptionalAdjustments;
@@ -2089,53 +2094,95 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
20892094
break;
20902095
}
20912096

2092-
case MatchKind::StaticNonStaticConflict:
2093-
// FIXME: Could emit a Fix-It here.
2094-
diags.diagnose(match.Witness, diag::protocol_witness_static_conflict,
2095-
!req->isInstanceMember());
2097+
case MatchKind::StaticNonStaticConflict: {
2098+
auto witness = match.Witness;
2099+
auto diag = diags.diagnose(witness, diag::protocol_witness_static_conflict,
2100+
!req->isInstanceMember());
2101+
if (req->isInstanceMember()) {
2102+
assert(isa<FuncDecl>(witness) || isa<VarDecl>(witness));
2103+
auto loc = isa<FuncDecl>(witness)
2104+
? cast<FuncDecl>(witness)->getStaticLoc()
2105+
: cast<VarDecl>(witness)
2106+
->getParentPatternBinding()
2107+
->getStaticLoc();
2108+
diag.fixItRemove(loc);
2109+
} else {
2110+
diag.fixItInsert(witness->getAttributeInsertionLoc(true),
2111+
getTokenText(tok::kw_static));
2112+
}
20962113
break;
2097-
2098-
case MatchKind::SettableConflict:
2099-
diags.diagnose(match.Witness, diag::protocol_witness_settable_conflict);
2114+
}
2115+
2116+
case MatchKind::SettableConflict: {
2117+
auto witness = match.Witness;
2118+
auto diag =
2119+
diags.diagnose(witness, diag::protocol_witness_settable_conflict);
2120+
auto PBD = cast<VarDecl>(witness)->getParentPatternBinding();
2121+
diag.fixItReplace(PBD->getStartLoc(), getTokenText(tok::kw_var));
21002122
break;
2123+
}
21012124

2102-
case MatchKind::PrefixNonPrefixConflict:
2103-
// FIXME: Could emit a Fix-It here.
2104-
diags.diagnose(match.Witness,
2105-
diag::protocol_witness_prefix_postfix_conflict, false,
2106-
match.Witness->getAttrs().hasAttribute<PostfixAttr>() ? 2
2107-
: 0);
2125+
case MatchKind::PrefixNonPrefixConflict: {
2126+
auto witness = match.Witness;
2127+
auto diag = diags.diagnose(
2128+
witness, diag::protocol_witness_prefix_postfix_conflict, false,
2129+
witness->getAttrs().hasAttribute<PostfixAttr>() ? 2 : 0);
2130+
if (witness->getAttrs().hasAttribute<PostfixAttr>()) {
2131+
auto attr = witness->getAttrs().getAttribute<PostfixAttr>();
2132+
diag.fixItReplace(attr->getLocation(), "prefix");
2133+
}
21082134
break;
2135+
}
2136+
2137+
case MatchKind::PostfixNonPostfixConflict: {
2138+
auto witness = match.Witness;
2139+
auto diag = diags.diagnose(
2140+
witness, diag::protocol_witness_prefix_postfix_conflict, true,
2141+
witness->getAttrs().hasAttribute<PrefixAttr>() ? 1 : 0);
2142+
if (witness->getAttrs().hasAttribute<PrefixAttr>()) {
2143+
auto attr = witness->getAttrs().getAttribute<PrefixAttr>();
2144+
diag.fixItReplace(attr->getLocation(), "postfix");
2145+
}
21092146

2110-
case MatchKind::PostfixNonPostfixConflict:
2111-
// FIXME: Could emit a Fix-It here.
2112-
diags.diagnose(match.Witness,
2113-
diag::protocol_witness_prefix_postfix_conflict, true,
2114-
match.Witness->getAttrs().hasAttribute<PrefixAttr>() ? 1
2115-
: 0);
21162147
break;
2117-
case MatchKind::MutatingConflict:
2118-
// FIXME: Could emit a Fix-It here.
2119-
diags.diagnose(match.Witness,
2120-
diag::protocol_witness_mutation_modifier_conflict,
2121-
SelfAccessKind::Mutating);
2148+
}
2149+
case MatchKind::MutatingConflict: {
2150+
auto witness = match.Witness;
2151+
auto diag = diags.diagnose(
2152+
witness, diag::protocol_witness_mutation_modifier_conflict,
2153+
SelfAccessKind::Mutating);
2154+
auto attr = witness->getAttrs().getAttribute<MutatingAttr>();
2155+
diag.fixItRemove(attr->getLocation());
2156+
21222157
break;
2123-
case MatchKind::NonMutatingConflict:
2124-
// FIXME: Could emit a Fix-It here.
2125-
diags.diagnose(match.Witness,
2126-
diag::protocol_witness_mutation_modifier_conflict,
2127-
SelfAccessKind::NonMutating);
2158+
}
2159+
case MatchKind::NonMutatingConflict: {
2160+
auto witness = match.Witness;
2161+
auto diag = diags.diagnose(
2162+
witness, diag::protocol_witness_mutation_modifier_conflict,
2163+
SelfAccessKind::NonMutating);
2164+
auto attr = witness->getAttrs().getAttribute<NonMutatingAttr>();
2165+
diag.fixItRemove(attr->getLocation());
21282166
break;
2129-
case MatchKind::ConsumingConflict:
2130-
// FIXME: Could emit a Fix-It here.
2131-
diags.diagnose(match.Witness,
2132-
diag::protocol_witness_mutation_modifier_conflict,
2133-
SelfAccessKind::Consuming);
2167+
}
2168+
case MatchKind::ConsumingConflict: {
2169+
auto witness = match.Witness;
2170+
auto diag = diags.diagnose(
2171+
witness, diag::protocol_witness_mutation_modifier_conflict,
2172+
SelfAccessKind::Consuming);
2173+
auto attr = witness->getAttrs().getAttribute<ConsumingAttr>();
2174+
diag.fixItRemove(attr->getLocation());
21342175
break;
2135-
case MatchKind::RethrowsConflict:
2136-
// FIXME: Could emit a Fix-It here.
2137-
diags.diagnose(match.Witness, diag::protocol_witness_rethrows_conflict);
2176+
}
2177+
case MatchKind::RethrowsConflict: {
2178+
auto witness = match.Witness;
2179+
auto diag =
2180+
diags.diagnose(match.Witness, diag::protocol_witness_rethrows_conflict);
2181+
assert(isa<FuncDecl>(witness));
2182+
auto FD = cast<FuncDecl>(witness);
2183+
diag.fixItReplace(FD->getThrowsLoc(), getTokenText(tok::kw_rethrows));
21382184
break;
2185+
}
21392186
case MatchKind::NonObjC:
21402187
diags.diagnose(match.Witness, diag::protocol_witness_not_objc);
21412188
break;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
prefix operator ^^^
4+
postfix operator ^^^^
5+
6+
protocol Foo {
7+
var bar1: Int { get set } // expected-note {{protocol requires property 'bar1' with type 'Int'; do you want to add a stub?}}
8+
static var bar2: Int { get set } // expected-note {{protocol requires property 'bar2' with type 'Int'; do you want to add a stub?}}
9+
var bar3: Int { get set } // expected-note {{protocol requires property 'bar3' with type 'Int'; do you want to add a stub?}}
10+
static prefix func ^^^(value: Self) -> Int // expected-note {{protocol requires function '^^^' with type '(ConformsToFoo) -> Int'; do you want to add a stub?}}
11+
static postfix func ^^^^(value: Self) -> Int // expected-note {{protocol requires function '^^^^' with type '(ConformsToFoo) -> Int'; do you want to add a stub?}}
12+
func bar4() // expected-note {{protocol requires function 'bar4()' with type '() -> ()'; do you want to add a stub?}}
13+
func bar5(closure: () throws -> Int) rethrows // expected-note {{protocol requires function 'bar5(closure:)' with type '(() throws -> Int) throws -> ()'; do you want to add a stub?}}
14+
func bar6() // expected-note {{protocol requires function 'bar6()' with type '() -> ()'; do you want to add a stub?}}
15+
}
16+
17+
struct ConformsToFoo: Foo { // expected-error {{type 'ConformsToFoo' does not conform to protocol 'Foo'}}
18+
let bar1: Int // expected-note {{candidate is not settable, but protocol requires it}}{{3-6=var}}
19+
var bar2: Int // expected-note {{candidate operates on an instance, not a type as required}}{{3-3=static}}
20+
static var bar3: Int = 1 // expected-note {{candidate operates on a type, not an instance as required}}{{3-10=}}
21+
static postfix func ^^^(value: ConformsToFoo) -> Int { return 0 } // expected-error {{operator implementation without matching operator declaration}}
22+
// expected-note@-1 {{candidate is postfix, not prefix as required}}{{10-17=prefix}}
23+
static prefix func ^^^^(value: ConformsToFoo) -> Int { return 0 } // expected-error {{operator implementation without matching operator declaration}}
24+
// expected-note@-1 {{candidate is prefix, not postfix as required}}{{10-16=postfix}}
25+
mutating func bar4() {} // expected-note {{candidate is marked 'mutating' but protocol does not allow it}}{{3-12=}}
26+
func bar5(closure: () throws -> Int) throws {} // expected-note {{candidate is not 'rethrows', but protocol requires it}}{{40-46=rethrows}}
27+
func bar6() throws {} // expected-note {{candidate throws, but protocol does not allow it}}{{15-22=}}
28+
}

0 commit comments

Comments
 (0)