Files
swift-mirror/lib/Sema/CSDiagnostics.cpp
Pavel Yaskevich d6a3a31ae9 [ConstraintSystem] Detect and fix use of method references in key path
Referencing (instance or static) methods in the key path is not
currently allowed, solver should be responsible for early detection
and diagnosis of both standalone e.g. `\.foo` and chained
e.g. `\.foo.bar` (where foo is a method) references in key path
components.

```swift
struct S {
  func foo() -> Int { return 42 }
}

let _: KeyPath<S, Int> = \.foo
```

Resolves: rdar://problem/49413561
2019-04-22 14:56:15 -07:00

2489 lines
86 KiB
C++

//===--- CSDiagnostics.cpp - Constraint Diagnostics -----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements diagnostics for constraint system.
//
//===----------------------------------------------------------------------===//
#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"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/ProtocolConformanceRef.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Types.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/Parse/Lexer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
using namespace swift;
using namespace constraints;
FailureDiagnostic::~FailureDiagnostic() {}
bool FailureDiagnostic::diagnose(bool asNote) {
return asNote ? diagnoseAsNote() : diagnoseAsError();
}
bool FailureDiagnostic::diagnoseAsNote() {
return false;
}
std::pair<Expr *, bool> FailureDiagnostic::computeAnchor() const {
auto &cs = getConstraintSystem();
auto *locator = getLocator();
// Resolve the locator to a specific expression.
SourceRange range;
bool isSubscriptMember =
(!locator->getPath().empty() && locator->getPath().back().getKind() ==
ConstraintLocator::SubscriptMember);
ConstraintLocator *resolved = simplifyLocator(cs, locator, range);
if (!resolved || !resolved->getAnchor())
return {locator->getAnchor(), true};
Expr *anchor = resolved->getAnchor();
// FIXME: Work around an odd locator representation that doesn't separate the
// base of a subscript member from the member access.
if (isSubscriptMember) {
if (auto subscript = dyn_cast<SubscriptExpr>(anchor))
anchor = subscript->getBase();
}
return {anchor, !resolved->getPath().empty()};
}
Type FailureDiagnostic::getType(Expr *expr) const {
return resolveType(CS.getType(expr));
}
template <typename... ArgTypes>
InFlightDiagnostic
FailureDiagnostic::emitDiagnostic(ArgTypes &&... Args) const {
auto &cs = getConstraintSystem();
return cs.TC.diagnose(std::forward<ArgTypes>(Args)...);
}
Expr *FailureDiagnostic::findParentExpr(Expr *subExpr) const {
return E ? E->getParentMap()[subExpr] : nullptr;
}
Expr *FailureDiagnostic::getArgumentExprFor(Expr *anchor) const {
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
if (auto *call = dyn_cast_or_null<CallExpr>(findParentExpr(UDE)))
return call->getArg();
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
return UME->getArgument();
} else if (auto *call = dyn_cast<CallExpr>(anchor)) {
return call->getArg();
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
return SE->getIndex();
}
return nullptr;
}
Type RequirementFailure::getOwnerType() const {
return getType(getRawAnchor())
->getInOutObjectType()
->getMetatypeInstanceType();
}
const GenericContext *RequirementFailure::getGenericContext() const {
if (auto *genericCtx = AffectedDecl->getAsGenericContext())
return genericCtx;
return AffectedDecl->getDeclContext()->getAsDecl()->getAsGenericContext();
}
const Requirement &RequirementFailure::getRequirement() const {
// If this is a conditional requirement failure we need to
// fetch conformance from constraint system associated with
// type requirement this conditional conformance belongs to.
auto requirements = isConditional()
? Conformance->getConditionalRequirements()
: Signature->getRequirements();
return requirements[getRequirementIndex()];
}
ProtocolConformance *RequirementFailure::getConformanceForConditionalReq(
ConstraintLocator *locator) {
auto &cs = getConstraintSystem();
auto path = locator->getPath();
assert(!path.empty());
if (!path.back().isConditionalRequirement()) {
assert(path.back().isTypeParameterRequirement());
return nullptr;
}
auto *typeReqLoc = cs.getConstraintLocator(getRawAnchor(), path.drop_back(),
/*summaryFlags=*/0);
auto result = llvm::find_if(
cs.CheckedConformances,
[&](const std::pair<ConstraintLocator *, ProtocolConformanceRef>
&conformance) { return conformance.first == typeReqLoc; });
assert(result != cs.CheckedConformances.end());
auto conformance = result->second;
assert(conformance.isConcrete());
return conformance.getConcrete();
}
ValueDecl *RequirementFailure::getDeclRef() const {
auto &cs = getConstraintSystem();
auto &TC = getTypeChecker();
auto *anchor = getRawAnchor();
auto *locator = cs.getConstraintLocator(anchor);
if (isFromContextualType()) {
auto type = cs.getContextualType();
assert(type);
auto *alias = dyn_cast<TypeAliasType>(type.getPointer());
return alias ? alias->getDecl() : type->getAnyGeneric();
}
if (auto *AE = dyn_cast<CallExpr>(anchor)) {
// NOTE: In valid code, the function can only be a TypeExpr
assert(isa<TypeExpr>(AE->getFn()) ||
isa<OverloadedDeclRefExpr>(AE->getFn()));
ConstraintLocatorBuilder ctor(locator);
locator = cs.getConstraintLocator(
ctor.withPathElement(PathEltKind::ApplyFunction)
.withPathElement(PathEltKind::ConstructorMember));
} else if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
ConstraintLocatorBuilder member(locator);
if (TC.getSelfForInitDelegationInConstructor(getDC(), UDE)) {
member = member.withPathElement(PathEltKind::ConstructorMember);
} else {
member = member.withPathElement(PathEltKind::Member);
}
locator = cs.getConstraintLocator(member);
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
locator = cs.getConstraintLocator(locator, PathEltKind::UnresolvedMember);
} else if (isa<SubscriptExpr>(anchor)) {
ConstraintLocatorBuilder subscript(locator);
locator = cs.getConstraintLocator(
subscript.withPathElement(PathEltKind::SubscriptMember));
} else if (isa<MemberRefExpr>(anchor)) {
ConstraintLocatorBuilder memberRef(locator);
locator =
cs.getConstraintLocator(memberRef.withPathElement(PathEltKind::Member));
}
auto overload = getOverloadChoiceIfAvailable(locator);
if (overload)
return overload->choice.getDecl();
auto ownerType = getOwnerType();
if (auto *NA = dyn_cast<TypeAliasType>(ownerType.getPointer()))
return NA->getDecl();
return ownerType->getAnyGeneric();
}
GenericSignature *RequirementFailure::getSignature(ConstraintLocator *locator) {
if (isConditional())
return Conformance->getGenericSignature();
auto path = locator->getPath();
for (auto iter = path.rbegin(); iter != path.rend(); ++iter) {
const auto &elt = *iter;
if (elt.getKind() == ConstraintLocator::OpenedGeneric)
return elt.getGenericSignature();
}
llvm_unreachable("Type requirement failure should always have signature");
}
bool RequirementFailure::isFromContextualType() const {
auto path = getLocator()->getPath();
assert(!path.empty());
return path.front().getKind() == ConstraintLocator::ContextualType;
}
const DeclContext *RequirementFailure::getRequirementDC() const {
// In case of conditional requirement failure, we don't
// have to guess where the it comes from.
if (isConditional())
return Conformance->getDeclContext();
const auto &req = getRequirement();
auto *DC = AffectedDecl->getDeclContext();
do {
if (auto *sig = DC->getGenericSignatureOfContext()) {
if (sig->isRequirementSatisfied(req))
return DC;
}
} while ((DC = DC->getParent()));
return AffectedDecl->getAsGenericContext();
}
bool RequirementFailure::isStaticOrInstanceMember(const ValueDecl *decl) {
if (decl->isInstanceMember())
return true;
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(decl))
return AFD->isStatic() && !AFD->isOperator();
return decl->isStatic();
}
bool RequirementFailure::diagnoseAsError() {
if (!canDiagnoseFailure())
return false;
auto *anchor = getRawAnchor();
const auto *reqDC = getRequirementDC();
auto *genericCtx = getGenericContext();
auto lhs = resolveType(getLHS());
auto rhs = resolveType(getRHS());
if (genericCtx != reqDC && (genericCtx->isChildContextOf(reqDC) ||
isStaticOrInstanceMember(AffectedDecl))) {
auto *NTD = reqDC->getSelfNominalTypeDecl();
emitDiagnostic(anchor->getLoc(), getDiagnosticInRereference(),
AffectedDecl->getDescriptiveKind(),
AffectedDecl->getFullName(), NTD->getDeclaredType(), lhs,
rhs);
} else {
emitDiagnostic(anchor->getLoc(), getDiagnosticOnDecl(),
AffectedDecl->getDescriptiveKind(),
AffectedDecl->getFullName(), lhs, rhs);
}
emitRequirementNote(reqDC->getAsDecl(), lhs, rhs);
return true;
}
bool RequirementFailure::diagnoseAsNote() {
const auto &req = getRequirement();
const auto *reqDC = getRequirementDC();
emitDiagnostic(reqDC->getAsDecl(), getDiagnosticAsNote(), getLHS(), getRHS(),
req.getFirstType(), req.getSecondType(), "");
return true;
}
void RequirementFailure::emitRequirementNote(const Decl *anchor, Type lhs,
Type rhs) const {
auto &req = getRequirement();
if (isConditional()) {
emitDiagnostic(anchor, diag::requirement_implied_by_conditional_conformance,
resolveType(Conformance->getType()),
Conformance->getProtocol()->getDeclaredInterfaceType());
return;
}
if (rhs->isEqual(req.getSecondType())) {
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
req.getFirstType(), lhs);
return;
}
if (lhs->isEqual(req.getFirstType())) {
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
req.getSecondType(), rhs);
return;
}
emitDiagnostic(anchor, diag::where_requirement_failure_both_subst,
req.getFirstType(), lhs, req.getSecondType(), rhs);
}
bool MissingConformanceFailure::diagnoseAsError() {
if (!canDiagnoseFailure())
return false;
auto *anchor = getAnchor();
auto ownerType = getOwnerType();
auto nonConformingType = getLHS();
auto protocolType = getRHS();
auto getArgumentAt = [](const ApplyExpr *AE, unsigned index) -> Expr * {
assert(AE);
auto *arg = AE->getArg();
if (auto *TE = dyn_cast<TupleExpr>(arg))
return TE->getElement(index);
assert(index == 0);
if (auto *PE = dyn_cast<ParenExpr>(arg))
return PE->getSubExpr();
return arg;
};
Optional<unsigned> atParameterPos;
// Sometimes fix is recorded by type-checking sub-expression
// during normal diagnostics, in such case call expression
// is unavailable.
if (Apply) {
if (auto *fnType = ownerType->getAs<AnyFunctionType>()) {
auto parameters = fnType->getParams();
for (auto index : indices(parameters)) {
if (parameters[index].getOldType()->isEqual(nonConformingType)) {
atParameterPos = index;
break;
}
}
}
}
if (nonConformingType->isExistentialType()) {
auto diagnostic = diag::protocol_does_not_conform_objc;
if (nonConformingType->isObjCExistentialType())
diagnostic = diag::protocol_does_not_conform_static;
emitDiagnostic(anchor->getLoc(), diagnostic, nonConformingType,
protocolType);
return true;
}
if (atParameterPos) {
// Requirement comes from one of the parameter types,
// let's try to point diagnostic to the argument expression.
auto *argExpr = getArgumentAt(Apply, *atParameterPos);
emitDiagnostic(argExpr->getLoc(),
diag::cannot_convert_argument_value_protocol,
nonConformingType, protocolType);
return true;
}
// If none of the special cases could be diagnosed,
// let's fallback to the most general diagnostic.
return RequirementFailure::diagnoseAsError();
}
bool LabelingFailure::diagnoseAsError() {
auto &cs = getConstraintSystem();
auto *anchor = getRawAnchor();
auto *argExpr = getArgumentExprFor(anchor);
if (!argExpr)
return false;
return diagnoseArgumentLabelError(cs.getASTContext(), argExpr, CorrectLabels,
isa<SubscriptExpr>(anchor));
}
bool NoEscapeFuncToTypeConversionFailure::diagnoseAsError() {
auto *anchor = getAnchor();
if (ConvertTo) {
emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type,
ConvertTo);
return true;
}
GenericTypeParamType *paramTy = nullptr;
auto path = getLocator()->getPath();
if (!path.empty()) {
auto &last = path.back();
if (last.getKind() == ConstraintLocator::GenericParameter)
paramTy = last.getGenericParameter();
}
if (paramTy) {
emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type,
paramTy);
} else {
emitDiagnostic(anchor->getLoc(), diag::unknown_escaping_use_of_noescape);
}
return true;
}
bool MissingForcedDowncastFailure::diagnoseAsError() {
if (hasComplexLocator())
return false;
auto &TC = getTypeChecker();
auto *expr = getAnchor();
if (auto *assignExpr = dyn_cast<AssignExpr>(expr))
expr = assignExpr->getSrc();
auto *coerceExpr = dyn_cast<CoerceExpr>(expr);
if (!coerceExpr)
return false;
auto *subExpr = coerceExpr->getSubExpr();
auto fromType = getType(subExpr)->getRValueType();
auto toType = resolveType(coerceExpr->getCastTypeLoc().getType());
auto castKind =
TC.typeCheckCheckedCast(fromType, toType, CheckedCastContextKind::None,
getDC(), coerceExpr->getLoc(), subExpr,
coerceExpr->getCastTypeLoc().getSourceRange());
switch (castKind) {
// Invalid cast.
case CheckedCastKind::Unresolved:
// Fix didn't work, let diagnoseFailureForExpr handle this.
return false;
case CheckedCastKind::Coercion:
case CheckedCastKind::BridgingCoercion:
llvm_unreachable("Coercions handled in other disjunction branch");
// Valid casts.
case CheckedCastKind::ArrayDowncast:
case CheckedCastKind::DictionaryDowncast:
case CheckedCastKind::SetDowncast:
case CheckedCastKind::ValueCast:
emitDiagnostic(coerceExpr->getLoc(), diag::missing_forced_downcast,
fromType, toType)
.highlight(coerceExpr->getSourceRange())
.fixItReplace(coerceExpr->getLoc(), "as!");
return true;
}
llvm_unreachable("unhandled cast kind");
}
bool MissingAddressOfFailure::diagnoseAsError() {
if (hasComplexLocator())
return false;
auto *anchor = getAnchor();
auto type = getType(anchor)->getRValueType();
emitDiagnostic(anchor->getLoc(), diag::missing_address_of, type)
.fixItInsert(anchor->getStartLoc(), "&");
return true;
}
bool MissingExplicitConversionFailure::diagnoseAsError() {
if (hasComplexLocator())
return false;
auto *DC = getDC();
auto &TC = getTypeChecker();
auto *anchor = getAnchor();
if (auto *assign = dyn_cast<AssignExpr>(anchor))
anchor = assign->getSrc();
if (auto *paren = dyn_cast<ParenExpr>(anchor))
anchor = paren->getSubExpr();
auto fromType = getType(anchor)->getRValueType();
Type toType = resolveType(ConvertingTo);
bool useAs = TC.isExplicitlyConvertibleTo(fromType, toType, DC);
bool useAsBang = !useAs && TC.checkedCastMaySucceed(fromType, toType, DC);
if (!useAs && !useAsBang)
return false;
auto *expr = getParentExpr();
// If we're performing pattern matching,
// "as" means something completely different...
if (auto binOpExpr = dyn_cast<BinaryExpr>(expr)) {
auto overloadedFn = dyn_cast<OverloadedDeclRefExpr>(binOpExpr->getFn());
if (overloadedFn && !overloadedFn->getDecls().empty()) {
ValueDecl *decl0 = overloadedFn->getDecls()[0];
if (decl0->getBaseName() == decl0->getASTContext().Id_MatchOperator)
return false;
}
}
bool needsParensInside = exprNeedsParensBeforeAddingAs(anchor);
bool needsParensOutside = exprNeedsParensAfterAddingAs(anchor, expr);
llvm::SmallString<2> insertBefore;
llvm::SmallString<32> insertAfter;
if (needsParensOutside) {
insertBefore += "(";
}
if (needsParensInside) {
insertBefore += "(";
insertAfter += ")";
}
insertAfter += useAs ? " as " : " as! ";
insertAfter += toType->getWithoutParens()->getString();
if (needsParensOutside)
insertAfter += ")";
auto diagID =
useAs ? diag::missing_explicit_conversion : diag::missing_forced_downcast;
auto diag = emitDiagnostic(anchor->getLoc(), diagID, fromType, toType);
if (!insertBefore.empty()) {
diag.fixItInsert(anchor->getStartLoc(), insertBefore);
}
diag.fixItInsertAfter(anchor->getEndLoc(), insertAfter);
return true;
}
bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() {
if (hasComplexLocator())
return false;
auto *anchor = getAnchor();
auto type = getType(anchor)->getRValueType();
bool resultIsOptional = ResultTypeIsOptional;
// If we've resolved the member overload to one that returns an optional
// type, then the result of the expression is optional (and we want to offer
// only a '?' fixit) even though the constraint system didn't need to add any
// additional optionality.
auto overload = getResolvedOverload(getLocator());
if (overload && overload->ImpliedType->getOptionalObjectType())
resultIsOptional = true;
return diagnoseBaseUnwrapForMemberAccess(anchor, type, Member,
resultIsOptional, SourceRange());
}
Optional<AnyFunctionType::Param>
MissingOptionalUnwrapFailure::getOperatorParameterFor(Expr *expr) const {
auto *parentExpr = findParentExpr(expr);
if (!parentExpr)
return None;
auto getArgIdx = [](TupleExpr *tuple, Expr *argExpr) -> unsigned {
for (unsigned i = 0, n = tuple->getNumElements(); i != n; ++i) {
if (tuple->getElement(i) == argExpr)
return i;
}
llvm_unreachable("argument is not in enclosing tuple?!");
};
auto *tupleExpr = dyn_cast<TupleExpr>(parentExpr);
if (!(tupleExpr && tupleExpr->isImplicit()))
return None;
parentExpr = findParentExpr(tupleExpr);
if (!(parentExpr && isa<ApplyExpr>(parentExpr)))
return None;
auto &cs = getConstraintSystem();
auto *fnExpr = cast<ApplyExpr>(parentExpr)->getFn();
if (auto overload =
getOverloadChoiceIfAvailable(cs.getConstraintLocator(fnExpr))) {
if (auto *decl = overload->choice.getDecl()) {
if (!decl->isOperator())
return None;
auto *fnType = overload->openedType->castTo<FunctionType>();
return fnType->getParams()[getArgIdx(tupleExpr, expr)];
}
}
return None;
}
void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt(
DeclContext *DC, Expr *expr) const {
auto *anchor = getAnchor();
// If anchor is an explicit address-of, or expression which produces
// an l-value (e.g. first argument of `+=` operator), let's not
// suggest default value here because that would produce r-value type.
if (isa<InOutExpr>(anchor))
return;
if (auto param = getOperatorParameterFor(anchor)) {
if (param->isInOut())
return;
}
auto diag = emitDiagnostic(expr->getLoc(), diag::unwrap_with_default_value);
auto &TC = getTypeChecker();
// Figure out what we need to parenthesize.
bool needsParensInside =
exprNeedsParensBeforeAddingNilCoalescing(TC, DC, expr);
bool needsParensOutside =
exprNeedsParensAfterAddingNilCoalescing(TC, DC, expr, getParentExpr());
llvm::SmallString<2> insertBefore;
llvm::SmallString<32> insertAfter;
if (needsParensOutside) {
insertBefore += "(";
}
if (needsParensInside) {
insertBefore += "(";
insertAfter += ")";
}
insertAfter += " ?? <" "#default value#" ">";
if (needsParensOutside)
insertAfter += ")";
if (!insertBefore.empty()) {
diag.fixItInsert(expr->getStartLoc(), insertBefore);
}
diag.fixItInsertAfter(expr->getEndLoc(), insertAfter);
}
// Suggest a force-unwrap.
void MissingOptionalUnwrapFailure::offerForceUnwrapFixIt(Expr *expr) const {
auto diag = emitDiagnostic(expr->getLoc(), diag::unwrap_with_force_value);
// If expr is optional as the result of an optional chain and this last
// dot isn't a member returning optional, then offer to force the last
// link in the chain, rather than an ugly parenthesized postfix force.
if (auto optionalChain = dyn_cast<OptionalEvaluationExpr>(expr)) {
if (auto dotExpr =
dyn_cast<UnresolvedDotExpr>(optionalChain->getSubExpr())) {
auto bind = dyn_cast<BindOptionalExpr>(dotExpr->getBase());
if (bind && !getType(dotExpr)->getOptionalObjectType()) {
diag.fixItReplace(SourceRange(bind->getLoc()), "!");
return;
}
}
}
if (expr->canAppendPostfixExpression(true)) {
diag.fixItInsertAfter(expr->getEndLoc(), "!");
} else {
diag.fixItInsert(expr->getStartLoc(), "(")
.fixItInsertAfter(expr->getEndLoc(), ")!");
}
}
class VarDeclMultipleReferencesChecker : public ASTWalker {
VarDecl *varDecl;
int count;
std::pair<bool, Expr *> walkToExprPre(Expr *E) {
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
if (DRE->getDecl() == varDecl)
count++;
}
return { true, E };
}
public:
VarDeclMultipleReferencesChecker(VarDecl *varDecl) : varDecl(varDecl),count(0) {}
int referencesCount() { return count; }
};
bool MissingOptionalUnwrapFailure::diagnoseAsError() {
if (hasComplexLocator())
return false;
auto *anchor = getAnchor();
if (auto assignExpr = dyn_cast<AssignExpr>(anchor))
anchor = assignExpr->getSrc();
auto *unwrappedExpr = anchor->getValueProvidingExpr();
if (auto *tryExpr = dyn_cast<OptionalTryExpr>(unwrappedExpr)) {
bool isSwift5OrGreater = getASTContext().isSwiftVersionAtLeast(5);
auto subExprType = getType(tryExpr->getSubExpr());
bool subExpressionIsOptional = (bool)subExprType->getOptionalObjectType();
if (isSwift5OrGreater && subExpressionIsOptional) {
// Using 'try!' won't change the type for a 'try?' with an optional
// sub-expr under Swift 5+, so just report that a missing unwrap can't be
// handled here.
return false;
}
emitDiagnostic(tryExpr->getTryLoc(), diag::missing_unwrap_optional_try,
getType(anchor)->getRValueType())
.fixItReplace({tryExpr->getTryLoc(), tryExpr->getQuestionLoc()},
"try!");
return true;
}
auto baseType = getBaseType();
auto unwrappedType = getUnwrappedType();
assert(!baseType->hasTypeVariable() &&
"Base type must not be a type variable");
assert(!unwrappedType->hasTypeVariable() &&
"Unwrapped type must not be a type variable");
if (!baseType->getOptionalObjectType())
return false;
emitDiagnostic(unwrappedExpr->getLoc(), diag::optional_not_unwrapped,
baseType, unwrappedType);
// If the expression we're unwrapping is the only reference to a
// local variable whose type isn't explicit in the source, then
// offer unwrapping fixits on the initializer as well.
if (auto declRef = dyn_cast<DeclRefExpr>(unwrappedExpr)) {
if (auto varDecl = dyn_cast<VarDecl>(declRef->getDecl())) {
bool singleUse = false;
AbstractFunctionDecl *AFD = nullptr;
if (auto contextDecl = varDecl->getDeclContext()->getAsDecl()) {
if ((AFD = dyn_cast<AbstractFunctionDecl>(contextDecl))) {
auto checker = VarDeclMultipleReferencesChecker(varDecl);
AFD->getBody()->walk(checker);
singleUse = checker.referencesCount() == 1;
}
}
PatternBindingDecl *binding = varDecl->getParentPatternBinding();
if (singleUse && binding && binding->getNumPatternEntries() == 1 &&
varDecl->getTypeSourceRangeForDiagnostics().isInvalid()) {
Expr *initializer = varDecl->getParentInitializer();
if (auto declRefExpr = dyn_cast<DeclRefExpr>(initializer)) {
if (declRefExpr->getDecl()
->getAttrs()
.hasAttribute<ImplicitlyUnwrappedOptionalAttr>()) {
emitDiagnostic(declRefExpr->getLoc(), diag::unwrap_iuo_initializer,
baseType);
}
}
auto fnTy = AFD->getInterfaceType()->castTo<AnyFunctionType>();
bool voidReturn =
fnTy->getResult()->isEqual(TupleType::getEmpty(getASTContext()));
auto diag = emitDiagnostic(varDecl->getLoc(), diag::unwrap_with_guard);
diag.fixItInsert(binding->getStartLoc(), "guard ");
if (voidReturn) {
diag.fixItInsertAfter(binding->getEndLoc(), " else { return }");
} else {
diag.fixItInsertAfter(binding->getEndLoc(), " else { return <"
"#default value#"
"> }");
}
diag.flush();
offerDefaultValueUnwrapFixIt(varDecl->getDeclContext(), initializer);
offerForceUnwrapFixIt(initializer);
}
}
}
offerDefaultValueUnwrapFixIt(getDC(), unwrappedExpr);
offerForceUnwrapFixIt(unwrappedExpr);
return true;
}
bool RValueTreatedAsLValueFailure::diagnoseAsError() {
Diag<StringRef> subElementDiagID;
Diag<Type> rvalueDiagID = diag::assignment_lhs_not_lvalue;
Expr *diagExpr = getRawAnchor();
SourceLoc loc = diagExpr->getLoc();
if (auto assignExpr = dyn_cast<AssignExpr>(diagExpr)) {
diagExpr = assignExpr->getDest();
}
if (auto callExpr = dyn_cast<ApplyExpr>(diagExpr)) {
Expr *argExpr = callExpr->getArg();
loc = callExpr->getFn()->getLoc();
if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
diagExpr = argExpr;
} else if (isa<BinaryExpr>(callExpr)) {
subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
auto argTuple = dyn_cast<TupleExpr>(argExpr);
diagExpr = argTuple->getElement(0);
} else if (getLocator()->getPath().size() > 0) {
auto lastPathElement = getLocator()->getPath().back();
assert(lastPathElement.getKind() ==
ConstraintLocator::PathElementKind::ApplyArgToParam);
subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
rvalueDiagID = diag::cannot_pass_rvalue_inout;
if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
diagExpr = argTuple->getElement(lastPathElement.getValue());
else if (auto parens = dyn_cast<ParenExpr>(argExpr))
diagExpr = parens->getSubExpr();
} else {
subElementDiagID = diag::assignment_lhs_is_apply_expression;
}
} else if (auto inoutExpr = dyn_cast<InOutExpr>(diagExpr)) {
if (auto restriction = getRestrictionForType(getType(inoutExpr))) {
PointerTypeKind pointerKind;
if (restriction->second == ConversionRestrictionKind::ArrayToPointer &&
restriction->first->getAnyPointerElementType(pointerKind) &&
(pointerKind == PTK_UnsafePointer ||
pointerKind == PTK_UnsafeRawPointer)) {
// If we're converting to an UnsafePointer, then the programmer
// specified an & unnecessarily. Produce a fixit hint to remove it.
emitDiagnostic(inoutExpr->getLoc(),
diag::extra_address_of_unsafepointer, restriction->first)
.highlight(inoutExpr->getSourceRange())
.fixItRemove(inoutExpr->getStartLoc());
return true;
}
}
subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
rvalueDiagID = diag::cannot_pass_rvalue_inout;
diagExpr = inoutExpr->getSubExpr();
} else if (isa<DeclRefExpr>(diagExpr)) {
subElementDiagID = diag::assignment_lhs_is_immutable_variable;
} else if (isa<ForceValueExpr>(diagExpr)) {
subElementDiagID = diag::assignment_bang_has_immutable_subcomponent;
} else if (isa<MemberRefExpr>(diagExpr)) {
subElementDiagID = diag::assignment_lhs_is_immutable_property;
} else if (auto member = dyn_cast<UnresolvedDotExpr>(diagExpr)) {
subElementDiagID = diag::assignment_lhs_is_immutable_property;
if (auto *ctor = dyn_cast<ConstructorDecl>(getDC())) {
if (auto *baseRef = dyn_cast<DeclRefExpr>(member->getBase())) {
if (baseRef->getDecl() == ctor->getImplicitSelfDecl() &&
ctor->getDelegatingOrChainedInitKind(nullptr) ==
ConstructorDecl::BodyInitKind::Delegating) {
emitDiagnostic(loc, diag::assignment_let_property_delegating_init,
member->getName());
if (auto *ref = getResolvedMemberRef(member)) {
emitDiagnostic(ref, diag::decl_declared_here, member->getName());
}
return true;
}
}
}
if (auto resolvedOverload = getResolvedOverload(getLocator())) {
if (resolvedOverload->Choice.getKind() ==
OverloadChoiceKind::DynamicMemberLookup)
subElementDiagID = diag::assignment_dynamic_property_has_immutable_base;
if (resolvedOverload->Choice.getKind() ==
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
if (!getType(member->getBase())->hasLValueType())
subElementDiagID =
diag::assignment_dynamic_property_has_immutable_base;
}
}
} else if (auto sub = dyn_cast<SubscriptExpr>(diagExpr)) {
subElementDiagID = diag::assignment_subscript_has_immutable_base;
} else {
subElementDiagID = diag::assignment_lhs_is_immutable_variable;
}
AssignmentFailure failure(diagExpr, getConstraintSystem(), loc,
subElementDiagID, rvalueDiagID);
return failure.diagnose();
}
bool TrailingClosureAmbiguityFailure::diagnoseAsNote() {
const auto *expr = getParentExpr();
auto *callExpr = dyn_cast<CallExpr>(expr);
if (!callExpr)
return false;
if (!callExpr->hasTrailingClosure())
return false;
if (callExpr->getFn() != getAnchor())
return false;
llvm::SmallMapVector<Identifier, const ValueDecl *, 8> choicesByLabel;
for (const auto &choice : Choices) {
auto *callee = dyn_cast<AbstractFunctionDecl>(choice.getDecl());
if (!callee)
return false;
const ParameterList *paramList = callee->getParameters();
const ParamDecl *param = paramList->getArray().back();
// Sanity-check that the trailing closure corresponds to this parameter.
if (!param->hasValidSignature() ||
!param->getInterfaceType()->is<AnyFunctionType>())
return false;
Identifier trailingClosureLabel = param->getArgumentName();
auto &choiceForLabel = choicesByLabel[trailingClosureLabel];
// FIXME: Cargo-culted from diagnoseAmbiguity: apparently the same decl can
// appear more than once?
if (choiceForLabel == callee)
continue;
// If just providing the trailing closure label won't solve the ambiguity,
// don't bother offering the fix-it.
if (choiceForLabel != nullptr)
return false;
choiceForLabel = callee;
}
// If we got here, then all of the choices have unique labels. Offer them in
// order.
for (const auto &choicePair : choicesByLabel) {
auto diag = emitDiagnostic(
expr->getLoc(), diag::ambiguous_because_of_trailing_closure,
choicePair.first.empty(), choicePair.second->getFullName());
swift::fixItEncloseTrailingClosure(getTypeChecker(), diag, callExpr,
choicePair.first);
}
return true;
}
AssignmentFailure::AssignmentFailure(Expr *destExpr, ConstraintSystem &cs,
SourceLoc diagnosticLoc)
: FailureDiagnostic(destExpr, cs, cs.getConstraintLocator(destExpr)),
Loc(diagnosticLoc),
DeclDiagnostic(findDeclDiagonstic(cs.getASTContext(), destExpr)),
TypeDiagnostic(diag::assignment_lhs_not_lvalue) {}
bool AssignmentFailure::diagnoseAsError() {
auto &cs = getConstraintSystem();
auto *DC = getDC();
auto *destExpr = getParentExpr();
// Walk through the destination expression, resolving what the problem is. If
// we find a node in the lvalue path that is problematic, this returns it.
auto immInfo = resolveImmutableBase(destExpr);
// Otherwise, we cannot resolve this because the available setter candidates
// are all mutating and the base must be mutating. If we dug out a
// problematic decl, we can produce a nice tailored diagnostic.
if (auto *VD = dyn_cast_or_null<VarDecl>(immInfo.second)) {
std::string message = "'";
message += VD->getName().str().str();
message += "'";
auto type = getType(immInfo.first);
auto bgt = type ? type->getAs<BoundGenericType>() : nullptr;
if (bgt && bgt->getDecl() == getASTContext().getKeyPathDecl())
message += " is a read-only key path";
else if (VD->isCaptureList())
message += " is an immutable capture";
else if (VD->isImplicit())
message += " is immutable";
else if (VD->isLet())
message += " is a 'let' constant";
else if (!VD->isSettable(DC))
message += " is a get-only property";
else if (!VD->isSetterAccessibleFrom(DC))
message += " setter is inaccessible";
else {
message += " is immutable";
}
emitDiagnostic(Loc, DeclDiagnostic, message)
.highlight(immInfo.first->getSourceRange());
// If there is a masked instance variable of the same type, emit a
// note to fixit prepend a 'self.'.
if (auto typeContext = DC->getInnermostTypeContext()) {
UnqualifiedLookup lookup(VD->getFullName(), typeContext,
getASTContext().getLazyResolver());
for (auto &result : lookup.Results) {
const VarDecl *typeVar = dyn_cast<VarDecl>(result.getValueDecl());
if (typeVar && typeVar != VD && typeVar->isSettable(DC) &&
typeVar->isSetterAccessibleFrom(DC) &&
typeVar->getType()->isEqual(VD->getType())) {
// But not in its own accessor.
auto AD =
dyn_cast_or_null<AccessorDecl>(DC->getInnermostMethodContext());
if (!AD || AD->getStorage() != typeVar) {
emitDiagnostic(Loc, diag::masked_instance_variable,
typeContext->getSelfTypeInContext())
.fixItInsert(Loc, "self.");
}
}
}
}
// If this is a simple variable marked with a 'let', emit a note to fixit
// hint it to 'var'.
VD->emitLetToVarNoteIfSimple(DC);
return true;
}
// If the underlying expression was a read-only subscript, diagnose that.
if (auto *SD = dyn_cast_or_null<SubscriptDecl>(immInfo.second)) {
StringRef message;
if (!SD->isSettable())
message = "subscript is get-only";
else if (!SD->isSetterAccessibleFrom(DC))
message = "subscript setter is inaccessible";
else
message = "subscript is immutable";
emitDiagnostic(Loc, DeclDiagnostic, message)
.highlight(immInfo.first->getSourceRange());
return true;
}
// If we're trying to set an unapplied method, say that.
if (auto *VD = immInfo.second) {
std::string message = "'";
message += VD->getBaseName().getIdentifier().str();
message += "'";
auto diagID = DeclDiagnostic;
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(VD)) {
if (AFD->hasImplicitSelfDecl()) {
message += " is a method";
diagID = diag::assignment_lhs_is_immutable_variable;
} else {
message += " is a function";
}
} else
message += " is not settable";
emitDiagnostic(Loc, diagID, message)
.highlight(immInfo.first->getSourceRange());
return true;
}
// If a keypath was the problem but wasn't resolved into a vardecl
// it is ambiguous or unable to be used for setting.
if (auto *KPE = dyn_cast_or_null<KeyPathExpr>(immInfo.first)) {
emitDiagnostic(Loc, DeclDiagnostic, "immutable key path")
.highlight(KPE->getSourceRange());
return true;
}
if (auto LE = dyn_cast<LiteralExpr>(immInfo.first)) {
emitDiagnostic(Loc, DeclDiagnostic, "literals are not mutable")
.highlight(LE->getSourceRange());
return true;
}
// If the expression is the result of a call, it is an rvalue, not a mutable
// lvalue.
if (auto *AE = dyn_cast<ApplyExpr>(immInfo.first)) {
// Handle literals, which are a call to the conversion function.
auto argsTuple =
dyn_cast<TupleExpr>(AE->getArg()->getSemanticsProvidingExpr());
if (isa<CallExpr>(AE) && AE->isImplicit() && argsTuple &&
argsTuple->getNumElements() == 1) {
if (auto LE = dyn_cast<LiteralExpr>(
argsTuple->getElement(0)->getSemanticsProvidingExpr())) {
emitDiagnostic(Loc, DeclDiagnostic, "literals are not mutable")
.highlight(LE->getSourceRange());
return true;
}
}
std::string name = "call";
if (isa<PrefixUnaryExpr>(AE) || isa<PostfixUnaryExpr>(AE))
name = "unary operator";
else if (isa<BinaryExpr>(AE))
name = "binary operator";
else if (isa<CallExpr>(AE))
name = "function call";
else if (isa<DotSyntaxCallExpr>(AE) || isa<DotSyntaxBaseIgnoredExpr>(AE))
name = "method call";
if (auto *DRE = dyn_cast<DeclRefExpr>(AE->getFn()->getValueProvidingExpr()))
name = std::string("'") +
DRE->getDecl()->getBaseName().getIdentifier().str().str() + "'";
emitDiagnostic(Loc, DeclDiagnostic, name + " returns immutable value")
.highlight(AE->getSourceRange());
return true;
}
if (auto contextualType = cs.getContextualType(immInfo.first)) {
Type neededType = contextualType->getInOutObjectType();
Type actualType = getType(immInfo.first)->getInOutObjectType();
if (!neededType->isEqual(actualType)) {
if (DeclDiagnostic.ID == diag::cannot_pass_rvalue_inout_subelement.ID) {
// We have a special diagnostic with tailored wording for this
// common case.
emitDiagnostic(Loc, diag::cannot_pass_rvalue_inout_converted,
actualType, neededType)
.highlight(immInfo.first->getSourceRange());
if (auto inoutExpr = dyn_cast<InOutExpr>(immInfo.first))
fixItChangeInoutArgType(inoutExpr->getSubExpr(), actualType,
neededType);
} else {
emitDiagnostic(Loc, DeclDiagnostic,
"implicit conversion from '" + actualType->getString() +
"' to '" + neededType->getString() +
"' requires a temporary")
.highlight(immInfo.first->getSourceRange());
}
return true;
}
}
if (auto IE = dyn_cast<IfExpr>(immInfo.first)) {
if (isLoadedLValue(IE)) {
emitDiagnostic(Loc, DeclDiagnostic,
"result of conditional operator '? :' is never mutable")
.highlight(IE->getQuestionLoc())
.highlight(IE->getColonLoc());
return true;
}
}
emitDiagnostic(Loc, TypeDiagnostic, getType(destExpr))
.highlight(immInfo.first->getSourceRange());
return true;
}
void AssignmentFailure::fixItChangeInoutArgType(const Expr *arg,
Type actualType,
Type neededType) const {
auto *DC = getDC();
auto *DRE = dyn_cast<DeclRefExpr>(arg);
if (!DRE)
return;
auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());
if (!VD)
return;
// Don't emit for non-local variables.
// (But in script-mode files, we consider module-scoped
// variables in the same file to be local variables.)
auto VDC = VD->getDeclContext();
bool isLocalVar = VDC->isLocalContext();
if (!isLocalVar && VDC->isModuleScopeContext()) {
auto argFile = DC->getParentSourceFile();
auto varFile = VDC->getParentSourceFile();
isLocalVar = (argFile == varFile && argFile->isScriptMode());
}
if (!isLocalVar)
return;
SmallString<32> scratch;
SourceLoc endLoc; // Filled in if we decide to diagnose this
SourceLoc startLoc; // Left invalid if we're inserting
auto isSimpleTypelessPattern = [](Pattern *P) -> bool {
if (auto VP = dyn_cast_or_null<VarPattern>(P))
P = VP->getSubPattern();
return P && isa<NamedPattern>(P);
};
auto typeRange = VD->getTypeSourceRangeForDiagnostics();
if (typeRange.isValid()) {
startLoc = typeRange.Start;
endLoc = typeRange.End;
} else if (isSimpleTypelessPattern(VD->getParentPattern())) {
endLoc = VD->getNameLoc();
scratch += ": ";
}
if (endLoc.isInvalid())
return;
scratch += neededType.getString();
// Adjust into the location where we actually want to insert
endLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, endLoc);
// Since we already adjusted endLoc, this will turn an insertion
// into a zero-character replacement.
if (!startLoc.isValid())
startLoc = endLoc;
emitDiagnostic(VD->getLoc(), diag::inout_change_var_type_if_possible,
actualType, neededType)
.fixItReplaceChars(startLoc, endLoc, scratch);
}
std::pair<Expr *, ValueDecl *>
AssignmentFailure::resolveImmutableBase(Expr *expr) const {
auto &cs = getConstraintSystem();
auto *DC = getDC();
expr = expr->getValueProvidingExpr();
// Provide specific diagnostics for assignment to subscripts whose base expr
// is known to be an rvalue.
if (auto *SE = dyn_cast<SubscriptExpr>(expr)) {
// If we found a decl for the subscript, check to see if it is a set-only
// subscript decl.
SubscriptDecl *member = nullptr;
if (SE->hasDecl())
member = dyn_cast_or_null<SubscriptDecl>(SE->getDecl().getDecl());
if (!member) {
auto loc =
cs.getConstraintLocator(SE, ConstraintLocator::SubscriptMember);
member = dyn_cast_or_null<SubscriptDecl>(getMemberRef(loc));
}
// If it isn't settable, return it.
if (member) {
if (!member->isSettable() || !member->isSetterAccessibleFrom(DC))
return {expr, member};
}
if (auto tupleExpr = dyn_cast<TupleExpr>(SE->getIndex())) {
if (tupleExpr->getNumElements() == 1 &&
tupleExpr->getElementName(0).str() == "keyPath") {
auto indexType = getType(tupleExpr->getElement(0));
if (auto bgt = indexType->getAs<BoundGenericType>()) {
if (bgt->getDecl() == getASTContext().getKeyPathDecl())
return resolveImmutableBase(tupleExpr->getElement(0));
}
}
}
// If it is settable, then the base must be the problem, recurse.
return resolveImmutableBase(SE->getBase());
}
// Look through property references.
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
// If we found a decl for the UDE, check it.
auto loc = cs.getConstraintLocator(UDE, ConstraintLocator::Member);
auto *member = getMemberRef(loc);
// If we can resolve a member, we can determine whether it is settable in
// this context.
if (member) {
auto *memberVD = dyn_cast<VarDecl>(member);
// If the member isn't a vardecl (e.g. its a funcdecl), or it isn't
// settable, then it is the problem: return it.
if (!memberVD || !member->isSettable(nullptr) ||
!memberVD->isSetterAccessibleFrom(DC))
return {expr, member};
}
// If we weren't able to resolve a member or if it is mutable, then the
// problem must be with the base, recurse.
return resolveImmutableBase(UDE->getBase());
}
if (auto *MRE = dyn_cast<MemberRefExpr>(expr)) {
// If the member isn't settable, then it is the problem: return it.
if (auto member = dyn_cast<AbstractStorageDecl>(MRE->getMember().getDecl()))
if (!member->isSettable(nullptr) || !member->isSetterAccessibleFrom(DC))
return {expr, member};
// If we weren't able to resolve a member or if it is mutable, then the
// problem must be with the base, recurse.
return resolveImmutableBase(MRE->getBase());
}
if (auto *DRE = dyn_cast<DeclRefExpr>(expr))
return {expr, DRE->getDecl()};
// Look through x!
if (auto *FVE = dyn_cast<ForceValueExpr>(expr))
return resolveImmutableBase(FVE->getSubExpr());
// Look through x?
if (auto *BOE = dyn_cast<BindOptionalExpr>(expr))
return resolveImmutableBase(BOE->getSubExpr());
// Look through implicit conversions
if (auto *ICE = dyn_cast<ImplicitConversionExpr>(expr))
if (!isa<LoadExpr>(ICE->getSubExpr()))
return resolveImmutableBase(ICE->getSubExpr());
if (auto *SAE = dyn_cast<SelfApplyExpr>(expr))
return resolveImmutableBase(SAE->getFn());
return {expr, nullptr};
}
ValueDecl *AssignmentFailure::getMemberRef(ConstraintLocator *locator) const {
auto member = getOverloadChoiceIfAvailable(locator);
if (!member || !member->choice.isDecl())
return nullptr;
auto *DC = getDC();
auto &TC = getTypeChecker();
auto *decl = member->choice.getDecl();
if (isa<SubscriptDecl>(decl) &&
isValidDynamicMemberLookupSubscript(cast<SubscriptDecl>(decl), DC, TC)) {
auto *subscript = cast<SubscriptDecl>(decl);
// If this is a keypath dynamic member lookup, we have to
// adjust the locator to find member referred by it.
if (isValidKeyPathDynamicMemberLookup(subscript, TC)) {
auto &cs = getConstraintSystem();
// Type has a following format:
// `(Self) -> (dynamicMember: {Writable}KeyPath<T, U>) -> U`
auto *fullType = member->openedFullType->castTo<FunctionType>();
auto *fnType = fullType->getResult()->castTo<FunctionType>();
auto paramTy = fnType->getParams()[0].getPlainType();
auto keyPath = paramTy->getAnyNominal();
auto memberLoc = cs.getConstraintLocator(
locator, LocatorPathElt::getKeyPathDynamicMember(keyPath));
auto memberRef = getOverloadChoiceIfAvailable(memberLoc);
return memberRef ? memberRef->choice.getDecl() : nullptr;
}
// If this is a string based dynamic lookup, there is no member declaration.
return nullptr;
}
return decl;
}
Diag<StringRef> AssignmentFailure::findDeclDiagonstic(ASTContext &ctx,
Expr *destExpr) {
if (isa<ApplyExpr>(destExpr) || isa<SelfApplyExpr>(destExpr))
return diag::assignment_lhs_is_apply_expression;
if (isa<UnresolvedDotExpr>(destExpr) || isa<MemberRefExpr>(destExpr))
return diag::assignment_lhs_is_immutable_property;
if (auto *subscript = dyn_cast<SubscriptExpr>(destExpr)) {
auto diagID = diag::assignment_subscript_has_immutable_base;
// If the destination is a subscript with a 'dynamicLookup:' label and if
// the tuple is implicit, then this was actually a @dynamicMemberLookup
// access. Emit a more specific diagnostic.
if (subscript->getIndex()->isImplicit() &&
subscript->getArgumentLabels().size() == 1 &&
subscript->getArgumentLabels().front() == ctx.Id_dynamicMember)
diagID = diag::assignment_dynamic_property_has_immutable_base;
return diagID;
}
return diag::assignment_lhs_is_immutable_variable;
}
bool ContextualFailure::diagnoseAsError() {
auto *anchor = getAnchor();
auto path = getLocator()->getPath();
assert(!path.empty());
if (diagnoseMissingFunctionCall())
return true;
Diag<Type, Type> diagnostic;
switch (path.back().getKind()) {
case ConstraintLocator::ClosureResult: {
diagnostic = diag::cannot_convert_closure_result;
break;
}
default:
return false;
}
auto diag = emitDiagnostic(anchor->getLoc(), diagnostic, FromType, ToType);
diag.highlight(anchor->getSourceRange());
(void)trySequenceSubsequenceFixIts(diag, getConstraintSystem(), FromType,
ToType, anchor);
return true;
}
bool ContextualFailure::diagnoseMissingFunctionCall() const {
auto &TC = getTypeChecker();
auto *srcFT = FromType->getAs<FunctionType>();
if (!srcFT || !srcFT->getParams().empty())
return false;
if (ToType->is<AnyFunctionType>() ||
!TC.isConvertibleTo(srcFT->getResult(), ToType, getDC()))
return false;
auto *anchor = getAnchor();
emitDiagnostic(anchor->getLoc(), diag::missing_nullary_call,
srcFT->getResult())
.highlight(anchor->getSourceRange())
.fixItInsertAfter(anchor->getEndLoc(), "()");
tryComputedPropertyFixIts(anchor);
return true;
}
bool ContextualFailure::trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
ConstraintSystem &CS,
Type fromType, Type toType,
Expr *expr) {
if (!CS.TC.Context.getStdlibModule())
return false;
auto String = CS.TC.getStringType(CS.DC);
auto Substring = CS.TC.getSubstringType(CS.DC);
if (!String || !Substring)
return false;
// Substring -> String conversion
// Wrap in String.init
if (fromType->isEqual(Substring)) {
if (toType->isEqual(String)) {
auto range = expr->getSourceRange();
diag.fixItInsert(range.Start, "String(");
diag.fixItInsertAfter(range.End, ")");
return true;
}
}
return false;
}
void ContextualFailure::tryComputedPropertyFixIts(Expr *expr) const {
if (!isa<ClosureExpr>(expr))
return;
// It is possible that we're looking at a stored property being
// initialized with a closure. Something like:
//
// var foo: Int = { return 0 }
//
// Let's offer another fix-it to remove the '=' to turn the stored
// property into a computed property. If the variable is immutable, then
// replace the 'let' with a 'var'.
PatternBindingDecl *PBD = nullptr;
if (auto TLCD = dyn_cast<TopLevelCodeDecl>(getDC())) {
if (TLCD->getBody()->isImplicit()) {
if (auto decl = TLCD->getBody()->getElement(0).dyn_cast<Decl *>()) {
if (auto binding = dyn_cast<PatternBindingDecl>(decl)) {
PBD = binding;
}
}
}
} else if (auto PBI = dyn_cast<PatternBindingInitializer>(getDC())) {
PBD = PBI->getBinding();
}
if (PBD) {
if (auto VD = PBD->getSingleVar()) {
auto entry = PBD->getPatternEntryForVarDecl(VD);
if (!VD->isStatic() &&
!VD->getAttrs().getAttribute<DynamicReplacementAttr>() &&
entry.getInit() && isa<ClosureExpr>(entry.getInit())) {
auto diag = emitDiagnostic(expr->getLoc(),
diag::extension_stored_property_fixit,
VD->getName());
diag.fixItRemove(entry.getEqualLoc());
if (VD->isLet()) {
diag.fixItReplace(PBD->getStartLoc(), getTokenText(tok::kw_var));
}
}
}
}
}
bool AutoClosureForwardingFailure::diagnoseAsError() {
auto path = getLocator()->getPath();
assert(!path.empty());
auto &last = path.back();
assert(last.getKind() == ConstraintLocator::ApplyArgToParam);
// We need a raw anchor here because `getAnchor()` is simplified
// to the argument expression.
auto *argExpr = getArgumentExpr(getRawAnchor(), last.getValue());
emitDiagnostic(argExpr->getLoc(), diag::invalid_autoclosure_forwarding)
.highlight(argExpr->getSourceRange())
.fixItInsertAfter(argExpr->getEndLoc(), "()");
return true;
}
bool NonOptionalUnwrapFailure::diagnoseAsError() {
auto *anchor = getAnchor();
auto diagnostic = diag::invalid_optional_chain;
if (isa<ForceValueExpr>(anchor))
diagnostic = diag::invalid_force_unwrap;
emitDiagnostic(anchor->getLoc(), diagnostic, BaseType)
.highlight(anchor->getSourceRange())
.fixItRemove(anchor->getEndLoc());
return true;
}
bool MissingCallFailure::diagnoseAsError() {
auto *baseExpr = getAnchor();
SourceLoc insertLoc = baseExpr->getEndLoc();
if (auto *FVE = dyn_cast<ForceValueExpr>(baseExpr))
baseExpr = FVE->getSubExpr();
// Calls are not yet supported by key path, but it
// is useful to record this fix to diagnose chaining
// where one of the key path components is a method
// reference.
if (isa<KeyPathExpr>(baseExpr))
return false;
if (auto *DRE = dyn_cast<DeclRefExpr>(baseExpr)) {
emitDiagnostic(baseExpr->getLoc(), diag::did_not_call_function,
DRE->getDecl()->getBaseName().getIdentifier())
.fixItInsertAfter(insertLoc, "()");
return true;
}
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(baseExpr)) {
emitDiagnostic(baseExpr->getLoc(), diag::did_not_call_method,
UDE->getName().getBaseIdentifier())
.fixItInsertAfter(insertLoc, "()");
return true;
}
if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(baseExpr)) {
if (auto *DRE = dyn_cast<DeclRefExpr>(DSCE->getFn())) {
emitDiagnostic(baseExpr->getLoc(), diag::did_not_call_method,
DRE->getDecl()->getBaseName().getIdentifier())
.fixItInsertAfter(insertLoc, "()");
return true;
}
}
emitDiagnostic(baseExpr->getLoc(), diag::did_not_call_function_value)
.fixItInsertAfter(insertLoc, "()");
return true;
}
bool SubscriptMisuseFailure::diagnoseAsError() {
auto &sourceMgr = getASTContext().SourceMgr;
auto *memberExpr = cast<UnresolvedDotExpr>(getRawAnchor());
auto *baseExpr = getAnchor();
auto memberRange = baseExpr->getSourceRange();
(void)simplifyLocator(getConstraintSystem(), getLocator(), memberRange);
auto nameLoc = DeclNameLoc(memberRange.Start);
auto diag = emitDiagnostic(baseExpr->getLoc(),
diag::could_not_find_subscript_member_did_you_mean,
getType(baseExpr));
diag.highlight(memberRange).highlight(nameLoc.getSourceRange());
auto *parentExpr = findParentExpr(memberExpr);
assert(parentExpr && "Couldn't find a parent expression for a member call?!");
auto *argExpr = cast<ApplyExpr>(parentExpr)->getArg();
auto toCharSourceRange = Lexer::getCharSourceRangeFromSourceRange;
auto lastArgSymbol = toCharSourceRange(sourceMgr, argExpr->getEndLoc());
diag.fixItReplace(SourceRange(argExpr->getStartLoc()),
getTokenText(tok::l_square));
diag.fixItRemove(nameLoc.getSourceRange());
diag.fixItRemove(SourceRange(memberExpr->getDotLoc()));
if (sourceMgr.extractText(lastArgSymbol) == getTokenText(tok::r_paren))
diag.fixItReplace(SourceRange(argExpr->getEndLoc()),
getTokenText(tok::r_square));
else
diag.fixItInsertAfter(argExpr->getEndLoc(), getTokenText(tok::r_square));
diag.flush();
if (auto overload = getOverloadChoiceIfAvailable(getLocator())) {
emitDiagnostic(overload->choice.getDecl(), diag::kind_declared_here,
DescriptiveDeclKind::Subscript);
}
return true;
}
bool SubscriptMisuseFailure::diagnoseAsNote() {
if (auto overload = getOverloadChoiceIfAvailable(getLocator())) {
emitDiagnostic(overload->choice.getDecl(), diag::found_candidate);
return true;
}
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;
}
bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
auto loc = getAnchor()->getLoc();
auto &cs = getConstraintSystem();
auto locator = getLocator();
if (loc.isInvalid()) {
return true;
}
Expr *expr = getParentExpr();
SourceRange baseRange = expr ? expr->getSourceRange() : SourceRange();
auto overload = getOverloadChoiceIfAvailable(locator);
if (!overload)
return false;
ValueDecl *decl = nullptr;
if (!overload->choice.isDecl()) {
auto baseTy = overload->choice.getBaseType();
if (auto MT = baseTy->getAs<MetatypeType>()) {
if (auto VD = dyn_cast<ValueDecl>(
MT->getMetatypeInstanceType()->getAnyNominal()->getAsDecl())) {
decl = VD;
}
} else {
return true;
}
}
auto member = decl ? decl : overload->choice.getDecl();
// If the base is an implicit self type reference, and we're in a
// an initializer, then the user wrote something like:
//
// class Foo { let x = 1, y = x }
//
// which runs in type context, not instance context, or
//
// class Bar {
// let otherwise = 1 // instance member
// var x: Int
// func init(x: Int =otherwise) { // default parameter
// self.x = x
// }
// }
//
// in which an instance member is used as a default value for a
// parameter.
//
// Produce a tailored diagnostic for these cases since this
// comes up and is otherwise non-obvious what is going on.
if (Name.isSimpleName(DeclBaseName::createConstructor()) &&
!BaseType->getRValueType()->is<AnyMetatypeType>()) {
if (auto ctorRef = dyn_cast<UnresolvedDotExpr>(getRawAnchor())) {
if (isa<SuperRefExpr>(ctorRef->getBase())) {
emitDiagnostic(loc, diag::super_initializer_not_in_initializer);
return true;
}
SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange();
emitDiagnostic(loc, diag::init_not_instance_member)
.fixItInsert(fixItRng.Start, "type(of: ")
.fixItInsertAfter(fixItRng.End, ")");
return true;
}
}
if (BaseType->is<AnyMetatypeType>() && !member->isStatic()) {
auto instanceTy = BaseType->getRValueType();
if (auto *AMT = instanceTy->getAs<AnyMetatypeType>()) {
instanceTy = AMT->getInstanceType();
}
if (getRawAnchor() &&
cs.DC->getContextKind() == DeclContextKind::Initializer) {
auto *TypeDC = cs.DC->getParent();
bool propertyInitializer = true;
// If the parent context is not a type context, we expect it
// to be a defaulted parameter in a function declaration.
if (!TypeDC->isTypeContext()) {
assert(TypeDC->getContextKind() ==
DeclContextKind::AbstractFunctionDecl &&
"Expected function decl context for initializer!");
TypeDC = TypeDC->getParent();
propertyInitializer = false;
}
assert(TypeDC->isTypeContext() && "Expected type decl context!");
if (TypeDC->getSelfNominalTypeDecl() == instanceTy->getAnyNominal()) {
if (propertyInitializer) {
emitDiagnostic(loc, diag::instance_member_in_initializer, Name);
return true;
} else {
emitDiagnostic(loc, diag::instance_member_in_default_parameter, Name);
return true;
}
}
}
auto maybeCallExpr = getRawAnchor();
if (auto UDE = dyn_cast<UnresolvedDotExpr>(maybeCallExpr)) {
maybeCallExpr = UDE->getBase();
}
if (auto callExpr = dyn_cast<ApplyExpr>(maybeCallExpr)) {
auto fnExpr = callExpr->getFn();
auto fnType = cs.getType(fnExpr)->getRValueType();
auto arg = callExpr->getArg();
if (fnType->is<ExistentialMetatypeType>()) {
emitDiagnostic(arg->getStartLoc(),
diag::missing_init_on_metatype_initialization)
.highlight(fnExpr->getSourceRange());
return true;
}
}
// Check whether the instance member is declared on parent context and if so
// provide more specialized message.
auto memberTypeContext = member->getDeclContext()->getInnermostTypeContext();
auto currentTypeContext = cs.DC->getInnermostTypeContext();
if (memberTypeContext && currentTypeContext &&
memberTypeContext->getSemanticDepth() <
currentTypeContext->getSemanticDepth()) {
emitDiagnostic(loc, diag::could_not_use_instance_member_on_type,
currentTypeContext->getDeclaredInterfaceType(), Name,
memberTypeContext->getDeclaredInterfaceType(), true)
.highlight(baseRange)
.highlight(member->getSourceRange());
return true;
}
if (isa<TypeExpr>(getRawAnchor())) {
emitDiagnostic(loc, diag::instance_member_use_on_type, instanceTy, Name)
.highlight(getRawAnchor()->getSourceRange());
return true;
}
// Just emit a generic "instance member cannot be used" error
emitDiagnostic(loc, diag::could_not_use_instance_member_on_type, instanceTy,
Name, instanceTy, false)
.highlight(getAnchor()->getSourceRange());
return true;
} else {
// If the base of the lookup is a protocol metatype, suggest
// to replace the metatype with 'Self'
// error saying the lookup cannot be on a protocol metatype
Optional<InFlightDiagnostic> Diag;
auto baseObjTy = BaseType->getRValueType();
if (auto metatypeTy = baseObjTy->getAs<MetatypeType>()) {
auto instanceTy = metatypeTy->getInstanceType();
// This will only happen if we have an unresolved dot expression
// (.foo) where foo is a protocol member and the contextual type is
// an optional protocol metatype.
if (auto objectTy = instanceTy->getOptionalObjectType()) {
instanceTy = objectTy;
baseObjTy = MetatypeType::get(objectTy);
}
assert(instanceTy->isExistentialType());
// Give a customized message if we're accessing a member type
// of a protocol -- otherwise a diagnostic talking about
// static members doesn't make a whole lot of sense
if (auto TAD = dyn_cast<TypeAliasDecl>(member)) {
Diag.emplace(emitDiagnostic(loc, diag::typealias_outside_of_protocol,
TAD->getName()));
} else if (auto ATD = dyn_cast<AssociatedTypeDecl>(member)) {
Diag.emplace(emitDiagnostic(loc, diag::assoc_type_outside_of_protocol,
ATD->getName()));
} else if (isa<ConstructorDecl>(member)) {
Diag.emplace(emitDiagnostic(loc, diag::construct_protocol_by_name,
instanceTy));
} else {
Diag.emplace(emitDiagnostic(loc,
diag::could_not_use_type_member_on_protocol_metatype,
baseObjTy, Name));
}
Diag->highlight(baseRange).highlight(getAnchor()->getSourceRange());
// See through function decl context
if (auto parent = cs.DC->getInnermostTypeContext()) {
// If we are in a protocol extension of 'Proto' and we see
// 'Proto.static', suggest 'Self.static'
if (auto extensionContext = parent->getExtendedProtocolDecl()) {
if (extensionContext->getDeclaredType()->isEqual(instanceTy)) {
Diag->fixItReplace(getAnchor()->getSourceRange(), "Self");
}
}
}
return true;
}
// If this is a reference to a static member by one of the key path
// components, let's provide a tailored diagnostic and return because
// that is unsupported so there is no fix-it.
if (locator->isForKeyPathComponent()) {
InvalidStaticMemberRefInKeyPath failure(expr, getConstraintSystem(),
member, locator);
return failure.diagnoseAsError();
}
if (isa<EnumElementDecl>(member)) {
Diag.emplace(emitDiagnostic(
loc, diag::could_not_use_enum_element_on_instance, Name));
} else {
Diag.emplace(emitDiagnostic(
loc, diag::could_not_use_type_member_on_instance, baseObjTy, Name));
}
Diag->highlight(getAnchor()->getSourceRange());
if (Name.isSimpleName(DeclBaseName::createConstructor()) &&
!baseObjTy->is<AnyMetatypeType>()) {
if (auto ctorRef = dyn_cast<UnresolvedDotExpr>(getRawAnchor())) {
SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange();
Diag->fixItInsert(fixItRng.Start, "type(of: ");
Diag->fixItInsertAfter(fixItRng.End, ")");
return true;
}
}
// Determine the contextual type of the expression
Type contextualType;
for (auto iterateCS = &cs; contextualType.isNull() && iterateCS;
iterateCS = iterateCS->baseCS) {
contextualType = iterateCS->getContextualType();
}
// Try to provide a fix-it that only contains a '.'
if (contextualType) {
if (baseObjTy->isEqual(contextualType)) {
Diag->fixItInsert(loc, ".");
return true;
}
}
// Check if the expression is the matching operator ~=, most often used in
// case statements. If so, try to provide a single dot fix-it
const Expr *contextualTypeNode = nullptr;
ConstraintSystem *lastCS = nullptr;
for (auto iterateCS = &cs; iterateCS; iterateCS = iterateCS->baseCS) {
lastCS = iterateCS;
contextualTypeNode = iterateCS->getContextualTypeNode();
}
// The '~=' operator is an overloaded decl ref inside a binaryExpr
if (auto binaryExpr = dyn_cast<BinaryExpr>(contextualTypeNode)) {
if (auto overloadedFn
= dyn_cast<OverloadedDeclRefExpr>(binaryExpr->getFn())) {
if (!overloadedFn->getDecls().empty()) {
// Fetch any declaration to check if the name is '~='
ValueDecl *decl0 = overloadedFn->getDecls()[0];
if (decl0->getBaseName() == decl0->getASTContext().Id_MatchOperator) {
assert(binaryExpr->getArg()->getElements().size() == 2);
// If the rhs of '~=' is the enum type, a single dot suffixes
// since the type can be inferred
Type secondArgType =
lastCS->getType(binaryExpr->getArg()->getElement(1));
if (secondArgType->isEqual(baseObjTy)) {
Diag->fixItInsert(loc, ".");
return true;
}
}
}
}
}
// Fall back to a fix-it with a full type qualifier
auto nominal = member->getDeclContext()->getSelfNominalTypeDecl();
SmallString<32> typeName;
llvm::raw_svector_ostream typeNameStream(typeName);
typeNameStream << nominal->getSelfInterfaceType() << ".";
Diag->fixItInsert(loc, typeNameStream.str());
return true;
}
return false;
}
bool PartialApplicationFailure::diagnoseAsError() {
auto &cs = getConstraintSystem();
auto *anchor = cast<UnresolvedDotExpr>(getRawAnchor());
RefKind kind = RefKind::MutatingMethod;
// If this is initializer delegation chain, we have a tailored message.
if (getOverloadChoiceIfAvailable(cs.getConstraintLocator(
anchor, ConstraintLocator::ConstructorMember))) {
kind = anchor->getBase()->isSuperExpr() ? RefKind::SuperInit
: RefKind::SelfInit;
}
auto diagnostic = CompatibilityWarning
? diag::partial_application_of_function_invalid_swift4
: diag::partial_application_of_function_invalid;
emitDiagnostic(anchor->getNameLoc(), diagnostic, kind);
return true;
}
bool InvalidDynamicInitOnMetatypeFailure::diagnoseAsError() {
auto *anchor = getRawAnchor();
emitDiagnostic(anchor->getLoc(), diag::dynamic_construct_class,
BaseType->getMetatypeInstanceType())
.highlight(BaseRange);
emitDiagnostic(Init, diag::note_nonrequired_initializer, Init->isImplicit(),
Init->getFullName());
return true;
}
bool InitOnProtocolMetatypeFailure::diagnoseAsError() {
auto *anchor = getRawAnchor();
if (IsStaticallyDerived) {
emitDiagnostic(anchor->getLoc(), diag::construct_protocol_by_name,
BaseType->getMetatypeInstanceType())
.highlight(BaseRange);
} else {
emitDiagnostic(anchor->getLoc(), diag::construct_protocol_value, BaseType)
.highlight(BaseRange);
}
return true;
}
bool ImplicitInitOnNonConstMetatypeFailure::diagnoseAsError() {
auto *apply = cast<ApplyExpr>(getRawAnchor());
auto loc = apply->getArg()->getStartLoc();
emitDiagnostic(loc, diag::missing_init_on_metatype_initialization)
.fixItInsert(loc, ".init");
return true;
}
bool MissingArgumentsFailure::diagnoseAsError() {
auto *locator = getLocator();
auto path = locator->getPath();
// TODO: Currently this is only intended to diagnose contextual failures.
if (path.empty() ||
!(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
path.back().getKind() == ConstraintLocator::ContextualType))
return false;
if (auto *closure = dyn_cast<ClosureExpr>(getAnchor()))
return diagnoseTrailingClosure(closure);
return false;
}
bool MissingArgumentsFailure::diagnoseTrailingClosure(ClosureExpr *closure) {
auto diff = Fn->getNumParams() - NumSynthesized;
// If the closure didn't specify any arguments and it is in a context that
// needs some, produce a fixit to turn "{...}" into "{ _,_ in ...}".
if (diff == 0) {
auto diag =
emitDiagnostic(closure->getStartLoc(),
diag::closure_argument_list_missing, NumSynthesized);
std::string fixText; // Let's provide fixits for up to 10 args.
if (Fn->getNumParams() <= 10) {
fixText += " ";
interleave(
Fn->getParams(),
[&fixText](const AnyFunctionType::Param &param) { fixText += '_'; },
[&fixText] { fixText += ','; });
fixText += " in ";
}
if (!fixText.empty()) {
// Determine if there is already a space after the { in the closure to
// make sure we introduce the right whitespace.
auto afterBrace = closure->getStartLoc().getAdvancedLoc(1);
auto text = getASTContext().SourceMgr.extractText({afterBrace, 1});
if (text.size() == 1 && text == " ")
fixText = fixText.erase(fixText.size() - 1);
else
fixText = fixText.erase(0, 1);
diag.fixItInsertAfter(closure->getStartLoc(), fixText);
}
return true;
}
auto params = closure->getParameters();
bool onlyAnonymousParams =
std::all_of(params->begin(), params->end(),
[](ParamDecl *param) { return !param->hasName(); });
auto diag =
emitDiagnostic(params->getStartLoc(), diag::closure_argument_list_tuple,
resolveType(Fn), Fn->getNumParams(), diff, diff == 1);
// If the number of parameters is less than number of inferred
// let's try to suggest a fix-it with the rest of the missing parameters.
if (!closure->hasExplicitResultType() &&
closure->getInLoc().isValid()) {
SmallString<32> fixIt;
llvm::raw_svector_ostream OS(fixIt);
OS << ",";
for (unsigned i = 0; i != NumSynthesized; ++i) {
OS << ((onlyAnonymousParams) ? "_" : "<#arg#>");
OS << ((i == NumSynthesized - 1) ? " " : ",");
}
diag.fixItInsertAfter(params->getEndLoc(), OS.str());
}
return true;
}
bool ClosureParamDestructuringFailure::diagnoseAsError() {
auto *closure = cast<ClosureExpr>(getAnchor());
auto params = closure->getParameters();
// In case of implicit parameters e.g. $0, $1 we
// can't really provide good fix-it because
// structure of parameter type itself is unclear.
for (auto *param : params->getArray()) {
if (param->isImplicit()) {
emitDiagnostic(params->getStartLoc(),
diag::closure_tuple_parameter_destructuring_implicit,
getParameterType());
return true;
}
}
auto diag = emitDiagnostic(params->getStartLoc(),
diag::closure_tuple_parameter_destructuring,
getParameterType());
auto *closureBody = closure->getBody();
if (!closureBody)
return true;
auto &sourceMgr = getASTContext().SourceMgr;
auto bodyStmts = closureBody->getElements();
SourceLoc bodyLoc;
// If the body is empty let's put the cursor
// right after "in", otherwise make it start
// location of the first statement in the body.
if (bodyStmts.empty())
bodyLoc = Lexer::getLocForEndOfToken(sourceMgr, closure->getInLoc());
else
bodyLoc = bodyStmts.front().getStartLoc();
SmallString<64> fixIt;
llvm::raw_svector_ostream OS(fixIt);
// If this is multi-line closure we'd have to insert new lines
// in the suggested 'let' to keep the structure of the code intact,
// otherwise just use ';' to keep everything on the same line.
auto inLine = sourceMgr.getLineNumber(closure->getInLoc());
auto bodyLine = sourceMgr.getLineNumber(bodyLoc);
auto isMultiLineClosure = bodyLine > inLine;
auto indent =
bodyStmts.empty() ? "" : Lexer::getIndentationForLine(sourceMgr, bodyLoc);
SmallString<16> parameter;
llvm::raw_svector_ostream parameterOS(parameter);
parameterOS << "(";
interleave(
params->getArray(),
[&](const ParamDecl *param) { parameterOS << param->getNameStr(); },
[&] { parameterOS << ", "; });
parameterOS << ")";
// Check if there are any explicit types associated
// with parameters, if there are, we'll have to add
// type information to the replacement argument.
bool explicitTypes =
llvm::any_of(params->getArray(), [](const ParamDecl *param) {
return param->getTypeLoc().getTypeRepr();
});
if (isMultiLineClosure)
OS << '\n' << indent;
// Let's form 'let <name> : [<type>]? = arg' expression.
OS << "let " << parameterOS.str() << " = arg"
<< (isMultiLineClosure ? "\n" + indent : "; ");
SmallString<64> argName;
llvm::raw_svector_ostream nameOS(argName);
if (explicitTypes) {
nameOS << "(arg: " << getParameterType()->getString() << ")";
} else {
nameOS << "(arg)";
}
if (closure->hasSingleExpressionBody()) {
// Let's see if we need to add result type to the argument/fix-it:
// - if the there is a result type associated with the closure;
// - and it's not a void type;
// - and it hasn't been explicitly written.
auto resultType = resolveType(ContextualType->getResult());
auto hasResult = [](Type resultType) -> bool {
return resultType && !resultType->isVoid();
};
auto isValidType = [](Type resultType) -> bool {
return resultType && !resultType->hasUnresolvedType() &&
!resultType->hasTypeVariable();
};
// If there an expected result type but it hasn't been explicitly
// provided, let's add it to the argument.
if (hasResult(resultType) && !closure->hasExplicitResultType()) {
nameOS << " -> ";
if (isValidType(resultType))
nameOS << resultType->getString();
else
nameOS << "<#Result#>";
}
if (auto stmt = bodyStmts.front().get<Stmt *>()) {
// If the body is a single expression with implicit return.
if (isa<ReturnStmt>(stmt) && stmt->isImplicit()) {
// And there is non-void expected result type,
// because we add 'let' expression to the body
// we need to make such 'return' explicit.
if (hasResult(resultType))
OS << "return ";
}
}
}
diag.fixItReplace(params->getSourceRange(), nameOS.str())
.fixItInsert(bodyLoc, OS.str());
return true;
}
bool OutOfOrderArgumentFailure::diagnoseAsError() {
auto *anchor = getRawAnchor();
auto *argExpr = isa<TupleExpr>(anchor) ? anchor : getArgumentExprFor(anchor);
if (!argExpr)
return false;
auto *tuple = cast<TupleExpr>(argExpr);
Identifier first = tuple->getElementName(ArgIdx);
Identifier second = tuple->getElementName(PrevArgIdx);
// Build a mapping from arguments to parameters.
SmallVector<unsigned, 4> argBindings(tuple->getNumElements());
for (unsigned paramIdx = 0; paramIdx != Bindings.size(); ++paramIdx) {
for (auto argIdx : Bindings[paramIdx])
argBindings[argIdx] = paramIdx;
}
auto argRange = [&](unsigned argIdx, Identifier label) -> SourceRange {
auto range = tuple->getElement(argIdx)->getSourceRange();
if (!label.empty())
range.Start = tuple->getElementNameLoc(argIdx);
unsigned paramIdx = argBindings[argIdx];
if (Bindings[paramIdx].size() > 1)
range.End = tuple->getElement(Bindings[paramIdx].back())->getEndLoc();
return range;
};
auto firstRange = argRange(ArgIdx, first);
auto secondRange = argRange(PrevArgIdx, second);
SourceLoc diagLoc = firstRange.Start;
auto addFixIts = [&](InFlightDiagnostic diag) {
diag.highlight(firstRange).highlight(secondRange);
// Move the misplaced argument by removing it from one location and
// inserting it in another location. To maintain argument comma
// separation, since the argument is always moving to an earlier index
// the preceding comma and whitespace is removed and a new trailing
// comma and space is inserted with the moved argument.
auto &SM = getASTContext().SourceMgr;
auto text = SM.extractText(
Lexer::getCharSourceRangeFromSourceRange(SM, firstRange));
auto removalRange =
SourceRange(Lexer::getLocForEndOfToken(
SM, tuple->getElement(ArgIdx - 1)->getEndLoc()),
firstRange.End);
diag.fixItRemove(removalRange);
diag.fixItInsert(secondRange.Start, text.str() + ", ");
};
// There are 4 diagnostic messages variations depending on
// labeled/unlabeled arguments.
if (first.empty() && second.empty()) {
addFixIts(emitDiagnostic(diagLoc,
diag::argument_out_of_order_unnamed_unnamed,
ArgIdx + 1, PrevArgIdx + 1));
} else if (first.empty() && !second.empty()) {
addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_unnamed_named,
ArgIdx + 1, second));
} else if (!first.empty() && second.empty()) {
addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_named_unnamed,
first, PrevArgIdx + 1));
} else {
addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_named_named,
first, second));
}
return true;
}
bool InaccessibleMemberFailure::diagnoseAsError() {
auto *anchor = getRawAnchor();
// Let's try to avoid over-diagnosing chains of inaccessible
// members e.g.:
//
// struct A {
// struct B {
// struct C {}
// }
// }
//
// _ = A.B.C()
//
// We'll have a fix for each `B', `C` and `C.init` but it makes
// sense to diagnose only `B` and consider the rest hidden.
Expr *baseExpr = nullptr;
DeclNameLoc nameLoc;
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
baseExpr = UDE->getBase();
nameLoc = UDE->getNameLoc();
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
nameLoc = UME->getNameLoc();
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
baseExpr = SE->getBase();
} else if (auto *call = dyn_cast<CallExpr>(anchor)) {
baseExpr = call->getFn();
}
if (baseExpr) {
auto &cs = getConstraintSystem();
auto *locator =
cs.getConstraintLocator(baseExpr, ConstraintLocator::Member);
if (llvm::any_of(cs.getFixes(), [&](const ConstraintFix *fix) {
return fix->getLocator() == locator;
}))
return false;
}
auto loc = nameLoc.isValid() ? nameLoc.getStartLoc() : anchor->getLoc();
auto accessLevel = Member->getFormalAccessScope().accessLevelForDiagnostics();
if (auto *CD = dyn_cast<ConstructorDecl>(Member)) {
emitDiagnostic(loc, diag::init_candidate_inaccessible,
CD->getResultInterfaceType(), accessLevel)
.highlight(nameLoc.getSourceRange());
} else {
emitDiagnostic(loc, diag::candidate_inaccessible, Member->getBaseName(),
accessLevel)
.highlight(nameLoc.getSourceRange());
}
emitDiagnostic(Member, diag::decl_declared_here, Member->getFullName());
return true;
}
bool AnyObjectKeyPathRootFailure::diagnoseAsError() {
// Diagnose use of AnyObject as root for a keypath
auto anchor = getAnchor();
auto loc = anchor->getLoc();
auto range = anchor->getSourceRange();
if (auto KPE = dyn_cast<KeyPathExpr>(anchor)) {
if (auto rootTyRepr = KPE->getRootType()) {
loc = rootTyRepr->getLoc();
range = rootTyRepr->getSourceRange();
}
}
emitDiagnostic(loc, diag::expr_swift_keypath_anyobject_root).highlight(range);
return true;
}
bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() {
auto *anchor = getRawAnchor();
auto *locator = getLocator();
auto loc = anchor->getLoc();
if (locator->isKeyPathSubscriptComponent()) {
auto *KPE = cast<KeyPathExpr>(anchor);
for (auto &elt : locator->getPath()) {
if (elt.isKeyPathComponent()) {
loc = KPE->getComponents()[elt.getValue()].getLoc();
break;
}
}
}
emitDiagnostic(loc, diag::expr_keypath_subscript_index_not_hashable,
resolveType(NonConformingType));
return true;
}
SourceLoc InvalidMemberRefInKeyPath::getLoc() const {
auto *anchor = getRawAnchor();
if (auto *KPE = dyn_cast<KeyPathExpr>(anchor)) {
auto *locator = getLocator();
auto component =
llvm::find_if(locator->getPath(), [](const LocatorPathElt &elt) {
return elt.isKeyPathComponent();
});
assert(component != locator->getPath().end());
return KPE->getComponents()[component->getValue()].getLoc();
}
return anchor->getLoc();
}
bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
emitDiagnostic(getLoc(), diag::expr_keypath_static_member, getName());
return true;
}
bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() {
emitDiagnostic(getLoc(), diag::expr_keypath_mutating_getter, getName());
return true;
}