Skip to content

Commit f1905e8

Browse files
committed
[QoI] Improve diagnostics for type member names shadowing top-level functions
Consider expression with an implicit 'self.' reference: extension Sequence { func test() -> Int { return max(1, 2) } } We currently shadow names that appear in nested scopes, so the top-level two-argument min()/max() functions are shadowed by the versions of min()/max() in Sequence (which don’t take the same number of arguments). This patch aims to improve situation on this front and produce better diagnostics when that happens, hinting the user about what went wrong and where matching function is declared. Resolves <rdar://problem/25341015>.
1 parent e41a29a commit f1905e8

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,11 @@ ERROR(argument_out_of_order_unnamed_unnamed,none,
836836
"unnamed argument #%0 must precede unnamed argument #%1",
837837
(unsigned, unsigned))
838838

839+
ERROR(member_shadows_global_function,none,
840+
"use of %0 refers to %1 %2 rather than %3 %4 in %5 %6",
841+
(DeclName, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, DeclName,
842+
DescriptiveDeclKind, DeclName))
843+
839844
ERROR(instance_member_use_on_type,none,
840845
"use of instance member %1 on type %0; "
841846
"did you mean to use a value of type %0 instead?", (Type, DeclName))

lib/Sema/CSDiag.cpp

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4581,6 +4581,186 @@ typeCheckArgumentChildIndependently(Expr *argExpr, Type argType,
45814581
TE->isImplicit(), TT);
45824582
}
45834583

