Skip to content

Commit 6791ef3

Browse files
authored
Merge pull request #21756 from xedin/rdar-36989788
[Diagnostics] Diagnose missing members via fixes
2 parents 66189dc + 74a8ee1 commit 6791ef3

20 files changed

+448
-143
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 27 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -523,10 +523,10 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
523523
/// Given a result of name lookup that had no viable results, diagnose the
524524
/// unviable ones.
525525
void diagnoseUnviableLookupResults(MemberLookupResult &lookupResults,
526-
Type baseObjTy, Expr *baseExpr,
526+
Expr *expr, Type baseObjTy, Expr *baseExpr,
527527
DeclName memberName, DeclNameLoc nameLoc,
528528
SourceLoc loc);
529-
529+
530530
/// Produce a diagnostic for a general overload resolution failure
531531
/// (irrespective of the exact expression kind).
532532
bool diagnoseGeneralOverloadFailure(Constraint *constraint);
@@ -913,125 +913,23 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy,
913913
return;
914914
}
915915

916-
/// When a user refers a enum case with a wrong member name, we try to find a enum
917-
/// element whose name differs from the wrong name only in convention; meaning their
918-
/// lower case counterparts are identical.
919-
/// - DeclName is valid when such a correct case is found; invalid otherwise.
920-
static DeclName
921-
findCorrectEnumCaseName(Type Ty, TypoCorrectionResults &corrections,
922-
DeclName memberName) {
923-
if (memberName.isSpecial() || !memberName.isSimpleName())
924-
return DeclName();
925-
if (!Ty->is<EnumType>() &&
926-
!Ty->is<BoundGenericEnumType>())
927-
return DeclName();
928-
auto candidate =
929-
corrections.getUniqueCandidateMatching([&](ValueDecl *candidate) {
930-
return (isa<EnumElementDecl>(candidate) &&
931-
candidate->getFullName().getBaseIdentifier().str()
932-
.equals_lower(memberName.getBaseIdentifier().str()));
933-
});
934-
return (candidate ? candidate->getFullName() : DeclName());
935-
}
936-
937916
/// Given a result of name lookup that had no viable results, diagnose the
938917
/// unviable ones.
939-
void FailureDiagnosis::
940-
diagnoseUnviableLookupResults(MemberLookupResult &result, Type baseObjTy,
941-
Expr *baseExpr,
942-
DeclName memberName, DeclNameLoc nameLoc,
943-
SourceLoc loc) {
918+
void FailureDiagnosis::diagnoseUnviableLookupResults(
919+
MemberLookupResult &result, Expr *E, Type baseObjTy, Expr *baseExpr,
920+
DeclName memberName, DeclNameLoc nameLoc, SourceLoc loc) {
944921
SourceRange baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange();
945-
922+
946923
// If we found no results at all, mention that fact.
947924
if (result.UnviableCandidates.empty()) {
948-
TypoCorrectionResults corrections(CS.TC, memberName, nameLoc);
949-
auto tryTypoCorrection = [&] {
950-
CS.TC.performTypoCorrection(CS.DC, DeclRefKind::Ordinary, baseObjTy,
951-
defaultMemberLookupOptions, corrections);
952-
};
953-
954-
// TODO: This should handle tuple member lookups, like x.1231 as well.
955-
if (memberName.getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
956-
diagnose(loc, diag::could_not_find_value_subscript, baseObjTy)
957-
.highlight(baseRange);
958-
} else if (memberName.getBaseName() == "deinit") {
959-
// Specialised diagnostic if trying to access deinitialisers
960-
diagnose(loc, diag::destructor_not_accessible).highlight(baseRange);
961-
} else if (auto metatypeTy = baseObjTy->getAs<MetatypeType>()) {
962-
auto instanceTy = metatypeTy->getInstanceType();
963-
tryTypoCorrection();
964-
965-
if (DeclName rightName = findCorrectEnumCaseName(instanceTy,
966-
corrections,
967-
memberName)) {
968-
diagnose(loc, diag::could_not_find_enum_case, instanceTy,
969-
memberName, rightName)
970-
.fixItReplace(nameLoc.getBaseNameLoc(),
971-
rightName.getBaseIdentifier().str());
972-
return;
973-
}
974-
975-
if (auto correction = corrections.claimUniqueCorrection()) {
976-
auto diagnostic =
977-
diagnose(loc, diag::could_not_find_type_member_corrected,
978-
instanceTy, memberName, correction->CorrectedName);
979-
diagnostic.highlight(baseRange).highlight(nameLoc.getSourceRange());
980-
correction->addFixits(diagnostic);
981-
} else {
982-
diagnose(loc, diag::could_not_find_type_member, instanceTy, memberName)
983-
.highlight(baseRange).highlight(nameLoc.getSourceRange());
984-
}
985-
} else if (auto moduleTy = baseObjTy->getAs<ModuleType>()) {
986-
diagnose(baseExpr->getLoc(), diag::no_member_of_module,
987-
moduleTy->getModule()->getName(), memberName)
988-
.highlight(baseRange)
989-
.highlight(nameLoc.getSourceRange());
990-
return;
991-
} else {
992-
auto emitBasicError = [&] {
993-
diagnose(loc, diag::could_not_find_value_member,
994-
baseObjTy, memberName)
995-
.highlight(baseRange).highlight(nameLoc.getSourceRange());
996-
};
997-
998-
// Check for a few common cases that can cause missing members.
999-
if (baseObjTy->is<EnumType>() && memberName.isSimpleName("rawValue")) {
1000-
auto loc = baseObjTy->castTo<EnumType>()->getDecl()->getNameLoc();
1001-
if (loc.isValid()) {
1002-
emitBasicError();
1003-
diagnose(loc, diag::did_you_mean_raw_type);
1004-
return;
1005-
}
1006-
} else if (baseObjTy->isAny()) {
1007-
emitBasicError();
1008-
diagnose(loc, diag::any_as_anyobject_fixit)
1009-
.fixItInsert(baseExpr->getStartLoc(), "(")
1010-
.fixItInsertAfter(baseExpr->getEndLoc(), " as AnyObject)");
1011-
return;
1012-
}
1013-
1014-
tryTypoCorrection();
1015-
1016-
if (auto correction = corrections.claimUniqueCorrection()) {
1017-
auto diagnostic =
1018-
diagnose(loc, diag::could_not_find_value_member_corrected,
1019-
baseObjTy, memberName, correction->CorrectedName);
1020-
diagnostic.highlight(baseRange).highlight(nameLoc.getSourceRange());
1021-
correction->addFixits(diagnostic);
1022-
} else {
1023-
emitBasicError();
1024-
}
1025-
}
1026-
1027-
// Note all the correction candidates.
1028-
corrections.noteAllCandidates();
1029-
1030-
// TODO: recover?
925+
MissingMemberFailure failure(nullptr, CS, baseObjTy, memberName,
926+
CS.getConstraintLocator(E));
927+
auto diagnosed = failure.diagnoseAsError();
928+
assert(diagnosed && "Failed to produce missing member diagnostic");
929+
(void)diagnosed;
1031930
return;
1032931
}
1033932

1034-
1035933
// Otherwise, we have at least one (and potentially many) viable candidates
1036934
// sort them out. If all of the candidates have the same problem (commonly
1037935
// because there is exactly one candidate!) diagnose this.
@@ -4632,7 +4530,7 @@ bool FailureDiagnosis::diagnoseMethodAttributeFailures(
46324530
// If one of the unviable candidates matches arguments exactly,
46334531
// that means that actual problem is related to function attributes.
46344532
if (unviableCandidates.closeness == CC_ExactMatch) {
4635-
diagnoseUnviableLookupResults(results, baseType, base, UDE->getName(),
4533+
diagnoseUnviableLookupResults(results, UDE, baseType, base, UDE->getName(),
46364534
UDE->getNameLoc(), UDE->getLoc());
46374535
return true;
46384536
}
@@ -7451,7 +7349,7 @@ bool FailureDiagnosis::diagnoseMemberFailures(
74517349
}
74527350

74537351
// FIXME: Dig out the property DeclNameLoc.
7454-
diagnoseUnviableLookupResults(result, baseObjTy, baseExpr, memberName,
7352+
diagnoseUnviableLookupResults(result, E, baseObjTy, baseExpr, memberName,
74557353
NameLoc, BaseLoc);
74567354
return true;
74577355
}
@@ -8219,6 +8117,20 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) {
82198117
}
82208118
}
82218119

8120+
// Before giving up completely let's try to see if there are any
8121+
// fixes recorded by constraint generator, which point to structural
8122+
// problems that might not result in solution even if fixed e.g.
8123+
// missing members involved in protocol composition in expression
8124+
// context which are interpreted as binary operator expressions instead.
8125+
{
8126+
bool diagnosed = false;
8127+
for (auto *fix : CS.getFixes())
8128+
diagnosed |= fix->diagnose(expr);
8129+
8130+
if (diagnosed)
8131+
return;
8132+
}
8133+
82228134
// If there are no posted constraints or failures, then there was
82238135
// not enough contextual information available to infer a type for the
82248136
// expression.

lib/Sema/CSDiagnostics.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "CSDiagnostics.h"
1818
#include "ConstraintSystem.h"
1919
#include "MiscDiagnostics.h"
20+
#include "TypoCorrection.h"
2021
#include "swift/AST/ASTContext.h"
2122
#include "swift/AST/Decl.h"
2223
#include "swift/AST/Expr.h"
@@ -1332,3 +1333,147 @@ bool SubscriptMisuseFailure::diagnoseAsNote() {
13321333
}
13331334
return false;
13341335
}
1336+
1337+
/// When a user refers a enum case with a wrong member name, we try to find a
1338+
/// enum element whose name differs from the wrong name only in convention;
1339+
/// meaning their lower case counterparts are identical.
1340+
/// - DeclName is valid when such a correct case is found; invalid otherwise.
1341+
DeclName MissingMemberFailure::findCorrectEnumCaseName(
1342+
Type Ty, TypoCorrectionResults &corrections, DeclName memberName) {
1343+
if (memberName.isSpecial() || !memberName.isSimpleName())
1344+
return DeclName();
1345+
if (!Ty->getEnumOrBoundGenericEnum())
1346+
return DeclName();
1347+
auto candidate =
1348+
corrections.getUniqueCandidateMatching([&](ValueDecl *candidate) {
1349+
return (isa<EnumElementDecl>(candidate) &&
1350+
candidate->getFullName().getBaseIdentifier().str().equals_lower(
1351+
memberName.getBaseIdentifier().str()));
1352+
});
1353+
return (candidate ? candidate->getFullName() : DeclName());
1354+
}
1355+
1356+
bool MissingMemberFailure::diagnoseAsError() {
1357+
auto &TC = getTypeChecker();
1358+
auto *anchor = getRawAnchor();
1359+
auto *baseExpr = getAnchor();
1360+
1361+
if (!anchor || !baseExpr)
1362+
return false;
1363+
1364+
if (auto *typeVar = BaseType->getAs<TypeVariableType>()) {
1365+
auto &CS = getConstraintSystem();
1366+
auto *memberLoc = typeVar->getImpl().getLocator();
1367+
// Don't try to diagnose anything besides first missing
1368+
// member in the chain. e.g. `x.foo().bar()` let's make
1369+
// sure to diagnose only `foo()` as missing because we
1370+
// don't really know much about what `bar()` is supposed
1371+
// to be.
1372+
if (CS.MissingMembers.count(memberLoc))
1373+
return false;
1374+
}
1375+
1376+
auto baseType = resolveType(BaseType)->getWithoutSpecifierType();
1377+
1378+
DeclNameLoc nameLoc(anchor->getStartLoc());
1379+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
1380+
nameLoc = UDE->getNameLoc();
1381+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
1382+
nameLoc = UME->getNameLoc();
1383+
}
1384+
1385+
auto emitBasicError = [&](Type baseType) {
1386+
auto diagnostic = diag::could_not_find_value_member;
1387+
1388+
if (auto *metatype = baseType->getAs<MetatypeType>()) {
1389+
baseType = metatype->getInstanceType();
1390+
diagnostic = diag::could_not_find_type_member;
1391+
}
1392+
1393+
if (baseType->is<TupleType>())
1394+
diagnostic = diag::could_not_find_tuple_member;
1395+
1396+
emitDiagnostic(anchor->getLoc(), diagnostic, baseType, Name)
1397+
.highlight(baseExpr->getSourceRange())
1398+
.highlight(nameLoc.getSourceRange());
1399+
};
1400+
1401+
TypoCorrectionResults corrections(TC, Name, nameLoc);
1402+
auto tryTypoCorrection = [&] {
1403+
TC.performTypoCorrection(getDC(), DeclRefKind::Ordinary, baseType,
1404+
defaultMemberLookupOptions, corrections);
1405+
};
1406+
1407+
if (Name.getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
1408+
emitDiagnostic(anchor->getLoc(), diag::could_not_find_value_subscript,
1409+
baseType)
1410+
.highlight(baseExpr->getSourceRange());
1411+
} else if (Name.getBaseName() == "deinit") {
1412+
// Specialised diagnostic if trying to access deinitialisers
1413+
emitDiagnostic(anchor->getLoc(), diag::destructor_not_accessible)
1414+
.highlight(baseExpr->getSourceRange());
1415+
} else if (auto metatypeTy = baseType->getAs<MetatypeType>()) {
1416+
auto instanceTy = metatypeTy->getInstanceType();
1417+
tryTypoCorrection();
1418+
1419+
if (DeclName rightName =
1420+
findCorrectEnumCaseName(instanceTy, corrections, Name)) {
1421+
emitDiagnostic(anchor->getLoc(), diag::could_not_find_enum_case,
1422+
instanceTy, Name, rightName)
1423+
.fixItReplace(nameLoc.getBaseNameLoc(),
1424+
rightName.getBaseIdentifier().str());
1425+
return true;
1426+
}
1427+
1428+
if (auto correction = corrections.claimUniqueCorrection()) {
1429+
auto diagnostic = emitDiagnostic(
1430+
anchor->getLoc(), diag::could_not_find_type_member_corrected,
1431+
instanceTy, Name, correction->CorrectedName);
1432+
diagnostic.highlight(baseExpr->getSourceRange())
1433+
.highlight(nameLoc.getSourceRange());
1434+
correction->addFixits(diagnostic);
1435+
} else {
1436+
emitBasicError(baseType);
1437+
}
1438+
} else if (auto moduleTy = baseType->getAs<ModuleType>()) {
1439+
emitDiagnostic(baseExpr->getLoc(), diag::no_member_of_module,
1440+
moduleTy->getModule()->getName(), Name)
1441+
.highlight(baseExpr->getSourceRange())
1442+
.highlight(nameLoc.getSourceRange());
1443+
return true;
1444+
} else {
1445+
// Check for a few common cases that can cause missing members.
1446+
auto *ED = baseType->getEnumOrBoundGenericEnum();
1447+
if (ED && Name.isSimpleName("rawValue")) {
1448+
auto loc = ED->getNameLoc();
1449+
if (loc.isValid()) {
1450+
emitBasicError(baseType);
1451+
emitDiagnostic(loc, diag::did_you_mean_raw_type);
1452+
return true;
1453+
}
1454+
} else if (baseType->isAny()) {
1455+
emitBasicError(baseType);
1456+
emitDiagnostic(anchor->getLoc(), diag::any_as_anyobject_fixit)
1457+
.fixItInsert(baseExpr->getStartLoc(), "(")
1458+
.fixItInsertAfter(baseExpr->getEndLoc(), " as AnyObject)");
1459+
return true;
1460+
}
1461+
1462+
tryTypoCorrection();
1463+
1464+
if (auto correction = corrections.claimUniqueCorrection()) {
1465+
auto diagnostic = emitDiagnostic(
1466+
anchor->getLoc(), diag::could_not_find_value_member_corrected,
1467+
baseType, Name, correction->CorrectedName);
1468+
diagnostic.highlight(baseExpr->getSourceRange())
1469+
.highlight(nameLoc.getSourceRange());
1470+
correction->addFixits(diagnostic);
1471+
} else {
1472+
emitBasicError(baseType);
1473+
}
1474+
}
1475+
1476+
// Note all the correction candidates.
1477+
corrections.noteAllCandidates();
1478+
return true;
1479+
}

