mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Diagnostics] Diagnose missing members via fixes
Try to fix constraint system in a way where member reference is going to be defined in terms of its use, which makes it seem like parameters match arguments exactly. Such helps to produce solutions and diagnose failures related to missing members precisely. These changes would be further extended to diagnose use of unavailable members and other structural member failures. Resolves: rdar://problem/34583132 Resolves: rdar://problem/36989788 Resolved: rdar://problem/39586166 Resolves: rdar://problem/40537782 Resolves: rdar://problem/46211109
This commit is contained in:
@@ -523,10 +523,10 @@ private:
|
||||
/// Given a result of name lookup that had no viable results, diagnose the
|
||||
/// unviable ones.
|
||||
void diagnoseUnviableLookupResults(MemberLookupResult &lookupResults,
|
||||
Type baseObjTy, Expr *baseExpr,
|
||||
Expr *expr, Type baseObjTy, Expr *baseExpr,
|
||||
DeclName memberName, DeclNameLoc nameLoc,
|
||||
SourceLoc loc);
|
||||
|
||||
|
||||
/// Produce a diagnostic for a general overload resolution failure
|
||||
/// (irrespective of the exact expression kind).
|
||||
bool diagnoseGeneralOverloadFailure(Constraint *constraint);
|
||||
@@ -913,125 +913,23 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy,
|
||||
return;
|
||||
}
|
||||
|
||||
/// When a user refers a enum case with a wrong member name, we try to find a enum
|
||||
/// element whose name differs from the wrong name only in convention; meaning their
|
||||
/// lower case counterparts are identical.
|
||||
/// - DeclName is valid when such a correct case is found; invalid otherwise.
|
||||
static DeclName
|
||||
findCorrectEnumCaseName(Type Ty, TypoCorrectionResults &corrections,
|
||||
DeclName memberName) {
|
||||
if (memberName.isSpecial() || !memberName.isSimpleName())
|
||||
return DeclName();
|
||||
if (!Ty->is<EnumType>() &&
|
||||
!Ty->is<BoundGenericEnumType>())
|
||||
return DeclName();
|
||||
auto candidate =
|
||||
corrections.getUniqueCandidateMatching([&](ValueDecl *candidate) {
|
||||
return (isa<EnumElementDecl>(candidate) &&
|
||||
candidate->getFullName().getBaseIdentifier().str()
|
||||
.equals_lower(memberName.getBaseIdentifier().str()));
|
||||
});
|
||||
return (candidate ? candidate->getFullName() : DeclName());
|
||||
}
|
||||
|
||||
/// Given a result of name lookup that had no viable results, diagnose the
|
||||
/// unviable ones.
|
||||
void FailureDiagnosis::
|
||||
diagnoseUnviableLookupResults(MemberLookupResult &result, Type baseObjTy,
|
||||
Expr *baseExpr,
|
||||
DeclName memberName, DeclNameLoc nameLoc,
|
||||
SourceLoc loc) {
|
||||
void FailureDiagnosis::diagnoseUnviableLookupResults(
|
||||
MemberLookupResult &result, Expr *E, Type baseObjTy, Expr *baseExpr,
|
||||
DeclName memberName, DeclNameLoc nameLoc, SourceLoc loc) {
|
||||
SourceRange baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange();
|
||||
|
||||
|
||||
// If we found no results at all, mention that fact.
|
||||
if (result.UnviableCandidates.empty()) {
|
||||
TypoCorrectionResults corrections(CS.TC, memberName, nameLoc);
|
||||
auto tryTypoCorrection = [&] {
|
||||
CS.TC.performTypoCorrection(CS.DC, DeclRefKind::Ordinary, baseObjTy,
|
||||
defaultMemberLookupOptions, corrections);
|
||||
};
|
||||
|
||||
// TODO: This should handle tuple member lookups, like x.1231 as well.
|
||||
if (memberName.getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
|
||||
diagnose(loc, diag::could_not_find_value_subscript, baseObjTy)
|
||||
.highlight(baseRange);
|
||||
} else if (memberName.getBaseName() == "deinit") {
|
||||
// Specialised diagnostic if trying to access deinitialisers
|
||||
diagnose(loc, diag::destructor_not_accessible).highlight(baseRange);
|
||||
} else if (auto metatypeTy = baseObjTy->getAs<MetatypeType>()) {
|
||||
auto instanceTy = metatypeTy->getInstanceType();
|
||||
tryTypoCorrection();
|
||||
|
||||
if (DeclName rightName = findCorrectEnumCaseName(instanceTy,
|
||||
corrections,
|
||||
memberName)) {
|
||||
diagnose(loc, diag::could_not_find_enum_case, instanceTy,
|
||||
memberName, rightName)
|
||||
.fixItReplace(nameLoc.getBaseNameLoc(),
|
||||
rightName.getBaseIdentifier().str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto correction = corrections.claimUniqueCorrection()) {
|
||||
auto diagnostic =
|
||||
diagnose(loc, diag::could_not_find_type_member_corrected,
|
||||
instanceTy, memberName, correction->CorrectedName);
|
||||
diagnostic.highlight(baseRange).highlight(nameLoc.getSourceRange());
|
||||
correction->addFixits(diagnostic);
|
||||
} else {
|
||||
diagnose(loc, diag::could_not_find_type_member, instanceTy, memberName)
|
||||
.highlight(baseRange).highlight(nameLoc.getSourceRange());
|
||||
}
|
||||
} else if (auto moduleTy = baseObjTy->getAs<ModuleType>()) {
|
||||
diagnose(baseExpr->getLoc(), diag::no_member_of_module,
|
||||
moduleTy->getModule()->getName(), memberName)
|
||||
.highlight(baseRange)
|
||||
.highlight(nameLoc.getSourceRange());
|
||||
return;
|
||||
} else {
|
||||
auto emitBasicError = [&] {
|
||||
diagnose(loc, diag::could_not_find_value_member,
|
||||
baseObjTy, memberName)
|
||||
.highlight(baseRange).highlight(nameLoc.getSourceRange());
|
||||
};
|
||||
|
||||
// Check for a few common cases that can cause missing members.
|
||||
if (baseObjTy->is<EnumType>() && memberName.isSimpleName("rawValue")) {
|
||||
auto loc = baseObjTy->castTo<EnumType>()->getDecl()->getNameLoc();
|
||||
if (loc.isValid()) {
|
||||
emitBasicError();
|
||||
diagnose(loc, diag::did_you_mean_raw_type);
|
||||
return;
|
||||
}
|
||||
} else if (baseObjTy->isAny()) {
|
||||
emitBasicError();
|
||||
diagnose(loc, diag::any_as_anyobject_fixit)
|
||||
.fixItInsert(baseExpr->getStartLoc(), "(")
|
||||
.fixItInsertAfter(baseExpr->getEndLoc(), " as AnyObject)");
|
||||
return;
|
||||
}
|
||||
|
||||
tryTypoCorrection();
|
||||
|
||||
if (auto correction = corrections.claimUniqueCorrection()) {
|
||||
auto diagnostic =
|
||||
diagnose(loc, diag::could_not_find_value_member_corrected,
|
||||
baseObjTy, memberName, correction->CorrectedName);
|
||||
diagnostic.highlight(baseRange).highlight(nameLoc.getSourceRange());
|
||||
correction->addFixits(diagnostic);
|
||||
} else {
|
||||
emitBasicError();
|
||||
}
|
||||
}
|
||||
|
||||
// Note all the correction candidates.
|
||||
corrections.noteAllCandidates();
|
||||
|
||||
// TODO: recover?
|
||||
MissingMemberFailure failure(nullptr, CS, baseObjTy, memberName,
|
||||
CS.getConstraintLocator(E));
|
||||
auto diagnosed = failure.diagnoseAsError();
|
||||
assert(diagnosed && "Failed to produce missing member diagnostic");
|
||||
(void)diagnosed;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Otherwise, we have at least one (and potentially many) viable candidates
|
||||
// sort them out. If all of the candidates have the same problem (commonly
|
||||
// because there is exactly one candidate!) diagnose this.
|
||||
@@ -4632,7 +4530,7 @@ bool FailureDiagnosis::diagnoseMethodAttributeFailures(
|
||||
// If one of the unviable candidates matches arguments exactly,
|
||||
// that means that actual problem is related to function attributes.
|
||||
if (unviableCandidates.closeness == CC_ExactMatch) {
|
||||
diagnoseUnviableLookupResults(results, baseType, base, UDE->getName(),
|
||||
diagnoseUnviableLookupResults(results, UDE, baseType, base, UDE->getName(),
|
||||
UDE->getNameLoc(), UDE->getLoc());
|
||||
return true;
|
||||
}
|
||||
@@ -7458,7 +7356,7 @@ bool FailureDiagnosis::diagnoseMemberFailures(
|
||||
}
|
||||
|
||||
// FIXME: Dig out the property DeclNameLoc.
|
||||
diagnoseUnviableLookupResults(result, baseObjTy, baseExpr, memberName,
|
||||
diagnoseUnviableLookupResults(result, E, baseObjTy, baseExpr, memberName,
|
||||
NameLoc, BaseLoc);
|
||||
return true;
|
||||
}
|
||||
@@ -8226,6 +8124,20 @@ void FailureDiagnosis::diagnoseAmbiguity(Expr *E) {
|
||||
}
|
||||
}
|
||||
|
||||
// Before giving up completely let's try to see if there are any
|
||||
// fixes recorded by constraint generator, which point to structural
|
||||
// problems that might not result in solution even if fixed e.g.
|
||||
// missing members involved in protocol composition in expression
|
||||
// context which are interpreted as binary operator expressions instead.
|
||||
{
|
||||
bool diagnosed = false;
|
||||
for (auto *fix : CS.getFixes())
|
||||
diagnosed |= fix->diagnose(expr);
|
||||
|
||||
if (diagnosed)
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are no posted constraints or failures, then there was
|
||||
// not enough contextual information available to infer a type for the
|
||||
// expression.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "CSDiagnostics.h"
|
||||
#include "ConstraintSystem.h"
|
||||
#include "MiscDiagnostics.h"
|
||||
#include "TypoCorrection.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/Expr.h"
|
||||
@@ -1332,3 +1333,147 @@ bool SubscriptMisuseFailure::diagnoseAsNote() {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// When a user refers a enum case with a wrong member name, we try to find a
|
||||
/// enum element whose name differs from the wrong name only in convention;
|
||||
/// meaning their lower case counterparts are identical.
|
||||
/// - DeclName is valid when such a correct case is found; invalid otherwise.
|
||||
DeclName MissingMemberFailure::findCorrectEnumCaseName(
|
||||
Type Ty, TypoCorrectionResults &corrections, DeclName memberName) {
|
||||
if (memberName.isSpecial() || !memberName.isSimpleName())
|
||||
return DeclName();
|
||||
if (!Ty->getEnumOrBoundGenericEnum())
|
||||
return DeclName();
|
||||
auto candidate =
|
||||
corrections.getUniqueCandidateMatching([&](ValueDecl *candidate) {
|
||||
return (isa<EnumElementDecl>(candidate) &&
|
||||
candidate->getFullName().getBaseIdentifier().str().equals_lower(
|
||||
memberName.getBaseIdentifier().str()));
|
||||
});
|
||||
return (candidate ? candidate->getFullName() : DeclName());
|
||||
}
|
||||
|
||||
bool MissingMemberFailure::diagnoseAsError() {
|
||||
auto &TC = getTypeChecker();
|
||||
auto *anchor = getRawAnchor();
|
||||
auto *baseExpr = getAnchor();
|
||||
|
||||
if (!anchor || !baseExpr)
|
||||
return false;
|
||||
|
||||
if (auto *typeVar = BaseType->getAs<TypeVariableType>()) {
|
||||
auto &CS = getConstraintSystem();
|
||||
auto *memberLoc = typeVar->getImpl().getLocator();
|
||||
// Don't try to diagnose anything besides first missing
|
||||
// member in the chain. e.g. `x.foo().bar()` let's make
|
||||
// sure to diagnose only `foo()` as missing because we
|
||||
// don't really know much about what `bar()` is supposed
|
||||
// to be.
|
||||
if (CS.MissingMembers.count(memberLoc))
|
||||
return false;
|
||||
}
|
||||
|
||||
auto baseType = resolveType(BaseType)->getWithoutSpecifierType();
|
||||
|
||||
DeclNameLoc nameLoc(anchor->getStartLoc());
|
||||
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
|
||||
nameLoc = UDE->getNameLoc();
|
||||
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
|
||||
nameLoc = UME->getNameLoc();
|
||||
}
|
||||
|
||||
auto emitBasicError = [&](Type baseType) {
|
||||
auto diagnostic = diag::could_not_find_value_member;
|
||||
|
||||
if (auto *metatype = baseType->getAs<MetatypeType>()) {
|
||||
baseType = metatype->getInstanceType();
|
||||
diagnostic = diag::could_not_find_type_member;
|
||||
}
|
||||
|
||||
if (baseType->is<TupleType>())
|
||||
diagnostic = diag::could_not_find_tuple_member;
|
||||
|
||||
emitDiagnostic(anchor->getLoc(), diagnostic, baseType, Name)
|
||||
.highlight(baseExpr->getSourceRange())
|
||||
.highlight(nameLoc.getSourceRange());
|
||||
};
|
||||
|
||||
TypoCorrectionResults corrections(TC, Name, nameLoc);
|
||||
auto tryTypoCorrection = [&] {
|
||||
TC.performTypoCorrection(getDC(), DeclRefKind::Ordinary, baseType,
|
||||
defaultMemberLookupOptions, corrections);
|
||||
};
|
||||
|
||||
if (Name.getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
|
||||
emitDiagnostic(anchor->getLoc(), diag::could_not_find_value_subscript,
|
||||
baseType)
|
||||
.highlight(baseExpr->getSourceRange());
|
||||
} else if (Name.getBaseName() == "deinit") {
|
||||
// Specialised diagnostic if trying to access deinitialisers
|
||||
emitDiagnostic(anchor->getLoc(), diag::destructor_not_accessible)
|
||||
.highlight(baseExpr->getSourceRange());
|
||||
} else if (auto metatypeTy = baseType->getAs<MetatypeType>()) {
|
||||
auto instanceTy = metatypeTy->getInstanceType();
|
||||
tryTypoCorrection();
|
||||
|
||||
if (DeclName rightName =
|
||||
findCorrectEnumCaseName(instanceTy, corrections, Name)) {
|
||||
emitDiagnostic(anchor->getLoc(), diag::could_not_find_enum_case,
|
||||
instanceTy, Name, rightName)
|
||||
.fixItReplace(nameLoc.getBaseNameLoc(),
|
||||
rightName.getBaseIdentifier().str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto correction = corrections.claimUniqueCorrection()) {
|
||||
auto diagnostic = emitDiagnostic(
|
||||
anchor->getLoc(), diag::could_not_find_type_member_corrected,
|
||||
instanceTy, Name, correction->CorrectedName);
|
||||
diagnostic.highlight(baseExpr->getSourceRange())
|
||||
.highlight(nameLoc.getSourceRange());
|
||||
correction->addFixits(diagnostic);
|
||||
} else {
|
||||
emitBasicError(baseType);
|
||||
}
|
||||
} else if (auto moduleTy = baseType->getAs<ModuleType>()) {
|
||||
emitDiagnostic(baseExpr->getLoc(), diag::no_member_of_module,
|
||||
moduleTy->getModule()->getName(), Name)
|
||||
.highlight(baseExpr->getSourceRange())
|
||||
.highlight(nameLoc.getSourceRange());
|
||||
return true;
|
||||
} else {
|
||||
// Check for a few common cases that can cause missing members.
|
||||
auto *ED = baseType->getEnumOrBoundGenericEnum();
|
||||
if (ED && Name.isSimpleName("rawValue")) {
|
||||
auto loc = ED->getNameLoc();
|
||||
if (loc.isValid()) {
|
||||
emitBasicError(baseType);
|
||||
emitDiagnostic(loc, diag::did_you_mean_raw_type);
|
||||
return true;
|
||||
}
|
||||
} else if (baseType->isAny()) {
|
||||
emitBasicError(baseType);
|
||||
emitDiagnostic(anchor->getLoc(), diag::any_as_anyobject_fixit)
|
||||
.fixItInsert(baseExpr->getStartLoc(), "(")
|
||||
.fixItInsertAfter(baseExpr->getEndLoc(), " as AnyObject)");
|
||||
return true;
|
||||
}
|
||||
|
||||
tryTypoCorrection();
|
||||
|
||||
if (auto correction = corrections.claimUniqueCorrection()) {
|
||||
auto diagnostic = emitDiagnostic(
|
||||
anchor->getLoc(), diag::could_not_find_value_member_corrected,
|
||||
baseType, Name, correction->CorrectedName);
|
||||
diagnostic.highlight(baseExpr->getSourceRange())
|
||||
.highlight(nameLoc.getSourceRange());
|
||||
correction->addFixits(diagnostic);
|
||||
} else {
|
||||
emitBasicError(baseType);
|
||||
}
|
||||
}
|
||||
|
||||
// Note all the correction candidates.
|
||||
corrections.noteAllCandidates();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/DiagnosticEngine.h"
|
||||
#include "swift/AST/Expr.h"
|
||||
#include "swift/AST/Identifier.h"
|
||||
#include "swift/AST/Types.h"
|
||||
#include "swift/Basic/SourceLoc.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
@@ -646,6 +647,33 @@ public:
|
||||
bool diagnoseAsNote() override;
|
||||
};
|
||||
|
||||
/// Diagnose situations when member referenced by name is missing
|
||||
/// from the associated base type, e.g.
|
||||
///
|
||||
/// ```swift
|
||||
/// struct S {}
|
||||
/// func foo(_ s: S) {
|
||||
/// let _: Int = s.foo(1, 2) // expected type is `(Int, Int) -> Int`
|
||||
/// }
|
||||
/// ```
|
||||
class MissingMemberFailure final : public FailureDiagnostic {
|
||||
Type BaseType;
|
||||
DeclName Name;
|
||||
|
||||
public:
|
||||
MissingMemberFailure(Expr *root, ConstraintSystem &cs, Type baseType,
|
||||
DeclName memberName, ConstraintLocator *locator)
|
||||
: FailureDiagnostic(root, cs, locator), BaseType(baseType),
|
||||
Name(memberName) {}
|
||||
|
||||
bool diagnoseAsError() override;
|
||||
|
||||
private:
|
||||
static DeclName findCorrectEnumCaseName(Type Ty,
|
||||
TypoCorrectionResults &corrections,
|
||||
DeclName memberName);
|
||||
};
|
||||
|
||||
} // end namespace constraints
|
||||
} // end namespace swift
|
||||
|
||||
|
||||
@@ -247,7 +247,9 @@ UseSubscriptOperator *UseSubscriptOperator::create(ConstraintSystem &cs,
|
||||
}
|
||||
|
||||
bool DefineMemberBasedOnUse::diagnose(Expr *root, bool asNote) const {
|
||||
return false;
|
||||
auto failure = MissingMemberFailure(root, getConstraintSystem(), BaseType,
|
||||
Name, getLocator());
|
||||
return failure.diagnose(asNote);
|
||||
}
|
||||
|
||||
DefineMemberBasedOnUse *
|
||||
|
||||
@@ -945,6 +945,7 @@ public:
|
||||
friend class SplitterStep;
|
||||
friend class ComponentStep;
|
||||
friend class TypeVariableStep;
|
||||
friend class MissingMemberFailure;
|
||||
|
||||
class SolverScope;
|
||||
|
||||
@@ -1784,6 +1785,8 @@ public:
|
||||
return !solverState || solverState->recordFixes;
|
||||
}
|
||||
|
||||
ArrayRef<ConstraintFix *> getFixes() const { return Fixes; }
|
||||
|
||||
bool shouldSuppressDiagnostics() const {
|
||||
return Options.contains(ConstraintSystemFlags::SuppressDiagnostics);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func testRenamedTrueEnum() {
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: use of unresolved identifier 'TrueEnumValue'
|
||||
_ = TrueEnumValue
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'TrueEnum' has no member 'TrueEnumValue'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:16: error: type 'TrueEnum' has no member 'TrueEnumValue'
|
||||
_ = TrueEnum.TrueEnumValue
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:16: error: 'Value' has been renamed to 'value'
|
||||
@@ -172,14 +172,14 @@ func testRenamedTrueEnum() {
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: use of unresolved identifier 'TrueEnumRenamed'
|
||||
_ = TrueEnumRenamed
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'TrueEnum' has no member 'TrueEnumRenamed'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:16: error: type 'TrueEnum' has no member 'TrueEnumRenamed'
|
||||
_ = TrueEnum.TrueEnumRenamed
|
||||
|
||||
// CHECK-DIAGS-5: [[@LINE+1]]:16: error: 'Renamed' has been renamed to 'renamedSwiftUnversioned'
|
||||
_ = TrueEnum.Renamed
|
||||
// CHECK-DIAGS-4: [[@LINE-1]]:16: error: 'Renamed' has been renamed to 'renamedSwift4'
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'TrueEnum' has no member 'renamed'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:16: error: type 'TrueEnum' has no member 'renamed'
|
||||
_ = TrueEnum.renamed
|
||||
|
||||
// CHECK-DIAGS-5-NOT: :[[@LINE+1]]:16:
|
||||
@@ -193,14 +193,14 @@ func testRenamedTrueEnum() {
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: use of unresolved identifier 'TrueEnumAliasRenamed'
|
||||
_ = TrueEnumAliasRenamed
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'TrueEnum' has no member 'TrueEnumAliasRenamed'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:16: error: type 'TrueEnum' has no member 'TrueEnumAliasRenamed'
|
||||
_ = TrueEnum.TrueEnumAliasRenamed
|
||||
|
||||
// CHECK-DIAGS-5: [[@LINE+1]]:16: error: 'AliasRenamed' has been renamed to 'aliasRenamedSwiftUnversioned'
|
||||
_ = TrueEnum.AliasRenamed
|
||||
// CHECK-DIAGS-4: [[@LINE-1]]:16: error: 'AliasRenamed' has been renamed to 'aliasRenamedSwift4'
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'TrueEnum' has no member 'aliasRenamed'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:16: error: type 'TrueEnum' has no member 'aliasRenamed'
|
||||
_ = TrueEnum.aliasRenamed
|
||||
|
||||
// CHECK-DIAGS-5-NOT: :[[@LINE+1]]:16:
|
||||
@@ -216,7 +216,7 @@ func testRenamedOptionyEnum() {
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: use of unresolved identifier 'OptionyEnumValue'
|
||||
_ = OptionyEnumValue
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'OptionyEnum' has no member 'OptionyEnumValue'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:19: error: type 'OptionyEnum' has no member 'OptionyEnumValue'
|
||||
_ = OptionyEnum.OptionyEnumValue
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:19: error: 'Value' has been renamed to 'value'
|
||||
@@ -227,14 +227,14 @@ func testRenamedOptionyEnum() {
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: use of unresolved identifier 'OptionyEnumRenamed'
|
||||
_ = OptionyEnumRenamed
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'OptionyEnum' has no member 'OptionyEnumRenamed'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:19: error: type 'OptionyEnum' has no member 'OptionyEnumRenamed'
|
||||
_ = OptionyEnum.OptionyEnumRenamed
|
||||
|
||||
// CHECK-DIAGS-5: [[@LINE+1]]:19: error: 'Renamed' has been renamed to 'renamedSwiftUnversioned'
|
||||
_ = OptionyEnum.Renamed
|
||||
// CHECK-DIAGS-4: [[@LINE-1]]:19: error: 'Renamed' has been renamed to 'renamedSwift4'
|
||||
|
||||
// CHECK-DIAGS: [[@LINE+1]]:7: error: type 'OptionyEnum' has no member 'renamed'
|
||||
// CHECK-DIAGS: [[@LINE+1]]:19: error: type 'OptionyEnum' has no member 'renamed'
|
||||
_ = OptionyEnum.renamed
|
||||
|
||||
// CHECK-DIAGS-5-NOT: :[[@LINE+1]]:19:
|
||||
|
||||
@@ -38,6 +38,7 @@ func foo(x: P1 & Any & P2.Type?) { // expected-error {{non-protocol, non-class t
|
||||
let _: (P1 & P2).Type = x! // expected-error {{cannot force unwrap value of non-optional type 'P1'}}
|
||||
let _: Int = x!.p1() // expected-error {{cannot force unwrap value of non-optional type 'P1'}}
|
||||
let _: Int? = x?.p2 // expected-error {{cannot use optional chaining on non-optional value of type 'P1'}}
|
||||
// expected-error@-1 {{value of type 'P1' has no member 'p2'}}
|
||||
}
|
||||
|
||||
func bar() -> ((P1 & P2)?).Type {
|
||||
|
||||
@@ -57,4 +57,4 @@ func f23798944() {
|
||||
}
|
||||
}
|
||||
|
||||
.sr_3506 = 0 // expected-error {{reference to member 'sr_3506' cannot be resolved without a contextual type}}
|
||||
.sr_3506 = 0 // expected-error {{type 'Int' has no member 'sr_3506'}}
|
||||
|
||||
@@ -556,6 +556,7 @@ r32432145 { _,_ in
|
||||
// rdar://problem/30106822 - Swift ignores type error in closure and presents a bogus error about the caller
|
||||
[1, 2].first { $0.foo = 3 }
|
||||
// expected-error@-1 {{value of type 'Int' has no member 'foo'}}
|
||||
// expected-error@-2 {{cannot convert value of type '()' to closure result type 'Bool'}}
|
||||
|
||||
// rdar://problem/32433193, SR-5030 - Higher-order function diagnostic mentions the wrong contextual type conversion problem
|
||||
protocol A_SR_5030 {
|
||||
|
||||
@@ -107,3 +107,20 @@ struct Foo_32551313 {
|
||||
return E_32551313.Left("", Foo_32551313()) // expected-error {{extra argument in call}}
|
||||
}
|
||||
}
|
||||
|
||||
func rdar34583132() {
|
||||
enum E {
|
||||
case timeOut
|
||||
}
|
||||
|
||||
struct S {
|
||||
func foo(_ x: Int) -> E { return .timeOut }
|
||||
}
|
||||
|
||||
func bar(_ s: S) {
|
||||
guard s.foo(1 + 2) == .timeout else {
|
||||
// expected-error@-1 {{enum type 'E' has no case 'timeout'; did you mean 'timeOut'}}
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ struct Delegate {
|
||||
}
|
||||
|
||||
extension Array {
|
||||
subscript(safe safe: Int) -> Element? { // expected-note {{found this candidate}}
|
||||
subscript(safe safe: Int) -> Element? {
|
||||
get { }
|
||||
set { }
|
||||
}
|
||||
@@ -84,6 +84,4 @@ struct ShellTask {
|
||||
|
||||
let delegate = Delegate(shellTasks: [])
|
||||
_ = delegate.shellTasks[safe: 0]?.commandLine.compactMap({ $0.asString.hasPrefix("") ? $0 : nil }).count ?? 0
|
||||
// expected-error@-1 {{ambiguous reference to member 'subscript'}}
|
||||
|
||||
// FIXME: Horrible diagnostic, but at least we no longer crash
|
||||
// expected-error@-1 {{value of type 'String' has no member 'asString'}}
|
||||
|
||||
@@ -466,3 +466,54 @@ struct Outer {
|
||||
|
||||
// rdar://problem/39514009 - don't crash when trying to diagnose members with special names
|
||||
print("hello")[0] // expected-error {{value of tuple type '()' has no member 'subscript'}}
|
||||
|
||||
|
||||
func rdar40537782() {
|
||||
class A {}
|
||||
class B : A {
|
||||
override init() {}
|
||||
func foo() -> A { return A() }
|
||||
}
|
||||
|
||||
struct S<T> {
|
||||
init(_ a: T...) {}
|
||||
}
|
||||
|
||||
func bar<T>(_ t: T) {
|
||||
_ = S(B(), .foo(), A()) // expected-error {{type 'A' has no member 'foo'}}
|
||||
}
|
||||
}
|
||||
|
||||
func rdar36989788() {
|
||||
struct A<T> {
|
||||
func foo() -> A<T> {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
func bar<T>(_ x: A<T>) -> (A<T>, A<T>) {
|
||||
return (x.foo(), x.undefined()) // expected-error {{value of type 'A<T>' has no member 'undefined'}}
|
||||
}
|
||||
}
|
||||
|
||||
func rdar46211109() {
|
||||
struct MyIntSequenceStruct: Sequence {
|
||||
struct Iterator: IteratorProtocol {
|
||||
var current = 0
|
||||
mutating func next() -> Int? {
|
||||
return current + 1
|
||||
}
|
||||
}
|
||||
|
||||
func makeIterator() -> Iterator {
|
||||
return Iterator()
|
||||
}
|
||||
}
|
||||
|
||||
func foo<E, S: Sequence>(_ type: E.Type) -> S? where S.Element == E {
|
||||
return nil
|
||||
}
|
||||
|
||||
let _: MyIntSequenceStruct? = foo(Int.Self)
|
||||
// expected-error@-1 {{type 'Int' has no member 'Self'}}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,7 @@ case iPadHair.HairForceOne: // expected-error{{generic enum type 'iPadHair' is a
|
||||
()
|
||||
case Watch.Edition: // TODO: should warn that cast can't succeed with currently known conformances
|
||||
()
|
||||
// TODO: Bad error message
|
||||
case .HairForceOne: // expected-error{{cannot convert}}
|
||||
case .HairForceOne: // expected-error{{type 'HairType' has no member 'HairForceOne'}}
|
||||
()
|
||||
default:
|
||||
break
|
||||
|
||||
@@ -180,7 +180,7 @@ func foo(x: E, intVal: Int) {
|
||||
switch x {
|
||||
print() // expected-error {{all statements inside a switch must be covered by a 'case' or 'default'}}
|
||||
#if ENABLE_C
|
||||
case .NOT_EXIST: // expected-error {{pattern cannot match values of type 'E'}}
|
||||
case .NOT_EXIST: // expected-error {{type 'E' has no member 'NOT_EXIST'}}
|
||||
break
|
||||
case .C:
|
||||
break
|
||||
|
||||
@@ -107,7 +107,7 @@ enum Voluntary<T> : Equatable {
|
||||
}
|
||||
|
||||
switch foo {
|
||||
case .Naught: // expected-error{{pattern cannot match values of type 'Foo'}}
|
||||
case .Naught: // expected-error{{type 'Foo' has no member 'Naught'}}
|
||||
()
|
||||
case .A, .B, .C:
|
||||
()
|
||||
@@ -152,7 +152,7 @@ case .Twain,
|
||||
var notAnEnum = 0
|
||||
|
||||
switch notAnEnum {
|
||||
case .Foo: // expected-error{{pattern cannot match values of type 'Int'}}
|
||||
case .Foo: // expected-error{{type 'Int' has no member 'Foo'}}
|
||||
()
|
||||
}
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ Gronk: // expected-error {{switch must be exhaustive}} expected-note{{do you wan
|
||||
|
||||
func enumElementSyntaxOnTuple() {
|
||||
switch (1, 1) {
|
||||
case .Bar: // expected-error {{pattern cannot match values of type '(Int, Int)'}}
|
||||
case .Bar: // expected-error {{value of tuple type '(Int, Int)' has no member 'Bar'}}
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
||||
@@ -14,5 +14,5 @@ struct S2 {
|
||||
}
|
||||
}
|
||||
|
||||
let s1: S1.Type = .A // expected-error{{type of expression is ambiguous without more context}}
|
||||
let s1: S1.Type = .A // expected-error {{type 'S1.Type' has no member 'A'}}
|
||||
let s2: S2.`Type` = .A // no-error
|
||||
|
||||
Reference in New Issue
Block a user