4584+
static bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr,
4585+
CalleeCandidateInfo &CCI,
4586+
ArrayRef<Identifier> argLabels,
4587+
ConstraintSystem *CS) {
4588+
// If candidate list is empty it means that problem is somewhere else,
4589+
// since we need to have candidates which might be shadowing other funcs.
4590+
if (CCI.empty() || !CCI[0].getDecl())
4591+
return false;
4592+
4593+
auto &TC = CS->TC;
4594+
// Call expression is formed as 'foo.bar' where 'foo' might be an
4595+
// implicit "Self" reference, such use wouldn't provide good diagnostics
4596+
// for situations where instance members have equal names to functions in
4597+
// Swift Standard Library e.g. min/max.
4598+
auto UDE = dyn_cast<UnresolvedDotExpr>(fnExpr);
4599+
if (!UDE)
4600+
return false;
4601+
4602+
auto baseExpr = dyn_cast<DeclRefExpr>(UDE->getBase());
4603+
if (!baseExpr)
4604+
return false;
4605+
4606+
auto baseDecl = baseExpr->getDecl();
4607+
if (!baseExpr->isImplicit() || baseDecl->getName() != TC.Context.Id_self)
4608+
return false;
4609+
4610+
// Our base expression is an implicit 'self.' reference e.g.
4611+
//
4612+
// extension Sequence {
4613+
// func test() -> Int {
4614+
// return max(1, 2)
4615+
// }
4616+
// }
4617+
//
4618+
// In this example the Sequence class already has two methods named 'max'
4619+
// none of which accept two arguments, but there is a function in
4620+
// Swift Standard Library called 'max' which does accept two arguments,
4621+
// so user might have called that by mistake without realizing that
4622+
// compiler would add implicit 'self.' prefix to the call of 'max'.
4623+
ExprCleaner cleanup(argExpr);
4624+
4625+
// Let's type check argument expression without any contextual information.
4626+
ConcreteDeclRef ref = nullptr;
4627+
auto typeResult = TC.getTypeOfExpressionWithoutApplying(argExpr, CS->DC, ref);
4628+
if (!typeResult.hasValue())
4629+
return false;
4630+
4631+
auto argType = typeResult.getValue();
4632+
4633+
// If argument type couldn't be properly resolved or has errors,
4634+
// we can't diagnose anything in here, it points to the different problem.
4635+
if (isUnresolvedOrTypeVarType(argType) || argType->hasError())
4636+
return false;
4637+
4638+
auto context = CS->DC;
4639+
using CandididateMap =
4640+
llvm::SmallDenseMap<ValueDecl *, llvm::SmallVector<OverloadChoice, 2>>;
4641+
4642+
auto getBaseKind = [](ValueDecl *base) -> DescriptiveDeclKind {
4643+
DescriptiveDeclKind kind = DescriptiveDeclKind::Module;
4644+
if (!base)
4645+
return kind;
4646+
4647+
auto context = base->getDeclContext();
4648+
do {
4649+
if (isa<ExtensionDecl>(context))
4650+
return DescriptiveDeclKind::Extension;
4651+
4652+
if (auto nominal = dyn_cast<NominalTypeDecl>(context)) {
4653+
kind = nominal->getDescriptiveKind();
4654+
break;
4655+
}
4656+
4657+
context = context->getParent();
4658+
} while (context);
4659+
4660+
return kind;
4661+
};
4662+
4663+
auto getBaseName = [](DeclContext *context) -> DeclName {
4664+
if (context->isTypeContext()) {
4665+
auto generic = context->getAsGenericTypeOrGenericTypeExtensionContext();
4666+
return generic->getName();
4667+
} else if (context->isModuleScopeContext())
4668+
return context->getParentModule()->getName();
4669+
else
4670+
llvm_unreachable("Unsupported base");
4671+
};
4672+
4673+
auto diagnoseShadowing = [&](ValueDecl *base,
4674+
ArrayRef<OverloadChoice> candidates) -> bool {
4675+
CalleeCandidateInfo calleeInfo(base ? base->getInterfaceType() : nullptr,
4676+
candidates, CCI.hasTrailingClosure, CS,
4677+
base);
4678+
4679+
calleeInfo.filterList(argType, argLabels);
4680+
if (calleeInfo.closeness != CC_ExactMatch)
4681+
return false;
4682+
4683+
auto choice = calleeInfo.candidates[0].getDecl();
4684+
auto baseKind = getBaseKind(base);
4685+
auto baseName = getBaseName(choice->getDeclContext());
4686+
4687+
auto origCandidate = CCI[0].getDecl();
4688+
TC.diagnose(UDE->getLoc(), diag::member_shadows_global_function,
4689+
UDE->getName(), origCandidate->getDescriptiveKind(),
4690+
origCandidate->getFullName(), choice->getDescriptiveKind(),
4691+
choice->getFullName(), baseKind, baseName);
4692+
4693+
auto topLevelDiag = diag::fix_unqualified_access_top_level;
4694+
if (baseKind == DescriptiveDeclKind::Module)
4695+
topLevelDiag = diag::fix_unqualified_access_top_level_multi;
4696+
4697+
auto name = baseName.getBaseName();
4698+
SmallString<32> namePlusDot = name.str();
4699+
namePlusDot.push_back('.');
4700+
4701+
TC.diagnose(UDE->getLoc(), topLevelDiag, namePlusDot,
4702+
choice->getDescriptiveKind(), name)
4703+
.fixItInsert(UDE->getStartLoc(), namePlusDot);
4704+
4705+
for (auto &candidate : calleeInfo.candidates) {
4706+
if (auto decl = candidate.getDecl())
4707+
TC.diagnose(decl, diag::decl_declared_here, decl->getFullName());
4708+
}
4709+
4710+
return true;
4711+
};
4712+
4713+
// For each of the parent contexts, let's try to find any candidates
4714+
// which have the same name and the same number of arguments as callee.
4715+
while (context->getParent()) {
4716+
auto result = TC.lookupUnqualified(context, UDE->getName(), UDE->getLoc());
4717+
context = context->getParent();
4718+
4719+
if (!result || result.empty())
4720+
continue;
4721+
4722+
CandididateMap candidates;
4723+
for (const auto &candidate : result) {
4724+
auto base = candidate.Base;
4725+
if ((base && base->isInvalid()) || candidate->isInvalid())
4726+
continue;
4727+
4728+
// If base is present but it doesn't represent a valid nominal,
4729+
// we can't use current candidate as one of the choices.
4730+
if (base && !base->getInterfaceType()->getNominalOrBoundGenericNominal())
4731+
continue;
4732+
4733+
auto context = candidate->getDeclContext();
4734+
// We are only interested in static or global functions, because
4735+
// there is no way to call anything else properly.
4736+
if (!candidate->isStatic() && !context->isModuleScopeContext())
4737+
continue;
4738+
4739+
OverloadChoice choice(base ? base->getInterfaceType() : nullptr,
4740+
candidate, false, UDE->getFunctionRefKind());
4741+
4742+
if (base) { // Let's group all of the candidates have a common base.
4743+
candidates[base].push_back(choice);
4744+
continue;
4745+
}
4746+
4747+
// If there is no base, it means this is one of the global functions,
4748+
// let's try to diagnose its shadowing inline.
4749+
if (diagnoseShadowing(base, choice))
4750+
return true;
4751+
}
4752+
4753+
if (candidates.empty())
4754+
continue;
4755+
4756+
for (const auto &candidate : candidates) {
4757+
if (diagnoseShadowing(candidate.getFirst(), candidate.getSecond()))
4758+
return true;
4759+
}
4760+
}
4761+
4762+
return false;
4763+
}
45844764

