Skip to content

Commit 79b0a7e

Browse files
authored
[CSDiagnostics] Emit fix-its to insert requirement stubs for missing protocols in context (#33628)
* [CSDiagnostics] Emit fix-its to insert requirement stubs in editor mode for missing protocols in context * [CSDiagnostics] Only include missing requirements in stub fix-it for missing protocols in context The conforming type may already have declarations that could satisfy a requirement, so we shouldn't include it in the fix-it
1 parent 48a7bc9 commit 79b0a7e

File tree

3 files changed

+87
-10
lines changed

3 files changed

+87
-10
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -465,21 +465,22 @@ class NormalProtocolConformance : public RootProtocolConformance,
465465
uint64_t LoaderContextData;
466466
friend class ASTContext;
467467

468+
void resolveLazyInfo() const;
469+
470+
void differenceAndStoreConditionalRequirements() const;
471+
472+
public:
468473
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
469474
SourceLoc loc, DeclContext *dc,
470475
ProtocolConformanceState state)
471-
: RootProtocolConformance(ProtocolConformanceKind::Normal, conformingType),
472-
ProtocolAndState(protocol, state), Loc(loc), ContextAndInvalid(dc, false)
473-
{
476+
: RootProtocolConformance(ProtocolConformanceKind::Normal,
477+
conformingType),
478+
ProtocolAndState(protocol, state), Loc(loc),
479+
ContextAndInvalid(dc, false) {
474480
assert(!conformingType->hasArchetype() &&
475481
"ProtocolConformances should store interface types");
476482
}
477483

478-
void resolveLazyInfo() const;
479-
480-
void differenceAndStoreConditionalRequirements() const;
481-
482-
public:
483484
/// Get the protocol being conformed to.
484485
ProtocolDecl *getProtocol() const { return ProtocolAndState.getPointer(); }
485486

lib/Sema/CSDiagnostics.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#include "CSDiagnostics.h"
1818
#include "ConstraintSystem.h"
1919
#include "MiscDiagnostics.h"
20+
#include "TypeCheckProtocol.h"
2021
#include "TypoCorrection.h"
2122
#include "swift/AST/ASTContext.h"
23+
#include "swift/AST/ASTPrinter.h"
2224
#include "swift/AST/Decl.h"
2325
#include "swift/AST/ExistentialLayout.h"
2426
#include "swift/AST/Expr.h"
@@ -2768,9 +2770,11 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
27682770

27692771
// Let's build a list of protocols that the context does not conform to.
27702772
SmallVector<std::string, 8> missingProtoTypeStrings;
2773+
SmallVector<ProtocolDecl *, 8> missingProtocols;
27712774
for (auto protocol : layout.getProtocols()) {
27722775
if (!TypeChecker::conformsToProtocol(fromType, protocol->getDecl(), getDC())) {
27732776
missingProtoTypeStrings.push_back(protocol->getString());
2777+
missingProtocols.push_back(protocol->getDecl());
27742778
}
27752779
}
27762780

@@ -2792,8 +2796,6 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
27922796

27932797
// Emit a diagnostic to inform the user that they need to conform to the
27942798
// missing protocols.
2795-
//
2796-
// TODO: Maybe also insert the requirement stubs?
27972799
auto conformanceDiag =
27982800
emitDiagnostic(diag::assign_protocol_conformance_fix_it, unwrappedToType,
27992801
nominal->getDescriptiveKind(), fromType);
@@ -2808,6 +2810,35 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
28082810
conformanceDiag.fixItInsert(nameEndLoc, ": " + protoString);
28092811
}
28102812

2813+
// Emit fix-its to insert requirement stubs if we're in editor mode.
2814+
if (!getASTContext().LangOpts.DiagnosticsEditorMode) {
2815+
return true;
2816+
}
2817+
2818+
{
2819+
llvm::SmallString<128> Text;
2820+
llvm::raw_svector_ostream SS(Text);
2821+
llvm::SetVector<ValueDecl *> missingWitnesses;
2822+
for (auto protocol : missingProtocols) {
2823+
auto conformance = NormalProtocolConformance(
2824+
nominal->getDeclaredType(), protocol, SourceLoc(), nominal,
2825+
ProtocolConformanceState::Incomplete);
2826+
ConformanceChecker checker(getASTContext(), &conformance,
2827+
missingWitnesses);
2828+
checker.resolveValueWitnesses();
2829+
checker.resolveTypeWitnesses();
2830+
}
2831+
2832+
for (auto decl : missingWitnesses) {
2833+
swift::printRequirementStub(decl, nominal, nominal->getDeclaredType(),
2834+
nominal->getStartLoc(), SS);
2835+
}
2836+
2837+
if (!Text.empty()) {
2838+
conformanceDiag.fixItInsertAfter(nominal->getBraces().Start, Text.str());
2839+
}
2840+
}
2841+
28112842
return true;
28122843
}
28132844

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-swift-frontend -typecheck -diagnostics-editor-mode -verify %s
2+
3+
// Test that we emit fix-its to insert requirement stubs for the missing protocol conformance, in addition to adding the conformance.
4+
5+
protocol P {
6+
func method()
7+
var property: Int { get }
8+
}
9+
10+
class C {
11+
var p: P?
12+
13+
func assign() {
14+
p = self
15+
// expected-error@-1 {{cannot assign value of type 'C' to type 'P?'}}
16+
// expected-note@-2 {{add missing conformance to 'P' to class 'C'}} {{8-8=: P}} {{10-10=\n func method() {\n <#code#>\n \}\n\n var property: Int\n}}
17+
}
18+
}
19+
20+
// Test that we don't emit fix-it to insert a requirement stub if there is already a satisfying witness.
21+
22+
class C1 {
23+
var p: P?
24+
25+
func assign() {
26+
p = self
27+
// expected-error@-1 {{cannot assign value of type 'C1' to type 'P?'}}
28+
// expected-note@-2 {{add missing conformance to 'P' to class 'C1'}} {{9-9=: P}} {{11-11=\n var property: Int\n}}
29+
}
30+
31+
func method() {}
32+
}
33+
34+
class C2 {
35+
var p: P?
36+
37+
func assign() {
38+
p = self
39+
// expected-error@-1 {{cannot assign value of type 'C2' to type 'P?'}}
40+
// expected-note@-2 {{add missing conformance to 'P' to class 'C2'}} {{9-9=: P}}
41+
}
42+
43+
func method() {}
44+
var property: Int = 0
45+
}

0 commit comments

Comments
 (0)