Skip to content

Commit be4e23f

Browse files
committed
New note for users that have wrongly assumed that generic param conformances use the same separators as type conformances.
1 parent aef3464 commit be4e23f

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ ERROR(could_not_find_enum_case,none,
9494

9595
NOTE(did_you_mean_raw_type,none,
9696
"did you mean to specify a raw type on the enum declaration?", ())
97+
98+
NOTE(did_you_mean_generic_param_as_conformance,none,
99+
"did you mean to declare %0 as a protocol conformance for %1?", (DeclName, Type))
97100

98101
NOTE(any_as_anyobject_fixit, none,
99102
"cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members", ())

lib/Sema/CSDiagnostics.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "swift/AST/SourceFile.h"
3232
#include "swift/AST/Stmt.h"
3333
#include "swift/AST/Types.h"
34+
#include "swift/AST/GenericEnvironment.h"
35+
#include "swift/AST/NameLookupRequests.h"
3436
#include "swift/Basic/SourceLoc.h"
3537
#include "swift/Parse/Lexer.h"
3638
#include "llvm/ADT/ArrayRef.h"
@@ -3109,6 +3111,53 @@ DeclName MissingMemberFailure::findCorrectEnumCaseName(
31093111
return (candidate ? candidate->getName() : DeclName());
31103112
}
31113113

3114+
bool MissingMemberFailure::findUnintendedExtraGenericParam(Type instanceTy, FunctionRefKind functionKind) {
3115+
auto archetype = instanceTy->getAs<ArchetypeType>();
3116+
if (!archetype)
3117+
return false;
3118+
auto genericTy = archetype->mapTypeOutOfContext()->getAs<GenericTypeParamType>();
3119+
if (!genericTy)
3120+
return false;
3121+
3122+
for (auto param : archetype->getGenericEnvironment()->getGenericParams()) {
3123+
// Find a param at the same depth and one index past the type we're dealing with
3124+
if (param->getDepth() != genericTy->getDepth() || param->getIndex() != genericTy->getIndex() + 1)
3125+
continue;
3126+
3127+
auto &cs = getConstraintSystem();
3128+
auto paramDecl = param->getDecl();
3129+
auto descriptor = UnqualifiedLookupDescriptor(
3130+
DeclNameRef(param->getName()), paramDecl->getDeclContext()->getParentForLookup(), paramDecl->getStartLoc(),
3131+
UnqualifiedLookupFlags::KnownPrivate | UnqualifiedLookupFlags::TypeLookup);
3132+
auto lookup = evaluateOrDefault(cs.getASTContext().evaluator, UnqualifiedLookupRequest{descriptor}, {});
3133+
for (auto &result : lookup) {
3134+
if (auto proto = dyn_cast_or_null<ProtocolDecl>(result.getValueDecl())) {
3135+
auto memberLookup = cs.performMemberLookup(
3136+
ConstraintKind::ValueMember, getName().withoutArgumentLabels(),
3137+
proto->getDeclaredType(), functionKind, getLocator(),
3138+
/*includeInaccessibleMembers=*/true);
3139+
if (memberLookup.ViableCandidates.size() || memberLookup.UnviableCandidates.size()) {
3140+
SourceLoc loc = genericTy->getDecl()->getSourceRange().End;
3141+
StringRef replacement;
3142+
3143+
if (archetype->getConformsTo().size()) {
3144+
loc = loc.getAdvancedLoc(archetype->getConformsTo().back()->getName().getLength());
3145+
replacement = " &";
3146+
} else {
3147+
loc = loc.getAdvancedLoc(archetype->getName().getLength());
3148+
replacement = ":";
3149+
}
3150+
emitDiagnosticAt(loc, diag::did_you_mean_generic_param_as_conformance, paramDecl->getName(), archetype)
3151+
.fixItReplaceChars(loc, loc.getAdvancedLoc(1), replacement);
3152+
return true;
3153+
}
3154+
}
3155+
}
3156+
break;
3157+
}
3158+
return false;
3159+
}
3160+
31123161
bool MissingMemberFailure::diagnoseAsError() {
31133162
auto anchor = getRawAnchor();
31143163
auto memberBase = getAnchor();
@@ -3217,6 +3266,7 @@ bool MissingMemberFailure::diagnoseAsError() {
32173266
}
32183267
} else {
32193268
emitBasicError(baseType);
3269+
findUnintendedExtraGenericParam(instanceTy, FunctionRefKind::DoubleApply);
32203270
}
32213271
} else if (auto moduleTy = baseType->getAs<ModuleType>()) {
32223272
emitDiagnosticAt(::getLoc(memberBase), diag::no_member_of_module,
@@ -3278,6 +3328,7 @@ bool MissingMemberFailure::diagnoseAsError() {
32783328
correction->addFixits(diagnostic);
32793329
} else {
32803330
emitBasicError(baseType);
3331+
findUnintendedExtraGenericParam(baseType, FunctionRefKind::SingleApply);
32813332
}
32823333
}
32833334
}

lib/Sema/CSDiagnostics.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,8 @@ class MissingMemberFailure final : public InvalidMemberRefFailure {
10661066
static DeclName findCorrectEnumCaseName(Type Ty,
10671067
TypoCorrectionResults &corrections,
10681068
DeclNameRef memberName);
1069+
1070+
bool findUnintendedExtraGenericParam(Type instanceTy, FunctionRefKind functionKind);
10691071
};
10701072

10711073
/// Diagnose cases where a member only accessible on generic constraints
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol Proto {
4+
init?(string: String)
5+
func foo()
6+
}
7+
8+
struct TypedFoo<T: Hashable, Proto> {
9+
// expected-note@-1 {{did you mean to declare 'Proto' as a protocol conformance for 'T'?}} {{28-29= &}}
10+
func foo(value: String) {
11+
if let str = T.init(string:value) { // expected-error {{type 'T' has no member 'init'}}
12+
print(str)
13+
}
14+
}
15+
}
16+
17+
func bar<T, Proto>(arg: T) { // expected-error {{generic parameter 'Proto' is not used in function signature}}
18+
// expected-note@-1 {{did you mean to declare 'Proto' as a protocol conformance for 'T'?}} {{11-12=:}}
19+
arg.foo() // expected-error {{value of type 'T' has no member 'foo'}}
20+
}
21+
22+
func baz<T: Hashable, Proto>(arg: T) { // expected-error {{generic parameter 'Proto' is not used in function signature}}
23+
arg.ugh() // expected-error {{value of type 'T' has no member 'ugh'}}
24+
}

0 commit comments

Comments
 (0)