blob: 2c19ee4fb33d2f137ef206242514b2c5ee7de4be [file] [log] [blame]
//===--- 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 "MiscDiagnostics.h"
#include "TypeCheckProtocol.h"
#include "TypoCorrection.h"
#include "swift/Sema/IDETypeChecking.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.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/SourceFile.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/PointerUnion.h"
#include "llvm/ADT/SmallString.h"
#include <string>
using namespace swift;
using namespace constraints;
static bool hasFixFor(const Solution &solution, ConstraintLocator *locator) {
return llvm::any_of(solution.Fixes, [&locator](const ConstraintFix *fix) {
return fix->getLocator() == locator;
});
}
FailureDiagnostic::~FailureDiagnostic() {}
bool FailureDiagnostic::diagnose(bool asNote) {
return asNote ? diagnoseAsNote() : diagnoseAsError();
}
bool FailureDiagnostic::diagnoseAsNote() {
return false;
}
ASTNode FailureDiagnostic::getAnchor() const {
auto *locator = getLocator();
// Resolve the locator to a specific expression.
auto anchor = locator->getAnchor();
{
SourceRange range;
auto path = locator->getPath();
simplifyLocator(anchor, path, range);
if (!anchor)
return locator->getAnchor();
}
// FIXME: Work around an odd locator representation that doesn't separate the
// base of a subscript member from the member access.
if (locator->isLastElement<LocatorPathElt::SubscriptMember>()) {
if (auto subscript = getAsExpr<SubscriptExpr>(anchor))
anchor = subscript->getBase();
}
return anchor;
}
Type FailureDiagnostic::getType(ASTNode node, bool wantRValue) const {
return resolveType(getRawType(node), /*reconstituteSugar=*/false, wantRValue);
}
Type FailureDiagnostic::getRawType(ASTNode node) const {
return S.getType(node);
}
template <typename... ArgTypes>
InFlightDiagnostic
FailureDiagnostic::emitDiagnostic(ArgTypes &&... Args) const {
return emitDiagnosticAt(getLoc(), std::forward<ArgTypes>(Args)...);
}
template <typename... ArgTypes>
InFlightDiagnostic
FailureDiagnostic::emitDiagnosticAt(ArgTypes &&... Args) const {
auto &DE = getASTContext().Diags;
return DE.diagnose(std::forward<ArgTypes>(Args)...);
}
Expr *FailureDiagnostic::findParentExpr(const Expr *subExpr) const {
auto &cs = getConstraintSystem();
return cs.getParentExpr(const_cast<Expr *>(subExpr));
}
Expr *
FailureDiagnostic::getArgumentListExprFor(ConstraintLocator *locator) const {
auto path = locator->getPath();
auto iter = path.begin();
if (!locator->findFirst<LocatorPathElt::ApplyArgument>(iter))
return nullptr;
// Form a new locator that ends at the ApplyArgument element, then simplify
// to get the argument list.
auto newPath = ArrayRef<LocatorPathElt>(path.begin(), iter + 1);
auto argListLoc = getConstraintLocator(locator->getAnchor(), newPath);
return getAsExpr(simplifyLocatorToAnchor(argListLoc));
}
Expr *FailureDiagnostic::getBaseExprFor(const Expr *anchor) const {
if (!anchor)
return nullptr;
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor))
return UDE->getBase();
else if (auto *SE = dyn_cast<SubscriptExpr>(anchor))
return SE->getBase();
else if (auto *MRE = dyn_cast<MemberRefExpr>(anchor))
return MRE->getBase();
else if (auto *call = dyn_cast<CallExpr>(anchor)) {
auto fnType = getType(call->getFn());
if (fnType->isCallableNominalType(getDC())) {
return call->getFn();
}
}
return nullptr;
}
Type FailureDiagnostic::restoreGenericParameters(
Type type,
llvm::function_ref<void(GenericTypeParamType *, Type)> substitution) {
llvm::SmallPtrSet<GenericTypeParamType *, 4> processed;
return type.transform([&](Type type) -> Type {
if (auto *typeVar = type->getAs<TypeVariableType>()) {
type = resolveType(typeVar);
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
if (processed.insert(GP).second)
substitution(GP, type);
return GP;
}
}
return type;
});
}
bool FailureDiagnostic::conformsToKnownProtocol(
Type type, KnownProtocolKind protocol) const {
auto &cs = getConstraintSystem();
return constraints::conformsToKnownProtocol(cs.DC, type, protocol);
}
Type RequirementFailure::getOwnerType() const {
auto anchor = getRawAnchor();
// If diagnostic is anchored at assignment expression
// it means that requirement failure happend while trying
// to convert source to destination, which means that
// owner type is actually not an assignment expression
// itself but its source.
if (auto *assignment = getAsExpr<AssignExpr>(anchor))
anchor = assignment->getSrc();
return getType(anchor)->getInOutObjectType()->getMetatypeInstanceType();
}
const GenericContext *RequirementFailure::getGenericContext() const {
if (auto *genericCtx = AffectedDecl->getAsGenericContext())
return genericCtx;
auto parentDecl = AffectedDecl->getDeclContext()->getAsDecl();
if (!parentDecl)
return nullptr;
return parentDecl->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 &solution = getSolution();
auto reqElt = locator->castLastElementTo<LocatorPathElt::AnyRequirement>();
if (!reqElt.isConditionalRequirement())
return nullptr;
auto path = locator->getPath();
auto *typeReqLoc = getConstraintLocator(getRawAnchor(), path.drop_back());
auto result = llvm::find_if(
solution.Conformances,
[&](const std::pair<ConstraintLocator *, ProtocolConformanceRef>
&conformance) { return conformance.first == typeReqLoc; });
assert(result != solution.Conformances.end());
auto conformance = result->second;
assert(conformance.isConcrete());
return conformance.getConcrete();
}
ValueDecl *RequirementFailure::getDeclRef() const {
// Get a declaration associated with given type (if any).
// This is used to retrieve affected declaration when
// failure is in any way contextual, and declaration can't
// be fetched directly from constraint system.
auto getAffectedDeclFromType = [](Type type) -> ValueDecl * {
assert(type);
// If problem is related to a typealias, let's point this
// diagnostic directly to its declaration without desugaring.
if (auto *alias = dyn_cast<TypeAliasType>(type.getPointer()))
return alias->getDecl();
if (auto *opaque = type->getAs<OpaqueTypeArchetypeType>())
return opaque->getDecl();
return type->getAnyGeneric();
};
// If the locator is for a result builder body result type, the requirement
// came from the function's return type.
if (getLocator()->isForResultBuilderBodyResult()) {
auto *func = getAsDecl<FuncDecl>(getAnchor());
return getAffectedDeclFromType(func->getResultInterfaceType());
}
if (isFromContextualType())
return getAffectedDeclFromType(getContextualType(getRawAnchor()));
if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) {
// If there is a declaration associated with this
// failure e.g. an overload choice of the call
// expression, let's see whether failure is
// associated with it directly or rather with
// one of its parents.
if (auto *decl = overload->choice.getDeclOrNull()) {
// If declaration is an operator let's always use
// it to produce `in reference to` diagnostics.
if (decl->isOperator())
return decl;
auto *DC = decl->getDeclContext();
do {
if (auto *parent = DC->getAsDecl()) {
if (auto *GC = parent->getAsGenericContext()) {
// FIXME: Is this intending an exact match?
if (GC->getGenericSignature().getPointer() != Signature.getPointer())
continue;
// If this is a signature if an extension
// then it means that code has referenced
// something incorrectly and diagnostic
// should point to the referenced declaration.
if (isa<ExtensionDecl>(parent))
break;
return cast<ValueDecl>(parent);
}
}
} while ((DC = DC->getParent()));
return decl;
}
}
return getAffectedDeclFromType(getOwnerType());
}
GenericSignature RequirementFailure::getSignature(ConstraintLocator *locator) {
if (isConditional())
return Conformance->getGenericSignature();
if (auto genericElt = locator->findLast<LocatorPathElt::OpenedGeneric>())
return genericElt->getSignature();
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() {
const auto *reqDC = getRequirementDC();
auto *genericCtx = getGenericContext();
auto lhs = getLHS();
auto rhs = getRHS();
if (auto *OTD = dyn_cast<OpaqueTypeDecl>(AffectedDecl)) {
auto *namingDecl = OTD->getNamingDecl();
emitDiagnostic(diag::type_does_not_conform_in_opaque_return,
namingDecl->getDescriptiveKind(), namingDecl->getName(),
lhs, rhs, rhs->isAnyObject());
if (auto *repr = namingDecl->getOpaqueResultTypeRepr()) {
emitDiagnosticAt(repr->getLoc(), diag::opaque_return_type_declared_here)
.highlight(repr->getSourceRange());
}
return true;
}
if (reqDC->isTypeContext() && genericCtx != reqDC &&
(genericCtx->isChildContextOf(reqDC) ||
isStaticOrInstanceMember(AffectedDecl))) {
auto *NTD = reqDC->getSelfNominalTypeDecl();
emitDiagnostic(
getDiagnosticInRereference(), AffectedDecl->getDescriptiveKind(),
AffectedDecl->getName(), NTD->getDeclaredType(), lhs, rhs);
} else {
emitDiagnostic(getDiagnosticOnDecl(), AffectedDecl->getDescriptiveKind(),
AffectedDecl->getName(), lhs, rhs);
}
emitRequirementNote(reqDC->getAsDecl(), lhs, rhs);
return true;
}
bool RequirementFailure::diagnoseAsNote() {
const auto &req = getRequirement();
const auto *reqDC = getRequirementDC();
emitDiagnosticAt(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 (req.getKind() != RequirementKind::SameType) {
if (auto wrappedType = lhs->getOptionalObjectType()) {
auto kind = (req.getKind() == RequirementKind::Superclass ?
ConstraintKind::Subtype : ConstraintKind::ConformsTo);
if (TypeChecker::typesSatisfyConstraint(wrappedType, rhs,
/*openArchetypes=*/false,
kind, getDC()))
emitDiagnostic(diag::wrapped_type_satisfies_requirement, wrappedType);
}
}
if (isConditional()) {
emitDiagnosticAt(anchor,
diag::requirement_implied_by_conditional_conformance,
resolveType(Conformance->getType()),
Conformance->getProtocol()->getDeclaredInterfaceType());
return;
}
if (req.getKind() == RequirementKind::Layout ||
rhs->isEqual(req.getSecondType())) {
emitDiagnosticAt(anchor, diag::where_requirement_failure_one_subst,
req.getFirstType(), lhs);
return;
}
if (lhs->isEqual(req.getFirstType())) {
emitDiagnosticAt(anchor, diag::where_requirement_failure_one_subst,
req.getSecondType(), rhs);
return;
}
emitDiagnosticAt(anchor, diag::where_requirement_failure_both_subst,
req.getFirstType(), lhs, req.getSecondType(), rhs);
}
bool MissingConformanceFailure::diagnoseAsError() {
auto anchor = getAnchor();
auto nonConformingType = getLHS();
auto protocolType = getRHS();
// If this is a requirement of a pattern-matching operator,
// let's see whether argument already has a fix associated
// with it and if so skip conformance error, otherwise we'd
// produce an unrelated `<type> doesn't conform to Equatable protocol`
// diagnostic.
if (isPatternMatchingOperator(anchor)) {
auto *expr = castToExpr(anchor);
if (auto *binaryOp = dyn_cast_or_null<BinaryExpr>(findParentExpr(expr))) {
auto *caseExpr = binaryOp->getArg()->getElement(0);
llvm::SmallPtrSet<Expr *, 4> anchors;
for (const auto *fix : getSolution().Fixes) {
if (auto anchor = fix->getAnchor()) {
if (anchor.is<Expr *>())
anchors.insert(getAsExpr(anchor));
}
}
bool hasFix = false;
forEachExprInConstraintSystem(caseExpr, [&](Expr *expr) -> Expr * {
hasFix |= anchors.count(expr);
return hasFix ? nullptr : expr;
});
if (hasFix)
return false;
}
}
// If the problem has been (unambiguously) determined to be related
// to one of of the standard comparison operators and argument is
// enum with associated values, let's produce a tailored note which
// says that conformances for enums with associated values can't be
// synthesized.
if (isStandardComparisonOperator(anchor)) {
auto *expr = castToExpr(anchor);
auto isEnumWithAssociatedValues = [](Type type) -> bool {
if (auto *enumType = type->getAs<EnumType>())
return !enumType->getDecl()->hasOnlyCasesWithoutAssociatedValues();
return false;
};
// Limit this to `Equatable` and `Comparable` protocols for now.
auto *protocol = getRHS()->castTo<ProtocolType>()->getDecl();
if (isEnumWithAssociatedValues(getLHS()) &&
(protocol->isSpecificProtocol(KnownProtocolKind::Equatable) ||
protocol->isSpecificProtocol(KnownProtocolKind::Comparable))) {
if (RequirementFailure::diagnoseAsError()) {
auto opName = getOperatorName(expr);
emitDiagnostic(diag::no_binary_op_overload_for_enum_with_payload,
opName->str());
return true;
}
}
}
if (diagnoseAsAmbiguousOperatorRef())
return true;
if (nonConformingType->isObjCExistentialType()) {
emitDiagnostic(diag::protocol_does_not_conform_static, nonConformingType,
protocolType);
return true;
}
if (diagnoseTypeCannotConform(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 MissingConformanceFailure::diagnoseTypeCannotConform(
Type nonConformingType, Type protocolType) const {
if (getRequirement().getKind() == RequirementKind::Layout ||
!(nonConformingType->is<AnyFunctionType>() ||
nonConformingType->is<TupleType>() ||
nonConformingType->isExistentialType() ||
nonConformingType->is<AnyMetatypeType>())) {
return false;
}
emitDiagnostic(diag::type_cannot_conform,
nonConformingType->isExistentialType(),
nonConformingType,
nonConformingType->isEqual(protocolType),
protocolType);
emitDiagnostic(diag::only_concrete_types_conform_to_protocols);
if (auto *OTD = dyn_cast<OpaqueTypeDecl>(AffectedDecl)) {
auto *namingDecl = OTD->getNamingDecl();
if (auto *repr = namingDecl->getOpaqueResultTypeRepr()) {
emitDiagnosticAt(repr->getLoc(), diag::required_by_opaque_return,
namingDecl->getDescriptiveKind(),
namingDecl->getName())
.highlight(repr->getSourceRange());
}
return true;
}
auto &req = getRequirement();
auto *reqDC = getRequirementDC();
auto *genericCtx = getGenericContext();
auto noteLocation = reqDC->getAsDecl()->getLoc();
if (!noteLocation.isValid())
noteLocation = getLoc();
if (isConditional()) {
emitDiagnosticAt(noteLocation,
diag::requirement_implied_by_conditional_conformance,
resolveType(Conformance->getType()),
Conformance->getProtocol()->getDeclaredInterfaceType());
} else if (genericCtx != reqDC && (genericCtx->isChildContextOf(reqDC) ||
isStaticOrInstanceMember(AffectedDecl))) {
emitDiagnosticAt(noteLocation, diag::required_by_decl_ref,
AffectedDecl->getDescriptiveKind(),
AffectedDecl->getName(),
reqDC->getSelfNominalTypeDecl()->getDeclaredType(),
req.getFirstType(), nonConformingType);
} else {
emitDiagnosticAt(noteLocation, diag::required_by_decl,
AffectedDecl->getDescriptiveKind(),
AffectedDecl->getName(), req.getFirstType(),
nonConformingType);
}
return true;
}
bool MissingConformanceFailure::diagnoseAsAmbiguousOperatorRef() {
auto anchor = getRawAnchor();
auto *ODRE = getAsExpr<OverloadedDeclRefExpr>(anchor);
if (!ODRE)
return false;
auto name = ODRE->getDecls().front()->getBaseName();
if (!(name.isOperator() && getLHS()->isStdlibType() && getRHS()->isStdlibType()))
return false;
// If this is an operator reference and both types are from stdlib,
// let's produce a generic diagnostic about invocation and a note
// about missing conformance just in case.
auto operatorID = name.getIdentifier();
auto *fnType = getType(anchor)->getAs<AnyFunctionType>();
auto params = fnType->getParams();
if (params.size() == 2) {
auto lhsType = params[0].getPlainType();
auto rhsType = params[1].getPlainType();
if (lhsType->isEqual(rhsType)) {
emitDiagnostic(diag::cannot_apply_binop_to_same_args, operatorID.str(),
lhsType);
} else {
emitDiagnostic(diag::cannot_apply_binop_to_args, operatorID.str(),
lhsType, rhsType);
}
} else {
emitDiagnostic(diag::cannot_apply_unop_to_arg, operatorID.str(),
params[0].getPlainType());
}
diagnoseAsNote();
return true;
}
Optional<Diag<Type, Type>> GenericArgumentsMismatchFailure::getDiagnosticFor(
ContextualTypePurpose context) {
switch (context) {
case CTP_Initialization:
case CTP_AssignSource:
return diag::cannot_convert_assign;
case CTP_ReturnStmt:
case CTP_ReturnSingleExpr:
return diag::cannot_convert_to_return_type;
case CTP_DefaultParameter:
case CTP_AutoclosureDefaultParameter:
return diag::cannot_convert_default_arg_value;
case CTP_YieldByValue:
return diag::cannot_convert_yield_value;
case CTP_CallArgument:
return diag::cannot_convert_argument_value;
case CTP_ClosureResult:
return diag::cannot_convert_closure_result;
case CTP_ArrayElement:
return diag::cannot_convert_array_element;
case CTP_DictionaryKey:
return diag::cannot_convert_dict_key;
case CTP_DictionaryValue:
return diag::cannot_convert_dict_value;
case CTP_CoerceOperand:
return diag::cannot_convert_coerce;
case CTP_SubscriptAssignSource:
return diag::cannot_convert_subscript_assign;
case CTP_Condition:
return diag::cannot_convert_condition_value;
case CTP_WrappedProperty:
return diag::wrapped_value_mismatch;
case CTP_ComposedPropertyWrapper:
return diag::composed_property_wrapper_mismatch;
case CTP_ThrowStmt:
case CTP_ForEachStmt:
case CTP_Unused:
case CTP_CannotFail:
case CTP_YieldByReference:
case CTP_CalleeResult:
case CTP_EnumCaseRawValue:
break;
}
return None;
}
void GenericArgumentsMismatchFailure::emitNoteForMismatch(int position) {
auto *locator = getLocator();
// Since there could be implicit conversions associated with argument
// to parameter conversions, let's use parameter type as a source of
// generic parameter information.
auto paramSourceTy =
locator->isLastElement<LocatorPathElt::ApplyArgToParam>() ? getRequired()
: getActual();
auto genericTypeDecl = paramSourceTy->getAnyGeneric();
auto param = genericTypeDecl->getGenericParams()->getParams()[position];
auto lhs = getActual()->getGenericArgs()[position];
auto rhs = getRequired()->getGenericArgs()[position];
auto noteLocation = param->getLoc();
if (!noteLocation.isValid())
noteLocation = getLoc();
emitDiagnosticAt(noteLocation, diag::generic_argument_mismatch,
param->getName(), lhs, rhs);
}
bool GenericArgumentsMismatchFailure::diagnoseAsError() {
auto anchor = getAnchor();
auto fromType = getFromType();
auto toType = getToType();
// This is a situation where right-hand size type is wrapped
// into a number of optionals and argument isn't e.g.
//
// func test(_: UnsafePointer<Int>??) {}
//
// var value: Float = 0
// test(&value)
//
// `value` has to get implicitly wrapped into 2 optionals
// before pointer types could be compared.
auto path = getLocator()->getPath();
unsigned toDrop = 0;
for (const auto &elt : llvm::reverse(path)) {
if (!elt.is<LocatorPathElt::OptionalPayload>())
break;
// Disregard optional payload element to look at its source.
++toDrop;
}
path = path.drop_back(toDrop);
Optional<Diag<Type, Type>> diagnostic;
if (path.empty()) {
if (isExpr<AssignExpr>(anchor)) {
diagnostic = getDiagnosticFor(CTP_AssignSource);
} else if (isExpr<CoerceExpr>(anchor)) {
diagnostic = getDiagnosticFor(CTP_CoerceOperand);
} else {
return false;
}
} else {
const auto &last = path.back();
switch (last.getKind()) {
case ConstraintLocator::ContextualType: {
auto purpose = getContextualTypePurpose();
assert(!(purpose == CTP_Unused || purpose == CTP_CannotFail));
// If this is call to a closure e.g. `let _: A = { B() }()`
// let's point diagnostic to its result.
if (auto *call = getAsExpr<CallExpr>(anchor)) {
auto *fnExpr = call->getFn();
if (auto *closure = dyn_cast<ClosureExpr>(fnExpr)) {
purpose = CTP_ClosureResult;
if (closure->hasSingleExpressionBody())
anchor = closure->getSingleExpressionBody();
}
}
diagnostic = getDiagnosticFor(purpose);
break;
}
case ConstraintLocator::AutoclosureResult:
case ConstraintLocator::ApplyArgToParam:
case ConstraintLocator::ApplyArgument: {
diagnostic = diag::cannot_convert_argument_value;
break;
}
case ConstraintLocator::ParentType: {
diagnostic = diag::cannot_convert_parent_type;
break;
}
case ConstraintLocator::ClosureBody:
case ConstraintLocator::ClosureResult: {
diagnostic = diag::cannot_convert_closure_result;
break;
}
case ConstraintLocator::TupleElement: {
auto rawAnchor = getRawAnchor();
if (isExpr<ArrayExpr>(rawAnchor)) {
diagnostic = getDiagnosticFor(CTP_ArrayElement);
} else if (isExpr<DictionaryExpr>(rawAnchor)) {
auto eltLoc = last.castTo<LocatorPathElt::TupleElement>();
diagnostic = getDiagnosticFor(
eltLoc.getIndex() == 0 ? CTP_DictionaryKey : CTP_DictionaryValue);
}
break;
}
case ConstraintLocator::UnresolvedMemberChainResult: {
diagnostic = diag::cannot_convert_chain_result_type;
break;
}
default:
break;
}
}
if (!diagnostic) {
// Handle all mismatches involving an `AssignExpr`
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor)) {
diagnostic = getDiagnosticFor(CTP_AssignSource);
fromType = getType(assignExpr->getSrc());
toType = getType(assignExpr->getDest());
} else {
// If we couldn't find a specific diagnostic let's fallback to
// attempt to handle cases where we have an apply arg to param.
auto applyInfo = getFunctionArgApplyInfo(getLocator());
if (applyInfo) {
diagnostic = diag::cannot_convert_argument_value;
fromType = applyInfo->getArgType();
toType = applyInfo->getParamType();
}
}
}
if (!diagnostic)
return false;
emitDiagnosticAt(::getLoc(anchor), *diagnostic, fromType, toType);
emitNotesForMismatches();
return true;
}
bool LabelingFailure::diagnoseAsError() {
auto *argExpr = getArgumentListExprFor(getLocator());
if (!argExpr)
return false;
return diagnoseArgumentLabelError(getASTContext(), argExpr, CorrectLabels,
isExpr<SubscriptExpr>(getRawAnchor()));
}
bool LabelingFailure::diagnoseAsNote() {
auto *argExpr = getArgumentListExprFor(getLocator());
if (!argExpr)
return false;
SmallVector<Identifier, 4> argLabels;
if (isa<ParenExpr>(argExpr)) {
argLabels.push_back(Identifier());
} else if (auto *tuple = dyn_cast<TupleExpr>(argExpr)) {
argLabels.append(tuple->getElementNames().begin(),
tuple->getElementNames().end());
} else {
return false;
}
auto stringifyLabels = [](ArrayRef<Identifier> labels) -> std::string {
std::string str;
for (auto label : labels) {
str += label.empty() ? "_" : label.str();
str += ':';
}
return "(" + str + ")";
};
auto selectedOverload = getCalleeOverloadChoiceIfAvailable(getLocator());
if (!selectedOverload)
return false;
const auto &choice = selectedOverload->choice;
if (auto *decl = choice.getDeclOrNull()) {
emitDiagnosticAt(decl, diag::candidate_expected_different_labels,
stringifyLabels(argLabels),
stringifyLabels(CorrectLabels));
return true;
}
return false;
}
bool ArrayLiteralToDictionaryConversionFailure::diagnoseAsError() {
ArrayExpr *AE = getAsExpr<ArrayExpr>(getAnchor());
assert(AE);
if (AE->getNumElements() == 0) {
emitDiagnostic(diag::should_use_empty_dictionary_literal)
.fixItInsertAfter(getLoc(), ":");
return true;
}
auto CTP = getConstraintSystem().getContextualTypePurpose(AE);
emitDiagnostic(diag::should_use_dictionary_literal,
getToType()->lookThroughAllOptionalTypes(),
CTP == CTP_Initialization);
auto diagnostic = emitDiagnostic(diag::meant_dictionary_lit);
if (AE->getNumElements() == 1)
diagnostic.fixItInsertAfter(AE->getElement(0)->getEndLoc(), ": <#value#>");
return true;
}
bool NoEscapeFuncToTypeConversionFailure::diagnoseAsError() {
if (diagnoseParameterUse())
return true;
if (auto *typeVar = getRawFromType()->getAs<TypeVariableType>()) {
if (auto *GP = typeVar->getImpl().getGenericParameter()) {
emitDiagnostic(diag::converting_noescape_to_type, GP);
return true;
}
}
emitDiagnostic(diag::converting_noescape_to_type, getToType());
return true;
}
bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
auto convertTo = getToType();
// If the other side is not a function, we have common case diagnostics
// which handle function-to-type conversion diagnostics.
if (!convertTo->is<FunctionType>())
return false;
auto anchor = getAnchor();
auto diagnostic = diag::general_noescape_to_escaping;
ParamDecl *PD = nullptr;
if (auto *DRE = getAsExpr<DeclRefExpr>(anchor)) {
PD = dyn_cast<ParamDecl>(DRE->getDecl());
// If anchor is not a parameter declaration there
// is no need to dig up more information.
if (!PD)
return false;
// Let's check whether this is a function parameter passed
// as an argument to another function which accepts @escaping
// function at that position.
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) {
auto paramInterfaceTy = argApplyInfo->getParamInterfaceType();
if (paramInterfaceTy->isTypeParameter()) {
auto diagnoseGenericParamFailure = [&](GenericTypeParamDecl *decl) {
emitDiagnostic(diag::converting_noespace_param_to_generic_type,
PD->getName(), paramInterfaceTy);
auto declLoc = decl->getLoc();
if (declLoc.isValid())
emitDiagnosticAt(decl, diag::generic_parameters_always_escaping);
};
// If this is a situation when non-escaping parameter is passed
// to the argument which represents generic parameter, there is
// a tailored diagnostic for that.
if (auto *DMT = paramInterfaceTy->getAs<DependentMemberType>()) {
diagnoseGenericParamFailure(DMT->getRootGenericParam()->getDecl());
return true;
}
if (auto *GP = paramInterfaceTy->getAs<GenericTypeParamType>()) {
diagnoseGenericParamFailure(GP->getDecl());
return true;
}
}
// If there are no generic parameters involved, this could
// only mean that parameter is expecting @escaping function type.
diagnostic = diag::passing_noescape_to_escaping;
}
} else if (auto *AE = getAsExpr<AssignExpr>(getRawAnchor())) {
if (auto *DRE = dyn_cast<DeclRefExpr>(AE->getSrc())) {
PD = dyn_cast<ParamDecl>(DRE->getDecl());
diagnostic = diag::assigning_noescape_to_escaping;
}
}
if (!PD)
return false;
emitDiagnostic(diagnostic, PD->getName());
// Give a note and fix-it
auto note = emitDiagnosticAt(PD, diag::noescape_parameter, PD->getName());
SourceLoc reprLoc;
SourceLoc autoclosureEndLoc;
if (auto *repr = PD->getTypeRepr()) {
reprLoc = repr->getStartLoc();
if (auto *attrRepr = dyn_cast<AttributedTypeRepr>(repr)) {
autoclosureEndLoc = Lexer::getLocForEndOfToken(
getASTContext().SourceMgr,
attrRepr->getAttrs().getLoc(TAK_autoclosure));
}
}
if (!PD->isAutoClosure()) {
note.fixItInsert(reprLoc, "@escaping ");
} else {
note.fixItInsertAfter(autoclosureEndLoc, " @escaping");
}
return true;
}
ASTNode InvalidCoercionFailure::getAnchor() const {
auto anchor = FailureDiagnostic::getAnchor();
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor))
return assignExpr->getSrc();
return anchor;
}
bool InvalidCoercionFailure::diagnoseAsError() {
auto fromType = getFromType();
auto toType = getToType();
emitDiagnostic(diag::cannot_coerce_to_type, fromType, toType);
if (UseConditionalCast) {
emitDiagnostic(diag::missing_optional_downcast)
.highlight(getSourceRange())
.fixItReplace(getLoc(), "as?");
} else {
emitDiagnostic(diag::missing_forced_downcast)
.highlight(getSourceRange())
.fixItReplace(getLoc(), "as!");
}
return true;
}
bool MissingAddressOfFailure::diagnoseAsError() {
auto argTy = getFromType();
auto paramTy = getToType();
if (paramTy->getAnyPointerElementType()) {
emitDiagnostic(diag::cannot_convert_argument_value, argTy, paramTy)
.fixItInsert(getSourceRange().Start, "&");
} else {
emitDiagnostic(diag::missing_address_of, argTy)
.fixItInsert(getSourceRange().Start, "&");
}
return true;
}
ASTNode MissingExplicitConversionFailure::getAnchor() const {
auto anchor = FailureDiagnostic::getAnchor();
if (auto *assign = getAsExpr<AssignExpr>(anchor))
return assign->getSrc();
if (auto *paren = getAsExpr<ParenExpr>(anchor))
return paren->getSubExpr();
return anchor;
}
bool MissingExplicitConversionFailure::diagnoseAsError() {
auto *DC = getDC();
auto *anchor = castToExpr(getAnchor());
auto fromType = getFromType();
auto toType = getToType();
if (!toType->hasTypeRepr())
return false;
bool useAs = TypeChecker::isExplicitlyConvertibleTo(fromType, toType, DC);
auto *expr = findParentExpr(anchor);
if (!expr)
expr = const_cast<Expr *>(anchor);
// 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 diagnose = [&]() {
if (useAs) {
return emitDiagnostic(diag::missing_explicit_conversion, fromType,
toType);
} else {
// Emit error diagnostic.
emitDiagnostic(diag::cannot_coerce_to_type, fromType, toType);
// Emit and return note suggesting as! where the fix-it will be placed.
return emitDiagnostic(diag::missing_forced_downcast);
}
};
auto diag = diagnose();
if (!insertBefore.empty()) {
diag.fixItInsert(getSourceRange().Start, insertBefore);
}
diag.fixItInsertAfter(getSourceRange().End, insertAfter);
return true;
}
SourceRange MemberAccessOnOptionalBaseFailure::getSourceRange() const {
if (auto componentPathElt =
getLocator()->getLastElementAs<LocatorPathElt::KeyPathComponent>()) {
auto anchor = getAnchor();
auto keyPathExpr = castToExpr<KeyPathExpr>(anchor);
if (componentPathElt->getIndex() == 0) {
if (auto rootType = keyPathExpr->getRootType()) {
return rootType->getSourceRange();
} else {
return keyPathExpr->getComponents().front().getLoc();
}
} else {
auto componentIdx = componentPathElt->getIndex() - 1;
auto component = keyPathExpr->getComponents()[componentIdx];
return component.getSourceRange();
}
}
return FailureDiagnostic::getSourceRange();
}
bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() {
auto baseType = getMemberBaseType();
auto locator = getLocator();
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 = getOverloadChoiceIfAvailable(locator);
if (overload && overload->openedType->getOptionalObjectType())
resultIsOptional = true;
auto unwrappedBaseType = baseType->getOptionalObjectType();
if (!unwrappedBaseType)
return false;
auto sourceRange = getSourceRange();
auto componentPathElt =
locator->getLastElementAs<LocatorPathElt::KeyPathComponent>();
if (componentPathElt && componentPathElt->getIndex() == 0) {
// For members where the base type is an optional key path root
// let's emit a tailored note suggesting to use its unwrapped type.
auto *keyPathExpr = castToExpr<KeyPathExpr>(getAnchor());
if (auto rootType = keyPathExpr->getRootType()) {
emitDiagnostic(diag::optional_base_not_unwrapped, baseType, Member,
unwrappedBaseType);
emitDiagnostic(diag::optional_base_remove_optional_for_keypath_root,
unwrappedBaseType)
.fixItReplace(rootType->getSourceRange(),
unwrappedBaseType.getString());
} else {
emitDiagnostic(diag::invalid_optional_infered_keypath_root, baseType,
Member, unwrappedBaseType);
emitDiagnostic(diag::optional_key_path_root_base_chain, Member)
.fixItInsert(sourceRange.End, "?.");
emitDiagnostic(diag::optional_key_path_root_base_unwrap, Member)
.fixItInsert(sourceRange.End, "!.");
}
} else {
emitDiagnostic(diag::optional_base_not_unwrapped, baseType, Member,
unwrappedBaseType);
// FIXME: It would be nice to immediately offer "base?.member ?? defaultValue"
// for non-optional results where that would be appropriate. For the moment
// always offering "?" means that if the user chooses chaining, we'll end up
// in MissingOptionalUnwrapFailure:diagnose() to offer a default value during
// the next compile.
emitDiagnostic(diag::optional_base_chain, Member)
.fixItInsertAfter(sourceRange.End, "?");
if (!resultIsOptional) {
emitDiagnostic(diag::unwrap_with_force_value)
.fixItInsertAfter(sourceRange.End, "!");
}
}
return true;
}
void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt(
DeclContext *DC, const Expr *expr) const {
assert(expr);
auto *anchor = getAsExpr(getAnchor());
// If anchor is n 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 (!anchor || isa<InOutExpr>(anchor))
return;
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator()))
if (argApplyInfo->getParameterFlags().isInOut())
return;
auto diag = emitDiagnosticAt(expr->getLoc(), diag::unwrap_with_default_value);
// Figure out what we need to parenthesize.
bool needsParensInside =
exprNeedsParensBeforeAddingNilCoalescing(DC, const_cast<Expr *>(expr));
auto parentExpr = findParentExpr(anchor);
if (parentExpr && isa<UnresolvedMemberChainResultExpr>(parentExpr))
parentExpr = findParentExpr(parentExpr);
bool needsParensOutside = exprNeedsParensAfterAddingNilCoalescing(
DC, const_cast<Expr *>(expr), parentExpr);
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(
const Expr *expr) const {
auto diag = emitDiagnosticAt(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(), ")!");
}
}
// FIXME: This walks a partially-type checked function body, which
// is not guaranteed to yield consistent results. We should come up
// with another way of performing this analysis, for example by moving
// it to a post-type checking pass in MiscDiagnostics.
class VarDeclMultipleReferencesChecker : public ASTWalker {
DeclContext *DC;
VarDecl *varDecl;
int count;
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
if (DRE->getDecl() == varDecl)
++count;
}
// FIXME: We can see UnresolvedDeclRefExprs here because we have
// not yet run preCheckExpression() on the entire function body
// yet.
//
// We could consider pre-checking more eagerly.
if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(E)) {
auto name = UDRE->getName();
auto loc = UDRE->getLoc();
if (name.isSimpleName(varDecl->getName()) && loc.isValid()) {
auto *otherDecl =
ASTScope::lookupSingleLocalDecl(DC->getParentSourceFile(),
name.getFullName(), loc);
if (otherDecl == varDecl)
++count;
}
}
return { true, E };
}
public:
VarDeclMultipleReferencesChecker(DeclContext *DC, VarDecl *varDecl)
: DC(DC), varDecl(varDecl),count(0) {}
int referencesCount() { return count; }
};
bool MissingOptionalUnwrapFailure::diagnoseAsError() {
if (!getUnwrappedType()->isBool()) {
if (diagnoseConversionToBool())
return true;
}
auto *anchor = castToExpr(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;
}
emitDiagnosticAt(tryExpr->getTryLoc(), diag::missing_unwrap_optional_try,
getType(anchor))
.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(!baseType->isHole() && "Base type must not be a type hole");
assert(!unwrappedType->hasTypeVariable() &&
"Unwrapped type must not be a type variable");
assert(!unwrappedType->isHole() && "Unwrapped type must not be a type hole");
if (!baseType->getOptionalObjectType())
return false;
emitDiagnosticAt(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 ((AFD = dyn_cast<AbstractFunctionDecl>(varDecl->getDeclContext()))) {
auto checker = VarDeclMultipleReferencesChecker(getDC(), varDecl);
AFD->getBody()->walk(checker);
singleUse = checker.referencesCount() == 1;
}
PatternBindingDecl *binding = varDecl->getParentPatternBinding();
if (singleUse && binding && binding->getNumPatternEntries() == 1 &&
varDecl->getTypeSourceRangeForDiagnostics().isInvalid()) {
auto *initializer = varDecl->getParentInitializer();
if (!initializer)
return true;
if (auto declRefExpr = dyn_cast<DeclRefExpr>(initializer)) {
if (declRefExpr->getDecl()->isImplicitlyUnwrappedOptional()) {
emitDiagnosticAt(declRefExpr->getLoc(),
diag::unwrap_iuo_initializer, baseType);
}
}
auto fnTy = AFD->getInterfaceType()->castTo<AnyFunctionType>();
bool voidReturn =
fnTy->getResult()->isEqual(TupleType::getEmpty(getASTContext()));
auto diag =
emitDiagnosticAt(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;
auto diagExpr = castToExpr(getRawAnchor());
SourceLoc loc = diagExpr->getLoc();
// Assignment is not allowed inside of a condition,
// so let's not diagnose immutability, because
// most likely the problem is related to use of `=` itself.
if (getContextualTypePurpose(diagExpr) == CTP_Condition)
return false;
// If the failure happened at the end of an unresolved member chain, it should
// be diagnosed instead as though it happened at the last element.
if (auto chainExpr = dyn_cast<UnresolvedMemberChainResultExpr>(diagExpr))
diagExpr = chainExpr->getSubExpr();
if (auto assignExpr = dyn_cast<AssignExpr>(diagExpr)) {
// Let's check whether this is an attempt to assign
// variable or property to itself.
if (TypeChecker::diagnoseSelfAssignment(assignExpr))
return true;
diagExpr = assignExpr->getDest();
}
if (auto callExpr = dyn_cast<ApplyExpr>(diagExpr)) {
Expr *argExpr = callExpr->getArg();
loc = callExpr->getFn()->getLoc();
auto *locator = getLocator();
// `argument attribute` is used for identification purposes
// only, so it could be looked through in this situation.
if (locator->isLastElement<LocatorPathElt::ArgumentAttribute>()) {
auto path = locator->getPath();
locator = getConstraintLocator(getRawAnchor(), path.drop_back());
}
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;
diagExpr = castToExpr(simplifyLocatorToAnchor(locator));
} else if (auto argElt =
locator
->getLastElementAs<LocatorPathElt::ApplyArgToParam>()) {
subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
rvalueDiagID = diag::cannot_pass_rvalue_inout;
if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
diagExpr = argTuple->getElement(argElt->getArgIdx());
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 *parentExpr = findParentExpr(inoutExpr)) {
if (auto *call =
dyn_cast_or_null<ApplyExpr>(findParentExpr(parentExpr))) {
// Since this `inout` expression is an argument to a call/operator
// let's figure out whether this is an impliict conversion from
// array to an unsafe pointer type and diagnose it.
unsigned argIdx = 0;
if (auto *TE = dyn_cast<TupleExpr>(parentExpr)) {
for (unsigned n = TE->getNumElements(); argIdx != n; ++argIdx) {
if (TE->getElement(argIdx) == inoutExpr)
break;
}
}
auto *argLoc = getConstraintLocator(
call, {ConstraintLocator::ApplyArgument,
LocatorPathElt::ApplyArgToParam(argIdx, argIdx,
ParameterTypeFlags())});
if (auto info = getFunctionArgApplyInfo(argLoc)) {
auto paramType = info->getParamType();
auto argType = getType(inoutExpr)->getWithoutSpecifierType();
PointerTypeKind ptr;
if (isArrayType(argType) &&
paramType->getAnyPointerElementType(ptr) &&
(ptr == PTK_UnsafePointer || ptr == PTK_UnsafeRawPointer)) {
emitDiagnosticAt(inoutExpr->getLoc(),
diag::extra_address_of_unsafepointer, paramType)
.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().initKind ==
BodyInitKind::Delegating) {
emitDiagnosticAt(loc, diag::assignment_let_property_delegating_init,
member->getName());
if (auto overload = getOverloadChoiceIfAvailable(
getConstraintLocator(member, ConstraintLocator::Member))) {
if (auto *ref = overload->choice.getDeclOrNull())
emitDiagnosticAt(ref, diag::decl_declared_here,
ref->getName());
}
return true;
}
}
}
if (auto resolvedOverload = getOverloadChoiceIfAvailable(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(), /*wantRValue=*/false)->hasLValueType())
subElementDiagID =
diag::assignment_dynamic_property_has_immutable_base;
}
}
} else if (isa<SubscriptExpr>(diagExpr)) {
subElementDiagID = diag::assignment_subscript_has_immutable_base;
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(diagExpr)) {
subElementDiagID = diag::assignment_lhs_is_immutable_property;
} else {
subElementDiagID = diag::assignment_lhs_is_immutable_variable;
}
AssignmentFailure failure(diagExpr, getSolution(), loc, subElementDiagID,
rvalueDiagID);
return failure.diagnose();
}
bool RValueTreatedAsLValueFailure::diagnoseAsNote() {
auto overload = getCalleeOverloadChoiceIfAvailable(getLocator());
if (!(overload && overload->choice.isDecl()))
return false;
auto *decl = overload->choice.getDecl();
emitDiagnosticAt(decl, diag::candidate_is_not_assignable,
decl->getDescriptiveKind(), decl->getName());
return true;
}
static VarDecl *findSimpleReferencedVarDecl(const Expr *E) {
if (auto *LE = dyn_cast<LoadExpr>(E))
E = LE->getSubExpr();
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
return dyn_cast<VarDecl>(DRE->getDecl());
return nullptr;
}
static std::pair<VarDecl *, VarDecl *> findReferencedVarDecl(const Expr *E) {
E = E->getValueProvidingExpr();
if (auto *LE = dyn_cast<LoadExpr>(E))
return findReferencedVarDecl(LE->getSubExpr());
if (auto *AE = dyn_cast<AssignExpr>(E))
return findReferencedVarDecl(AE->getDest());
if (auto *D = findSimpleReferencedVarDecl(E))
return std::make_pair(nullptr, D);
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
if (auto *BaseDecl = findSimpleReferencedVarDecl(MRE->getBase()))
return std::make_pair(BaseDecl, cast<VarDecl>(MRE->getMember().getDecl()));
}
return std::make_pair(nullptr, nullptr);
}
bool TypeChecker::diagnoseSelfAssignment(const Expr *expr) {
auto *assignExpr = dyn_cast<AssignExpr>(expr);
if (!assignExpr)
return false;
auto *dstExpr = assignExpr->getDest();
auto *srcExpr = assignExpr->getSrc();
auto dstDecl = findReferencedVarDecl(dstExpr);
auto srcDecl = findReferencedVarDecl(srcExpr);
if (dstDecl.second &&
dstDecl.second->hasStorage() &&
dstDecl == srcDecl) {
auto &DE = dstDecl.second->getASTContext().Diags;
DE.diagnose(expr->getLoc(), dstDecl.first ? diag::self_assignment_prop
: diag::self_assignment_var)
.highlight(dstExpr->getSourceRange())
.highlight(srcExpr->getSourceRange());
return true;
}
return false;
}
bool TrailingClosureAmbiguityFailure::diagnoseAsNote() {
auto *anchor = castToExpr(getAnchor());
const auto *expr = findParentExpr(anchor);
auto *callExpr = dyn_cast_or_null<CallExpr>(expr);
if (!callExpr)
return false;
if (!callExpr->hasTrailingClosure())
return false;
if (callExpr->getFn() != anchor)
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->hasInterfaceType() ||
!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 = emitDiagnosticAt(
expr->getLoc(), diag::ambiguous_because_of_trailing_closure,
choicePair.first.empty(), choicePair.second->getName());
swift::fixItEncloseTrailingClosure(getASTContext(), diag, callExpr,
choicePair.first);
}
return true;
}
AssignmentFailure::AssignmentFailure(Expr *destExpr, const Solution &solution,
SourceLoc diagnosticLoc)
: FailureDiagnostic(solution, destExpr), DestExpr(destExpr),
Loc(diagnosticLoc),
DeclDiagnostic(findDeclDiagonstic(getASTContext(), destExpr)),
TypeDiagnostic(diag::assignment_lhs_not_lvalue) {}
bool AssignmentFailure::diagnoseAsError() {
auto *DC = getDC();
// 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.
Expr *immutableExpr;
Optional<OverloadChoice> choice;
std::tie(immutableExpr, choice) = resolveImmutableBase(DestExpr);
// Attempt diagnostics based on the overload choice.
if (choice.hasValue()) {
auto getKeyPathArgument = [](SubscriptExpr *expr) {
auto *TE = dyn_cast<TupleExpr>(expr->getIndex());
assert(TE->getNumElements() == 1);
assert(TE->getElementName(0).str() == "keyPath");
return TE->getElement(0);
};
if (!choice->isDecl()) {
if (choice->getKind() == OverloadChoiceKind::KeyPathApplication &&
!isa<ApplyExpr>(immutableExpr)) {
std::string message = "key path is read-only";
if (auto *SE = dyn_cast<SubscriptExpr>(immutableExpr)) {
if (auto *DRE = dyn_cast<DeclRefExpr>(getKeyPathArgument(SE))) {
auto identifier = DRE->getDecl()->getBaseIdentifier();
message =
"'" + identifier.str().str() + "' is a read-only key path";
}
}
emitDiagnosticAt(Loc, DeclDiagnostic, message)
.highlight(immutableExpr->getSourceRange());
return true;
}
return false;
}
// 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<VarDecl>(choice->getDecl())) {
std::string message = "'";
message += VD->getName().str().str();
message += "'";
auto type = getType(immutableExpr);
if (isKnownKeyPathType(type))
message += " is read-only";
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";
}
emitDiagnosticAt(Loc, DeclDiagnostic, message)
.highlight(immutableExpr->getSourceRange());
// If there is a masked property of the same type, emit a
// note to fixit prepend a 'self.' or 'Type.'.
if (auto typeContext = DC->getInnermostTypeContext()) {
SmallVector<ValueDecl *, 2> results;
DC->lookupQualified(typeContext->getSelfNominalTypeDecl(),
VD->createNameRef(), NL_QualifiedDefault, results);
auto foundProperty = llvm::find_if(results, [&](ValueDecl *decl) {
// We're looking for a settable property that is the same type as the
// var we found.
auto *var = dyn_cast<VarDecl>(decl);
if (!var || var == VD)
return false;
if (!var->isSettable(DC) || !var->isSetterAccessibleFrom(DC))
return false;
if (!var->getType()->isEqual(VD->getType()))
return false;
// Don't suggest a property if we're in one of its accessors.
auto *methodDC = DC->getInnermostMethodContext();
if (auto *AD = dyn_cast_or_null<AccessorDecl>(methodDC))
if (AD->getStorage() == var)
return false;
return true;
});
if (foundProperty != results.end()) {
auto startLoc = immutableExpr->getStartLoc();
auto *property = *foundProperty;
auto selfTy = typeContext->getSelfTypeInContext();
// If we found an instance property, suggest inserting "self.",
// otherwise suggest "Type." for a static property.
std::string fixItText;
if (property->isInstanceMember()) {
fixItText = "self.";
} else {
fixItText = selfTy->getString() + ".";
}
emitDiagnosticAt(startLoc, diag::masked_mutable_property, fixItText,
property->getDescriptiveKind(), selfTy)
.fixItInsert(startLoc, fixItText);
}
}
// 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>(choice->getDecl())) {
StringRef message;
if (!SD->supportsMutation())
message = "subscript is get-only";
else if (!SD->isSetterAccessibleFrom(DC))
message = "subscript setter is inaccessible";
else
message = "subscript is immutable";
emitDiagnosticAt(Loc, DeclDiagnostic, message)
.highlight(immutableExpr->getSourceRange());
return true;
}
// If we're trying to set an unapplied method, say that.
if (auto *VD = choice->getDecl()) {
std::string message = "'";
message += VD->getBaseIdentifier().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";
emitDiagnosticAt(Loc, diagID, message)
.highlight(immutableExpr->getSourceRange());
return true;
}
}
// Fall back to producing diagnostics based on the expression since we
// couldn't determine anything from the OverloadChoice.
// 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>(immutableExpr)) {
emitDiagnosticAt(Loc, DeclDiagnostic, "immutable key path")
.highlight(KPE->getSourceRange());
return true;
}
if (auto LE = dyn_cast<LiteralExpr>(immutableExpr)) {
emitDiagnosticAt(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>(immutableExpr)) {
// 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())) {
emitDiagnosticAt(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()->getBaseIdentifier().str().str() + "'";
emitDiagnosticAt(Loc, DeclDiagnostic, name + " returns immutable value")
.highlight(AE->getSourceRange());
return true;
}
if (auto contextualType = getContextualType(immutableExpr)) {
Type neededType = contextualType->getInOutObjectType();
Type actualType = getType(immutableExpr)->getInOutObjectType();
if (!neededType->isEqual(actualType)) {
if (DeclDiagnostic.ID != diag::cannot_pass_rvalue_inout_subelement.ID) {
emitDiagnosticAt(Loc, DeclDiagnostic,
"implicit conversion from '" +
actualType->getString() + "' to '" +
neededType->getString() + "' requires a temporary")
.highlight(immutableExpr->getSourceRange());
}
return true;
}
}
if (auto IE = dyn_cast<IfExpr>(immutableExpr)) {
emitDiagnosticAt(Loc, DeclDiagnostic,
"result of conditional operator '? :' is never mutable")
.highlight(IE->getQuestionLoc())
.highlight(IE->getColonLoc());
return true;
}
emitDiagnosticAt(Loc, TypeDiagnostic, getType(DestExpr))
.highlight(immutableExpr->getSourceRange());
return true;
}
std::pair<Expr *, Optional<OverloadChoice>>
AssignmentFailure::resolveImmutableBase(Expr *expr) const {
auto *DC = getDC();
expr = expr->getValueProvidingExpr();
auto isImmutable = [&DC](ValueDecl *decl) {
if (auto *storage = dyn_cast<AbstractStorageDecl>(decl))
return !storage->isSettable(nullptr) ||
!storage->isSetterAccessibleFrom(DC);
// If this is not something which could possibly be mutable,
// then it's immutable.
return true;
};
// 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.
if (SE->hasDecl()) {
const auto &declRef = SE->getDecl();
if (auto *subscript =
dyn_cast_or_null<SubscriptDecl>(declRef.getDecl())) {
if (isImmutable(subscript))
return {expr, OverloadChoice(getType(SE->getBase()), subscript,
FunctionRefKind::DoubleApply)};
}
}
Optional<OverloadChoice> member = getMemberRef(
getConstraintLocator(SE, ConstraintLocator::SubscriptMember));
// If it isn't settable, return it.
if (member) {
if (member->isDecl() && isImmutable(member->getDecl()))
return {expr, member};
// We still have a choice, the choice is not a decl
if (!member->isDecl()) {
// This must be a keypath application
assert(member->getKind() == OverloadChoiceKind::KeyPathApplication);
auto *argType = getType(SE->getIndex())->castTo<TupleType>();
assert(argType->getNumElements() == 1);
auto indexType = resolveType(argType->getElementType(0));
if (auto bgt = indexType->getAs<BoundGenericType>()) {
// In Swift versions lower than 5, this check will fail as read only
// key paths can masquerade as writable for compatibilty reasons.
// This is fine as in this case we just fall back on old diagnostics.
auto &ctx = getASTContext();
if (bgt->getDecl() == ctx.getKeyPathDecl() ||
bgt->getDecl() == ctx.getPartialKeyPathDecl()) {
return {expr, member};
}
}
}
}
// 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 = 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 && member->isDecl() && isImmutable(member->getDecl()))
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 (isImmutable(member))
return {expr, OverloadChoice(getType(MRE->getBase()), member,
FunctionRefKind::SingleApply)};
// 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 *UME = dyn_cast<UnresolvedMemberExpr>(expr)) {
auto loc = getConstraintLocator(UME, ConstraintLocator::UnresolvedMember);
auto member = getMemberRef(loc);
// If we can resolve a member, we can determine whether it is settable in
// this context.
if (member && member->isDecl() && isImmutable(member->getDecl()))
return {expr, member};
else
return {expr, None};
}
if (auto *DRE = dyn_cast<DeclRefExpr>(expr))
return {expr,
OverloadChoice(Type(), DRE->getDecl(), FunctionRefKind::Unapplied)};
// 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, None};
}
Optional<OverloadChoice>
AssignmentFailure::getMemberRef(ConstraintLocator *locator) const {
auto member = getOverloadChoiceIfAvailable(locator);
if (!member)
return None;
if (!member->choice.isDecl())
return member->choice;
auto *DC = getDC();
auto *decl = member->choice.getDecl();
if (isa<SubscriptDecl>(decl) &&
isValidDynamicMemberLookupSubscript(cast<SubscriptDecl>(decl), DC)) {
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)) {
// 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 = getConstraintLocator(
locator, LocatorPathElt::KeyPathDynamicMember(keyPath));
auto memberRef = getOverloadChoiceIfAvailable(memberLoc);
return memberRef ? Optional<OverloadChoice>(memberRef->choice) : None;
}
// If this is a string based dynamic lookup, there is no member declaration.
return None;
}
return member->choice;
}
Diag<StringRef> AssignmentFailure::findDeclDiagonstic(ASTContext &ctx,
const 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();
if (CTP == CTP_ReturnSingleExpr || CTP == CTP_ReturnStmt) {
// Special case the "conversion to void".
if (getToType()->isVoid()) {
emitDiagnostic(diag::cannot_return_value_from_void_func)
.highlight(getSourceRange());
return true;
}
}
if (diagnoseConversionToNil())
return true;
if (path.empty()) {
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
emitDiagnosticAt(KPE->getLoc(),
diag::expr_smart_keypath_value_covert_to_contextual_type,
getFromType(), getToType());
return true;
}
if (diagnoseCoercionToUnrelatedType())
return true;
if (isExpr<OptionalTryExpr>(anchor)) {
emitDiagnostic(diag::cannot_convert_initializer_value, getFromType(),
getToType());
return true;
}
if (isExpr<AssignExpr>(anchor)) {
auto diagnostic = emitDiagnostic(diag::cannot_convert_assign,
getFromType(), getToType());
tryIntegerCastFixIts(diagnostic);
return true;
}
return false;
}
if (diagnoseMissingFunctionCall())
return true;
// Special case of some common conversions involving Swift.String
// indexes, catching cases where people attempt to index them with an integer.
if (isIntegerToStringIndexConversion()) {
emitDiagnostic(diag::string_index_not_integer, getFromType())
.highlight(getSourceRange());
emitDiagnostic(diag::string_index_not_integer_note);
return true;
}
auto fromType = getFromType();
auto toType = getToType();
Diag<Type, Type> diagnostic;
switch (path.back().getKind()) {
case ConstraintLocator::ClosureBody:
case ConstraintLocator::ClosureResult: {
auto *closure = castToExpr<ClosureExpr>(getRawAnchor());
if (closure->hasExplicitResultType() &&
closure->getExplicitResultTypeRepr()) {
auto resultRepr = closure->getExplicitResultTypeRepr();
emitDiagnosticAt(resultRepr->getStartLoc(),
diag::incorrect_explicit_closure_result, fromType,
toType)
.fixItReplace(resultRepr->getSourceRange(), toType.getString());
return true;
}
diagnostic = diag::cannot_convert_closure_result;
break;
}
case ConstraintLocator::Condition: {
// Tailored diagnostics for optional or assignment use
// in condition expression.
if (diagnoseConversionToBool())
return true;
diagnostic = diag::cannot_convert_condition_value;
break;
}
case ConstraintLocator::InstanceType: {
if (diagnoseCoercionToUnrelatedType())
return true;
break;
}
case ConstraintLocator::TernaryBranch: {
auto *ifExpr = castToExpr<IfExpr>(getRawAnchor());
fromType = getType(ifExpr->getThenExpr());
toType = getType(ifExpr->getElseExpr());
diagnostic = diag::if_expr_cases_mismatch;
break;
}
case ConstraintLocator::ContextualType: {
if (diagnoseConversionToBool())
return true;
if (diagnoseThrowsTypeMismatch())
return true;
if (diagnoseYieldByReferenceMismatch())
return true;
if (isExpr<OptionalTryExpr>(anchor) ||
isExpr<OptionalEvaluationExpr>(anchor)) {
auto objectType = fromType->getOptionalObjectType();
if (objectType->isEqual(toType)) {
MissingOptionalUnwrapFailure failure(getSolution(), getType(anchor),
toType,
getConstraintLocator(anchor));
if (failure.diagnoseAsError())
return true;
}
}
if (CTP == CTP_ForEachStmt) {
if (fromType->isAnyExistentialType()) {
emitDiagnostic(diag::type_cannot_conform,
/*isExistentialType=*/true, fromType,
fromType->isEqual(toType), toType);
emitDiagnostic(diag::only_concrete_types_conform_to_protocols);
return true;
}
emitDiagnostic(
diag::foreach_sequence_does_not_conform_to_expected_protocol,
fromType, toType, bool(fromType->getOptionalObjectType()))
.highlight(getSourceRange());
return true;
}
if (auto *call = getAsExpr<CallExpr>(anchor)) {
if (isa<ClosureExpr>(call->getFn()))
CTP = CTP_ClosureResult;
}
if (auto msg = getDiagnosticFor(CTP, toType)) {
diagnostic = *msg;
break;
}
return false;
}
case ConstraintLocator::UnresolvedMemberChainResult: {
auto &solution = getSolution();
auto overload =
getCalleeOverloadChoiceIfAvailable(getConstraintLocator(anchor));
if (!(overload && overload->choice.isDecl()))
return false;
auto *choice = overload->choice.getDecl();
auto fnType = fromType->getAs<FunctionType>();
if (!fnType) {
emitDiagnostic(diag::expected_result_in_contextual_member,
choice->getName(), fromType, toType);
return true;
}
// If member type is a function and contextual type matches
// its result type, most likely problem is related to a
// missing call e.g.:
//
// struct S {
// static func foo() -> S {}
// }
//
// let _: S = .foo
auto params = fnType->getParams();
ParameterListInfo info(
params, choice,
hasAppliedSelf(overload->choice, [&solution](Type type) {
return solution.simplifyType(type);
}));
auto numMissingArgs = llvm::count_if(
indices(params), [&info](const unsigned paramIdx) -> bool {
return !info.hasDefaultArgument(paramIdx);
});
if (numMissingArgs == 0 || numMissingArgs > 1) {
auto applyFixIt = [&](InFlightDiagnostic &diagnostic) {
// If there are no parameters we can suggest a fix-it
// to form an explicit call.
if (numMissingArgs == 0)
diagnostic.fixItInsertAfter(getSourceRange().End, "()");
};
if (fnType->getResult()->isEqual(toType)) {
auto diag = emitDiagnostic(
diag::expected_parens_in_contextual_member_type,
choice->getName(), fnType->getResult());
applyFixIt(diag);
} else {
auto diag = emitDiagnostic(diag::expected_parens_in_contextual_member,
choice->getName());
applyFixIt(diag);
}
} else {
emitDiagnostic(diag::expected_argument_in_contextual_member,
choice->getName(), params.front().getPlainType());
}
return true;
}
case ConstraintLocator::ResultBuilderBodyResult: {
diagnostic = *getDiagnosticFor(CTP_Initialization, toType);
break;
}
default:
return false;
}
auto diag = emitDiagnostic(diagnostic, fromType, toType);
diag.highlight(getSourceRange());
(void)tryFixIts(diag);
return true;
}
bool ContextualFailure::diagnoseAsNote() {
auto overload = getCalleeOverloadChoiceIfAvailable(getLocator());
if (!(overload && overload->choice.isDecl()))
return false;
auto *decl = overload->choice.getDecl();
emitDiagnosticAt(decl, diag::found_candidate_type, getFromType());
return true;
}
static Optional<Diag<Type>>
getContextualNilDiagnostic(ContextualTypePurpose CTP) {
switch (CTP) {
case CTP_Unused:
case CTP_CannotFail:
llvm_unreachable("These contextual type purposes cannot fail with a "
"conversion type specified!");
case CTP_CalleeResult:
llvm_unreachable("CTP_CalleeResult does not actually install a "
"contextual type");
case CTP_Initialization:
return diag::cannot_convert_initializer_value_nil;
case CTP_ReturnSingleExpr:
case CTP_ReturnStmt:
return diag::cannot_convert_to_return_type_nil;
case CTP_ThrowStmt:
case CTP_ForEachStmt:
case CTP_YieldByReference:
case CTP_WrappedProperty:
case CTP_ComposedPropertyWrapper:
return None;
case CTP_EnumCaseRawValue:
return diag::cannot_convert_raw_initializer_value_nil;
case CTP_DefaultParameter:
case CTP_AutoclosureDefaultParameter:
return diag::cannot_convert_default_arg_value_nil;
case CTP_YieldByValue:
return diag::cannot_convert_yield_value_nil;
case CTP_CallArgument:
return diag::cannot_convert_argument_value_nil;
case CTP_ClosureResult:
return diag::cannot_convert_closure_result_nil;
case CTP_ArrayElement:
return diag::cannot_convert_array_element_nil;
case CTP_DictionaryKey:
return diag::cannot_convert_dict_key_nil;
case CTP_DictionaryValue:
return diag::cannot_convert_dict_value_nil;
case CTP_CoerceOperand:
return diag::cannot_convert_coerce_nil;
case CTP_AssignSource:
return diag::cannot_convert_assign_nil;
case CTP_SubscriptAssignSource:
return diag::cannot_convert_subscript_assign_nil;
case CTP_Condition:
return diag::cannot_convert_condition_value_nil;
}
llvm_unreachable("Unhandled ContextualTypePurpose in switch");
}
bool ContextualFailure::diagnoseConversionToNil() const {
auto anchor = getAnchor();
if (!isExpr<NilLiteralExpr>(anchor))
return false;
auto *locator = getLocator();
Optional<ContextualTypePurpose> CTP;
// Easy case were failure has been identified as contextual already.
if (locator->isLastElement<LocatorPathElt::ContextualType>()) {
CTP = getContextualTypePurpose();
} else {
// Here we need to figure out where where `nil` is located.
// It could be e.g. an argument to a subscript/call, assignment
// source like `s[0] = nil` or an array element like `[nil]` or
// `[nil: 42]` as a sub-expression to a larger one.
auto *parentExpr = findParentExpr(getAsExpr(anchor));
// Looks like it's something similar to `let _ = nil`.
if (!parentExpr) {
emitDiagnostic(diag::unresolved_nil_literal);
return true;
}
// Two choices here - whether it's a regular assignment
// e.g. `let _: S = nil` or a subscript one e.g. `s[0] = nil`.
if (auto *AE = dyn_cast<AssignExpr>(parentExpr)) {
CTP = isa<SubscriptExpr>(AE->getDest()) ? CTP_SubscriptAssignSource
: CTP_AssignSource;
} else if (isa<ArrayExpr>(parentExpr)) {
CTP = CTP_ArrayElement;
} else if (isa<ClosureExpr>(parentExpr)) {
CTP = CTP_ClosureResult;
} else if (isa<ParenExpr>(parentExpr) || isa<TupleExpr>(parentExpr)) {
auto *enclosingExpr = findParentExpr(parentExpr);
if (!enclosingExpr) {
// If there is no enclosing expression it's something like
// `(nil)` or `(a: nil)` which can't be inferred without a
// contextual type.
emitDiagnostic(diag::unresolved_nil_literal);
return true;
}
if (auto *TE = dyn_cast<TupleExpr>(parentExpr)) {
// In case of dictionary e.g. `[42: nil]` we need to figure
// out whether nil is a "key" or a "value".
if (isa<DictionaryExpr>(enclosingExpr)) {
assert(TE->getNumElements() == 2);
CTP = TE->getElement(0) == castToExpr(anchor) ? CTP_DictionaryKey
: CTP_DictionaryValue;
} else {
// Can't initialize one of the tuple elements with `nil`.
CTP = CTP_Initialization;
}
}
// `nil` is passed as an argument to a parameter which doesn't
// expect it e.g. `foo(a: nil)`, `s[x: nil]` or `\S.[x: nil]`.
// FIXME: Find a more robust way of checking this.
if (isa<ApplyExpr>(enclosingExpr) || isa<SubscriptExpr>(enclosingExpr) ||
isa<KeyPathExpr>(enclosingExpr))
CTP = CTP_CallArgument;
} else if (isa<CoerceExpr>(parentExpr)) {
// `nil` is passed as a left-hand side of the coercion
// operator e.g. `nil as Foo`
CTP = CTP_CoerceOperand;
} else {
// Otherwise let's produce a generic `nil` conversion diagnostic.
emitDiagnostic(diag::cannot_use_nil_with_this_type, getToType());
return true;
}
}
if (!CTP)
return false;
if (CTP == CTP_ThrowStmt) {
emitDiagnostic(diag::cannot_throw_nil);
return true;
}
auto diagnostic = getContextualNilDiagnostic(*CTP);
if (!diagnostic)
return false;
emitDiagnostic(*diagnostic, getToType());
if (CTP == CTP_Initialization) {
auto *patternTR = getContextualTypeLoc(getRawAnchor()).getTypeRepr();
if (!patternTR)
return true;
auto diag = emitDiagnosticAt(patternTR->getLoc(), diag::note_make_optional,
OptionalType::get(getToType()));
if (patternTR->isSimple()) {
diag.fixItInsertAfter(patternTR->getEndLoc(), "?");
} else {
diag.fixItInsert(patternTR->getStartLoc(), "(");
diag.fixItInsertAfter(patternTR->getEndLoc(), ")?");
}
}
return true;
}
void ContextualFailure::tryFixIts(InFlightDiagnostic &diagnostic) const {
auto *locator = getLocator();
// Can't apply any of the fix-its below if this failure
// is related to `inout` argument.
if (locator->isLastElement<LocatorPathElt::LValueConversion>())
return;
if (trySequenceSubsequenceFixIts(diagnostic))
return;
if (tryIntegerCastFixIts(diagnostic))
return;
if (tryProtocolConformanceFixIt(diagnostic))
return;
if (tryTypeCoercionFixIt(diagnostic))
return;
}
bool ContextualFailure::diagnoseMissingFunctionCall() const {
if (getLocator()
->isLastElement<LocatorPathElt::UnresolvedMemberChainResult>())
return false;
auto *srcFT = getFromType()->getAs<FunctionType>();
if (!srcFT ||
!(srcFT->getParams().empty() ||
getLocator()->isLastElement<LocatorPathElt::PatternMatch>()))
return false;
auto toType = getToType();
if (toType->is<AnyFunctionType>() ||
!TypeChecker::isConvertibleTo(srcFT->getResult(), toType, getDC()))
return false;
// Diagnose cases where the pattern tried to match associated values but
// the case we found had none.
if (auto match =
getLocator()->getLastElementAs<LocatorPathElt::PatternMatch>()) {
if (auto enumElementPattern =
dyn_cast<EnumElementPattern>(match->getPattern())) {
emitDiagnosticAt(enumElementPattern->getNameLoc(),
diag::enum_element_pattern_assoc_values_mismatch,
enumElementPattern->getName());
emitDiagnosticAt(enumElementPattern->getNameLoc(),
diag::enum_element_pattern_assoc_values_remove)
.fixItRemove(enumElementPattern->getSubPattern()->getSourceRange());
return true;
}
}
emitDiagnostic(diag::missing_nullary_call, srcFT->getResult())
.highlight(getSourceRange())
.fixItInsertAfter(getSourceRange().End, "()");
tryComputedPropertyFixIts();
return true;
}
bool ContextualFailure::diagnoseCoercionToUnrelatedType() const {
auto anchor = getAnchor();
if (auto *coerceExpr = getAsExpr<CoerceExpr>(anchor)) {
const auto fromType = getType(coerceExpr->getSubExpr());
const auto toType = getType(coerceExpr->getCastTypeRepr());
auto diagnostic = getDiagnosticFor(CTP_CoerceOperand, toType);
auto diag = emitDiagnostic(*diagnostic, fromType, toType);
diag.highlight(getSourceRange());
(void)tryFixIts(diag);
return true;
}
return false;
}
bool ContextualFailure::diagnoseConversionToBool() const {
auto toType = getToType();
if (!toType->isBool())
return false;
auto *anchor = castToExpr(getAnchor());
// Check for "=" converting to Bool. The user probably meant ==.
if (auto *AE = dyn_cast<AssignExpr>(anchor->getValueProvidingExpr())) {
emitDiagnosticAt(AE->getEqualLoc(), diag::use_of_equal_instead_of_equality)
.fixItReplace(AE->getEqualLoc(), "==")
.highlight(AE->getDest()->getLoc())
.highlight(AE->getSrc()->getLoc());
return true;
}
// Determine if the boolean negation operator was applied to the anchor. This
// upwards traversal of the AST is somewhat fragile, but enables much better
// diagnostics if someone attempts to use an optional or integer as a boolean
// condition.
SourceLoc notOperatorLoc;
if (Expr *parent = findParentExpr(anchor)) {
if (isa<ParenExpr>(parent) && parent->isImplicit()) {
if ((parent = findParentExpr(parent))) {
auto parentOperatorApplication = dyn_cast<PrefixUnaryExpr>(parent);
if (parentOperatorApplication) {
auto operatorRefExpr =
dyn_cast<DeclRefExpr>(parentOperatorApplication->getFn());
if (operatorRefExpr && operatorRefExpr->getDecl()->getBaseName() ==
getASTContext().Id_NegationOperator) {
notOperatorLoc = operatorRefExpr->getLoc();
}
}
}
}
}
// If we're trying to convert something from optional type to Bool, then a
// comparison against nil was probably expected.
auto fromType = getFromType();
if (fromType->getOptionalObjectType()) {
StringRef prefix = "((";
StringRef suffix;
if (notOperatorLoc.isValid())
suffix = ") == nil)";
else
suffix = ") != nil)";
// Check if we need the inner parentheses.
// Technically we only need them if there's something in 'expr' with
// lower precedence than '!=', but the code actually comes out nicer
// in most cases with parens on anything non-trivial.
if (anchor->canAppendPostfixExpression()) {
prefix = prefix.drop_back();
suffix = suffix.drop_front();
}
// FIXME: The outer parentheses may be superfluous too.
emitDiagnostic(diag::optional_used_as_boolean, fromType,
notOperatorLoc.isValid())
.fixItInsert(getSourceRange().Start, prefix)
.fixItInsertAfter(getSourceRange().End, suffix)
.fixItRemove(notOperatorLoc);
return true;
}
// If we're trying to convert something from optional type to an integer, then
// a comparison against nil was probably expected.
if (conformsToKnownProtocol(fromType, KnownProtocolKind::BinaryInteger) &&
conformsToKnownProtocol(fromType,
KnownProtocolKind::ExpressibleByIntegerLiteral)) {
StringRef prefix = "((";
StringRef suffix;
if (notOperatorLoc.isValid())
suffix = ") == 0)";
else
suffix = ") != 0)";
// Check if we need the inner parentheses.
// Technically we only need them if there's something in 'expr' with
// lower precedence than '!=', but the code actually comes out nicer
// in most cases with parens on anything non-trivial.
if (anchor->canAppendPostfixExpression()) {
prefix = prefix.drop_back();
suffix = suffix.drop_front();
}
// FIXME: The outer parentheses may be superfluous too.
emitDiagnostic(diag::integer_used_as_boolean, fromType,
notOperatorLoc.isValid())
.fixItInsert(getSourceRange().Start, prefix)
.fixItInsertAfter(getSourceRange().End, suffix)
.fixItRemove(notOperatorLoc);
return true;
}
return false;
}
bool ContextualFailure::diagnoseThrowsTypeMismatch() const {
// If this is conversion failure due to a return statement with an argument
// that cannot be coerced to the result type of the function, emit a
// specific error.
if (CTP != CTP_ThrowStmt)
return false;
auto anchor = getAnchor();
// If we tried to throw the error code of an error type, suggest object
// construction.
auto &Ctx = getASTContext();
if (auto errorCodeProtocol =
Ctx.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) {
Type errorCodeType = getFromType();
auto conformance = TypeChecker::conformsToProtocol(
errorCodeType, errorCodeProtocol, getDC());
if (conformance) {
Type errorType =
conformance
.getTypeWitnessByName(errorCodeType, getASTContext().Id_ErrorType)
->getCanonicalType();
if (errorType) {
auto diagnostic = emitDiagnostic(diag::cannot_throw_error_code,
errorCodeType, errorType);
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
diagnostic.fixItInsert(UDE->getDotLoc(), "(");
diagnostic.fixItInsertAfter(UDE->getEndLoc(), ")");
}
return true;
}
}
}
// The conversion destination of throw is always ErrorType (at the moment)
// if this ever expands, this should be a specific form like () is for
// return.
emitDiagnostic(diag::cannot_convert_thrown_type, getFromType())
.highlight(getSourceRange());
return true;
}
bool ContextualFailure::diagnoseYieldByReferenceMismatch() const {
if (CTP != CTP_YieldByReference)
return false;
auto anchor = getAnchor();
auto exprType = getType(anchor, /*wantRValue=*/false);
auto contextualType = getToType();
if (auto exprLV = exprType->getAs<LValueType>()) {
emitDiagnostic(diag::cannot_yield_wrong_type_by_reference,
exprLV->getObjectType(), contextualType);
} else if (exprType->isEqual(contextualType)) {
emitDiagnostic(diag::cannot_yield_rvalue_by_reference_same_type, exprType);
} else {
emitDiagnostic(diag::cannot_yield_rvalue_by_reference, exprType,
contextualType);
}
return true;
}
bool ContextualFailure::tryIntegerCastFixIts(
InFlightDiagnostic &diagnostic) const {
auto fromType = getFromType();
auto toType = getToType();
auto anchor = getAnchor();
auto exprRange = getSourceRange();
if (auto *assignment = getAsExpr<AssignExpr>(anchor)) {
toType = toType->lookThroughAllOptionalTypes();
anchor = assignment->getSrc();
exprRange = assignment->getSrc()->getSourceRange();
}
if (!isIntegerType(fromType) || !isIntegerType(toType))
return false;
auto getInnerCastedExpr = [&](const Expr *expr) -> Expr * {
if (auto *CE = dyn_cast<CoerceExpr>(expr))
return CE->getSubExpr();
auto *CE = dyn_cast<CallExpr>(expr);
if (!CE)
return nullptr;
if (!isa<ConstructorRefCallExpr>(CE->getFn()))
return nullptr;
auto *parenE = dyn_cast<ParenExpr>(CE->getArg());
if (!parenE)
return nullptr;
return parenE->getSubExpr();
};
if (auto *expr = getAsExpr(anchor)) {
if (Expr *innerE = getInnerCastedExpr(expr)) {
Type innerTy = getType(innerE);
if (TypeChecker::isConvertibleTo(innerTy, toType, getDC())) {
// Remove the unnecessary cast.
diagnostic.fixItRemoveChars(getLoc(), innerE->getStartLoc())
.fixItRemove(getSourceRange().End);
return true;
}
}
}
// Add a wrapping integer cast.
std::string convWrapBefore = toType.getString();
convWrapBefore += "(";
std::string convWrapAfter = ")";
diagnostic.fixItInsert(exprRange.Start, convWrapBefore);
diagnostic.fixItInsertAfter(exprRange.End, convWrapAfter);
return true;
}
bool ContextualFailure::trySequenceSubsequenceFixIts(
InFlightDiagnostic &diagnostic) const {
if (!getASTContext().getStdlibModule())
return false;
auto String = getASTContext().getStringDecl()->getDeclaredInterfaceType();
auto Substring = getASTContext().getSubstringDecl()->getDeclaredInterfaceType();
// Substring -> String conversion
// Wrap in String.init
if (getFromType()->isEqual(Substring)) {
if (getToType()->isEqual(String)) {
auto *anchor = castToExpr(getAnchor())->getSemanticsProvidingExpr();
if (auto *CE = dyn_cast<CoerceExpr>(anchor)) {
anchor = CE->getSubExpr();
}
if (auto *call = dyn_cast<CallExpr>(anchor)) {
auto *fnExpr = call->getFn();
if (auto *closure = dyn_cast<ClosureExpr>(fnExpr)) {
if (closure->hasSingleExpressionBody())
anchor = closure->getSingleExpressionBody();
}
}
auto range = anchor->getSourceRange();
diagnostic.fixItInsert(range.Start, "String(");
diagnostic.fixItInsertAfter(range.End, ")");
return true;
}
}
return false;
}
bool ContextualFailure::tryTypeCoercionFixIt(
InFlightDiagnostic &diagnostic) const {
auto fromType = getFromType();
auto toType = getToType();
// Look through optional types; casts can add them, but can't remove extra
// ones.
bool bothOptional =
fromType->getOptionalObjectType() && toType->getOptionalObjectType();
if (bothOptional)
fromType = fromType->getOptionalObjectType();
toType = toType->lookThroughAllOptionalTypes();
if (!toType->hasTypeRepr())
return false;
CheckedCastKind Kind =
TypeChecker::typeCheckCheckedCast(fromType, toType,
CheckedCastContextKind::None, getDC(),
SourceLoc(), nullptr, SourceRange());
if (Kind != CheckedCastKind::Unresolved) {
bool canUseAs = Kind == CheckedCastKind::Coercion ||
Kind == CheckedCastKind::BridgingCoercion;
if (bothOptional && canUseAs)
toType = OptionalType::get(toType);
diagnostic.fixItInsert(Lexer::getLocForEndOfToken(getASTContext().SourceMgr,
getSourceRange().End),
diag::insert_type_coercion, canUseAs, toType);
return true;
}
return false;
}
bool ContextualFailure::tryProtocolConformanceFixIt(
InFlightDiagnostic &diagnostic) const {
auto innermostTyCtx = getDC()->getInnermostTypeContext();
if (!innermostTyCtx)
return false;
auto nominal = innermostTyCtx->getSelfNominalTypeDecl();
if (!nominal)
return false;
auto fromType = getFromType();
// We need to get rid of optionals and parens as it's not relevant when
// printing the diagnostic and the fix-it.
auto unwrappedToType =
getToType()->lookThroughAllOptionalTypes()->getWithoutParens();
// If the protocol requires a class & we don't have one (maybe the context
// is a struct), then bail out instead of offering a broken fix-it later on.
auto requiresClass = false;
ExistentialLayout layout;
if (unwrappedToType->isExistentialType()) {
layout = unwrappedToType->getExistentialLayout();
requiresClass = layout.requiresClass();
}
if (requiresClass && !fromType->is<ClassType>()) {
return false;
}
// We can only offer a fix-it if we're assigning to a protocol type and
// the type we're assigning is the same as the innermost type context.
bool shouldOfferFixIt = nominal->getSelfTypeInContext()->isEqual(fromType) &&
unwrappedToType->isExistentialType();
if (!shouldOfferFixIt)
return false;
diagnostic.flush();
// Let's build a list of protocols that the context does not conform to.
SmallVector<std::string, 8> missingProtoTypeStrings;
SmallVector<ProtocolDecl *, 8> missingProtocols;
for (auto protocol : layout.getProtocols()) {
if (!TypeChecker::conformsToProtocol(fromType, protocol->getDecl(), getDC())) {
missingProtoTypeStrings.push_back(protocol->getString());
missingProtocols.push_back(protocol->getDecl());
}
}
// If we have a protocol composition type and we don't conform to all
// the protocols of the composition, then store the composition directly.
// This is because we need to append 'Foo & Bar' instead of 'Foo, Bar' in
// order to match the written type.
if (auto compositionTy = unwrappedToType->getAs<ProtocolCompositionType>()) {
if (compositionTy->getMembers().size() == missingProtoTypeStrings.size()) {
missingProtoTypeStrings = {compositionTy->getString()};
}
}
assert(!missingProtoTypeStrings.empty() &&
"type already conforms to all the protocols?");
// Combine all protocol names together, separated by commas.
std::string protoString = llvm::join(missingProtoTypeStrings, ", ");
// Emit a diagnostic to inform the user that they need to conform to the
// missing protocols.
auto conformanceDiag =
emitDiagnostic(diag::assign_protocol_conformance_fix_it, unwrappedToType,
nominal->getDescriptiveKind(), fromType);
if (nominal->getInherited().size() > 0) {
auto lastInherited = nominal->getInherited().back().getLoc();
auto lastInheritedEndLoc =
Lexer::getLocForEndOfToken(getASTContext().SourceMgr, lastInherited);
conformanceDiag.fixItInsert(lastInheritedEndLoc, ", " + protoString);
} else {
auto nameEndLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr,
nominal->getNameLoc());
conformanceDiag.fixItInsert(nameEndLoc, ": " + protoString);
}
// Emit fix-its to insert requirement stubs if we're in editor mode.
if (!getASTContext().LangOpts.DiagnosticsEditorMode) {
return true;
}
{
llvm::SmallString<128> Text;
llvm::raw_svector_ostream SS(Text);
llvm::SetVector<MissingWitness> missingWitnesses;
for (auto protocol : missingProtocols) {
auto conformance = NormalProtocolConformance(
nominal->getDeclaredType(), protocol, SourceLoc(), nominal,
ProtocolConformanceState::Incomplete);
ConformanceChecker checker(getASTContext(), &conformance,
missingWitnesses);
checker.resolveValueWitnesses();
checker.resolveTypeWitnesses();
}
for (auto decl : missingWitnesses) {
swift::printRequirementStub(decl.requirement, nominal, nominal->getDeclaredType(),
nominal->getStartLoc(), SS);
}
if (!Text.empty()) {
conformanceDiag.fixItInsertAfter(nominal->getBraces().Start, Text.str());
}
}
return true;
}
void ContextualFailure::tryComputedPropertyFixIts() const {
if (!isExpr<ClosureExpr>(getAnchor()))
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()->getFirstElement().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()) {
const auto i = PBD->getPatternEntryIndexForVarDecl(VD);
auto *initExpr = PBD->getInit(i);
if (!VD->isStatic() &&
!VD->getAttrs().getAttribute<DynamicReplacementAttr>() &&
initExpr && isa<ClosureExpr>(initExpr)) {
auto diag = emitDiagnostic(diag::extension_stored_property_fixit,
VD->getName());
diag.fixItRemove(PBD->getEqualLoc(i));
if (VD->isLet()) {
diag.fixItReplace(PBD->getStartLoc(), getTokenText(tok::kw_var));
}
if (auto lazyAttr = VD->getAttrs().getAttribute<LazyAttr>()) {
diag.fixItRemove(lazyAttr->getRange());
}
}
}
}
}
bool ContextualFailure::isIntegerToStringIndexConversion() const {
auto kind = KnownProtocolKind::ExpressibleByIntegerLiteral;
auto fromType = getFromType();
auto toType = getToType()->getCanonicalType();
return (conformsToKnownProtocol(fromType, kind) &&
toType.getString() == "String.CharacterView.Index");
}
Optional<Diag<Type, Type>>
ContextualFailure::getDiagnosticFor(ContextualTypePurpose context,
Type contextualType) {
auto forProtocol = contextualType->isExistentialType();
switch (context) {
case CTP_Initialization: {
if (contextualType->isAnyObject())
return diag::cannot_convert_initializer_value_anyobject;
return forProtocol ? diag::cannot_convert_initializer_value_protocol
: diag::cannot_convert_initializer_value;
}
case CTP_ReturnStmt:
case CTP_ReturnSingleExpr: {
if (contextualType->isAnyObject())
return diag::cannot_convert_return_type_to_anyobject;
return forProtocol ? diag::cannot_convert_to_return_type_protocol
: diag::cannot_convert_to_return_type;
}
case CTP_EnumCaseRawValue:
return diag::cannot_convert_raw_initializer_value;
case CTP_DefaultParameter:
case CTP_AutoclosureDefaultParameter:
return forProtocol ? diag::cannot_convert_default_arg_value_protocol
: diag::cannot_convert_default_arg_value;
case CTP_YieldByValue:
return forProtocol ? diag::cannot_convert_yield_value_protocol
: diag::cannot_convert_yield_value;
case CTP_CallArgument: {
if (contextualType->isAnyObject())
return diag::cannot_convert_argument_value_anyobject;
return forProtocol ? diag::cannot_convert_argument_value_protocol
: diag::cannot_convert_argument_value;
}
case CTP_ClosureResult:
return forProtocol ? diag::cannot_convert_closure_result_protocol
: diag::cannot_convert_closure_result;
case CTP_ArrayElement:
return forProtocol ? diag::cannot_convert_array_element_protocol
: diag::cannot_convert_array_element;
case CTP_DictionaryKey:
return forProtocol ? diag::cannot_convert_dict_key_protocol
: diag::cannot_convert_dict_key;
case CTP_DictionaryValue:
return forProtocol ? diag::cannot_convert_dict_value_protocol
: diag::cannot_convert_dict_value;
case CTP_CoerceOperand:
return forProtocol ? diag::cannot_convert_coerce_protocol
: diag::cannot_convert_coerce;
case CTP_AssignSource: {
if (contextualType->isAnyObject())
return diag::cannot_convert_assign_anyobject;
return forProtocol ? diag::cannot_convert_assign_protocol
: diag::cannot_convert_assign;
}
case CTP_SubscriptAssignSource:
return forProtocol ? diag::cannot_convert_subscript_assign_protocol
: diag::cannot_convert_subscript_assign;
case CTP_Condition:
return diag::cannot_convert_condition_value;
case CTP_WrappedProperty:
return diag::wrapped_value_mismatch;
case CTP_ComposedPropertyWrapper:
return diag::composed_property_wrapper_mismatch;
case CTP_ThrowStmt:
case CTP_ForEachStmt:
case CTP_Unused:
case CTP_CannotFail:
case CTP_YieldByReference:
case CTP_CalleeResult:
break;
}
return None;
}
bool TupleContextualFailure::diagnoseAsError() {
Diag<Type, Type> diagnostic;
auto purpose = getContextualTypePurpose();
if (isNumElementsMismatch())
diagnostic = diag::tuple_types_not_convertible_nelts;
else if ((purpose == CTP_Initialization) && !getContextualType(getAnchor()))
diagnostic = diag::tuple_types_not_convertible;
else if (auto diag = getDiagnosticFor(purpose, getToType()))
diagnostic = *diag;
else
return false;
emitDiagnostic(diagnostic, getFromType(), getToType());
return true;
}
bool FunctionTypeMismatch::diagnoseAsError() {
auto purpose = getContextualTypePurpose();
auto diagnostic = getDiagnosticFor(purpose, getToType());
if (!diagnostic)
return false;
emitDiagnostic(*diagnostic, getFromType(), getToType());
return true;
}
bool AutoClosureForwardingFailure::diagnoseAsError() {
auto argRange = getSourceRange();
emitDiagnostic(diag::invalid_autoclosure_forwarding)
.highlight(argRange)
.fixItInsertAfter(argRange.End, "()");
return true;
}
bool AutoClosurePointerConversionFailure::diagnoseAsError() {
auto diagnostic = diag::invalid_autoclosure_pointer_conversion;
emitDiagnostic(diagnostic, getFromType(), getToType())
.highlight(getSourceRange());
return true;
}
bool NonOptionalUnwrapFailure::diagnoseAsError() {
auto anchor = getAnchor();
auto diagnostic = diag::invalid_optional_chain;
if (isExpr<ForceValueExpr>(anchor))
diagnostic = diag::invalid_force_unwrap;
auto range = getSourceRange();
emitDiagnostic(diagnostic, BaseType).highlight(range).fixItRemove(range.End);
return true;
}
ASTNode MissingCallFailure::getAnchor() const {
auto anchor = FailureDiagnostic::getAnchor();
if (auto *FVE = getAsExpr<ForceValueExpr>(anchor))
return FVE->getSubExpr();
return anchor;
}
bool MissingCallFailure::diagnoseAsError() {
auto anchor = getAnchor();
SourceLoc insertLoc = getSourceRange().End;
// 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 (isExpr<KeyPathExpr>(anchor))
return false;
auto path = getLocator()->getPath();
if (!path.empty()) {
const auto &last = path.back();
switch (last.getKind()) {
case ConstraintLocator::ContextualType:
case ConstraintLocator::ApplyArgToParam: {
auto fnType = getType(anchor)->castTo<FunctionType>();
emitDiagnostic(diag::missing_nullary_call, fnType->getResult())
.fixItInsertAfter(insertLoc, "()");
return true;
}
case ConstraintLocator::FunctionResult: {
path = path.drop_back();
if (path.back().getKind() != ConstraintLocator::AutoclosureResult)
break;
LLVM_FALLTHROUGH;
}
case ConstraintLocator::AutoclosureResult: {
auto loc = getConstraintLocator(getRawAnchor(), path.drop_back());
AutoClosureForwardingFailure failure(getSolution(), loc);
return failure.diagnoseAsError();
}
default:
break;
}
}
if (auto *DRE = getAsExpr<DeclRefExpr>(anchor)) {
emitDiagnostic(diag::did_not_call_function,
DRE->getDecl()->getBaseIdentifier())
.fixItInsertAfter(insertLoc, "()");
return true;
}
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
emitDiagnostic(diag::did_not_call_method,
UDE->getName().getBaseIdentifier())
.fixItInsertAfter(insertLoc, "()");
return true;
}
if (auto *DSCE = getAsExpr<DotSyntaxCallExpr>(anchor)) {
if (auto *DRE = dyn_cast<DeclRefExpr>(DSCE->getFn())) {
emitDiagnostic(diag::did_not_call_method,
DRE->getDecl()->getBaseIdentifier())
.fixItInsertAfter(insertLoc, "()");
return true;
}
}
if (auto *AE = getAsExpr<AssignExpr>(anchor)) {
auto *srcExpr = AE->getSrc();
if (auto *fnType = getType(srcExpr)->getAs<FunctionType>()) {
emitDiagnosticAt(srcExpr->getLoc(), diag::missing_nullary_call,
fnType->getResult())
.highlight(srcExpr->getSourceRange())
.fixItInsertAfter(srcExpr->getEndLoc(), "()");
return true;
}
}
emitDiagnostic(diag::did_not_call_function_value)
.fixItInsertAfter(insertLoc, "()");
return true;
}
bool ExtraneousPropertyWrapperUnwrapFailure::diagnoseAsError() {
auto newPrefix = usingProjection() ? "$" : "_";
if (auto *member = getReferencedMember()) {
emitDiagnostic(diag::incorrect_property_wrapper_reference_member,
member->getDescriptiveKind(), member->getName(), false,
getToType())
.fixItInsert(getLoc(), newPrefix);
return true;
}
emitDiagnostic(diag::incorrect_property_wrapper_reference, getPropertyName(),
getFromType(), getToType(), false)
.fixItInsert(getLoc(), newPrefix);
return true;
}
bool MissingPropertyWrapperUnwrapFailure::diagnoseAsError() {
auto endLoc = getLoc().getAdvancedLoc(1);
if (auto *member = getReferencedMember()) {
emitDiagnostic(diag::incorrect_property_wrapper_reference_member,
member->getDescriptiveKind(), member->getName(), true,
getToType())
.fixItRemoveChars(getLoc(), endLoc);
return true;
}
emitDiagnostic(diag::incorrect_property_wrapper_reference, getPropertyName(),
getFromType(), getToType(), true)
.fixItRemoveChars(getLoc(), endLoc);
return true;
}
bool SubscriptMisuseFailure::diagnoseAsError() {
auto *locator = getLocator();
auto &sourceMgr = getASTContext().SourceMgr;
auto *memberExpr = castToExpr<UnresolvedDotExpr>(getRawAnchor());
auto memberRange = getSourceRange();
{
auto rawAnchor = getRawAnchor();
auto path = locator->getPath();
simplifyLocator(rawAnchor, path, memberRange);
}
auto nameLoc = DeclNameLoc(memberRange.Start);
auto diag = emitDiagnostic(diag::could_not_find_subscript_member_did_you_mean,
getType(getAnchor()));
diag.highlight(memberRange).highlight(nameLoc.getSourceRange());
if (auto *parentExpr = dyn_cast_or_null<ApplyExpr>(findParentExpr(memberExpr))) {
auto *argExpr = 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));
} else {
diag.fixItReplace(SourceRange(memberExpr->getDotLoc(), memberExpr->getLoc()), "[<#index#>]");
}
diag.flush();
if (auto overload = getOverloadChoiceIfAvailable(locator)) {
emitDiagnosticAt(overload->choice.getDecl(), diag::kind_declared_here,
DescriptiveDeclKind::Subscript);
}
return true;
}
bool SubscriptMisuseFailure::diagnoseAsNote() {
if (auto overload = getOverloadChoiceIfAvailable(getLocator())) {
emitDiagnosticAt(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, DeclNameRef memberName) {
if (memberName.isSpecial() || !memberName.isSimpleName())
return DeclName();
if (!Ty->getEnumOrBoundGenericEnum())
return DeclName();
auto candidate =
corrections.getUniqueCandidateMatching([&](ValueDecl *candidate) {
return (isa<EnumElementDecl>(candidate) &&
candidate->getBaseIdentifier().str().equals_lower(
memberName.getBaseIdentifier().str()));
});
return (candidate ? candidate->getName() : DeclName());
}
bool MissingMemberFailure::diagnoseAsError() {
auto anchor = getRawAnchor();
auto memberBase = getAnchor();
if (diagnoseForDynamicCallable())
return true;
if (diagnoseInLiteralCollectionContext())
return true;
if (diagnoseForSubscriptMemberWithTupleBase())
return true;
auto baseType = resolveType(getBaseType())->getWithoutSpecifierType();
DeclNameLoc nameLoc(::getLoc(anchor));
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
nameLoc = UDE->getNameLoc();
} else if (auto *UME = getAsExpr<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;
bool hasUnresolvedPattern = false;
if (auto *E = getAsExpr(anchor)) {
forEachExprInConstraintSystem(const_cast<Expr *>(E), [&](Expr *expr) {
hasUnresolvedPattern |= isa<UnresolvedPatternExpr>(expr);
return hasUnresolvedPattern ? nullptr : expr;
});
}
if (hasUnresolvedPattern && !baseType->getAs<EnumType>()) {
emitDiagnostic(diag::cannot_match_unresolved_expr_pattern_with_value,
baseType);
return;
}
emitDiagnostic(diagnostic, baseType, getName())
.highlight(getSourceRange())
.highlight(nameLoc.getSourceRange());
};
TypoCorrectionResults corrections(getName(), nameLoc);
auto tryTypoCorrection = [&] (Type type) {
TypeChecker::performTypoCorrection(getDC(), DeclRefKind::Ordinary, type,
defaultMemberLookupOptions, corrections);
};
if (getName().getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
if (auto *metatype = baseType->getAs<MetatypeType>()) {
emitDiagnostic(diag::could_not_find_type_member,
metatype->getInstanceType(), getName())
.highlight(getSourceRange());
} else {
emitDiagnostic(diag::could_not_find_value_subscript, baseType)
.highlight(getSourceRange());
}
} else if (getName().getBaseName() == "deinit") {
// Specialised diagnostic if trying to access deinitialisers
emitDiagnostic(diag::destructor_not_accessible).highlight(getSourceRange());
} else if (auto metatypeTy = baseType->getAs<MetatypeType>()) {
auto instanceTy = metatypeTy->getInstanceType();
tryTypoCorrection(baseType);
if (DeclName rightName =
findCorrectEnumCaseName(instanceTy, corrections, getName())) {
emitDiagnostic(diag::could_not_find_enum_case, instanceTy, getName(),
rightName)
.fixItReplace(nameLoc.getBaseNameLoc(),
rightName.getBaseIdentifier().str());
return true;
}
if (auto correction = corrections.claimUniqueCorrection()) {
auto diagnostic =
emitDiagnostic(diag::could_not_find_type_member_corrected, instanceTy,
getName(), correction->CorrectedName);
diagnostic.highlight(getSourceRange())
.highlight(nameLoc.getSourceRange());
correction->addFixits(diagnostic);
} else if (instanceTy->getAnyNominal() &&
getName().getBaseName() == DeclBaseName::createConstructor()) {
auto &cs = getConstraintSystem();
auto result = cs.performMemberLookup(
ConstraintKind::ValueMember, getName().withoutArgumentLabels(),
metatypeTy, FunctionRefKind::DoubleApply, getLocator(),
/*includeInaccessibleMembers=*/true);
// If there are no `init` members at all produce a tailored
// diagnostic for that, otherwise fallback to generic
// "no such member" one.
if (result.ViableCandidates.empty() &&
result.UnviableCandidates.empty()) {
emitDiagnostic(diag::no_accessible_initializers, instanceTy)
.highlight(getSourceRange());
} else {
emitBasicError(baseType);
}
} else {
emitBasicError(baseType);
}
} else if (auto moduleTy = baseType->getAs<ModuleType>()) {
emitDiagnosticAt(::getLoc(memberBase), diag::no_member_of_module,
moduleTy->getModule()->getName(), getName())
.highlight(getSourceRange())
.highlight(nameLoc.getSourceRange());
return true;
} else {
// Check for a few common cases that can cause missing members.
auto *ED = baseType->getEnumOrBoundGenericEnum();
if (ED && getName().isSimpleName("rawValue")) {
auto loc = ED->getNameLoc();
if (loc.isValid()) {
emitBasicError(baseType);
emitDiagnosticAt(loc, diag::did_you_mean_raw_type);
return true;
}
} else if (baseType->isAny()) {
emitBasicError(baseType);
auto range = getSourceRange();
emitDiagnostic(diag::any_as_anyobject_fixit)
.fixItInsert(range.Start, "(")
.fixItInsertAfter(range.End, " as AnyObject)");
return true;
}
tryTypoCorrection(baseType);
// If locator points to the member found via key path dynamic member lookup,
// we provide a custom diagnostic and emit typo corrections for the wrapper type too.
if (getLocator()->isForKeyPathDynamicMemberLookup()) {
auto memberBaseType = getType(memberBase)->getWithoutSpecifierType();
tryTypoCorrection(memberBaseType);
if (auto correction = corrections.claimUniqueCorrection()) {
auto diagnostic = emitDiagnostic(
diag::could_not_find_value_dynamic_member_corrected, memberBaseType,
baseType, getName(), correction->CorrectedName);
diagnostic.highlight(getSourceRange())
.highlight(nameLoc.getSourceRange());
correction->addFixits(diagnostic);
} else {
auto diagnostic =
emitDiagnostic(diag::could_not_find_value_dynamic_member,
memberBaseType, baseType, getName());
diagnostic.highlight(getSourceRange())
.highlight(nameLoc.getSourceRange());
}
} else {
if (auto correction = corrections.claimUniqueCorrection()) {
auto diagnostic =
emitDiagnostic(diag::could_not_find_value_member_corrected,
baseType, getName(), correction->CorrectedName);
diagnostic.highlight(getSourceRange())
.highlight(nameLoc.getSourceRange());
correction->addFixits(diagnostic);
} else {
emitBasicError(baseType);
}
}
}
// Note all the correction candidates.
corrections.noteAllCandidates();
return true;
}
bool MissingMemberFailure::diagnoseForDynamicCallable() const {
auto *locator = getLocator();
if (!locator->isLastElement<LocatorPathElt::DynamicCallable>())
return false;
auto memberName = getName();
auto arguments = memberName.getArgumentNames();
assert(arguments.size() == 1);
auto &ctx = getASTContext();
if (arguments.front() == ctx.Id_withKeywordArguments) {
emitDiagnostic(diag::missing_dynamic_callable_kwargs_method, getBaseType());
return true;
}
return false;
}
bool MissingMemberFailure::diagnoseInLiteralCollectionContext() const {
auto *expr = castToExpr(getAnchor());
auto *parentExpr = findParentExpr(expr);
auto &solution = getSolution();
if (!(parentExpr && isa<UnresolvedMemberExpr>(expr)))
return false;
if (!isa<UnresolvedMemberChainResultExpr>(parentExpr))
return false;
parentExpr = findParentExpr(parentExpr);
if (!parentExpr)
return false;
auto parentType = getType(parentExpr);
if (!parentType->isKnownStdlibCollectionType() && !parentType->is<TupleType>())
return false;
if (isa<TupleExpr>(parentExpr)) {
parentExpr = findParentExpr(parentExpr);
if (!parentExpr)
return false;
}
if (auto *defaultableVar =
getRawType(parentExpr)->getAs<TypeVariableType>()) {
if (solution.DefaultedConstraints.count(
defaultableVar->getImpl().getLocator()) != 0) {
emitDiagnostic(diag::unresolved_member_no_inference, getName());
return true;
}
}
return false;
}
bool MissingMemberFailure::diagnoseForSubscriptMemberWithTupleBase() const {
auto locator = getLocator();
auto baseType = resolveType(getBaseType())->getWithoutSpecifierType();
auto *SE = getAsExpr<SubscriptExpr>(locator->getAnchor());
if (!SE)
return false;
auto tupleType = baseType->getAs<TupleType>();
// For non-tuple type or empty tuples, let's fallback to the general
// diagnostic logic.
if (!tupleType || tupleType->getNumElements() == 0)
return false;
auto *index = SE->getIndex();
if (SE->getNumArguments() == 1) {
auto *literal =
dyn_cast<IntegerLiteralExpr>(index->getSemanticsProvidingExpr());
llvm::Regex NumericRegex("^[0-9]+$");
// Literal expressions may have other types of representations e.g. 0x01,
// 0b01. So let's make sure to only suggest this tailored literal fix-it for
// number only literals.
if (literal && NumericRegex.match(literal->getDigitsText())) {
unsigned int literalValue = 0;
literal->getDigitsText().getAsInteger(/*Radix=*/0, literalValue);
// Verify if the literal value is within the bounds of tuple elements.
if (!literal->isNegative() &&
literalValue < tupleType->getNumElements()) {
llvm::SmallString<4> dotAccess;
llvm::raw_svector_ostream OS(dotAccess);
OS << "." << literalValue;
emitDiagnostic(
diag::could_not_find_subscript_member_tuple_did_you_mean_use_dot,
baseType, literal->getDigitsText())
.fixItReplace(index->getSourceRange(), OS.str());
return true;
}
}
// For subscript access on tuple base types where the subscript index is a
// string literal expression which value matches a tuple element label,
// let's suggest tuple label access.
auto stringLiteral =
dyn_cast<StringLiteralExpr>(index->getSemanticsProvidingExpr());
if (stringLiteral && !stringLiteral->getValue().empty() &&
llvm::any_of(tupleType->getElements(), [&](TupleTypeElt element) {
return element.getName().is(stringLiteral->getValue());
})) {
llvm::SmallString<16> dotAccess;
llvm::raw_svector_ostream OS(dotAccess);
OS << "." << stringLiteral->getValue();
emitDiagnostic(
diag::could_not_find_subscript_member_tuple_did_you_mean_use_dot,
baseType, stringLiteral->getValue())
.fixItReplace(index->getSourceRange(), OS.str());
return true;
}
}
emitDiagnostic(diag::could_not_find_subscript_member_tuple, baseType);
return true;
}
bool UnintendedExtraGenericParamMemberFailure::diagnoseAsError() {
MissingMemberFailure::diagnoseAsError();
auto baseType = resolveType(getBaseType())->getWithoutSpecifierType();
auto archetype = baseType->getMetatypeInstanceType()->castTo<ArchetypeType>();
auto genericTy =
archetype->mapTypeOutOfContext()->castTo<GenericTypeParamType>();
SourceLoc loc = genericTy->getDecl()->getSourceRange().End;
StringRef replacement;
if (archetype->getConformsTo().size()) {
loc = loc.getAdvancedLoc(
archetype->getConformsTo().back()->getName().getLength());
replacement = " &";
} else {
loc = loc.getAdvancedLoc(archetype->getName().getLength());
replacement = ":";
}
emitDiagnosticAt(loc, diag::did_you_mean_generic_param_as_conformance,
ParamName, archetype)
.fixItReplaceChars(loc, loc.getAdvancedLoc(1), replacement);
return true;
}
bool InvalidMemberRefOnExistential::diagnoseAsError() {
auto anchor = getRawAnchor();
DeclNameLoc nameLoc;
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
nameLoc = UDE->getNameLoc();
} else if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
nameLoc = UME->getNameLoc();
}
emitDiagnostic(diag::could_not_use_member_on_existential, getBaseType(),
getName())
.highlight(nameLoc.getSourceRange())
.highlight(getSourceRange());
return true;
}
bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
auto loc = getLoc();
auto *DC = getDC();
auto locator = getLocator();
if (loc.isInvalid()) {
return true;
}
auto getRootExpr = [this](const Expr *childExpr) {
auto *currExpr = const_cast<Expr *>(childExpr);
while (auto parent = findParentExpr(currExpr))
currExpr = parent;
return currExpr;
};
auto anchor = getAnchor();
if (!anchor.is<Expr *>())
return false;
Expr *expr = findParentExpr(castToExpr(anchor));
SourceRange baseRange = expr ? expr->getSourceRange() : SourceRange();
// 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->is<AnyMetatypeType>()) {
if (auto *ctorRef = getAsExpr<UnresolvedDotExpr>(getRawAnchor())) {
if (isa<SuperRefExpr>(ctorRef->getBase())) {
emitDiagnostic(diag::super_initializer_not_in_initializer);
return true;
}
auto isCallArgument = [this](Expr *expr) {
auto argExpr = findParentExpr(expr);
if (!argExpr)
return false;
auto possibleApplyExpr = findParentExpr(expr);
return possibleApplyExpr && isa<ApplyExpr>(possibleApplyExpr);
};
auto *initCall = findParentExpr(findParentExpr(ctorRef));
auto isMutable = [&DC](ValueDecl *decl) {
if (auto *storage = dyn_cast<AbstractStorageDecl>(decl))
return storage->isSettable(DC) && storage->isSetterAccessibleFrom(DC);
return true;
};
auto *baseLoc = getConstraintLocator(ctorRef->getBase());
if (auto selection = getCalleeOverloadChoiceIfAvailable(baseLoc)) {
OverloadChoice choice = selection->choice;
if (choice.isDecl() && isMutable(choice.getDecl()) &&
!isCallArgument(initCall) &&
getContextualTypePurpose(getRootExpr(ctorRef)) == CTP_Unused) {
auto fixItLoc = ctorRef->getBase()->getSourceRange().End;
emitDiagnostic(diag::init_not_instance_member_use_assignment)
.fixItInsertAfter(fixItLoc, " = ");
return true;
}
SourceRange fixItRng = ctorRef->getBase()->getSourceRange();
emitDiagnostic(diag::init_not_instance_member)
.fixItInsert(fixItRng.Start, "type(of: ")
.fixItInsertAfter(fixItRng.End, ")");
return true;
}
}
}
if (BaseType->is<AnyMetatypeType>() && !Member->isStatic()) {
auto instanceTy = BaseType;
if (auto *AMT = instanceTy->getAs<AnyMetatypeType>()) {
instanceTy = AMT->getInstanceType();
}
auto *DC = getDC();
if (DC->getContextKind() == DeclContextKind::Initializer) {
auto *TypeDC = 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(diag::instance_member_in_initializer, Name);
return true;
} else {
emitDiagnostic(diag::instance_member_in_default_parameter, Name);
return true;
}
}
}
if (auto *maybeCallExpr = getAsExpr(getRawAnchor())) {
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(maybeCallExpr)) {
maybeCallExpr = UDE->getBase();
}
if (auto callExpr = dyn_cast<ApplyExpr>(maybeCallExpr)) {
auto fnExpr = callExpr->getFn();
auto fnType = getType(fnExpr)->getRValueType();
auto arg = callExpr->getArg();
if (fnType->is<ExistentialMetatypeType>()) {
emitDiagnosticAt(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 = getDC()->getInnermostTypeContext();
if (memberTypeContext && currentTypeContext &&
memberTypeContext->getSemanticDepth() <
currentTypeContext->getSemanticDepth()) {
emitDiagnostic(diag::could_not_use_instance_member_on_type,
currentTypeContext->getDeclaredInterfaceType(), Name,
memberTypeContext->getDeclaredInterfaceType(), true)
.highlight(baseRange)
.highlight(Member->getSourceRange());
return true;
}
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(getRawAnchor())) {
auto *baseExpr = UDE->getBase();
if (isa<TypeExpr>(baseExpr)) {
emitDiagnostic(diag::instance_member_use_on_type, instanceTy, Name)
.highlight(getSourceRange());
return true;
}
}
// Just emit a generic "instance member cannot be used" error
emitDiagnostic(diag::could_not_use_instance_member_on_type, instanceTy,
Name, instanceTy, false)
.highlight(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 baseTy = BaseType;
if (auto metatypeTy = baseTy->getAs<AnyMetatypeType>()) {
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;
baseTy = MetatypeType::get(objectTy);
}
if (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 (isa<TypeAliasDecl>(Member)) {
Diag.emplace(
emitDiagnostic(diag::typealias_outside_of_protocol, Name));
} else if (isa<AssociatedTypeDecl>(Member)) {
Diag.emplace(
emitDiagnostic(diag::assoc_type_outside_of_protocol, Name));
} else if (isa<ConstructorDecl>(Member)) {
Diag.emplace(
emitDiagnostic(diag::construct_protocol_by_name, instanceTy));
} else {
Diag.emplace(emitDiagnostic(
diag::could_not_use_type_member_on_protocol_metatype, baseTy,
Name));
}
Diag->highlight(baseRange).highlight(getSourceRange());
// See through function decl context
if (auto parent = getDC()->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(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(getSolution(), Member, locator);
return failure.diagnoseAsError();
}
if (isa<EnumElementDecl>(Member)) {
Diag.emplace(
emitDiagnostic(diag::could_not_use_enum_element_on_instance, Name));
} else {
Diag.emplace(emitDiagnostic(diag::could_not_use_type_member_on_instance,
baseTy, Name));
}
Diag->highlight(getSourceRange());
if (Name.isSimpleName(DeclBaseName::createConstructor()) &&
!baseTy->is<AnyMetatypeType>()) {
if (auto ctorRef = getAsExpr<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 = getContextualType(getRawAnchor());
// Try to provide a fix-it that only contains a '.'
if (contextualType && baseTy->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 = getRootExpr(getAsExpr(getAnchor()));
// 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 = getType(binaryExpr->getArg()->getElement(1));
if (secondArgType->isEqual(baseTy)) {
Diag->fixItInsert(loc, ".");
return true;
}
}
}
}
}
// Fall back to a fix-it with a full type qualifier
Expr *baseExpr = nullptr;
if (const auto *SE = getAsExpr<SubscriptExpr>(getRawAnchor()))
baseExpr = SE->getBase();
else if (const auto UDE = getAsExpr<UnresolvedDotExpr>(getRawAnchor()))
baseExpr = UDE->getBase();
// An implicit 'self' reference base expression means we should
// prepend with qualification.
if (baseExpr && !baseExpr->isImplicit()) {
Diag->fixItReplace(baseExpr->getSourceRange(),
diag::replace_with_type, baseTy);
} else {
Diag->fixItInsert(loc, diag::insert_type_qualification, baseTy);
}
return true;
}
return false;
}
bool PartialApplicationFailure::diagnoseAsError() {
auto *anchor = castToExpr<UnresolvedDotExpr>(getRawAnchor());
RefKind kind = RefKind::MutatingMethod;
// If this is initializer delegation chain, we have a tailored message.
if (getOverloadChoiceIfAvailable(
getConstraintLocator(anchor, ConstraintLocator::ConstructorMember))) {
kind = anchor->getBase()->isSuperExpr() ? RefKind::SuperInit
: RefKind::SelfInit;
} else if (anchor->getBase()->isSuperExpr()) {
kind = RefKind::SuperMethod;
}
auto diagnostic = CompatibilityWarning
? diag::partial_application_of_function_invalid_swift4
: diag::partial_application_of_function_invalid;
emitDiagnosticAt(anchor->getNameLoc(), diagnostic, kind);
return true;
}
bool InvalidDynamicInitOnMetatypeFailure::diagnoseAsError() {
emitDiagnostic(diag::dynamic_construct_class,
BaseType->getMetatypeInstanceType())
.highlight(BaseRange);
emitDiagnosticAt(Init, diag::note_nonrequired_initializer, Init->isImplicit(),
Init->getName());
return true;
}
bool InitOnProtocolMetatypeFailure::diagnoseAsError() {
if (IsStaticallyDerived) {
emitDiagnostic(diag::construct_protocol_by_name,
BaseType->getMetatypeInstanceType())
.highlight(BaseRange);
} else {
emitDiagnostic(diag::construct_protocol_value, BaseType)
.highlight(BaseRange);
}
return true;
}
SourceLoc ImplicitInitOnNonConstMetatypeFailure::getLoc() const {
if (auto *apply = getAsExpr<ApplyExpr>(getRawAnchor()))
return apply->getArg()->getStartLoc();
return FailureDiagnostic::getLoc();
}
bool ImplicitInitOnNonConstMetatypeFailure::diagnoseAsError() {
emitDiagnostic(diag::missing_init_on_metatype_initialization)
.fixItInsert(getLoc(), ".init");
return true;
}
ASTNode MissingArgumentsFailure::getAnchor() const {
auto anchor = FailureDiagnostic::getAnchor();
if (auto *captureList = getAsExpr<CaptureListExpr>(anchor))
return captureList->getClosureBody();
return anchor;
}
bool MissingArgumentsFailure::diagnoseAsError() {
auto *locator = getLocator();
if (!(locator->isLastElement<LocatorPathElt::ApplyArgToParam>() ||
locator->isLastElement<LocatorPathElt::ContextualType>() ||
locator->isLastElement<LocatorPathElt::ApplyArgument>() ||
locator->isLastElement<LocatorPathElt::ClosureResult>() ||
locator->isLastElement<LocatorPathElt::ClosureBody>()))
return false;
// If this is a misplaced `missng argument` situation, it would be
// diagnosed by invalid conversion fix.
if (isMisplacedMissingArgument(getSolution(), locator))
return false;
auto anchor = getAnchor();
if (auto *closure = getAsExpr<ClosureExpr>(anchor))
return diagnoseClosure(closure);
// This is a situation where function type is passed as an argument
// to a function type parameter and their argument arity is different.
//
// ```
// func foo(_: (Int) -> Void) {}
// func bar() {}
//
// foo(bar) // `() -> Void` vs. `(Int) -> Void`
// ```
if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
auto info = *(getFunctionArgApplyInfo(locator));
auto *argExpr = info.getArgExpr();
emitDiagnosticAt(argExpr->getLoc(), diag::cannot_convert_argument_value,
info.getArgType(), info.getParamType());
// TODO: It would be great so somehow point out which arguments are missing.
return true;
}
// Function type has fewer arguments than expected by context:
//
// ```
// func foo() {}
// let _: (Int) -> Void = foo
// ```
if (locator->isLastElement<LocatorPathElt::ContextualType>()) {
emitDiagnostic(diag::cannot_convert_initializer_value, getType(anchor),
resolveType(getContextualType(getAnchor())));
// TODO: It would be great so somehow point out which arguments are missing.
return true;
}
if (diagnoseInvalidTupleDestructuring())
return true;
if (SynthesizedArgs.size() == 1)
return diagnoseSingleMissingArgument();
// At this point we know that this is a situation when
// there are multiple arguments missing, so let's produce
// a diagnostic which lists all of them and a fix-it
// to add arguments at appropriate positions.
SmallString<32> diagnostic;
llvm::raw_svector_ostream arguments(diagnostic);
interleave(
SynthesizedArgs,
[&](const SynthesizedArg &e) {
const auto paramIdx = e.paramIdx;
const auto &arg = e.param;
if (arg.hasLabel()) {
arguments << "'" << arg.getLabel().str() << "'";
} else {
arguments << "#" << (paramIdx + 1);
}
},
[&] { arguments << ", "; });
auto diag = emitDiagnostic(diag::missing_arguments_in_call, arguments.str());
Expr *fnExpr = nullptr;
Expr *argExpr = nullptr;
unsigned numArguments = 0;
Optional<unsigned> firstTrailingClosure = None;
std::tie(fnExpr, argExpr, numArguments, firstTrailingClosure) =
getCallInfo(getRawAnchor());
// TODO(diagnostics): We should be able to suggest this fix-it
// unconditionally.
if (argExpr && numArguments == 0) {
SmallString<32> scratch;
llvm::raw_svector_ostream fixIt(scratch);
interleave(
SynthesizedArgs,
[&](const SynthesizedArg &arg) {
forFixIt(fixIt, arg.param);
},
[&] { fixIt << ", "; });
auto *tuple = cast<TupleExpr>(argExpr);
diag.fixItInsertAfter(tuple->getLParenLoc(), fixIt.str());
}
diag.flush();
if (auto selectedOverload = getCalleeOverloadChoiceIfAvailable(locator)) {
if (auto *decl = selectedOverload->choice.getDeclOrNull()) {
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
}
}
return true;
}
bool MissingArgumentsFailure::diagnoseAsNote() {
auto *locator = getLocator();
if (auto overload = getCalleeOverloadChoiceIfAvailable(locator)) {
auto *fn = resolveType(overload->openedType)->getAs<AnyFunctionType>();
auto loc = overload->choice.getDecl()->getLoc();
if (loc.isInvalid())
loc = getLoc();
emitDiagnosticAt(loc, diag::candidate_partial_match,
fn->getParamListAsString(fn->getParams()));
return true;
}
return false;
}
bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
auto &ctx = getASTContext();
auto anchor = getRawAnchor();
if (!(isExpr<CallExpr>(anchor) || isExpr<SubscriptExpr>(anchor) ||
isExpr<UnresolvedMemberExpr>(anchor) ||
isExpr<ObjectLiteralExpr>(anchor)))
return false;
if (SynthesizedArgs.size() != 1)
return false;
const auto &argument = SynthesizedArgs.front();
auto position = argument.paramIdx;
auto label = argument.param.getLabel();
Expr *fnExpr = nullptr;
Expr *argExpr = nullptr;
unsigned numArgs = 0;
Optional<unsigned> firstTrailingClosure = None;
std::tie(fnExpr, argExpr, numArgs, firstTrailingClosure) =
getCallInfo(anchor);
if (!argExpr) {
return false;
}
// Will the parameter accept a trailing closure?
Type paramType = resolveType(argument.param.getPlainType());
bool paramAcceptsTrailingClosure = paramType
->lookThroughAllOptionalTypes()->is<AnyFunctionType>();
// Determine whether we're inserting as a trailing closure.
bool insertingTrailingClosure =
firstTrailingClosure && position > *firstTrailingClosure;
SmallString<32> insertBuf;
llvm::raw_svector_ostream insertText(insertBuf);
if (insertingTrailingClosure)
insertText << " ";
else if (position != 0)
insertText << ", ";
forFixIt(insertText, argument.param);
if (position == 0 && numArgs > 0 &&
(!firstTrailingClosure || position < *firstTrailingClosure))
insertText << ", ";
SourceLoc insertLoc;
if (position >= numArgs && insertingTrailingClosure) {
// Add a trailing closure to the end.
// fn { closure }:
// fn {closure} label: [argMissing]
// fn() { closure }:
// fn() {closure} label: [argMissing]
// fn(argX) { closure }:
// fn(argX) { closure } label: [argMissing]
insertLoc = Lexer::getLocForEndOfToken(
ctx.SourceMgr, argExpr->getEndLoc());
} else if (auto *TE = dyn_cast<TupleExpr>(argExpr)) {
// fn(argX, argY):
// fn([argMissing, ]argX, argY)
// fn(argX[, argMissing], argY)
// fn(argX) { closure }:
// fn([argMissing, ]argX) { closure }
// fn(argX[, argMissing]) { closure }
// fn(argX, argY):
// fn(argX, argY[, argMissing])
if (numArgs == 0) {
insertLoc = TE->getRParenLoc();
} else if (position != 0) {
auto argPos = std::min(TE->getNumElements(), position) - 1;
insertLoc = Lexer::getLocForEndOfToken(
ctx.SourceMgr, TE->getElement(argPos)->getEndLoc());
} else {
insertLoc = TE->getElementNameLoc(0);
if (insertLoc.isInvalid())
insertLoc = TE->getElement(0)->getStartLoc();
}
} else {
auto *PE = cast<ParenExpr>(argExpr);
if (PE->getRParenLoc().isValid()) {
// fn():
// fn([argMissing])
// fn(argX):
// fn(argX[, argMissing])
// fn([argMissing, ]argX)
// fn() { closure }:
// fn([argMissing]) {closure}
if (position == 0) {
insertLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr,
PE->getLParenLoc());
} else {
insertLoc = Lexer::getLocForEndOfToken(
ctx.SourceMgr, PE->getSubExpr()->getEndLoc());
}
} else {
// fn { closure }:
// fn[(argMissing)] { closure }
assert(!isExpr<SubscriptExpr>(anchor) && "bracket less subscript");
assert(firstTrailingClosure &&
"paren less ParenExpr without trailing closure");
insertBuf.insert(insertBuf.begin(), '(');
insertBuf.insert(insertBuf.end(), ')');
insertLoc =
Lexer::getLocForEndOfToken(ctx.SourceMgr, fnExpr->getEndLoc());
}
}
if (insertLoc.isInvalid())
return false;
// If we are trying to insert a trailing closure but the parameter
// corresponding to the missing argument doesn't support a trailing closure,
// don't provide a Fix-It.
// FIXME: It's possible to parenthesize and relabel the argument list to
// accomodate this, but it's tricky.
bool shouldEmitFixIt =
!(insertingTrailingClosure && !paramAcceptsTrailingClosure);
if (label.empty()) {
auto diag = emitDiagnosticAt(
insertLoc, diag::missing_argument_positional, position + 1);
if (shouldEmitFixIt)
diag.fixItInsert(insertLoc, insertText.str());
} else if (isPropertyWrapperInitialization()) {
auto *TE = cast<TypeExpr>(fnExpr);
emitDiagnosticAt(TE->getLoc(), diag::property_wrapper_missing_arg_init,
label, resolveType(TE->getInstanceType())->getString());
} else {
auto diag = emitDiagnosticAt(
insertLoc, diag::missing_argument_named, label);
if (shouldEmitFixIt)
diag.fixItInsert(insertLoc, insertText.str());
}
if (auto selectedOverload =
getCalleeOverloadChoiceIfAvailable(getLocator())) {
if (auto *decl = selectedOverload->choice.getDeclOrNull()) {
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
}
}
return true;
}
bool MissingArgumentsFailure::diagnoseClosure(const ClosureExpr *closure) {
FunctionType *funcType = nullptr;
auto *locator = getLocator();
if (locator->isForContextualType()) {
funcType = getContextualType(locator->getAnchor())->getAs<FunctionType>();
} else if (auto info = getFunctionArgApplyInfo(locator)) {
auto paramType = info->getParamType();
// Drop a single layer of optionality because argument could get injected
// into optional and that doesn't contribute to the problem.
if (auto objectType = paramType->getOptionalObjectType())
paramType = objectType;
funcType = paramType->getAs<FunctionType>();
} else if (locator->isLastElement<LocatorPathElt::ClosureResult>() ||
locator->isLastElement<LocatorPathElt::ClosureBody>()) {
// Based on the locator we know this this is something like this:
// `let _: () -> ((Int) -> Void) = { return {} }`.
funcType = getType(getRawAnchor())
->castTo<FunctionType>()
->getResult()
->castTo<FunctionType>();
}
if (!funcType)
return false;
unsigned numSynthesized = SynthesizedArgs.size();
auto diff = funcType->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 =
emitDiagnosticAt(closure->getStartLoc(),
diag::closure_argument_list_missing, numSynthesized);
std::string fixText; // Let's provide fixits for up to 10 args.
if (funcType->getNumParams() <= 10) {
fixText += " ";
interleave(
funcType->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 = emitDiagnosticAt(
params->getStartLoc(), diag::closure_argument_list_tuple,
resolveType(funcType), funcType->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 MissingArgumentsFailure::diagnoseInvalidTupleDestructuring() const {
auto *locator = getLocator();
if (!locator->isLastElement<LocatorPathElt::ApplyArgument>())
return false;
if (SynthesizedArgs.size() < 2)
return false;
auto anchor = getAnchor();
Expr *argExpr = nullptr;
// Something like `foo(x: (1, 2))`
if (auto *TE = getAsExpr<TupleExpr>(anchor)) {
if (TE->getNumElements() == 1)
argExpr = TE->getElement(0);
} else { // or `foo((1, 2))`
argExpr = castToExpr<ParenExpr>(anchor)->getSubExpr();
}
if (!(argExpr && getType(argExpr)->getRValueType()->is<TupleType>()))
return false;
auto selectedOverload = getCalleeOverloadChoiceIfAvailable(locator);
if (!selectedOverload)
return false;
auto *decl = selectedOverload->choice.getDeclOrNull();
if (!decl)
return false;
auto name = decl->getBaseName();
auto diagnostic =
emitDiagnostic(diag::cannot_convert_single_tuple_into_multiple_arguments,
decl->getDescriptiveKind(), name, name.isSpecial(),
SynthesizedArgs.size(), isa<TupleExpr>(argExpr));
// If argument is a literal tuple, let's suggest removal of parentheses.
if (auto *TE = dyn_cast<TupleExpr>(argExpr)) {
diagnostic.fixItRemove(TE->getLParenLoc()).fixItRemove(TE->getRParenLoc());
}
diagnostic.flush();
// Add a note which points to the overload choice location.
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
return true;
}
bool MissingArgumentsFailure::isPropertyWrapperInitialization() const {
auto *call = getAsExpr<CallExpr>(getRawAnchor());
if (!(call && call->isImplicit()))
return false;
auto TE = dyn_cast<TypeExpr>(call->getFn());
if (!TE)
return false;
auto instanceTy = TE->getInstanceType();
if (!instanceTy)
return false;
auto *NTD = resolveType(instanceTy)->getAnyNominal();
return NTD && NTD->getAttrs().hasAttribute<PropertyWrapperAttr>();
}
bool MissingArgumentsFailure::isMisplacedMissingArgument(
const Solution &solution, ConstraintLocator *locator) {
auto *calleeLocator = solution.getCalleeLocator(locator);
auto overloadChoice = solution.getOverloadChoiceIfAvailable(calleeLocator);
if (!overloadChoice)
return false;
auto *fnType =
solution.simplifyType(overloadChoice->openedType)->getAs<FunctionType>();
if (!(fnType && fnType->getNumParams() == 2))
return false;
auto anchor = locator->getAnchor();
auto hasFixFor = [&](FixKind kind, ConstraintLocator *locator) -> bool {
auto fix = llvm::find_if(solution.Fixes, [&](const ConstraintFix *fix) {
return fix->getLocator() == locator;
});
if (fix == solution.Fixes.end())
return false;
return (*fix)->getKind() == kind;
};
auto *callLocator =
solution.getConstraintLocator(anchor, {ConstraintLocator::ApplyArgument});
auto argFlags = fnType->getParams()[0].getParameterFlags();
auto *argLoc = solution.getConstraintLocator(
callLocator, LocatorPathElt::ApplyArgToParam(0, 0, argFlags));
if (!(hasFixFor(FixKind::AllowArgumentTypeMismatch, argLoc) &&
hasFixFor(FixKind::AddMissingArguments, callLocator)))
return false;
Expr *argExpr = nullptr;
if (auto *call = getAsExpr<CallExpr>(anchor)) {
argExpr = call->getArg();
} else if (auto *subscript = getAsExpr<SubscriptExpr>(anchor)) {
argExpr = subscript->getIndex();
} else {
return false;
}
Expr *argument = nullptr;
if (auto *PE = dyn_cast<ParenExpr>(argExpr)) {
argument = PE->getSubExpr();
} else {
auto *tuple = cast<TupleExpr>(argExpr);
if (tuple->getNumElements() != 1)
return false;
argument = tuple->getElement(0);
}
auto argType = solution.simplifyType(solution.getType(argument));
auto paramType = fnType->getParams()[1].getPlainType();
return TypeChecker::isConvertibleTo(argType, paramType, solution.getDC());
}
std::tuple<Expr *, Expr *, unsigned, Optional<unsigned>>
MissingArgumentsFailure::getCallInfo(ASTNode anchor) const {
if (auto *call = getAsExpr<CallExpr>(anchor)) {
return std::make_tuple(call->getFn(), call->getArg(),
call->getNumArguments(),
call->getUnlabeledTrailingClosureIndex());
} else if (auto *SE = getAsExpr<SubscriptExpr>(anchor)) {
return std::make_tuple(SE, SE->getIndex(), SE->getNumArguments(),
SE->getUnlabeledTrailingClosureIndex());
} else if (auto *OLE = getAsExpr<ObjectLiteralExpr>(anchor)) {
return std::make_tuple(OLE, OLE->getArg(), OLE->getNumArguments(),
OLE->getUnlabeledTrailingClosureIndex());
}
return std::make_tuple(nullptr, nullptr, 0, None);
}
void MissingArgumentsFailure::forFixIt(
llvm::raw_svector_ostream &out,
const AnyFunctionType::Param &argument) const {
if (argument.hasLabel())
out << argument.getLabel().str() << ": ";
// Explode inout type.
if (argument.isInOut())
out << "&";
auto resolvedType = resolveType(argument.getPlainType());
// @autoclosure; the type should be the result type.
if (argument.isAutoClosure())
resolvedType = resolvedType->castTo<FunctionType>()->getResult();
out << "<#" << resolvedType << "#>";
}
SourceLoc ClosureParamDestructuringFailure::getLoc() const {
auto *closure = castToExpr<ClosureExpr>(getAnchor());
auto paramList = closure->getParameters();
return paramList->getStartLoc();
}
SourceRange ClosureParamDestructuringFailure::getSourceRange() const {
auto *closure = castToExpr<ClosureExpr>(getAnchor());
auto paramList = closure->getParameters();
return paramList->getSourceRange();
}
bool ClosureParamDestructuringFailure::diagnoseAsError() {
auto *closure = castToExpr<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(diag::closure_tuple_parameter_destructuring_implicit,
getParameterType());
return true;
}
}
auto diag = emitDiagnostic(diag::closure_tuple_parameter_destructuring,
getParameterType());
auto *closureBody = closure->getBody();
if (!closureBody)
return true;
auto &sourceMgr = getASTContext().SourceMgr;
auto bodyStmts = closureBody->getElements();
SourceLoc bodyLoc;
SourceLoc inLoc = closure->getInLoc();
// If location for `in` is unknown we can't proceed
// since we'll not be able to figure out source line
// to place the fix-it on.
if (inLoc.isInvalid())
return true;
// 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, inLoc);
else
bodyLoc = bodyStmts.front().getStartLoc();
if (bodyLoc.isInvalid())
return true;
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.getLineAndColumnInBuffer(inLoc).first;
auto bodyLine = sourceMgr.getLineAndColumnInBuffer(bodyLoc).first;
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->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(getSourceRange(), nameOS.str())
.fixItInsert(bodyLoc, OS.str());
return true;
}
bool OutOfOrderArgumentFailure::diagnoseAsError() {
auto anchor = getRawAnchor();
auto *argExpr = isExpr<TupleExpr>(anchor)
? castToExpr<TupleExpr>(anchor)
: getArgumentListExprFor(getLocator());
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) {
// Don't add Fix-Its if one of the ranges is outside of the argument
// list, which can happen when we're splicing together an argument list
// from multiple sources.
auto &SM = getASTContext().SourceMgr;
auto argsRange = tuple->getSourceRange();
if (!SM.rangeContains(argsRange, firstRange) ||
!SM.rangeContains(argsRange, secondRange))
return;
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 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() + (isExpr<BinaryExpr>(anchor) ? "" : ", "));
};
// There are 4 diagnostic messages variations depending on
// labeled/unlabeled arguments.
if (first.empty() && second.empty()) {
addFixIts(
emitDiagnosticAt(diagLoc,
isExpr<BinaryExpr>(anchor)
? diag::argument_out_of_order_binary_op
: diag::argument_out_of_order_unnamed_unnamed,
ArgIdx + 1, PrevArgIdx + 1));
} else if (first.empty() && !second.empty()) {
addFixIts(emitDiagnosticAt(diagLoc,
diag::argument_out_of_order_unnamed_named,
ArgIdx + 1, second));
} else if (!first.empty() && second.empty()) {
addFixIts(emitDiagnosticAt(diagLoc,
diag::argument_out_of_order_named_unnamed, first,
PrevArgIdx + 1));
} else {
addFixIts(emitDiagnosticAt(diagLoc, diag::argument_out_of_order_named_named,
first, second));
}
return true;
}
bool ExtraneousArgumentsFailure::diagnoseAsError() {
// Simplified anchor would point directly to the
// argument in case of contextual mismatch.
auto anchor = getAnchor();
if (auto *closure = getAsExpr<ClosureExpr>(anchor)) {
auto fnType = ContextualType;
auto params = closure->getParameters();
auto diag = emitDiagnosticAt(
params->getStartLoc(), diag::closure_argument_list_tuple, fnType,
fnType->getNumParams(), params->size(), (params->size() == 1));
bool onlyAnonymousParams =
std::all_of(params->begin(), params->end(),
[](ParamDecl *param) { return !param->hasName(); });
// If closure expects no parameters but N was given,
// and all of them are anonymous let's suggest removing them.
if (fnType->getNumParams() == 0 && onlyAnonymousParams) {
auto inLoc = closure->getInLoc();
auto &sourceMgr = getASTContext().SourceMgr;
if (inLoc.isValid())
diag.fixItRemoveChars(params->getStartLoc(),
Lexer::getLocForEndOfToken(sourceMgr, inLoc));
}
return true;
}
if (isContextualMismatch()) {
auto *locator = getLocator();
emitDiagnostic(locator->isLastElement<LocatorPathElt::ContextualType>()
? diag::cannot_convert_initializer_value
: diag::cannot_convert_argument_value,
getType(anchor), ContextualType);
return true;
}
if (ExtraArgs.size() == 1) {
return diagnoseSingleExtraArgument();
}
if (ContextualType->getNumParams() == 0) {
if (auto argExpr = getArgumentListExprFor(getLocator())) {
emitDiagnostic(diag::extra_argument_to_nullary_call)
.highlight(argExpr->getSourceRange())
.fixItRemove(argExpr->getSourceRange());
return true;
}
}
if (ExtraArgs.size() < 2)
return false;
llvm::SmallString<64> positions;
llvm::raw_svector_ostream OS(positions);
interleave(
ExtraArgs,
[&](const std::pair<unsigned, AnyFunctionType::Param> &arg) {
OS << "#" << (arg.first + 1);
},
[&] { OS << ", "; });
emitDiagnostic(diag::extra_arguments_in_call, OS.str());
if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) {
if (auto *decl = overload->choice.getDeclOrNull()) {
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
}
}
return true;
}
bool ExtraneousArgumentsFailure::diagnoseAsNote() {
auto overload = getCalleeOverloadChoiceIfAvailable(getLocator());
if (!(overload && overload->choice.isDecl()))
return false;
auto *decl = overload->choice.getDecl();
auto numArgs = getTotalNumArguments();
emitDiagnosticAt(decl, diag::candidate_with_extraneous_args, ContextualType,
ContextualType->getNumParams(), numArgs, (numArgs == 1),
isExpr<ClosureExpr>(getAnchor()));
return true;
}
bool ExtraneousArgumentsFailure::diagnoseSingleExtraArgument() const {
auto *locator = getLocator();
// This specifically handles a case of `Void(...)` which generates
// constraints differently from other constructor invocations and
// wouldn't have `ApplyArgument` as a last element in the locator.
if (auto *call = getAsExpr<CallExpr>(getRawAnchor())) {
auto *TE = dyn_cast<TypeExpr>(call->getFn());
if (TE && getType(TE)->getMetatypeInstanceType()->isVoid()) {
emitDiagnosticAt(call->getLoc(), diag::extra_argument_to_nullary_call)
.highlight(call->getArg()->getSourceRange());
return true;
}
}
auto *arguments = getArgumentListExprFor(locator);
if (!arguments)
return false;
const auto &e = ExtraArgs.front();
auto index = e.first;
auto argument = e.second;
auto tuple = dyn_cast<TupleExpr>(arguments);
auto argExpr = tuple ? tuple->getElement(index)
: cast<ParenExpr>(arguments)->getSubExpr();
auto loc = argExpr->getLoc();
if (tuple && index == tuple->getNumElements() - 1 &&
tuple->hasTrailingClosure()) {
emitDiagnosticAt(loc, diag::extra_trailing_closure_in_call)
.highlight(argExpr->getSourceRange());
} else if (ContextualType->getNumParams() == 0) {
auto *PE = dyn_cast<ParenExpr>(arguments);
Expr *subExpr = nullptr;
if (PE)
subExpr = PE->getSubExpr();
if (subExpr && argument.getPlainType()->isVoid()) {
emitDiagnosticAt(loc, diag::extra_argument_to_nullary_call)
.fixItRemove(subExpr->getSourceRange());
} else {
emitDiagnosticAt(loc, diag::extra_argument_to_nullary_call)
.highlight(argExpr->getSourceRange());
}
} else if (argument.hasLabel()) {
emitDiagnosticAt(loc, diag::extra_argument_named, argument.getLabel())
.highlight(argExpr->getSourceRange());
} else {
emitDiagnosticAt(loc, diag::extra_argument_positional)
.highlight(argExpr->getSourceRange());
}
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 = getAsExpr<UnresolvedDotExpr>(anchor)) {
baseExpr = UDE->getBase();
nameLoc = UDE->getNameLoc();
} else if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
nameLoc = UME->getNameLoc();
} else if (auto *SE = getAsExpr<SubscriptExpr>(anchor)) {
baseExpr = SE->getBase();
} else if (auto *call = getAsExpr<CallExpr>(anchor)) {
baseExpr = call->getFn();
}
if (baseExpr) {
auto *locator = getConstraintLocator(baseExpr, ConstraintLocator::Member);
const auto &solution = getSolution();
if (llvm::any_of(solution.Fixes, [&locator](const ConstraintFix *fix) {
return fix->getLocator() == locator;
}))
return false;
}
auto loc = nameLoc.isValid() ? nameLoc.getStartLoc() : ::getLoc(anchor);
auto accessLevel = Member->getFormalAccessScope().accessLevelForDiagnostics();
if (auto *CD = dyn_cast<ConstructorDecl>(Member)) {
emitDiagnosticAt(loc, diag::init_candidate_inaccessible,
CD->getResultInterfaceType(), accessLevel)
.highlight(nameLoc.getSourceRange());
} else {
emitDiagnosticAt(loc, diag::candidate_inaccessible, Member->getBaseName(),
accessLevel)
.highlight(nameLoc.getSourceRange());
}
emitDiagnosticAt(Member, diag::decl_declared_here, Member->getName());
return true;
}
SourceLoc AnyObjectKeyPathRootFailure::getLoc() const {
auto anchor = getAnchor();
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
if (auto rootTyRepr = KPE->getRootType())
return rootTyRepr->getLoc();
}
return ::getLoc(anchor);
}
SourceRange AnyObjectKeyPathRootFailure::getSourceRange() const {
auto anchor = getAnchor();
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
if (auto rootTyRepr = KPE->getRootType())
return rootTyRepr->getSourceRange();
}
return ::getSourceRange(anchor);
}
bool AnyObjectKeyPathRootFailure::diagnoseAsError() {
// Diagnose use of AnyObject as root for a keypath
emitDiagnostic(diag::expr_swift_keypath_anyobject_root)
.highlight(getSourceRange());
return true;
}
SourceLoc KeyPathSubscriptIndexHashableFailure::getLoc() const {
auto *locator = getLocator();
if (locator->isKeyPathSubscriptComponent()) {
auto *KPE = castToExpr<KeyPathExpr>(getAnchor());
if (auto kpElt = locator->findFirst<LocatorPathElt::KeyPathComponent>())
return KPE->getComponents()[kpElt->getIndex()].getLoc();
}
return FailureDiagnostic::getLoc();
}
bool KeyPathSubscriptIndexHashableFailure::diagnoseAsError() {
emitDiagnostic(diag::expr_keypath_subscript_index_not_hashable,
resolveType(NonConformingType));
return true;
}
SourceLoc InvalidMemberRefInKeyPath::getLoc() const {
auto anchor = getRawAnchor();
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
auto *locator = getLocator();
auto component = locator->findFirst<LocatorPathElt::KeyPathComponent>();
assert(component);
return KPE->getComponents()[component->getIndex()].getLoc();
}
return ::getLoc(anchor);
}
bool InvalidStaticMemberRefInKeyPath::diagnoseAsError() {
emitDiagnostic(diag::expr_keypath_static_member, getName(),
isForKeyPathDynamicMemberLookup());
return true;
}
bool InvalidEnumCaseRefInKeyPath::diagnoseAsError() {
emitDiagnostic(diag::expr_keypath_enum_case, getName(),
isForKeyPathDynamicMemberLookup());
return true;
}
bool InvalidMemberWithMutatingGetterInKeyPath::diagnoseAsError() {
emitDiagnostic(diag::expr_keypath_mutating_getter, getName(),
isForKeyPathDynamicMemberLookup());
return true;
}
bool InvalidMethodRefInKeyPath::diagnoseAsError() {
emitDiagnostic(diag::expr_keypath_not_property, getKind(), getName(),
isForKeyPathDynamicMemberLookup());
return true;
}
SourceLoc InvalidUseOfAddressOf::getLoc() const {
auto anchor = getAnchor();
if (auto *assign = getAsExpr<AssignExpr>(anchor))
return assign->getSrc()->getLoc();
return ::getLoc(anchor);
}
bool InvalidUseOfAddressOf::diagnoseAsError() {
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) {
if (!argApplyInfo->getParameterFlags().isInOut()) {
emitDiagnostic(diag::extra_address_of, getToType())
.highlight(getSourceRange())
.fixItRemove(getSourceRange().Start);
return true;
}
}
emitDiagnostic(diag::extraneous_address_of);
return true;
}
bool ExtraneousReturnFailure::diagnoseAsError() {
emitDiagnostic(diag::cannot_return_value_from_void_func);
if (auto FD = dyn_cast<FuncDecl>(getDC())) {
// We only want to emit the note + fix-it if the function does not
// have an explicit return type. The reason we also need to check
// whether the parameter list has a valid loc is to guard against
// cases like like 'var foo: () { return 1 }' as here that loc will
// be invalid. We also need to check that the name is not empty,
// because certain decls will have empty name (like setters).
if (FD->getResultTypeRepr() == nullptr &&
FD->getParameters()->getStartLoc().isValid() &&
!FD->getBaseIdentifier().empty()) {
auto fixItLoc = Lexer::getLocForEndOfToken(
getASTContext().SourceMgr, FD->getParameters()->getEndLoc());
emitDiagnostic(diag::add_return_type_note)
.fixItInsert(fixItLoc, " -> <#Return Type#>");
}
}
return true;
}
bool CollectionElementContextualFailure::diagnoseAsError() {
auto anchor = getRawAnchor();
auto *locator = getLocator();
auto eltType = getFromType();
auto contextualType = getToType();
auto isFixedToDictionary = [&](ArrayExpr *anchor) {
return llvm::any_of(getSolution().Fixes, [&](ConstraintFix *fix) {
auto *fixAnchor = getAsExpr<ArrayExpr>(fix->getAnchor());
return fixAnchor && fixAnchor == anchor &&
fix->getKind() == FixKind::TreatArrayLiteralAsDictionary;
});
};
bool treatAsDictionary = false;
Optional<InFlightDiagnostic> diagnostic;
if (auto *AE = getAsExpr<ArrayExpr>(anchor)) {
if (!(treatAsDictionary = isFixedToDictionary(AE))) {
if (diagnoseMergedLiteralElements())
return true;
diagnostic.emplace(emitDiagnostic(diag::cannot_convert_array_element,
eltType, contextualType));
}
}
if (treatAsDictionary || isExpr<DictionaryExpr>(anchor)) {
auto eltLoc = locator->castLastElementTo<LocatorPathElt::TupleElement>();
switch (eltLoc.getIndex()) {
case 0: // key
diagnostic.emplace(emitDiagnostic(diag::cannot_convert_dict_key, eltType,
contextualType));
break;
case 1: // value
diagnostic.emplace(emitDiagnostic(diag::cannot_convert_dict_value,
eltType, contextualType));
break;
default:
break;
}
}
if (locator->isForSequenceElementType()) {
// If this is a conversion failure related to binding of `for-each`
// statement it has to be diagnosed as pattern match if there are
// holes present in the contextual type.
if (FailureDiagnostic::getContextualTypePurpose(getAnchor()) ==
ContextualTypePurpose::CTP_ForEachStmt &&
contextualType->hasUnresolvedType()) {
diagnostic.emplace(emitDiagnostic(
(contextualType->is<TupleType>() && !eltType->is<TupleType>())
? diag::cannot_match_expr_tuple_pattern_with_nontuple_value
: diag::cannot_match_unresolved_expr_pattern_with_value,
eltType));
} else {
diagnostic.emplace(
emitDiagnostic(contextualType->isExistentialType()
? diag::cannot_convert_sequence_element_protocol
: diag::cannot_convert_sequence_element_value,
eltType, contextualType));
}
}
if (!diagnostic)
return false;
(void)trySequenceSubsequenceFixIts(*diagnostic);
return true;
}
bool CollectionElementContextualFailure::diagnoseMergedLiteralElements() {
auto elementAnchor = simplifyLocatorToAnchor(getLocator());
if (!elementAnchor)
return false;
auto *typeVar = getRawType(elementAnchor)->getAs<TypeVariableType>();
if (!typeVar || !typeVar->getImpl().getAtomicLiteralKind())
return false;
// This element is a literal whose type variable could have been merged with others,
// but the conversion constraint to the array element type was only placed on one
// of them. So, we want to emit the error for each element whose type variable is in
// this equivalence class.
auto &cs = getConstraintSystem();
auto node = cs.getRepresentative(typeVar)->getImpl().getGraphNode();
for (const auto *typeVar : node->getEquivalenceClass()) {
auto anchor = typeVar->getImpl().getLocator()->getAnchor();
emitDiagnosticAt(constraints::getLoc(anchor), diag::cannot_convert_array_element,
getFromType(), getToType());
}
return true;
}
bool MissingContextualConformanceFailure::diagnoseAsError() {
auto anchor = getAnchor();
auto path = getLocator()->getPath();
Optional<Diag<Type, Type>> diagnostic;
if (path.empty()) {
assert(isExpr<AssignExpr>(anchor));
if (isa<SubscriptExpr>(castToExpr<AssignExpr>(anchor)->getDest())) {
diagnostic = getDiagnosticFor(CTP_SubscriptAssignSource, getToType());
} else {
diagnostic = getDiagnosticFor(CTP_AssignSource, getToType());
}
} else {
const auto &last = path.back();
switch (last.getKind()) {
case ConstraintLocator::ContextualType:
assert(Context != CTP_Unused);
diagnostic = getDiagnosticFor(Context, getToType());
break;
case ConstraintLocator::SequenceElementType: {
diagnostic = diag::cannot_convert_sequence_element_protocol;
break;
}
default:
break;
}
}
if (!diagnostic)
return false;
auto srcType = getFromType();
auto dstType = getToType();
emitDiagnostic(*diagnostic, srcType, dstType);
if (isExpr<InOutExpr>(anchor))
return true;
if (srcType->isAny() && dstType->isAnyObject()) {
emitDiagnostic(diag::any_as_anyobject_fixit)
.fixItInsertAfter(getSourceRange().End, " as AnyObject");
}
return true;
}
bool MissingGenericArgumentsFailure::hasLoc(GenericTypeParamType *GP) const {
return GP->getDecl()->getStartLoc().isValid();
}
bool MissingGenericArgumentsFailure::diagnoseAsError() {
llvm::SmallDenseMap<TypeRepr *, SmallVector<GenericTypeParamType *, 4>>
scopedParameters;
auto isScoped =
findArgumentLocations([&](TypeRepr *base, GenericTypeParamType *GP) {
scopedParameters[base].push_back(GP);
});
if (!isScoped) {
auto anchor = getAnchor();
assert(anchor.is<Expr *>() || anchor.is<TypeRepr *>());
return diagnoseForAnchor(anchor, Parameters);
}
bool diagnosed = false;
for (const auto &scope : scopedParameters)
diagnosed |= diagnoseForAnchor(scope.first, scope.second);
return diagnosed;
}
bool MissingGenericArgumentsFailure::diagnoseForAnchor(
ASTNode anchor, ArrayRef<GenericTypeParamType *> params) const {
bool diagnosed = false;
for (auto *GP : params)
diagnosed |= diagnoseParameter(anchor, GP);
if (!diagnosed)
return false;
auto *DC = getDeclContext();
if (!DC)
return true;
if (auto *SD = dyn_cast<SubscriptDecl>(DC)) {
emitDiagnosticAt(SD, diag::note_call_to_subscript, SD->getName());
return true;
}
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
if (isa<ConstructorDecl>(AFD)) {
emitDiagnosticAt(AFD, diag::note_call_to_initializer);
} else {
emitDiagnosticAt(AFD,
AFD->isOperator() ? diag::note_call_to_operator
: diag::note_call_to_func,
AFD->getName());
}
return true;
}
emitGenericSignatureNote(anchor);
return true;
}
bool MissingGenericArgumentsFailure::diagnoseParameter(
ASTNode anchor, GenericTypeParamType *GP) const {
auto &solution = getSolution();
auto loc = ::getLoc(anchor);
auto *locator = getLocator();
// Type variables associated with missing generic parameters are
// going to be completely cut off from the rest of constraint system,
// that's why we'd get two fixes in this case which is not ideal.
if (locator->isForContextualType() &&
llvm::count_if(solution.DefaultedConstraints,
[&GP](const ConstraintLocator *locator) {
return locator->getGenericParameter() == GP;
}) > 1) {
return false;
}
if (auto *CE = getAsExpr<ExplicitCastExpr>(getRawAnchor())) {
const auto castTo = getType(CE->getCastTypeRepr());
auto *NTD = castTo->getAnyNominal();
emitDiagnosticAt(loc, diag::unbound_generic_parameter_cast, GP,
NTD ? NTD->getDeclaredType() : castTo);
} else {
emitDiagnosticAt(loc, diag::unbound_generic_parameter, GP);
}
Type baseTyForNote;
auto *DC = getDeclContext();
if (!DC)
return true;
if (!hasLoc(GP))
return true;
if (auto *NTD =
dyn_cast_or_null<NominalTypeDecl>(DC->getSelfNominalTypeDecl())) {
baseTyForNote = NTD->getDeclaredType();
} else if (auto *TAD = dyn_cast<TypeAliasDecl>(DC)) {
baseTyForNote = TAD->getUnboundGenericType();
} else {
return true;
}
emitDiagnosticAt(GP->getDecl(), diag::archetype_declared_in_type, GP,
baseTyForNote);
return true;
}
void MissingGenericArgumentsFailure::emitGenericSignatureNote(
ASTNode anchor) const {
auto &solution = getSolution();
auto *paramDC = getDeclContext();
if (!paramDC)
return;
auto *GTD = dyn_cast<GenericTypeDecl>(paramDC);
if (!GTD || anchor.is<Expr *>())
return;
auto getParamDecl =
[](const ConstraintLocator *locator) -> GenericTypeParamDecl * {
return locator->isForGenericParameter()
? locator->getGenericParameter()->getDecl()
: nullptr;
};
llvm::SmallDenseMap<GenericTypeParamDecl *, Type> params;
for (auto &entry : solution.typeBindings) {
auto *typeVar = entry.first;
auto *GP = typeVar->getImpl().getGenericParameter();
if (!GP)
continue;
auto type = resolveType(typeVar);
assert(!type->is<TypeVariableType>());
// If this is one of the defaulted parameter types, attempt
// to emit placeholder for it instead of `Any`.
if (llvm::any_of(solution.DefaultedConstraints,
[&](const ConstraintLocator *locator) {
return GP->getDecl() == getParamDecl(locator);
}))
continue;
params[GP->getDecl()] = type;
}
auto getPreferredType = [&](const GenericTypeParamDecl *GP) -> Type {
auto type = params.find(GP);
return (type == params.end()) ? Type() : type->second;
};
SmallString<64> paramsAsString;
auto baseType = anchor.get<TypeRepr *>();
if (TypeChecker::getDefaultGenericArgumentsString(paramsAsString, GTD,
getPreferredType)) {
auto diagnostic = emitDiagnosticAt(
baseType->getLoc(), diag::unbound_generic_parameter_explicit_fix);
if (auto *genericTy = dyn_cast<GenericIdentTypeRepr>(baseType)) {
// If some of the eneric arguments have been specified, we need to
// replace existing signature with a new one.
diagnostic.fixItReplace(genericTy->getAngleBrackets(), paramsAsString);
} else {
// Otherwise we can simply insert new generic signature.
diagnostic.fixItInsertAfter(baseType->getEndLoc(), paramsAsString);
}
}
}
bool MissingGenericArgumentsFailure::findArgumentLocations(
llvm::function_ref<void(TypeRepr *, GenericTypeParamType *)> callback) {
using Callback = llvm::function_ref<void(TypeRepr *, GenericTypeParamType *)>;
auto *const typeRepr = [this]() -> TypeRepr * {
const auto anchor = getRawAnchor();
if (const auto *TE = getAsExpr<TypeExpr>(anchor))
return TE->getTypeRepr();
else if (const auto *ECE = getAsExpr<ExplicitCastExpr>(anchor))
return ECE->getCastTypeRepr();
else
return nullptr;
}();
if (!typeRepr)
return false;
struct AssociateMissingParams : public ASTWalker {
llvm::SmallVector<GenericTypeParamType *, 4> Params;
Callback Fn;
AssociateMissingParams(ArrayRef<GenericTypeParamType *> params,
Callback callback)
: Params(params.begin(), params.end()), Fn(callback) {}
bool walkToTypeReprPre(TypeRepr *T) override {
if (Params.empty())
return false;
auto *ident = dyn_cast<ComponentIdentTypeRepr>(T);
if (!ident)
return true;
auto *decl = dyn_cast_or_null<GenericTypeDecl>(ident->getBoundDecl());
if (!decl)
return true;
auto *paramList = decl->getGenericParams();
if (!paramList)
return true;
// There could a situation like `S<S>()`, so we need to be
// careful not to point at first `S` because it has all of
// its generic parameters specified.
if (auto *generic = dyn_cast<GenericIdentTypeRepr>(ident)) {
if (paramList->size() == generic->getNumGenericArgs())
return true;
}
for (auto *candidate : paramList->getParams()) {
auto result =
llvm::find_if(Params, [&](const GenericTypeParamType *param) {
return candidate == param->getDecl();
});
if (result != Params.end()) {
Fn(ident, *result);
Params.erase(result);
}
}
// Keep walking.
return true;
}
bool allParamsAssigned() const { return Params.empty(); }
} associator(Parameters, callback);
typeRepr->walk(associator);
return associator.allParamsAssigned();
}
SourceLoc SkipUnhandledConstructInResultBuilderFailure::getLoc() const {
if (auto stmt = unhandled.dyn_cast<Stmt *>())
return stmt->getStartLoc();
return unhandled.get<Decl *>()->getLoc();
}
/// Determine whether the given "if" chain has a missing "else".
static bool hasMissingElseInChain(IfStmt *ifStmt) {
if (!ifStmt->getElseStmt())
return true;
if (auto ifElse = dyn_cast<IfStmt>(ifStmt->getElseStmt()))
return hasMissingElseInChain(ifElse);
return false;
}
void SkipUnhandledConstructInResultBuilderFailure::diagnosePrimary(
bool asNote) {
if (auto stmt = unhandled.dyn_cast<Stmt *>()) {
emitDiagnostic(asNote ? diag::note_result_builder_control_flow
: diag::result_builder_control_flow,
builder->getName());
// Emit custom notes to help the user introduce the appropriate 'build'
// functions.
SourceLoc buildInsertionLoc;
std::string stubIndent;
Type componentType;
std::tie(buildInsertionLoc, stubIndent, componentType) =
determineResultBuilderBuildFixItInfo(builder);
if (buildInsertionLoc.isInvalid()) {
// Do nothing.
} else if (isa<IfStmt>(stmt) && hasMissingElseInChain(cast<IfStmt>(stmt))) {
auto diag = emitDiagnosticAt(
builder->getLoc(), diag::result_builder_missing_build_optional,
builder->getDeclaredInterfaceType());
std::string fixItString;
{
llvm::raw_string_ostream out(fixItString);
printResultBuilderBuildFunction(
builder, componentType, ResultBuilderBuildFunction::BuildOptional,
stubIndent, out);
}
diag.fixItInsert(buildInsertionLoc, fixItString);
} else if (isa<SwitchStmt>(stmt) || isa<IfStmt>(stmt)) {
auto diag = emitDiagnosticAt(
builder->getLoc(), diag::result_builder_missing_build_either,
builder->getDeclaredInterfaceType());
std::string fixItString;
{
llvm::raw_string_ostream out(fixItString);
printResultBuilderBuildFunction(
builder, componentType,
ResultBuilderBuildFunction::BuildEitherFirst,
stubIndent, out);
out << '\n';
printResultBuilderBuildFunction(
builder, componentType,
ResultBuilderBuildFunction::BuildEitherSecond,
stubIndent, out);
}
diag.fixItInsert(buildInsertionLoc, fixItString);
} else if (isa<ForEachStmt>(stmt)) {
auto diag = emitDiagnosticAt(
builder->getLoc(), diag::result_builder_missing_build_array,
builder->getDeclaredInterfaceType());
std::string fixItString;
{
llvm::raw_string_ostream out(fixItString);
printResultBuilderBuildFunction(
builder, componentType, ResultBuilderBuildFunction::BuildArray,
stubIndent, out);
}
diag.fixItInsert(buildInsertionLoc, fixItString);
}
} else {
emitDiagnostic(asNote ? diag::note_result_builder_decl
: diag::result_builder_decl,
builder->getName());
}
}
bool SkipUnhandledConstructInResultBuilderFailure::diagnoseAsError() {
diagnosePrimary(/*asNote=*/false);
emitDiagnosticAt(builder, diag::kind_declname_declared_here,
builder->getDescriptiveKind(), builder->getName());
return true;
}
bool SkipUnhandledConstructInResultBuilderFailure::diagnoseAsNote() {
diagnosePrimary(/*asNote=*/true);
return true;
}
bool MutatingMemberRefOnImmutableBase::diagnoseAsError() {
auto *anchor = castToExpr(getRawAnchor());
auto baseExpr = getBaseExprFor(anchor);
if (!baseExpr)
return false;
auto diagIDsubelt = diag::cannot_pass_rvalue_mutating_subelement;
auto diagIDmember = diag::cannot_pass_rvalue_mutating;
if (auto *storage = dyn_cast<AbstractStorageDecl>(Member)) {
if (storage->isGetterMutating()) {
diagIDsubelt = diag::cannot_pass_rvalue_mutating_getter_subelement;
diagIDmember = diag::cannot_pass_rvalue_mutating_getter;
}
}
const auto &solution = getSolution();
AssignmentFailure failure(baseExpr, solution, anchor->getLoc(), diagIDsubelt,
diagIDmember);
return failure.diagnoseAsError();
}
bool InvalidTupleSplatWithSingleParameterFailure::diagnoseAsError() {
auto selectedOverload = getCalleeOverloadChoiceIfAvailable(getLocator());
if (!selectedOverload || !selectedOverload->choice.isDecl())
return false;
auto *choice = selectedOverload->choice.getDecl();
auto *argExpr = getArgumentListExprFor(getLocator());
if (!argExpr)
return false;
using Substitution = std::pair<GenericTypeParamType *, Type>;
llvm::SmallVector<Substitution, 8> substitutions;
auto paramTy = restoreGenericParameters(
ParamType, [&](GenericTypeParamType *GP, Type resolvedType) {
substitutions.push_back(std::make_pair(GP, resolvedType));
});
DeclBaseName name = choice->getBaseName();
std::string subsStr;
if (!substitutions.empty()) {
llvm::array_pod_sort(
substitutions.begin(), substitutions.end(),
[](const std::pair<GenericTypeParamType *, Type> *lhs,
const std::pair<GenericTypeParamType *, Type> *rhs) -> int {
GenericParamKey key1(lhs->first);
GenericParamKey key2(rhs->first);
return key1 < key2 ? -1 : (key1 == key2) ? 0 : 1;
});
subsStr += " [with ";
interleave(
substitutions,
[&subsStr](const Substitution &substitution) {
subsStr += substitution.first->getString();
subsStr += " = ";
subsStr += substitution.second->getString();
},
[&subsStr] { subsStr += ", "; });
subsStr += ']';
}
auto diagnostic =
name.isSpecial()
? emitDiagnosticAt(argExpr->getLoc(),
diag::single_tuple_parameter_mismatch_special,
choice->getDescriptiveKind(), paramTy, subsStr)
: emitDiagnosticAt(
argExpr->getLoc(), diag::single_tuple_parameter_mismatch_normal,
choice->getDescriptiveKind(), name, paramTy, subsStr);
auto newLeftParenLoc = argExpr->getStartLoc();
if (auto *TE = dyn_cast<TupleExpr>(argExpr)) {
auto firstArgLabel = TE->getElementName(0);
// Cover situations like:
//
// func foo(x: (Int, Int)) {}
// foo(x: 0, 1)
//
// Where left paren should be suggested after the label,
// since the label belongs to the parameter itself.
if (!firstArgLabel.empty()) {
auto paramTuple = resolveType(ParamType)->castTo<TupleType>();
// If the label of the first argument matches the one required
// by the parameter it would be omitted from the fixed parameter type.
if (!paramTuple->getElement(0).hasName())
newLeftParenLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr,
TE->getElementNameLoc(0));
}
}
diagnostic.highlight(argExpr->getSourceRange())
.fixItInsertAfter(newLeftParenLoc, "(")
.fixItInsert(argExpr->getEndLoc(), ")");
return true;
}
bool ThrowingFunctionConversionFailure::diagnoseAsError() {
emitDiagnostic(diag::throws_functiontype_mismatch, getFromType(),
getToType());
return true;
}
bool AsyncFunctionConversionFailure::diagnoseAsError() {
emitDiagnostic(diag::async_functiontype_mismatch, getFromType(),
getToType());
return true;
}
bool InOutConversionFailure::diagnoseAsError() {
auto *locator = getLocator();
auto path = locator->getPath();
if (!path.empty() &&
path.back().getKind() == ConstraintLocator::FunctionArgument) {
if (auto argApplyInfo = getFunctionArgApplyInfo(locator)) {
emitDiagnostic(diag::cannot_convert_argument_value,
argApplyInfo->getArgType(), argApplyInfo->getParamType());
} else {
assert(locator->findLast<LocatorPathElt::ContextualType>());
auto anchor = getAnchor();
auto contextualType = getContextualType(anchor);
auto purpose = getContextualTypePurpose();
auto diagnostic = getDiagnosticFor(purpose, contextualType);
if (!diagnostic)
return false;
emitDiagnostic(*diagnostic, getType(anchor), contextualType);
}
return true;
}
emitDiagnostic(diag::cannot_pass_rvalue_inout_converted, getFromType(),
getToType());
fixItChangeArgumentType();
return true;
}
void InOutConversionFailure::fixItChangeArgumentType() const {
auto *argExpr = castToExpr(getAnchor());
auto *DC = getDC();
if (auto *IOE = dyn_cast<InOutExpr>(argExpr))
argExpr = IOE->getSubExpr();
auto *DRE = dyn_cast<DeclRefExpr>(argExpr);
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;
auto actualType = getFromType();
auto neededType = getToType();
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<BindingPattern>(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;
emitDiagnosticAt(VD, diag::inout_change_var_type_if_possible, actualType,
neededType)
.fixItReplaceChars(startLoc, endLoc, scratch);
}
bool ArgumentMismatchFailure::diagnoseAsError() {
if (diagnoseMisplacedMissingArgument())
return true;
if (diagnoseConversionToBool())
return true;
if (diagnoseArchetypeMismatch())
return true;
if (diagnosePatternMatchingMismatch())
return true;
if (diagnoseUseOfReferenceEqualityOperator())
return true;
if (diagnosePropertyWrapperMismatch())
return true;
if (diagnoseTrailingClosureMismatch())
return true;
auto argType = getFromType();
auto paramType = getToType();
if (paramType->isAnyObject()) {
emitDiagnostic(diag::cannot_convert_argument_value_anyobject, argType,
paramType);
return true;
}
Diag<Type, Type> diagnostic = diag::cannot_convert_argument_value;
// If parameter type is a protocol value, let's says that
// argument doesn't conform to a give protocol.
if (paramType->isExistentialType())
diagnostic = diag::cannot_convert_argument_value_protocol;
auto diag = emitDiagnostic(diagnostic, argType, paramType);
// If argument is an l-value type and parameter is a pointer type,
// let's match up its element type to the argument to see whether
// it would be appropriate to suggest adding `&`.
auto argument = getAnchor();
if (getType(argument, /*wantRValue=*/false)->is<LValueType>()) {
auto elementTy = paramType->getAnyPointerElementType();
if (elementTy && argType->isEqual(elementTy)) {
diag.fixItInsert(::getSourceRange(argument).Start, "&");
return true;
}
}
tryFixIts(diag);
return true;
}
bool ArgumentMismatchFailure::diagnoseAsNote() {
auto *locator = getLocator();
if (auto *callee = getCallee()) {
emitDiagnosticAt(
callee, diag::candidate_has_invalid_argument_at_position, getToType(),
getParamPosition(),
locator->isLastElement<LocatorPathElt::LValueConversion>());
return true;
}
return false;
}
bool ArgumentMismatchFailure::diagnoseUseOfReferenceEqualityOperator() const {
auto *locator = getLocator();
if (!isArgumentOfReferenceEqualityOperator(locator))
return false;
auto *binaryOp = castToExpr<BinaryExpr>(getRawAnchor());
auto *lhs = binaryOp->getArg()->getElement(0);
auto *rhs = binaryOp->getArg()->getElement(1);
auto name = *getOperatorName(binaryOp->getFn());
auto lhsType = getType(lhs);
auto rhsType = getType(rhs);
// If both arguments where incorrect e.g. both are function types,
// let's avoid producing a diagnostic second time, because first
// one would cover both arguments.
if (getAsExpr(getAnchor()) == rhs && rhsType->is<FunctionType>()) {
auto *argLoc = getConstraintLocator(
binaryOp,
{ConstraintLocator::ApplyArgument,
LocatorPathElt::ApplyArgToParam(0, 0, getParameterFlagsAtIndex(0))});
if (llvm::any_of(getSolution().Fixes, [&argLoc](const ConstraintFix *fix) {
return fix->getLocator() == argLoc;
}))
return true;
}
// Regardless of whether the type has reference or value semantics,
// comparison with nil is illegal, albeit for different reasons spelled
// out by the diagnosis.
if (isa<NilLiteralExpr>(lhs) || isa<NilLiteralExpr>(rhs)) {
std::string revisedName = std::string(name);
revisedName.pop_back();
auto loc = binaryOp->getLoc();
auto nonNilType = isa<NilLiteralExpr>(lhs) ? rhsType : lhsType;
auto nonNilExpr = isa<NilLiteralExpr>(lhs) ? rhs : lhs;
// If we made it here, then we're trying to perform a comparison with
// reference semantics rather than value semantics. The fixit will
// lop off the extra '=' in the operator.
if (nonNilType->getOptionalObjectType()) {
emitDiagnosticAt(
loc, diag::value_type_comparison_with_nil_illegal_did_you_mean,
nonNilType)
.fixItReplace(loc, revisedName);
} else {
emitDiagnosticAt(loc, diag::value_type_comparison_with_nil_illegal,
nonNilType)
.highlight(nonNilExpr->getSourceRange());
}
return true;
}
if (lhsType->is<FunctionType>() || rhsType->is<FunctionType>()) {
emitDiagnosticAt(binaryOp->getLoc(), diag::cannot_reference_compare_types,
name.str(), lhsType, rhsType)
.highlight(lhs->getSourceRange())
.highlight(rhs->getSourceRange());
return true;
}
return false;
}
bool ArgumentMismatchFailure::diagnosePatternMatchingMismatch() const {
if (!isArgumentOfPatternMatchingOperator(getLocator()))
return false;
auto *op = castToExpr<BinaryExpr>(getRawAnchor());
auto *lhsExpr = op->getArg()->getElement(0);
auto *rhsExpr = op->getArg()->getElement(1);
auto lhsType = getType(lhsExpr);
auto rhsType = getType(rhsExpr);
auto diagnostic =
lhsType->is<UnresolvedType>()
? emitDiagnostic(
diag::cannot_match_unresolved_expr_pattern_with_value, rhsType)
: emitDiagnostic(diag::cannot_match_expr_pattern_with_value, lhsType,
rhsType);
diagnostic.highlight(lhsExpr->getSourceRange());
diagnostic.highlight(rhsExpr->getSourceRange());
if (auto optUnwrappedType = rhsType->getOptionalObjectType()) {
if (lhsType->isEqual(optUnwrappedType)) {
diagnostic.fixItInsertAfter(lhsExpr->getEndLoc(), "?");
}
}
return true;
}
bool ArgumentMismatchFailure::diagnoseArchetypeMismatch() const {
auto *argTy = getFromType()->getAs<ArchetypeType>();
auto *paramTy = getToType()->getAs<ArchetypeType>();
if (!(argTy && paramTy))
return false;
// Produce this diagnostic only if the names
// of the generic parameters are the same.
if (argTy->getName() != paramTy->getName())
return false;
auto getGenericTypeDecl = [&](ArchetypeType *archetype) -> ValueDecl * {
auto paramType = archetype->getInterfaceType();
if (auto *GTPT = paramType->getAs<GenericTypeParamType>())
return GTPT->getDecl();
if (auto *DMT = paramType->getAs<DependentMemberType>())
return DMT->getAssocType();
return nullptr;
};
auto *argDecl = getGenericTypeDecl(argTy);
auto *paramDecl = getGenericTypeDecl(paramTy);
if (!(paramDecl && argDecl))
return false;
emitDiagnostic(diag::cannot_convert_argument_value_generic, argTy,
describeGenericType(argDecl), paramTy,
describeGenericType(paramDecl));
emitDiagnosticAt(argDecl, diag::descriptive_generic_type_declared_here,
describeGenericType(argDecl, true));
emitDiagnosticAt(paramDecl, diag::descriptive_generic_type_declared_here,
describeGenericType(paramDecl, true));
return true;
}
bool ArgumentMismatchFailure::diagnoseMisplacedMissingArgument() const {
const auto &solution = getSolution();
auto *locator = getLocator();
if (!MissingArgumentsFailure::isMisplacedMissingArgument(solution, locator))
return false;
// Assign new type variable to a type of a parameter.
auto *fnType = getFnType();
const auto &param = fnType->getParams()[0];
auto anchor = getRawAnchor();
MissingArgumentsFailure failure(
solution, {SynthesizedArg{0, param}},
getConstraintLocator(anchor, ConstraintLocator::ApplyArgument));
return failure.diagnoseSingleMissingArgument();
}
bool ArgumentMismatchFailure::diagnosePropertyWrapperMismatch() const {
auto argType = getFromType();
auto paramType = getToType();
// Verify that this is an implicit call to a property wrapper initializer
// in a form of `init(wrappedValue:)` or deprecated `init(initialValue:)`.
auto *call = getAsExpr<CallExpr>(getRawAnchor());
if (!(call && call->isImplicit() && isa<TypeExpr>(call->getFn()) &&
call->getNumArguments() == 1 &&
(call->getArgumentLabels().front() == getASTContext().Id_wrappedValue ||
call->getArgumentLabels().front() == getASTContext().Id_initialValue)))
return false;
auto argExpr = cast<TupleExpr>(call->getArg())->getElement(0);
// If this is an attempt to initialize property wrapper with opaque value
// of error type, let's just ignore that problem since original mismatch
// has been diagnosed already.
if (argExpr->isImplicit() && isa<OpaqueValueExpr>(argExpr) &&
argType->is<ErrorType>())
return true;
emitDiagnostic(diag::cannot_convert_initializer_value, argType, paramType);
return true;
}
bool ArgumentMismatchFailure::diagnoseTrailingClosureMismatch() const {
if (!Info.isTrailingClosure())
return false;
auto paramType = getToType();
if (paramType->lookThroughAllOptionalTypes()->is<AnyFunctionType>())
return false;
emitDiagnostic(diag::trailing_closure_bad_param, paramType)
.highlight(getSourceRange());
if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) {
if (auto *decl = overload->choice.getDeclOrNull()) {
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
}
}
return true;
}
void ExpandArrayIntoVarargsFailure::tryDropArrayBracketsFixIt(
const Expr *anchor) const {
// If this is an array literal, offer to remove the brackets and pass the
// elements directly as variadic arguments.
if (auto *arrayExpr = dyn_cast<ArrayExpr>(anchor)) {
auto diag = emitDiagnosticAt(arrayExpr->getLoc(),
diag::suggest_pass_elements_directly);
diag.fixItRemove(arrayExpr->getLBracketLoc())
.fixItRemove(arrayExpr->getRBracketLoc());
// Handle the case where the array literal has a trailing comma.
if (arrayExpr->getNumCommas() == arrayExpr->getNumElements())
diag.fixItRemove(arrayExpr->getCommaLocs().back());
}
}
bool ExpandArrayIntoVarargsFailure::diagnoseAsError() {
if (auto *anchor = getAsExpr(getAnchor())) {
emitDiagnostic(diag::cannot_convert_array_to_variadic, getFromType(),
getToType());
tryDropArrayBracketsFixIt(anchor);
// TODO: Array splat fix-it once that's supported.
return true;
}
return false;
}
bool ExpandArrayIntoVarargsFailure::diagnoseAsNote() {
auto overload = getCalleeOverloadChoiceIfAvailable(getLocator());
auto *anchor = getAsExpr(getAnchor());
if (!overload || !anchor)
return false;
if (auto chosenDecl = overload->choice.getDeclOrNull()) {
emitDiagnosticAt(chosenDecl, diag::candidate_would_match_array_to_variadic,
getToType());
tryDropArrayBracketsFixIt(anchor);
return true;
}
return false;
}
bool ExtraneousCallFailure::diagnoseAsError() {
auto anchor = getAnchor();
auto *locator = getLocator();
// If this is something like `foo()` where `foo` is a variable
// or a property, let's suggest dropping `()`.
auto removeParensFixIt = [&](InFlightDiagnostic &diagnostic) {
auto *argLoc =
getConstraintLocator(getRawAnchor(), ConstraintLocator::ApplyArgument);
if (auto *TE = getAsExpr<TupleExpr>(simplifyLocatorToAnchor(argLoc))) {
if (TE->getNumElements() == 0) {
diagnostic.fixItRemove(TE->getSourceRange());
}
}
};
if (auto overload = getCalleeOverloadChoiceIfAvailable(locator)) {
if (auto *decl = overload->choice.getDeclOrNull()) {
if (auto *enumCase = dyn_cast<EnumElementDecl>(decl)) {
auto diagnostic =
emitDiagnostic(diag::unexpected_arguments_in_enum_case,
enumCase->getBaseIdentifier());
removeParensFixIt(diagnostic);
return true;
}
}
}
if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
auto *baseExpr = UDE->getBase();
auto *call = castToExpr<CallExpr>(getRawAnchor());
if (getType(baseExpr)->isAnyObject()) {
emitDiagnostic(diag::cannot_call_with_params,
UDE->getName().getBaseName().userFacingName(),
getType(call->getArg())->getString(),
isa<TypeExpr>(baseExpr));
return true;
}
}
auto diagnostic =
emitDiagnostic(diag::cannot_call_non_function_value, getType(anchor));
removeParensFixIt(diagnostic);
return true;
}
void NonEphemeralConversionFailure::emitSuggestionNotes() const {
auto getPointerKind = [](Type ty) -> PointerTypeKind {
PointerTypeKind pointerKind;
auto pointeeType = ty->lookThroughSingleOptionalType()
->getAnyPointerElementType(pointerKind);
assert(pointeeType && "Expected a pointer!");
(void)pointeeType;
return pointerKind;
};
// This must stay in sync with diag::ephemeral_use_array_with_unsafe_buffer
// and diag::ephemeral_use_with_unsafe_pointer.
enum AlternativeKind {
AK_Raw = 0,
AK_MutableRaw,
AK_Typed,
AK_MutableTyped,
};
auto getAlternativeKind = [&]() -> Optional<AlternativeKind> {
switch (getPointerKind(getParamType())) {
case PTK_UnsafeRawPointer:
return AK_Raw;
case PTK_UnsafeMutableRawPointer:
return AK_MutableRaw;
case PTK_UnsafePointer:
return AK_Typed;
case PTK_UnsafeMutablePointer:
return AK_MutableTyped;
case PTK_AutoreleasingUnsafeMutablePointer:
return None;
}
llvm_unreachable("invalid pointer kind");
};
// First emit a note about the implicit conversion only lasting for the
// duration of the call.
auto *argExpr = getArgExpr();
emitDiagnosticAt(
argExpr->getLoc(), diag::ephemeral_pointer_argument_conversion_note,
getArgType(), getParamType(), getCallee(), getCalleeFullName())
.highlight(argExpr->getSourceRange());
// Then try to find a suitable alternative.
switch (ConversionKind) {
case ConversionRestrictionKind::ArrayToPointer: {
// Don't suggest anything for optional arrays, as there's currently no
// direct alternative.
if (getArgType()->getOptionalObjectType())
break;
// We can suggest using withUnsafe[Mutable][Bytes/BufferPointer].
if (auto alternative = getAlternativeKind())
emitDiagnosticAt(argExpr->getLoc(),
diag::ephemeral_use_array_with_unsafe_buffer,
*alternative);
break;
}
case ConversionRestrictionKind::StringToPointer: {
// Don't suggest anything for optional strings, as there's currently no
// direct alternative.
if (getArgType()->getOptionalObjectType())
break;
// We can suggest withCString as long as the resulting pointer is
// immutable.
switch (getPointerKind(getParamType())) {
case PTK_UnsafePointer:
case PTK_UnsafeRawPointer:
emitDiagnosticAt(argExpr->getLoc(),
diag::ephemeral_use_string_with_c_string);
break;
case PTK_UnsafeMutableRawPointer:
case PTK_UnsafeMutablePointer:
case PTK_AutoreleasingUnsafeMutablePointer:
// There's nothing really sensible we can suggest for a mutable pointer.
break;
}
break;
}
case ConversionRestrictionKind::InoutToPointer:
// For an arbitrary inout-to-pointer, we can suggest
// withUnsafe[Mutable][Bytes/Pointer].
if (auto alternative = getAlternativeKind())
emitDiagnosticAt(argExpr->getLoc(),
diag::ephemeral_use_with_unsafe_pointer, *alternative);
break;
case ConversionRestrictionKind::DeepEquality:
case ConversionRestrictionKind::Superclass:
case ConversionRestrictionKind::Existential:
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
case ConversionRestrictionKind::ExistentialMetatypeToMetatype:
case ConversionRestrictionKind::ValueToOptional:
case ConversionRestrictionKind::OptionalToOptional:
case ConversionRestrictionKind::ClassMetatypeToAnyObject:
case ConversionRestrictionKind::ExistentialMetatypeToAnyObject:
case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass:
case ConversionRestrictionKind::PointerToPointer:
case ConversionRestrictionKind::ArrayUpcast:
case ConversionRestrictionKind::DictionaryUpcast:
case ConversionRestrictionKind::SetUpcast:
case ConversionRestrictionKind::HashableToAnyHashable:
case ConversionRestrictionKind::CFTollFreeBridgeToObjC:
case ConversionRestrictionKind::ObjCTollFreeBridgeToCF:
llvm_unreachable("Expected an ephemeral conversion!");
}
}
bool NonEphemeralConversionFailure::diagnosePointerInit() const {
auto *constructor = dyn_cast_or_null<ConstructorDecl>(getCallee());
if (!constructor)
return false;
auto constructedTy = getFnType()->getResult();
// Strip off a level of optionality if we have a failable initializer.
if (constructor->isFailable())
constructedTy = constructedTy->getOptionalObjectType();
// This must stay in sync with diag::cannot_construct_dangling_pointer.
enum ConstructorKind {
CK_Pointer = 0,
CK_BufferPointer,
};
// Consider OpaquePointer as well as the other kinds of pointers.
auto isConstructingPointer =
constructedTy->getAnyPointerElementType() ||
constructedTy->getAnyNominal() == getASTContext().getOpaquePointerDecl();
ConstructorKind constructorKind;
auto parameterCount = constructor->getParameters()->size();
if (isConstructingPointer && parameterCount == 1) {
constructorKind = CK_Pointer;
} else if (constructedTy->getAnyBufferPointerElementType() &&
parameterCount == 2) {
constructorKind = CK_BufferPointer;
} else {
return false;
}
auto diagID = DowngradeToWarning
? diag::cannot_construct_dangling_pointer_warning
: diag::cannot_construct_dangling_pointer;
auto anchor = getRawAnchor();
emitDiagnosticAt(::getLoc(anchor), diagID, constructedTy, constructorKind)
.highlight(::getSourceRange(anchor));
emitSuggestionNotes();
return true;
}
bool NonEphemeralConversionFailure::diagnoseAsNote() {
// We can only emit a useful note if we have a callee.
if (auto *callee = getCallee()) {
emitDiagnosticAt(callee, diag::candidate_performs_illegal_ephemeral_conv,
getParamPosition());
return true;
}
return false;
}
bool NonEphemeralConversionFailure::diagnoseAsError() {
// Emit a specialized diagnostic for
// Unsafe[Mutable][Raw]Pointer.init([mutating]:) &
// Unsafe[Mutable][Raw]BufferPointer.init(start:count:).
if (diagnosePointerInit())
return true;
// Otherwise, emit a more general diagnostic.
SmallString<8> scratch;
auto argDesc = getArgDescription(scratch);
auto *argExpr = getArgExpr();
if (isa<InOutExpr>(argExpr)) {
auto diagID = DowngradeToWarning
? diag::cannot_use_inout_non_ephemeral_warning
: diag::cannot_use_inout_non_ephemeral;
emitDiagnosticAt(argExpr->getLoc(), diagID, argDesc, getCallee(),
getCalleeFullName())
.highlight(argExpr->getSourceRange());
} else {
auto diagID = DowngradeToWarning
? diag::cannot_pass_type_to_non_ephemeral_warning
: diag::cannot_pass_type_to_non_ephemeral;
emitDiagnosticAt(argExpr->getLoc(), diagID, getArgType(), argDesc,
getCallee(), getCalleeFullName())
.highlight(argExpr->getSourceRange());
}
emitSuggestionNotes();
return true;
}
bool AssignmentTypeMismatchFailure::diagnoseMissingConformance() const {
auto srcType = getFromType();
auto dstType = getToType()->lookThroughAllOptionalTypes();
llvm::SmallPtrSet<ProtocolDecl *, 4> srcMembers;
llvm::SmallPtrSet<ProtocolDecl *, 4> dstMembers;
auto retrieveProtocols = [](Type type,
llvm::SmallPtrSetImpl<ProtocolDecl *> &members) {
if (auto *protocol = type->getAs<ProtocolType>())
members.insert(protocol->getDecl());
if (auto *composition = type->getAs<ProtocolCompositionType>()) {
for (auto member : composition->getMembers()) {
if (auto *protocol = member->getAs<ProtocolType>())
members.insert(protocol->getDecl());
}
}
};
retrieveProtocols(srcType, srcMembers);
retrieveProtocols(dstType, dstMembers);
if (srcMembers.empty() || dstMembers.empty())
return false;
// Let's check whether there is an overlap between source and destination.
for (auto *member : srcMembers)
dstMembers.erase(member);
if (dstMembers.size() == 1)
dstType = (*dstMembers.begin())->getDeclaredType();
emitDiagnostic(diag::cannot_convert_assign_protocol, srcType, dstType);
return true;
}
bool AssignmentTypeMismatchFailure::diagnoseAsError() {
if (diagnoseMissingConformance())
return true;
return ContextualFailure::diagnoseAsError();
}
bool AssignmentTypeMismatchFailure::diagnoseAsNote() {
auto anchor = getAnchor();
if (auto overload =
getCalleeOverloadChoiceIfAvailable(getConstraintLocator(anchor))) {
if (auto *decl = overload->choice.getDeclOrNull()) {
emitDiagnosticAt(decl,
diag::cannot_convert_candidate_result_to_contextual_type,
decl->getName(), getFromType(), getToType());
return true;
}
}
return false;
}
bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() {
auto *anchor = castToExpr(getAnchor());
// Member reference could be wrapped into a number of parens
// e.g. `((.foo))`.
auto *parentExpr = findParentExpr(anchor);
// Look through immediate call of unresolved member (e.g., `.foo(0)`).
if (parentExpr && isa<CallExpr>(parentExpr))
parentExpr = findParentExpr(parentExpr);
// FIXME: We should probably look through the entire member chain so that
// something like `let _ = .foo().bar` gets the "no contextual type" error
// rather than the "Cannot infer contextual base" error.
UnresolvedMemberChainResultExpr *resultExpr = nullptr;
if (parentExpr && isa<UnresolvedMemberChainResultExpr>(parentExpr)) {
resultExpr = cast<UnresolvedMemberChainResultExpr>(parentExpr);
parentExpr = findParentExpr(parentExpr);
}
do {
// If we have found something which isn't a paren let's stop,
// otherwise let's keep unwrapping until there are either no
// more parens or no more parents...
if (!parentExpr || !isa<ParenExpr>(parentExpr))
break;
} while ((parentExpr = findParentExpr(parentExpr)));
auto diagnostic = parentExpr || (resultExpr && getContextualType(resultExpr))
? diag::cannot_infer_base_of_unresolved_member
: diag::unresolved_member_no_inference;
emitDiagnostic(diagnostic, MemberName).highlight(getSourceRange());
return true;
}
bool UnableToInferClosureParameterType::diagnoseAsError() {
auto *closure = castToExpr<ClosureExpr>(getRawAnchor());
// Let's check whether this closure is an argument to
// a call which couldn't be properly resolved e.g.
// missing member or invalid contextual reference and
// if so let's not diagnose this problem because main
// issue here is inability to establish context for
// closure inference.
//
// TODO(diagnostics): Once we gain an ability to determine
// originating source of type holes this check could be
// significantly simplified.
{
auto &solution = getSolution();
// If there is a contextual mismatch associated with this
// closure, let's not diagnose any parameter type issues.
if (hasFixFor(solution, getConstraintLocator(
closure, LocatorPathElt::ContextualType())))
return false;
if (auto *parentExpr = findParentExpr(closure)) {
while (parentExpr &&
(isa<TupleExpr>(parentExpr) || isa<ParenExpr>(parentExpr))) {
parentExpr = findParentExpr(parentExpr);
}
if (parentExpr) {
// Missing or invalid member reference in call.
if (auto *AE = dyn_cast<ApplyExpr>(parentExpr)) {
if (getType(AE->getFn())->is<UnresolvedType>())
return false;
}
// Any fix anchored on parent expression makes it unnecessary
// to diagnose unability to infer parameter type because it's
// an indication that proper context couldn't be established to
// resolve the closure.
ASTNode parentNode(parentExpr);
if (llvm::any_of(solution.Fixes,
[&parentNode](const ConstraintFix *fix) -> bool {
return fix->getAnchor() == parentNode;
}))
return false;
}
}
}
auto paramIdx = getLocator()
->castLastElementTo<LocatorPathElt::TupleElement>()
.getIndex();
auto *PD = closure->getParameters()->get(paramIdx);
llvm::SmallString<16> id;
llvm::raw_svector_ostream OS(id);
if (PD->isAnonClosureParam()) {
OS << "$" << paramIdx;
} else {
OS << "'" << PD->getParameterName() << "'";
}
auto loc = PD->isAnonClosureParam() ? getLoc() : PD->getLoc();
emitDiagnosticAt(loc, diag::cannot_infer_closure_parameter_type, OS.str());
return true;
}
bool UnableToInferClosureReturnType::diagnoseAsError() {
auto *closure = castToExpr<ClosureExpr>(getRawAnchor());
auto diagnostic = emitDiagnostic(diag::cannot_infer_closure_result_type,
closure->hasSingleExpressionBody());
// If there is a location for an 'in' token, then the argument list was
// specified somehow but no return type was. Insert a "-> ReturnType "
// before the in token.
if (closure->getInLoc().isValid()) {
diagnostic.fixItInsert(closure->getInLoc(),
diag::insert_closure_return_type_placeholder,
/*argListSpecified=*/false);
} else if (closure->getParameters()->size() == 0) {
// Otherwise, the closure must take zero arguments.
//
// As such, we insert " () -> ReturnType in " right after the '{' that
// starts the closure body.
diagnostic.fixItInsertAfter(closure->getBody()->getLBraceLoc(),
diag::insert_closure_return_type_placeholder,
/*argListSpecified=*/true);
}
return true;
}
static std::pair<StringRef, StringRef>
getImportModuleAndDefaultType(const ASTContext &ctx,
const ObjectLiteralExpr *expr) {
const auto &target = ctx.LangOpts.Target;
switch (expr->getLiteralKind()) {
case ObjectLiteralExpr::colorLiteral: {
if (target.isMacOSX()) {
return std::make_pair("AppKit", "NSColor");
} else if (target.isiOS() || target.isTvOS()) {
return std::make_pair("UIKit", "UIColor");
}
break;
}
case ObjectLiteralExpr::imageLiteral: {
if (target.isMacOSX()) {
return std::make_pair("AppKit", "NSImage");
} else if (target.isiOS() || target.isTvOS()) {
return std::make_pair("UIKit", "UIImage");
}
break;
}
case ObjectLiteralExpr::fileLiteral: {
return std::make_pair("Foundation", "URL");
}
}
return std::make_pair("", "");
}
SourceLoc UnableToInferProtocolLiteralType::getLoc() const {
return ::getLoc(getRawAnchor());
}
bool UnableToInferProtocolLiteralType::diagnoseAsError() {
auto &ctx = getASTContext();
auto *expr = castToExpr<ObjectLiteralExpr>(getRawAnchor());
StringRef importModule;
StringRef importDefaultTypeName;
std::tie(importModule, importDefaultTypeName) =
getImportModuleAndDefaultType(ctx, expr);
auto plainName = expr->getLiteralKindPlainName();
emitDiagnostic(diag::object_literal_default_type_missing, plainName);
if (!importModule.empty()) {
emitDiagnostic(diag::object_literal_resolve_import, importModule,
importDefaultTypeName, plainName);
}
return true;
}
bool MissingQuialifierInMemberRefFailure::diagnoseAsError() {
auto selectedOverload = getOverloadChoiceIfAvailable(getLocator());
if (!selectedOverload)
return false;
auto *UDE = castToExpr<UnresolvedDotExpr>(getRawAnchor());
auto baseType = getType(UDE->getBase());
auto methodKind = baseType->isAnyExistentialType()
? DescriptiveDeclKind::StaticMethod
: DescriptiveDeclKind::Method;
auto choice = selectedOverload->choice.getDeclOrNull();
if (!choice)
return false;
auto *DC = choice->getDeclContext();
if (!(DC->isModuleContext() || DC->isModuleScopeContext())) {
emitDiagnostic(diag::member_shadows_function, UDE->getName(), methodKind,
choice->getDescriptiveKind(), choice->getName());
return true;
}
auto qualifier = DC->getParentModule()->getName();
emitDiagnostic(diag::member_shadows_global_function, UDE->getName(),
methodKind, choice->getDescriptiveKind(),
choice->getName(), qualifier);
SmallString<32> namePlusDot = qualifier.str();
namePlusDot.push_back('.');
emitDiagnostic(diag::fix_unqualified_access_top_level_multi, namePlusDot,
choice->getDescriptiveKind(), qualifier)
.fixItInsert(UDE->getStartLoc(), namePlusDot);
emitDiagnosticAt(choice, diag::decl_declared_here, choice->getName());
return true;
}
bool CoercionAsForceCastFailure::diagnoseAsError() {
emitDiagnostic(diag::coercion_may_fail_warning, getFromType(), getToType())
.highlight(getSourceRange());
return true;
}
bool KeyPathRootTypeMismatchFailure::diagnoseAsError() {
auto locator = getLocator();
assert(locator->isKeyPathRoot() && "Expected a key path root");
auto baseType = getFromType();
auto rootType = getToType();
emitDiagnostic(diag::expr_keypath_root_type_mismatch,
rootType, baseType);
return true;
}
bool MultiArgFuncKeyPathFailure::diagnoseAsError() {
// Diagnose use a keypath where a function with multiple arguments is expected
emitDiagnostic(diag::expr_keypath_multiparam_func_conversion,
resolveType(functionType));
return true;
}
bool UnableToInferKeyPathRootFailure::diagnoseAsError() {
assert(isExpr<KeyPathExpr>(getAnchor()) && "Expected key path expression");
auto &ctx = getASTContext();
auto contextualType = getContextualType(getAnchor());
auto *keyPathExpr = castToExpr<KeyPathExpr>(getAnchor());
auto emitKeyPathDiagnostic = [&]() {
if (contextualType &&
contextualType->getAnyNominal() == ctx.getAnyKeyPathDecl()) {
return emitDiagnostic(
diag::cannot_infer_keypath_root_anykeypath_context);
}
return emitDiagnostic(
diag::cannot_infer_contextual_keypath_type_specify_root);
};
emitKeyPathDiagnostic()
.highlight(keyPathExpr->getLoc())
.fixItInsertAfter(keyPathExpr->getStartLoc(), "<#Root#>");
return true;
}
Optional<Diag<Type, Type>>
AbstractRawRepresentableFailure::getDiagnostic() const {
auto *locator = getLocator();
if (locator->isForContextualType()) {
return diag::cannot_convert_initializer_value;
} else if (locator->isForAssignment()) {
return diag::cannot_convert_assign;
} else if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
return diag::cannot_convert_argument_value;
}
return None;
}
bool AbstractRawRepresentableFailure::diagnoseAsError() {
auto message = getDiagnostic();
if (!message)
return false;
auto diagnostic = emitDiagnostic(*message, getFromType(), getToType());
fixIt(diagnostic);
return true;
}
bool AbstractRawRepresentableFailure::diagnoseAsNote() {
auto *locator = getLocator();
Optional<InFlightDiagnostic> diagnostic;
if (locator->isForContextualType()) {
auto overload = getCalleeOverloadChoiceIfAvailable(locator);
if (!overload)
return false;
if (auto *decl = overload->choice.getDeclOrNull()) {
diagnostic.emplace(emitDiagnosticAt(
decl, diag::cannot_convert_candidate_result_to_contextual_type,
decl->getName(), ExpectedType, RawReprType));
}
} else if (auto argConv =
locator->getLastElementAs<LocatorPathElt::ApplyArgToParam>()) {
diagnostic.emplace(
emitDiagnostic(diag::candidate_has_invalid_argument_at_position,
RawReprType, argConv->getParamIdx(), /*inOut=*/false));
}
if (diagnostic) {
fixIt(*diagnostic);
return true;
}
return false;
}
void MissingRawRepresentableInitFailure::fixIt(
InFlightDiagnostic &diagnostic) const {
if (auto *E = getAsExpr(getAnchor())) {
auto range = E->getSourceRange();
auto rawReprObjType = RawReprType->getOptionalObjectType();
auto valueObjType = ExpectedType->getOptionalObjectType();
if (rawReprObjType && valueObjType) {
std::string mapCodeFix;
// Check whether expression has been be wrapped in parens first.
if (!E->canAppendPostfixExpression()) {
diagnostic.fixItInsert(range.Start, "(");
mapCodeFix += ")";
}
mapCodeFix += ".map { ";
mapCodeFix += rawReprObjType->getString();
mapCodeFix += "(rawValue: $0) }";
diagnostic.fixItInsertAfter(range.End, mapCodeFix);
} else if (rawReprObjType) {
diagnostic
.fixItInsert(range.Start, rawReprObjType->getString() + "(rawValue: ")
.fixItInsertAfter(range.End, ")");
} else if (valueObjType) {
diagnostic.flush();
std::string fixItBefore = RawReprType->getString() + "(rawValue: ";
std::string fixItAfter;
if (!E->canAppendPostfixExpression(true)) {
fixItBefore += "(";
fixItAfter += ")";
}
fixItAfter += "!) ?? <#default value#>";
emitDiagnostic(diag::construct_raw_representable_from_unwrapped_value,
RawReprType, valueObjType)
.highlight(range)
.fixItInsert(range.Start, fixItBefore)
.fixItInsertAfter(range.End, fixItAfter);
} else {
diagnostic
.fixItInsert(range.Start, RawReprType->getString() + "(rawValue: ")
.fixItInsertAfter(range.End, ") ?? <#default value#>");
}
}
}
void MissingRawValueFailure::fixIt(InFlightDiagnostic &diagnostic) const {
auto *E = getAsExpr(getAnchor());
if (!E)
return;
std::string fix;
auto range = E->getSourceRange();
if (!E->canAppendPostfixExpression()) {
diagnostic.fixItInsert(range.Start, "(");
fix += ")";
}
// If raw representable is an optional we need to map its raw value out
// out first and then, if destination is not optional, allow to specify
// default value.
if (RawReprType->getOptionalObjectType()) {
fix += "?.rawValue";
if (!ExpectedType->getOptionalObjectType())
fix += " ?? <#default value#>";
} else {
fix += ".rawValue";
}
diagnostic.fixItInsertAfter(range.End, fix);
}
bool MissingOptionalUnwrapKeyPathFailure::diagnoseAsError() {
emitDiagnostic(diag::optional_not_unwrapped, getFromType(),
getFromType()->lookThroughSingleOptionalType());
emitDiagnostic(diag::optional_keypath_application_base)
.fixItInsertAfter(getLoc(), "?");
emitDiagnostic(diag::unwrap_with_force_value)
.fixItInsertAfter(getLoc(), "!");
return true;
}
SourceLoc MissingOptionalUnwrapKeyPathFailure::getLoc() const {
auto *SE = castToExpr<SubscriptExpr>(getAnchor());
return SE->getBase()->getEndLoc();
}
bool TrailingClosureRequiresExplicitLabel::diagnoseAsError() {
auto argInfo = *getFunctionArgApplyInfo(getLocator());
{
auto diagnostic = emitDiagnostic(
diag::unlabeled_trailing_closure_deprecated, argInfo.getParamLabel());
fixIt(diagnostic, argInfo);
}
if (auto *callee = argInfo.getCallee()) {
emitDiagnosticAt(callee, diag::decl_declared_here, callee->getName());
}
return true;
}
void TrailingClosureRequiresExplicitLabel::fixIt(
InFlightDiagnostic &diagnostic, const FunctionArgApplyInfo &info) const {
auto &ctx = getASTContext();
// Dig out source locations.
SourceLoc existingRParenLoc;
SourceLoc leadingCommaLoc;
auto anchor = getRawAnchor();
auto *arg = info.getArgListExpr();
Expr *fn = nullptr;
if (auto *applyExpr = getAsExpr<ApplyExpr>(anchor)) {
fn = applyExpr->getFn();
} else {
// Covers subscripts, unresolved members etc.
fn = getAsExpr(anchor);
}
if (!fn)
return;
auto *trailingClosure = info.getArgExpr();
if (auto tupleExpr = dyn_cast<TupleExpr>(arg)) {
existingRParenLoc = tupleExpr->getRParenLoc();
assert(tupleExpr->getNumElements() >= 2 && "Should be a ParenExpr?");
leadingCommaLoc = Lexer::getLocForEndOfToken(
ctx.SourceMgr,
tupleExpr->getElements()[tupleExpr->getNumElements() - 2]->getEndLoc());
} else {
auto parenExpr = cast<ParenExpr>(arg);
existingRParenLoc = parenExpr->getRParenLoc();
}
// Figure out the text to be inserted before the trailing closure.
SmallString<16> insertionText;
SourceLoc insertionLoc;
if (leadingCommaLoc.isValid()) {
insertionText += ", ";
assert(existingRParenLoc.isValid());
insertionLoc = leadingCommaLoc;
} else if (existingRParenLoc.isInvalid()) {
insertionText += "(";
insertionLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr, fn->getEndLoc());
} else {
insertionLoc = existingRParenLoc;
}
// Add the label, if there is one.
auto paramName = info.getParamLabel();
if (!paramName.empty()) {
insertionText += paramName.str();
insertionText += ": ";
}
// If there is an existing right parentheses/brace, remove it while we
// insert the new text.
if (existingRParenLoc.isValid()) {
SourceLoc afterExistingRParenLoc =
Lexer::getLocForEndOfToken(ctx.SourceMgr, existingRParenLoc);
diagnostic.fixItReplaceChars(insertionLoc, afterExistingRParenLoc,
insertionText);
} else {
// Insert the appropriate prefix.
diagnostic.fixItInsert(insertionLoc, insertionText);
}
// Insert a right parenthesis/brace after the closing '}' of the trailing
// closure;
SourceLoc newRParenLoc =
Lexer::getLocForEndOfToken(ctx.SourceMgr, trailingClosure->getEndLoc());
diagnostic.fixItInsert(newRParenLoc,
isExpr<SubscriptExpr>(anchor) ? "]" : ")");
}
bool InvalidEmptyKeyPathFailure::diagnoseAsError() {
auto *KPE = getAsExpr<KeyPathExpr>(getAnchor());
assert(KPE && KPE->hasSingleInvalidComponent() &&
"Expected a malformed key path expression");
// If we have a string interpolation represented as key path expressions
// e.g. \(x), \(x, a: 1). Let's skip it because this would be already
// diagnosed and it is not the case for an extra empty key path diagnostic.
auto *root = KPE->getParsedRoot();
if (root && (isa<ParenExpr>(root) || isa<TupleExpr>(root)))
return true;
emitDiagnostic(diag::expr_swift_keypath_empty);
return true;
}
bool MissingContextualTypeForNil::diagnoseAsError() {
auto *expr = castToExpr<NilLiteralExpr>(getAnchor());
// If this is a standalone `nil` literal expression e.g.
// `_ = nil`, let's diagnose it here because solver can't
// attempt any types for it.
auto *parentExpr = findParentExpr(expr);
while (parentExpr && isa<IdentityExpr>(parentExpr))
parentExpr = findParentExpr(parentExpr);
// In cases like `_ = nil?` AST would have `nil`
// wrapped in `BindOptionalExpr`.
if (parentExpr && isa<BindOptionalExpr>(parentExpr))
parentExpr = findParentExpr(parentExpr);
if (parentExpr) {
// `_ = nil as? ...`
if (isa<ConditionalCheckedCastExpr>(parentExpr)) {
emitDiagnostic(diag::conditional_cast_from_nil);
return true;
}
// `_ = nil!`
if (isa<ForceValueExpr>(parentExpr)) {
emitDiagnostic(diag::cannot_force_unwrap_nil_literal);
return true;
}
// `_ = nil?`
if (isa<OptionalEvaluationExpr>(parentExpr)) {
emitDiagnostic(diag::unresolved_nil_literal);
return true;
}
}
emitDiagnostic(diag::unresolved_nil_literal);
return true;
}
bool ReferenceToInvalidDeclaration::diagnoseAsError() {
auto *decl = castToExpr<DeclRefExpr>(getAnchor())->getDecl();
assert(decl);
auto &DE = getASTContext().Diags;
// This problem should have been already diagnosed during
// validation of the declaration.
if (DE.hadAnyError())
return true;
// If no errors have been emitted yet, let's emit one
// about reference to an invalid declaration.
emitDiagnostic(diag::reference_to_invalid_decl, decl->getName());
emitDiagnosticAt(decl, diag::decl_declared_here, decl->getName());
return true;
}
bool InvalidReturnInResultBuilderBody::diagnoseAsError() {
auto *closure = castToExpr<ClosureExpr>(getAnchor());
auto returnStmts = TypeChecker::findReturnStatements(closure);
assert(!returnStmts.empty());
auto loc = returnStmts.front()->getReturnLoc();
emitDiagnosticAt(loc, diag::result_builder_disabled_by_return, BuilderType);
// Note that one can remove all of the return statements.
{
auto diag = emitDiagnosticAt(loc, diag::result_builder_remove_returns);
for (auto returnStmt : returnStmts)
diag.fixItRemove(returnStmt->getReturnLoc());
}
return true;
}
bool MemberMissingExplicitBaseTypeFailure::diagnoseAsError() {
auto UME = castToExpr<UnresolvedMemberExpr>(getAnchor());
auto memberName = UME->getName().getBaseIdentifier().str();
auto &DE = getASTContext().Diags;
auto &solution = getSolution();
auto selected = solution.getOverloadChoice(getLocator());
auto baseType =
resolveType(selected.choice.getBaseType()->getMetatypeInstanceType());
SmallVector<Type, 4> optionals;
auto baseTyUnwrapped = baseType->lookThroughAllOptionalTypes(optionals);
if (!optionals.empty()) {
auto baseTyName = baseType->getCanonicalType().getString();
auto baseTyUnwrappedName = baseTyUnwrapped->getString();
auto loc = UME->getLoc();
auto startLoc = UME->getStartLoc();
DE.diagnoseWithNotes(
DE.diagnose(loc, diag::optional_ambiguous_case_ref, baseTyName,
baseTyUnwrappedName, memberName),
[&]() {
DE.diagnose(UME->getDotLoc(), diag::optional_fixit_ambiguous_case_ref)
.fixItInsert(startLoc, "Optional");
DE.diagnose(UME->getDotLoc(),
diag::type_fixit_optional_ambiguous_case_ref,
baseTyUnwrappedName, memberName)
.fixItInsert(startLoc, baseTyUnwrappedName);
});
} else {
auto baseTypeName = baseType->getCanonicalType().getString();
auto baseOptionalTypeName =
OptionalType::get(baseType)->getCanonicalType().getString();
DE.diagnoseWithNotes(
DE.diagnose(UME->getLoc(), diag::optional_ambiguous_case_ref,
baseTypeName, baseOptionalTypeName, memberName),
[&]() {
DE.diagnose(UME->getDotLoc(),
diag::type_fixit_optional_ambiguous_case_ref,
baseOptionalTypeName, memberName)
.fixItInsert(UME->getDotLoc(), baseOptionalTypeName);
DE.diagnose(UME->getDotLoc(),
diag::type_fixit_optional_ambiguous_case_ref,
baseTypeName, memberName)
.fixItInsert(UME->getDotLoc(), baseTypeName);
});
}
return true;
}