45854765
/// Emit a class of diagnostics that we only know how to generate when there is
45864766
/// exactly one candidate we know about. Return true if an error is emitted.
@@ -4984,6 +5164,10 @@ bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI,
49845164
}
49855165
}
49865166

5167+
// Try to diagnose errors related to the use of implicit self reference.
5168+
if (diagnoseImplicitSelfErrors(fnExpr, argExpr, CCI, argLabels, CS))
5169+
return true;
5170+
49875171
// Do all the stuff that we only have implemented when there is a single
49885172
// candidate.
49895173
if (diagnoseSingleCandidateFailures(CCI, fnExpr, argExpr, argLabels))

test/Constraints/members.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,3 +590,43 @@ do {
590590
throw SR_2193_Error.Boom
591591
} catch let e as SR_2193_Error.Boom { // expected-error {{enum element 'Boom' is not a member type of 'SR_2193_Error'}}
592592
}
593+
594+
// rdar://problem/25341015
595+
extension Sequence {
596+
func r25341015_1() -> Int {
597+
return max(1, 2) // expected-error {{use of 'max' refers to instance method 'max(by:)' rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}}
598+
}
599+
}
600+
601+
class C_25341015 {
602+
static func baz(_ x: Int, _ y: Int) {} // expected-note {{'baz' declared here}}
603+
func baz() {}
604+
func qux() {
605+
baz(1, 2) // expected-error {{use of 'baz' refers to instance method 'baz()' rather than static method 'baz' in class 'C_25341015'}} expected-note {{use 'C_25341015.' to reference the static method}}
606+
}
607+
}
608+
609+
struct S_25341015 {
610+
static func foo(_ x: Int, y: Int) {} // expected-note {{'foo(_:y:)' declared here}}
611+
612+
func foo(z: Int) {}
613+
func bar() {
614+
foo(1, y: 2) // expected-error {{use of 'foo' refers to instance method 'foo(z:)' rather than static method 'foo(_:y:)' in struct 'S_25341015'}} expected-note {{use 'S_25341015.' to reference the static method}}
615+
}
616+
}
617+
618+
func r25341015() {
619+
func baz(_ x: Int, _ y: Int) {}
620+
class Bar {
621+
func baz() {}
622+
func qux() {
623+
baz(1, 2) // expected-error {{argument passed to call that takes no arguments}}
624+
}
625+
}
626+
}
627+
628+
func r25341015_local(x: Int, y: Int) {}
629+
func r25341015_inner() {
630+
func r25341015_local() {}
631+
r25341015_local(x: 1, y: 2) // expected-error {{argument passed to call that takes no arguments}}
632+
}

0 commit comments

Comments
 (0)