lib/Sema/CSDiagnostics.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/AST/Decl.h"
2424
#include "swift/AST/DiagnosticEngine.h"
2525
#include "swift/AST/Expr.h"
26+
#include "swift/AST/Identifier.h"
2627
#include "swift/AST/Types.h"
2728
#include "swift/Basic/SourceLoc.h"
2829
#include "llvm/ADT/ArrayRef.h"
@@ -646,6 +647,33 @@ class SubscriptMisuseFailure final : public FailureDiagnostic {
646647
bool diagnoseAsNote() override;
647648
};
648649

650+
/// Diagnose situations when member referenced by name is missing
651+
/// from the associated base type, e.g.
652+
///
653+
/// ```swift
654+
/// struct S {}
655+
/// func foo(_ s: S) {
656+
/// let _: Int = s.foo(1, 2) // expected type is `(Int, Int) -> Int`
657+
/// }
658+
/// ```
659+
class MissingMemberFailure final : public FailureDiagnostic {
660+
Type BaseType;
661+
DeclName Name;
662+
663+
public:
664+
MissingMemberFailure(Expr *root, ConstraintSystem &cs, Type baseType,
665+
DeclName memberName, ConstraintLocator *locator)
666+
: FailureDiagnostic(root, cs, locator), BaseType(baseType),
667+
Name(memberName) {}
668+
669+
bool diagnoseAsError() override;
670+
671+
private:
672+
static DeclName findCorrectEnumCaseName(Type Ty,
673+
TypoCorrectionResults &corrections,
674+
DeclName memberName);
675+
};
676+
649677
} // end namespace constraints
650678
} // end namespace swift
651679

0 commit comments

Comments
 (0)