//===--- CSDiagnostics.cpp - Constraint Diagnostics -----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements diagnostics for constraint system. // //===----------------------------------------------------------------------===// #include "CSDiagnostics.h" #include "ConstraintSystem.h" #include "MiscDiagnostics.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Types.h" #include "llvm/ADT/ArrayRef.h" using namespace swift; using namespace constraints; FailureDiagnostic::~FailureDiagnostic() {} std::pair FailureDiagnostic::computeAnchor() const { auto &cs = getConstraintSystem(); auto *locator = getLocator(); // Resolve the locator to a specific expression. SourceRange range; bool isSubscriptMember = (!locator->getPath().empty() && locator->getPath().back().getKind() == ConstraintLocator::SubscriptMember); ConstraintLocator *resolved = simplifyLocator(cs, locator, range); if (!resolved || !resolved->getAnchor()) return {locator->getAnchor(), true}; Expr *anchor = resolved->getAnchor(); // FIXME: Work around an odd locator representation that doesn't separate the // base of a subscript member from the member access. if (isSubscriptMember) { if (auto subscript = dyn_cast(anchor)) anchor = subscript->getBase(); } return {anchor, !resolved->getPath().empty()}; } Type FailureDiagnostic::getType(Expr *expr) const { auto &cs = getConstraintSystem(); return solution.simplifyType(cs.getType(expr)); } template InFlightDiagnostic FailureDiagnostic::emitDiagnostic(ArgTypes &&... Args) const { auto &cs = getConstraintSystem(); return cs.TC.diagnose(std::forward(Args)...); } Type RequirementFailure::getOwnerType() const { return getType(getAnchor())->getInOutObjectType()->getMetatypeInstanceType(); } const Requirement &RequirementFailure::getRequirement() const { auto *genericCtx = AffectedDecl->getAsGenericContext(); return genericCtx->getGenericRequirements()[getRequirementIndex()]; } ValueDecl *RequirementFailure::getDeclRef() const { auto &cs = getConstraintSystem(); auto *anchor = getAnchor(); auto *locator = cs.getConstraintLocator(anchor); if (auto *AE = dyn_cast(anchor)) { assert(isa(AE->getFn())); ConstraintLocatorBuilder ctor(locator); locator = cs.getConstraintLocator( ctor.withPathElement(PathEltKind::ApplyFunction) .withPathElement(PathEltKind::ConstructorMember)); } else if (auto *UDE = dyn_cast(anchor)) { ConstraintLocatorBuilder member(locator); locator = cs.getConstraintLocator(member.withPathElement(PathEltKind::Member)); } auto overload = getOverloadChoiceIfAvailable(locator); if (overload) return overload->choice.getDecl(); auto ownerType = getOwnerType(); if (auto *NA = dyn_cast(ownerType.getPointer())) return NA->getDecl(); return ownerType->getAnyGeneric(); } const DeclContext *RequirementFailure::getRequirementDC() const { const auto &req = getRequirement(); auto *DC = AffectedDecl->getDeclContext(); do { auto *D = DC->getInnermostDeclarationDeclContext(); if (!D) break; if (auto *GC = D->getAsGenericContext()) { auto *sig = GC->getGenericSignature(); if (sig && sig->isRequirementSatisfied(req)) return DC; } DC = DC->getParent(); } while (DC); return AffectedDecl->getAsGenericContext(); } bool MissingConformanceFailure::diagnose() { auto *anchor = getAnchor(); auto ownerType = getOwnerType(); auto type = getNonConformingType(); auto protocolType = getProtocolType(); // Find `ApplyExpr` based on a function expression attached to it. auto findApplyExpr = [](Expr *parent, Expr *fnExpr) -> ApplyExpr * { ApplyExpr *applyExpr = nullptr; parent->forEachChildExpr([&applyExpr, &fnExpr](Expr *subExpr) -> Expr * { auto *AE = dyn_cast(subExpr); if (!AE || AE->getFn() != fnExpr) return subExpr; applyExpr = AE; return nullptr; }); return applyExpr; }; auto getArgumentAt = [](ApplyExpr *AE, unsigned index) -> Expr * { assert(AE); auto *arg = AE->getArg(); if (auto *TE = dyn_cast(arg)) return TE->getElement(index); assert(index == 0); if (auto *PE = dyn_cast(arg)) return PE->getSubExpr(); return arg; }; auto *applyExpr = findApplyExpr(getParentExpr(), anchor); Optional atParameterPos; // Sometimes fix is recorded by type-checking sub-expression // during normal diagnostics, in such case call expression // is unavailable. if (applyExpr) { // If this is a static, initializer or operator call, // let's not try to diagnose it here, but refer to expression // diagnostics. if (isa(applyExpr) || isa(applyExpr) || isa(applyExpr) || isa(anchor)) return false; if (auto *fnType = ownerType->getAs()) { auto parameters = fnType->getParams(); for (auto index : indices(parameters)) { if (parameters[index].getType()->isEqual(type)) { atParameterPos = index; break; } } } } if (type->isExistentialType()) { auto diagnostic = diag::protocol_does_not_conform_objc; if (type->isObjCExistentialType()) diagnostic = diag::protocol_does_not_conform_static; emitDiagnostic(anchor->getLoc(), diagnostic, type, protocolType); } else if (atParameterPos) { // Requirement comes from one of the parameter types, // let's try to point diagnostic to the argument expression. auto *argExpr = getArgumentAt(applyExpr, *atParameterPos); emitDiagnostic(argExpr->getLoc(), diag::cannot_convert_argument_value_protocol, type, protocolType); } else { const auto &req = getRequirement(); auto *genericCtx = AffectedDecl->getAsGenericContext(); const auto *reqDC = getRequirementDC(); if (reqDC != genericCtx) { auto *NTD = reqDC->getAsNominalTypeOrNominalTypeExtensionContext(); emitDiagnostic(anchor->getLoc(), diag::type_does_not_conform_in_decl_ref, AffectedDecl->getDescriptiveKind(), AffectedDecl->getFullName(), NTD->getDeclaredType(), type, protocolType); } else { emitDiagnostic(anchor->getLoc(), diag::type_does_not_conform_decl_owner, AffectedDecl->getDescriptiveKind(), AffectedDecl->getFullName(), type, protocolType); } emitDiagnostic(reqDC->getAsDeclOrDeclExtensionContext(), diag::where_type_does_not_conform_type, req.getFirstType(), type); } return true; } bool LabelingFailure::diagnose() { auto &cs = getConstraintSystem(); auto *call = cast(getAnchor()); return diagnoseArgumentLabelError(cs.getASTContext(), call->getArg(), CorrectLabels, isa(call->getFn())); } bool NoEscapeFuncToTypeConversionFailure::diagnose() { auto *anchor = getAnchor(); if (ConvertTo) { emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, ConvertTo); return true; } auto path = getLocator()->getPath(); if (path.empty()) return false; auto &last = path.back(); if (last.getKind() != ConstraintLocator::Archetype) return false; auto *archetype = last.getArchetype(); emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, archetype); return true; } bool MissingForcedDowncastFailure::diagnose() { if (hasComplexLocator()) return false; auto &TC = getTypeChecker(); auto *coerceExpr = dyn_cast(getAnchor()); if (!coerceExpr) return false; auto *subExpr = coerceExpr->getSubExpr(); auto fromType = getType(subExpr)->getRValueType(); auto toType = resolveType(coerceExpr->getCastTypeLoc().getType()); auto castKind = TC.typeCheckCheckedCast(fromType, toType, CheckedCastContextKind::None, getDC(), coerceExpr->getLoc(), subExpr, coerceExpr->getCastTypeLoc().getSourceRange()); switch (castKind) { // Invalid cast. case CheckedCastKind::Unresolved: // Fix didn't work, let diagnoseFailureForExpr handle this. return false; case CheckedCastKind::Coercion: case CheckedCastKind::BridgingCoercion: llvm_unreachable("Coercions handled in other disjunction branch"); // Valid casts. case CheckedCastKind::ArrayDowncast: case CheckedCastKind::DictionaryDowncast: case CheckedCastKind::SetDowncast: case CheckedCastKind::ValueCast: emitDiagnostic(coerceExpr->getLoc(), diag::missing_forced_downcast, fromType, toType) .highlight(coerceExpr->getSourceRange()) .fixItReplace(coerceExpr->getLoc(), "as!"); return true; } } bool MissingAddressOfFailure::diagnose() { if (hasComplexLocator()) return false; auto *anchor = getAnchor(); auto type = getType(anchor)->getRValueType(); emitDiagnostic(anchor->getLoc(), diag::missing_address_of, type) .fixItInsert(anchor->getStartLoc(), "&"); return true; } bool MissingExplicitConversionFailure::diagnose() { if (hasComplexLocator()) return false; auto *DC = getDC(); auto &TC = getTypeChecker(); auto *anchor = getAnchor(); if (auto *paren = dyn_cast(anchor)) anchor = paren->getSubExpr(); auto fromType = getType(anchor)->getRValueType(); Type toType = resolveType(ConvertingTo); bool useAs = TC.isExplicitlyConvertibleTo(fromType, toType, DC); bool useAsBang = !useAs && TC.checkedCastMaySucceed(fromType, toType, DC); if (!useAs && !useAsBang) return false; auto *expr = getParentExpr(); // If we're performing pattern matching, // "as" means something completely different... if (auto binOpExpr = dyn_cast(expr)) { auto overloadedFn = dyn_cast(binOpExpr->getFn()); if (overloadedFn && !overloadedFn->getDecls().empty()) { ValueDecl *decl0 = overloadedFn->getDecls()[0]; if (decl0->getBaseName() == decl0->getASTContext().Id_MatchOperator) return false; } } bool needsParensInside = exprNeedsParensBeforeAddingAs(anchor); bool needsParensOutside = exprNeedsParensAfterAddingAs(anchor, expr); llvm::SmallString<2> insertBefore; llvm::SmallString<32> insertAfter; if (needsParensOutside) { insertBefore += "("; } if (needsParensInside) { insertBefore += "("; insertAfter += ")"; } insertAfter += useAs ? " as " : " as! "; insertAfter += toType->getWithoutParens()->getString(); if (needsParensOutside) insertAfter += ")"; auto diagID = useAs ? diag::missing_explicit_conversion : diag::missing_forced_downcast; auto diag = emitDiagnostic(anchor->getLoc(), diagID, fromType, toType); if (!insertBefore.empty()) { diag.fixItInsert(anchor->getStartLoc(), insertBefore); } diag.fixItInsertAfter(anchor->getEndLoc(), insertAfter); return true; } bool MemberAccessOnOptionalBaseFailure::diagnose() { if (hasComplexLocator()) return false; auto *anchor = getAnchor(); auto type = getType(anchor)->getRValueType(); bool resultIsOptional = ResultTypeIsOptional; // If we've resolved the member overload to one that returns an optional // type, then the result of the expression is optional (and we want to offer // only a '?' fixit) even though the constraint system didn't need to add any // additional optionality. auto overload = getResolvedOverload(getLocator()); if (overload && overload->ImpliedType->getOptionalObjectType()) resultIsOptional = true; return diagnoseBaseUnwrapForMemberAccess(anchor, type, Member, resultIsOptional, SourceRange()); } // Suggest a default value via ?? static void offerDefaultValueUnwrapFixit(TypeChecker &TC, DeclContext *DC, Expr *expr) { auto diag = TC.diagnose(expr->getLoc(), diag::unwrap_with_default_value); // Figure out what we need to parenthesize. bool needsParensInside = exprNeedsParensBeforeAddingNilCoalescing(TC, DC, expr); bool needsParensOutside = exprNeedsParensAfterAddingNilCoalescing(TC, DC, expr, expr); 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. static void offerForceUnwrapFixit(ConstraintSystem &CS, Expr *expr) { auto diag = CS.TC.diagnose(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(expr)) { if (auto dotExpr = dyn_cast(optionalChain->getSubExpr())) { auto bind = dyn_cast(dotExpr->getBase()); if (bind && !CS.getType(dotExpr)->getOptionalObjectType()) { diag.fixItReplace(SourceRange(bind->getLoc()), "!"); return; } } } if (expr->canAppendPostfixExpression(true)) { diag.fixItInsertAfter(expr->getEndLoc(), "!"); } else { diag.fixItInsert(expr->getStartLoc(), "(") .fixItInsertAfter(expr->getEndLoc(), ")!"); } } class VarDeclMultipleReferencesChecker : public ASTWalker { VarDecl *varDecl; int count; std::pair walkToExprPre(Expr *E) { if (auto *DRE = dyn_cast(E)) { if (DRE->getDecl() == varDecl) count++; } return { true, E }; } public: VarDeclMultipleReferencesChecker(VarDecl *varDecl) : varDecl(varDecl),count(0) {} int referencesCount() { return count; } }; static bool diagnoseUnwrap(ConstraintSystem &CS, Expr *expr, Type type) { Type unwrappedType = type->getOptionalObjectType(); if (!unwrappedType) return false; CS.TC.diagnose(expr->getLoc(), diag::optional_not_unwrapped, type, 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(expr)) { if (auto varDecl = dyn_cast(declRef->getDecl())) { bool singleUse = false; AbstractFunctionDecl *AFD = nullptr; if (auto contextDecl = varDecl->getDeclContext()->getAsDeclOrDeclExtensionContext()) { if ((AFD = dyn_cast(contextDecl))) { auto checker = VarDeclMultipleReferencesChecker(varDecl); AFD->getBody()->walk(checker); singleUse = checker.referencesCount() == 1; } } PatternBindingDecl *binding = varDecl->getParentPatternBinding(); if (singleUse && binding && binding->getNumPatternEntries() == 1 && varDecl->getTypeSourceRangeForDiagnostics().isInvalid()) { Expr *initializer = varDecl->getParentInitializer(); if (auto declRefExpr = dyn_cast(initializer)) { if (declRefExpr->getDecl()->getAttrs().hasAttribute()) { CS.TC.diagnose(declRefExpr->getLoc(), diag::unwrap_iuo_initializer, type); } } auto fnTy = AFD->getInterfaceType()->castTo(); bool voidReturn = fnTy->getResult()->isEqual(TupleType::getEmpty(CS.DC->getASTContext())); auto diag = CS.TC.diagnose(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(CS.TC, varDecl->getDeclContext(), initializer); offerForceUnwrapFixit(CS, initializer); } } } offerDefaultValueUnwrapFixit(CS.TC, CS.DC, expr); offerForceUnwrapFixit(CS, expr); return true; } bool MissingOptionalUnwrapFailure::diagnose() { if (hasComplexLocator()) return false; auto *anchor = getAnchor(); auto *unwrapped = anchor->getValueProvidingExpr(); auto type = getType(anchor)->getRValueType(); auto *tryExpr = dyn_cast(unwrapped); if (!tryExpr) return diagnoseUnwrap(getConstraintSystem(), unwrapped, type); emitDiagnostic(tryExpr->getTryLoc(), diag::missing_unwrap_optional_try, type) .fixItReplace({tryExpr->getTryLoc(), tryExpr->getQuestionLoc()}, "try!"); return true; }