Skip to content

Commit f7fd83d

Browse files
authored
Merge pull request swiftlang#32220 from gregomni/unintended_generic_param
[QoI] Detect unintended generic params and provide a note and fixit.
2 parents acb16cf + c73b144 commit f7fd83d

File tree

7 files changed

+162
-2
lines changed

7 files changed

+162
-2
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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3335,6 +3335,30 @@ bool MissingMemberFailure::diagnoseInLiteralCollectionContext() const {
33353335
return false;
33363336
}
33373337

3338+
bool UnintendedExtraGenericParamMemberFailure::diagnoseAsError() {
3339+
MissingMemberFailure::diagnoseAsError();
3340+
3341+
auto baseType = resolveType(getBaseType())->getWithoutSpecifierType();
3342+
auto archetype = baseType->getMetatypeInstanceType()->castTo<ArchetypeType>();
3343+
auto genericTy =
3344+
archetype->mapTypeOutOfContext()->castTo<GenericTypeParamType>();
3345+
SourceLoc loc = genericTy->getDecl()->getSourceRange().End;
3346+
StringRef replacement;
3347+
3348+
if (archetype->getConformsTo().size()) {
3349+
loc = loc.getAdvancedLoc(
3350+
archetype->getConformsTo().back()->getName().getLength());
3351+
replacement = " &";
3352+
} else {
3353+
loc = loc.getAdvancedLoc(archetype->getName().getLength());
3354+
replacement = ":";
3355+
}
3356+
emitDiagnosticAt(loc, diag::did_you_mean_generic_param_as_conformance,
3357+
ParamName, archetype)
3358+
.fixItReplaceChars(loc, loc.getAdvancedLoc(1), replacement);
3359+
return true;
3360+
}
3361+
33383362
bool InvalidMemberRefOnExistential::diagnoseAsError() {
33393363
auto anchor = getRawAnchor();
33403364

lib/Sema/CSDiagnostics.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,7 @@ class InvalidMemberRefFailure : public FailureDiagnostic {
10391039
/// let _: Int = s.foo(1, 2) // expected type is `(Int, Int) -> Int`
10401040
/// }
10411041
/// ```
1042-
class MissingMemberFailure final : public InvalidMemberRefFailure {
1042+
class MissingMemberFailure : public InvalidMemberRefFailure {
10431043
public:
10441044
MissingMemberFailure(const Solution &solution, Type baseType,
10451045
DeclNameRef memberName, ConstraintLocator *locator)
@@ -1068,6 +1068,22 @@ class MissingMemberFailure final : public InvalidMemberRefFailure {
10681068
DeclNameRef memberName);
10691069
};
10701070

1071+
class UnintendedExtraGenericParamMemberFailure final
1072+
: public MissingMemberFailure {
1073+
Identifier ParamName;
1074+
1075+
public:
1076+
UnintendedExtraGenericParamMemberFailure(const Solution &solution,
1077+
Type baseType,
1078+
DeclNameRef memberName,
1079+
Identifier paramName,
1080+
ConstraintLocator *locator)
1081+
: MissingMemberFailure(solution, baseType, memberName, locator),
1082+
ParamName(paramName) {}
1083+
1084+
bool diagnoseAsError() override;
1085+
};
1086+
10711087
/// Diagnose cases where a member only accessible on generic constraints
10721088
/// requiring conformance to a protocol is used on a value of the
10731089
/// existential protocol type e.g.

lib/Sema/CSFix.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,23 @@ DefineMemberBasedOnUse::create(ConstraintSystem &cs, Type baseType,
531531
DefineMemberBasedOnUse(cs, baseType, member, alreadyDiagnosed, locator);
532532
}
533533

534+
bool DefineMemberBasedOnUnintendedGenericParam::diagnose(
535+
const Solution &solution, bool asNote) const {
536+
UnintendedExtraGenericParamMemberFailure failure(solution, BaseType, Name,
537+
ParamName, getLocator());
538+
return failure.diagnose(asNote);
539+
}
540+
541+
DefineMemberBasedOnUnintendedGenericParam *
542+
DefineMemberBasedOnUnintendedGenericParam::create(ConstraintSystem &cs,
543+
Type baseType,
544+
DeclNameRef member,
545+
Identifier paramName,
546+
ConstraintLocator *locator) {
547+
return new (cs.getAllocator()) DefineMemberBasedOnUnintendedGenericParam(
548+
cs, baseType, member, paramName, locator);
549+
}
550+
534551
AllowMemberRefOnExistential *
535552
AllowMemberRefOnExistential::create(ConstraintSystem &cs, Type baseType,
536553
ValueDecl *member, DeclNameRef memberName,

lib/Sema/CSFix.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,33 @@ class DefineMemberBasedOnUse final : public ConstraintFix {
884884
}
885885
};
886886

887+
class DefineMemberBasedOnUnintendedGenericParam final : public ConstraintFix {
888+
Type BaseType;
889+
DeclNameRef Name;
890+
Identifier ParamName;
891+
892+
DefineMemberBasedOnUnintendedGenericParam(ConstraintSystem &cs, Type baseType,
893+
DeclNameRef member,
894+
Identifier paramName,
895+
ConstraintLocator *locator)
896+
: ConstraintFix(cs, FixKind::DefineMemberBasedOnUse, locator),
897+
BaseType(baseType), Name(member), ParamName(paramName) {}
898+
899+
public:
900+
std::string getName() const override {
901+
llvm::SmallVector<char, 16> scratch;
902+
auto memberName = Name.getString(scratch);
903+
return "allow access to invalid member '" + memberName.str() +
904+
"' on archetype presumed intended to conform to protocol";
905+
}
906+
907+
bool diagnose(const Solution &solution, bool asNote = false) const override;
908+
909+
static DefineMemberBasedOnUnintendedGenericParam *
910+
create(ConstraintSystem &cs, Type baseType, DeclNameRef member,
911+
Identifier paramName, ConstraintLocator *locator);
912+
};
913+
887914
class AllowInvalidMemberRef : public ConstraintFix {
888915
Type BaseType;
889916
ValueDecl *Member;

lib/Sema/CSSimplify.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
//
1616
//===----------------------------------------------------------------------===//
1717

18-
#include "CSFix.h"
1918
#include "CSDiagnostics.h"
19+
#include "CSFix.h"
2020
#include "ConstraintSystem.h"
2121
#include "swift/AST/ExistentialLayout.h"
2222
#include "swift/AST/GenericEnvironment.h"
2323
#include "swift/AST/GenericSignature.h"
2424
#include "swift/AST/Initializer.h"
25+
#include "swift/AST/NameLookupRequests.h"
2526
#include "swift/AST/ParameterList.h"
2627
#include "swift/AST/PropertyWrappers.h"
2728
#include "swift/AST/ProtocolConformance.h"
@@ -6932,6 +6933,54 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
69326933
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
69336934
}
69346935

6936+
// If base is an archetype or metatype of archetype, check for an unintended
6937+
// extra generic parameter.
6938+
if (auto archetype =
6939+
baseTy->getMetatypeInstanceType()->getAs<ArchetypeType>()) {
6940+
if (auto genericTy =
6941+
archetype->mapTypeOutOfContext()->getAs<GenericTypeParamType>()) {
6942+
for (auto param :
6943+
archetype->getGenericEnvironment()->getGenericParams()) {
6944+
// Find a param at the same depth and one index past the type we're
6945+
// dealing with
6946+
if (param->getDepth() != genericTy->getDepth() ||
6947+
param->getIndex() != genericTy->getIndex() + 1)
6948+
continue;
6949+
auto paramDecl = param->getDecl();
6950+
if (!paramDecl)
6951+
continue;
6952+
6953+
auto descriptor = UnqualifiedLookupDescriptor(
6954+
DeclNameRef(param->getName()),
6955+
paramDecl->getDeclContext()->getParentForLookup(),
6956+
paramDecl->getStartLoc(),
6957+
UnqualifiedLookupFlags::KnownPrivate |
6958+
UnqualifiedLookupFlags::TypeLookup);
6959+
auto lookup = evaluateOrDefault(
6960+
Context.evaluator, UnqualifiedLookupRequest{descriptor}, {});
6961+
for (auto &result : lookup) {
6962+
if (auto proto =
6963+
dyn_cast_or_null<ProtocolDecl>(result.getValueDecl())) {
6964+
auto result =
6965+
baseTy->is<MetatypeType>()
6966+
? solveWithNewBaseOrName(ExistentialMetatypeType::get(
6967+
proto->getDeclaredType()),
6968+
member)
6969+
: solveWithNewBaseOrName(proto->getDeclaredType(),
6970+
member);
6971+
if (result == SolutionKind::Solved)
6972+
return recordFix(
6973+
DefineMemberBasedOnUnintendedGenericParam::create(
6974+
*this, baseTy, member, param->getName(),
6975+
locator))
6976+
? SolutionKind::Error
6977+
: SolutionKind::Solved;
6978+
}
6979+
}
6980+
}
6981+
}
6982+
}
6983+
69356984
if (auto *funcType = baseTy->getAs<FunctionType>()) {
69366985
// We can't really suggest anything useful unless
69376986
// function takes no arguments, otherwise it
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)