//===--- MiscDiagnostics.cpp - AST-Level Diagnostics ----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2019 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 AST-level diagnostics. // //===----------------------------------------------------------------------===// #include "MiscDiagnostics.h" #include "TypeCheckAvailability.h" #include "TypeCheckConcurrency.h" #include "TypeCheckInvertible.h" #include "TypeChecker.h" #include "swift/AST/ASTBridging.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/AvailabilitySpec.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Expr.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/Pattern.h" #include "swift/AST/SemanticAttrs.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Defer.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" #include "swift/Parse/Lexer.h" #include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/IDETypeChecking.h" #include "clang/AST/DeclObjC.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/SaveAndRestore.h" #define DEBUG_TYPE "Sema" using namespace swift; using namespace constraints; /// Return true if this expression is an implicit promotion from T to T?. static Expr *isImplicitPromotionToOptional(Expr *E) { if (E->isImplicit()) if (auto IIOE = dyn_cast( E->getSemanticsProvidingExpr())) return IIOE->getSubExpr(); return nullptr; } /// Diagnose syntactic restrictions of expressions. /// /// - Module values may only occur as part of qualification. /// - Metatype names cannot generally be used as values: they need a "T.self" /// qualification unless used in narrow case (e.g. T() for construction). /// - '_' may only exist on the LHS of an assignment expression. /// - warn_unqualified_access values must not be accessed except via qualified /// lookup. /// - Partial application of some decls isn't allowed due to implementation /// limitations. /// - "&" (aka InOutExpressions) may only exist directly in function call /// argument lists. /// - 'self.init' and 'super.init' cannot be wrapped in a larger expression /// or statement. /// - Warn about promotions to optional in specific syntactic forms. /// - Error about collection literals that default to Any collections in /// invalid positions. /// - Marker protocols cannot occur as the type of an as? or is expression. /// - KeyPath expressions cannot refer to effectful properties / subscripts /// - SingleValueStmtExprs may only appear in certain places and has /// restrictions on the control flow allowed. /// - Move expressions must have a declref expr subvalue. /// static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, bool isExprStmt) { class DiagnoseWalker : public BaseDiagnosticWalker { SmallPtrSet AlreadyDiagnosedMetatypes; SmallPtrSet AlreadyDiagnosedBitCasts; bool IsExprStmt; ASTContext &Ctx; const DeclContext *DC; public: DiagnoseWalker(const DeclContext *DC, bool isExprStmt) : IsExprStmt(isExprStmt), Ctx(DC->getASTContext()), DC(DC) {} PreWalkAction walkToTypeReprPre(TypeRepr *T) override { return Action::Continue(); } PreWalkResult walkToExprPre(Expr *E) override { // See through implicit conversions of the expression. We want to be able // to associate the parent of this expression with the ultimate callee. auto Base = E; while (auto Conv = dyn_cast(Base)) Base = Conv->getSubExpr(); if (auto *DRE = dyn_cast(Base)) { // Verify metatype uses. if (isa(DRE->getDecl())) { if (isa(DRE->getDecl())) checkUseOfModule(DRE); else checkUseOfMetaTypeName(Base); } // Verify warn_unqualified_access uses. checkUnqualifiedAccessUse(DRE); // Verify that special decls are eliminated. checkForDeclWithSpecialTypeCheckingSemantics(DRE); // Verify that `unsafeBitCast` isn't misused. checkForSuspiciousBitCasts(DRE, nullptr); } if (auto *MRE = dyn_cast(Base)) { if (isa(MRE->getMember().getDecl())) checkUseOfMetaTypeName(Base); } // Don't diagnose a missing '.self' for type value expressions. if (isa(Base) && !isa(E)) checkUseOfMetaTypeName(Base); if (auto *KPE = dyn_cast(E)) checkForInvalidKeyPath(KPE); // Check function calls, looking through implicit conversions on the // function and inspecting the arguments directly. if (auto *Call = dyn_cast(E)) { // Warn about surprising implicit optional promotions. checkOptionalPromotions(Call); // Check the callee, looking through implicit conversions. auto base = Call->getFn(); unsigned uncurryLevel = 0; while (auto conv = dyn_cast(base)) base = conv->getSubExpr(); const auto findDynamicMemberRefExpr = [](Expr *e) -> DynamicMemberRefExpr* { if (auto open = dyn_cast(e)) { return dyn_cast(open->getSubExpr()); } return nullptr; }; if (auto force = dyn_cast(base)) { if (auto ref = findDynamicMemberRefExpr(force->getSubExpr())) base = ref; } else if (auto bind = dyn_cast(base)) { if (auto ref = findDynamicMemberRefExpr(bind->getSubExpr())) base = ref; } while (auto ignoredBase = dyn_cast(base)) base = ignoredBase->getRHS(); ConcreteDeclRef callee; if (auto *calleeDRE = dyn_cast(base)) { checkForSuspiciousBitCasts(calleeDRE, Call); callee = calleeDRE->getDeclRef(); // Otherwise, try to drill down through member calls for the purposes // of argument-matching code below. } else if (auto selfApply = dyn_cast(base)) { ++uncurryLevel; base = selfApply->getSemanticFn(); if (auto calleeDRE = dyn_cast(base)) callee = calleeDRE->getDeclRef(); // Otherwise, check for a dynamic member. } else if (auto dynamicMRE = dyn_cast(base)) { ++uncurryLevel; callee = dynamicMRE->getMember(); } if (callee) { auto *args = Call->getArgs(); for (auto idx : indices(*args)) { auto *arg = args->getExpr(idx); checkMagicIdentifierMismatch(callee, uncurryLevel, idx, arg); // InOutExprs can be wrapped in some implicit casts. Expr *unwrapped = arg; if (auto *IIO = dyn_cast(arg)) unwrapped = IIO->getSubExpr(); if (isa(unwrapped) || isa(unwrapped) || isa(unwrapped)) { auto operand = cast(unwrapped)->getSubExpr(); if (auto *IOE = dyn_cast(operand)) operand = IOE->getSubExpr(); // Also do some additional work based on how the function uses // the argument. checkConvertedPointerArgument(callee, uncurryLevel, idx, unwrapped, operand); } } } } // If we have an assignment expression, scout ahead for acceptable _'s. if (auto *AE = dyn_cast(E)) { auto destExpr = AE->getDest(); // If the user is assigning the result of a function that returns // Void to _ then warn, because that is redundant. if (auto DAE = dyn_cast(destExpr)) { if (auto CE = dyn_cast(AE->getSrc())) { if (getAsDecl( CE->getCalledValue(/*skipFunctionConversions=*/true)) && CE->getType()->isVoid()) { Ctx.Diags .diagnose(DAE->getLoc(), diag::discard_expr_void_result_redundant) .fixItRemoveChars(DAE->getStartLoc(), AE->getSrc()->getStartLoc()); } } } } // Diagnose 'self.init' or 'super.init' nested in another expression // or closure. if (auto *rebindSelfExpr = dyn_cast(E)) { if (!Parent.isNull() || !IsExprStmt || DC->getParent()->isLocalContext()) { bool isChainToSuper; (void)rebindSelfExpr->getCalledConstructor(isChainToSuper); Ctx.Diags.diagnose(E->getLoc(), diag::init_delegation_nested, isChainToSuper, !IsExprStmt); } } // Diagnose single-element tuple expressions. if (auto *tupleExpr = dyn_cast(E)) { if (tupleExpr->getNumElements() == 1 && !isa(tupleExpr->getElement(0))) { Ctx.Diags.diagnose(tupleExpr->getElementNameLoc(0), diag::tuple_single_element) .fixItRemoveChars(tupleExpr->getElementNameLoc(0), tupleExpr->getElement(0)->getStartLoc()); } } auto diagnoseDuplicateLabels = [&](SourceLoc loc, ArrayRef labels) { llvm::SmallDenseSet names; names.reserve(labels.size()); for (auto name : labels) { if (name.empty()) continue; auto inserted = names.insert(name).second; if (!inserted) { Ctx.Diags.diagnose(loc, diag::tuple_duplicate_label); return; } } }; // FIXME: Duplicate labels on enum payloads should be diagnosed // when declared, not when called. if (auto *CE = dyn_cast_or_null(E)) { auto calledValue = CE->getCalledValue(/*skipFunctionConversions=*/true); if (calledValue && isa(calledValue)) { auto *args = CE->getArgs(); SmallVector scratch; diagnoseDuplicateLabels(args->getLoc(), args->getArgumentLabels(scratch)); } } if (auto *tupleExpr = dyn_cast(E)) { // Diagnose tuple expressions with duplicate element label. diagnoseDuplicateLabels(tupleExpr->getLoc(), tupleExpr->getElementNames()); // Diagnose attempts to form a tuple with any noncopyable elements. if (E->getType()->isNoncopyable() && !Ctx.LangOpts.hasFeature(Feature::MoveOnlyTuples)) { auto noncopyableTy = E->getType(); assert(noncopyableTy->is() && "will use poor wording"); Ctx.Diags.diagnose(E->getLoc(), diag::tuple_containing_move_only_not_supported, noncopyableTy); } } // Specially diagnose some checked casts that are illegal. if (auto cast = dyn_cast(E)) { checkCheckedCastExpr(cast); } // Diagnose move expression uses where the sub expression is not a declref // expr. if (auto *consumeExpr = dyn_cast(E)) { checkConsumeExpr(consumeExpr); } // Diagnose copy expression uses where the sub expression is not a declref // expr. if (auto *copyExpr = dyn_cast(E)) { checkCopyExpr(copyExpr); } // Diagnose move expression uses where the sub expression is not a declref expr if (auto *borrowExpr = dyn_cast(E)) { checkBorrowExpr(borrowExpr); } return Action::Continue(E); } /// Visit each component of the keypath and emit a diagnostic if they /// refer to a member that meets any of the following: /// - has effects. /// - is a noncopyable type. void checkForInvalidKeyPath(KeyPathExpr *keyPath) { for (const auto &component : keyPath->getComponents()) { if (component.hasDeclRef()) { auto decl = component.getDeclRef().getDecl(); // Check for effects if (auto asd = dyn_cast(decl)) { if (asd->getEffectfulGetAccessor()) { Ctx.Diags.diagnose(component.getLoc(), diag::effectful_keypath_component, asd, /*dynamic member lookup*/ false); Ctx.Diags.diagnose(asd->getLoc(), diag::kind_declared_here, asd->getDescriptiveKind()); } } // Check for the ability to copy. if (component.getComponentType()->isNoncopyable()) { Ctx.Diags.diagnose(component.getLoc(), diag::expr_keypath_noncopyable_type, component.getComponentType()->getRValueType()); } } } } void checkCheckedCastExpr(CheckedCastExpr *cast) { Type castType = cast->getCastType(); if (!castType) return; if (castType->isNoncopyable()) { // can't cast anything to move-only; there should be no valid ones. Ctx.Diags.diagnose(cast->getLoc(), diag::noncopyable_cast); return; } // no support for runtime casts from move-only types. // as of now there is no type it could be cast to except itself, so // there's no reason for it to happen at runtime. if (auto fromType = cast->getSubExpr()->getType()) { if (fromType->isNoncopyable()) { // can't cast move-only to anything. Ctx.Diags.diagnose(cast->getLoc(), diag::noncopyable_cast); return; } } // now, look for conditional casts to marker protocols. if (!isa(cast) && !isa(cast)) return; if(!castType->isExistentialType()) return; auto layout = castType->getExistentialLayout(); for (auto proto : layout.getProtocols()) { if (proto->isMarkerProtocol() && !proto->getInvertibleProtocolKind()) { // can't conditionally cast to a marker protocol Ctx.Diags.diagnose(cast->getLoc(), diag::marker_protocol_cast, proto->getName()); } } } void checkConsumeExpr(ConsumeExpr *consumeExpr) { auto diags = findSyntacticErrorForConsume(DC->getParentModule(), consumeExpr->getLoc(), consumeExpr->getSubExpr()); for (auto &diag : diags) diag.emit(Ctx); // As of now, SE-366 is not correctly implemented (rdar://102780553), // so warn about certain consume's being no-ops today that will no longer // be a no-op in the future once we fix this. if (auto ty = consumeExpr->getType()) { bool shouldWarn = true; // Look through any load. auto *expr = consumeExpr->getSubExpr(); if (auto *load = dyn_cast(expr)) expr = load->getSubExpr(); // Don't warn if explicit ownership was provided on a parameter. // Those seem to be checked just fine in SIL. if (auto *declRef = dyn_cast(expr)) { if (auto *decl = declRef->getDecl()) { if (auto *paramDecl = dyn_cast(decl)) { switch (paramDecl->getSpecifier()) { case ParamSpecifier::InOut: case ParamSpecifier::Borrowing: case ParamSpecifier::Consuming: case ParamSpecifier::ImplicitlyCopyableConsuming: shouldWarn = false; break; case ParamSpecifier::Default: case ParamSpecifier::LegacyShared: case ParamSpecifier::LegacyOwned: break; // warn } } } } // Only warn about obviously concrete BitwiseCopyable types, since we // know those won't get checked for consumption. if (diags.empty() && shouldWarn && !ty->hasError() && !ty->hasTypeParameter() && !ty->hasUnboundGenericType() && !ty->hasArchetype()) { auto bitCopy = Ctx.getProtocol(KnownProtocolKind::BitwiseCopyable); if (checkConformance(ty, bitCopy)) { Ctx.Diags.diagnose(consumeExpr->getLoc(), diag::consume_of_bitwisecopyable_noop, ty) .fixItRemoveChars(consumeExpr->getStartLoc(), consumeExpr->getSubExpr()->getStartLoc()); } } } } void checkCopyExpr(CopyExpr *copyExpr) { // Do not allow for copy_expr to be used with pure move only types. We // /do/ allow it to be used with no implicit copy types though. if (copyExpr->getType()->isNoncopyable()) { Ctx.Diags.diagnose( copyExpr->getLoc(), diag::copy_expression_cannot_be_used_with_noncopyable_types); } // We only allow for copy_expr to be applied directly to lvalues. We do // not allow currently for it to be applied to fields. auto *subExpr = copyExpr->getSubExpr(); if (auto *li = dyn_cast(subExpr)) subExpr = li->getSubExpr(); if (!isa(subExpr)) { Ctx.Diags.diagnose(copyExpr->getLoc(), diag::copy_expression_not_passed_lvalue); } } void checkBorrowExpr(BorrowExpr *borrowExpr) { // Allow for a chain of member_ref exprs that end in a decl_ref expr. auto *subExpr = borrowExpr->getSubExpr(); while (auto *memberRef = dyn_cast(subExpr)) subExpr = memberRef->getBase(); if (!isa(subExpr)) { Ctx.Diags.diagnose(borrowExpr->getLoc(), diag::borrow_expression_not_passed_lvalue); } } static Expr *lookThroughArgument(Expr *arg) { while (1) { if (auto conv = dyn_cast(arg)) arg = conv->getSubExpr(); else if (auto *PE = dyn_cast(arg)) arg = PE->getSubExpr(); else break; } return arg; } void checkConvertedPointerArgument(ConcreteDeclRef callee, unsigned uncurryLevel, unsigned argIndex, Expr *pointerExpr, Expr *storage) { if (!isPointerIdentityArgument(callee, uncurryLevel, argIndex)) return; // Flag that the argument is non-accessing. if (auto inout = dyn_cast(pointerExpr)) { inout->setNonAccessing(true); } else if (auto array = dyn_cast(pointerExpr)) { array->setNonAccessing(true); } // TODO: warn if taking the address of 'storage' will definitely // yield a temporary address. } /// Is the given call argument, known to be of pointer type, just used /// for its pointer identity? bool isPointerIdentityArgument(ConcreteDeclRef ref, unsigned uncurryLevel, unsigned argIndex) { // FIXME: derive this from an attribute instead of hacking it based // on the target name! auto decl = ref.getDecl(); // Assume that == and != are non-accessing uses. if (decl->isOperator()) { auto op = decl->getBaseName(); if (op == "==" || op == "!=") return true; return false; } // NSObject.addObserver(_:forKeyPath:options:context:) if (uncurryLevel == 1 && argIndex == 3) { return decl->getName().isCompoundName("addObserver", { "", "forKeyPath", "options", "context" }); } // NSObject.removeObserver(_:forKeyPath:context:) if (uncurryLevel == 1 && argIndex == 2) { return decl->getName().isCompoundName("removeObserver", { "", "forKeyPath", "context" }); } return false; } /// We have a collection literal with a defaulted type, e.g. of [Any]. Emit /// an error if it was inferred to this type in an invalid context, which is /// one in which the parent expression is not itself a collection literal. void checkTypeDefaultedCollectionExpr(CollectionExpr *c) { // If the parent is a non-expression, or is not itself a literal, then // produce an error with a fixit to add the type as an explicit // annotation. if (c->getNumElements() == 0) Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_empty) .highlight(c->getSourceRange()); else { assert(c->getType()->hasTypeRepr() && "a defaulted type should always be printable"); Ctx.Diags .diagnose(c->getLoc(), diag::collection_literal_heterogeneous, c->getType()) .highlight(c->getSourceRange()) .fixItInsertAfter(c->getEndLoc(), " as " + c->getType()->getString()); } } void checkMagicIdentifierMismatch(ConcreteDeclRef callee, unsigned uncurryLevel, unsigned argIndex, Expr *arg) { // We only care about args in the arg list. if (uncurryLevel != (callee.getDecl()->hasCurriedSelf() ? 1 : 0)) return; // Get underlying params for both callee and caller, if declared. auto *calleeParam = getParameterAt(callee, argIndex); auto *callerParam = dyn_cast_or_null( arg->getReferencedDecl(/*stopAtParenExpr=*/true).getDecl() ); // (Otherwise, we don't need to do anything.) if (!calleeParam || !callerParam) return; auto calleeDefaultArg = getMagicIdentifierDefaultArgKind(calleeParam); auto callerDefaultArg = getMagicIdentifierDefaultArgKind(callerParam); // If one of the parameters doesn't have a default arg, or they're both // compatible, everything's fine. if (!calleeDefaultArg || !callerDefaultArg || areMagicIdentifiersCompatible(*calleeDefaultArg, *callerDefaultArg)) return; StringRef calleeDefaultArgString = MagicIdentifierLiteralExpr::getKindString(*calleeDefaultArg); StringRef callerDefaultArgString = MagicIdentifierLiteralExpr::getKindString(*callerDefaultArg); // Emit main warning Ctx.Diags.diagnose(arg->getLoc(), diag::default_magic_identifier_mismatch, callerParam->getName(), callerDefaultArgString, calleeParam->getName(), calleeDefaultArgString); // Add "change caller default arg" fixit SourceLoc callerDefaultArgLoc = callerParam->getStructuralDefaultExpr()->getLoc(); Ctx.Diags.diagnose(callerDefaultArgLoc, diag::change_caller_default_to_match_callee, callerParam->getName(), calleeDefaultArgString) .fixItReplace(callerDefaultArgLoc, calleeDefaultArgString); // Add "silence with parens" fixit Ctx.Diags.diagnose(arg->getLoc(), diag::silence_default_magic_identifier_mismatch) .fixItInsert(arg->getStartLoc(), "(") .fixItInsertAfter(arg->getEndLoc(), ")"); // Point to callee parameter Ctx.Diags.diagnose(calleeParam, diag::decl_declared_here, calleeParam); } std::optional getMagicIdentifierDefaultArgKind(const ParamDecl *param) { switch (param->getDefaultArgumentKind()) { #define MAGIC_IDENTIFIER(NAME, STRING) \ case DefaultArgumentKind::NAME: \ return MagicIdentifierLiteralExpr::Kind::NAME; #include "swift/AST/MagicIdentifierKinds.def" case DefaultArgumentKind::None: case DefaultArgumentKind::Normal: case DefaultArgumentKind::Inherited: case DefaultArgumentKind::NilLiteral: case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: case DefaultArgumentKind::StoredProperty: case DefaultArgumentKind::ExpressionMacro: return std::nullopt; } llvm_unreachable("Unhandled DefaultArgumentKind in " "getMagicIdentifierDefaultArgKind"); } static bool areMagicIdentifiersCompatible(MagicIdentifierLiteralExpr::Kind a, MagicIdentifierLiteralExpr::Kind b) { if (a == b) return true; // The rest of this handles special compatibility rules between the // `*SpelledAsFile` cases and various other File-related cases. // // The way we're going to do this is a bit magical. We will arrange the // cases in MagicIdentifierLiteralExpr::Kind so that they sort in // this order: // // #fileID < Swift 6 #file < #filePath < Swift 5 #file < others // // Before we continue, let's verify that this holds. using Kind = MagicIdentifierLiteralExpr::Kind; static_assert(Kind::FileID < Kind::FileIDSpelledAsFile, "#fileID < Swift 6 #file"); static_assert(Kind::FileIDSpelledAsFile < Kind::FilePath, "Swift 6 #file < #filePath"); static_assert(Kind::FilePath < Kind::FilePathSpelledAsFile, "#filePath < Swift 5 #file"); static_assert(Kind::FilePathSpelledAsFile < Kind::Line, "Swift 5 #file < #line"); static_assert(Kind::FilePathSpelledAsFile < Kind::Column, "Swift 5 #file < #column"); static_assert(Kind::FilePathSpelledAsFile < Kind::Function, "Swift 5 #file < #function"); static_assert(Kind::FilePathSpelledAsFile < Kind::DSOHandle, "Swift 5 #file < #dsohandle"); // The rules are all commutative, so we will take the greater of the two // kinds. auto maxKind = std::max(a, b); // Both Swift 6 #file and Swift 5 #file are greater than all of the cases // they're compatible with. So if `maxCase` is one of those two, the other // case must have been compatible with it! return maxKind == Kind::FileIDSpelledAsFile || maxKind == Kind::FilePathSpelledAsFile; } void checkUseOfModule(DeclRefExpr *E) { // Allow module values as a part of: // - ignored base expressions; // - expressions that failed to type check. if (auto *ParentExpr = Parent.getAsExpr()) { if (isa(ParentExpr) || isa(ParentExpr)) return; } Ctx.Diags.diagnose(E->getStartLoc(), diag::value_of_module_type); } // Diagnose metatype values that don't appear as part of a property, // method, or constructor reference. void checkUseOfMetaTypeName(Expr *E) { // If we've already checked this at a higher level, we're done. if (!AlreadyDiagnosedMetatypes.insert(E).second) return; DiagnosticBehavior behavior = DiagnosticBehavior::Error; if (auto *ParentExpr = Parent.getAsExpr()) { if (ParentExpr->isValidParentOfTypeExpr(E)) return; // In Swift < 6 warn about // - plain type name passed as an argument to a subscript, dynamic // subscript, or ObjC literal since it used to be accepted. // - member type expressions rooted on non-identifier types, e.g. // '[X].Y' since they used to be accepted without the '.self'. if (!Ctx.LangOpts.isSwiftVersionAtLeast(6)) { if (isa(ParentExpr) || isa(ParentExpr) || isa(ParentExpr)) { auto *argList = ParentExpr->getArgs(); assert(argList); if (argList->isUnlabeledUnary()) behavior = DiagnosticBehavior::Warning; } else if (auto *TE = dyn_cast(E)) { if (auto *QualIdentTR = dyn_cast_or_null( TE->getTypeRepr())) { if (!isa(QualIdentTR->getRoot())) { behavior = DiagnosticBehavior::Warning; } } } } } // Is this a protocol metatype? Ctx.Diags .diagnose(E->getStartLoc(), diag::value_of_metatype_type, behavior == DiagnosticBehavior::Warning) .limitBehavior(behavior); // Add fix-it to insert '()', only if this is a metatype of // non-existential type and has any initializers. bool isExistential = false; if (auto metaTy = E->getType()->getAs()) { auto instanceTy = metaTy->getInstanceType(); isExistential = instanceTy->isExistentialType(); if (!isExistential && instanceTy->mayHaveMembers() && !TypeChecker::lookupMember(const_cast(DC), instanceTy, DeclNameRef::createConstructor()).empty()) { Ctx.Diags.diagnose(E->getEndLoc(), diag::add_parens_to_type) .fixItInsertAfter(E->getEndLoc(), "()"); } } // Add fix-it to insert ".self". auto diag = Ctx.Diags.diagnose(E->getEndLoc(), diag::add_self_to_type); if (E->canAppendPostfixExpression()) { diag.fixItInsertAfter(E->getEndLoc(), ".self"); } else { diag.fixItInsert(E->getStartLoc(), "("); diag.fixItInsertAfter(E->getEndLoc(), ").self"); } } void checkUnqualifiedAccessUse(const DeclRefExpr *DRE) { const Decl *D = DRE->getDecl(); if (!D->getAttrs().hasAttribute()) return; if (auto *parentExpr = Parent.getAsExpr()) { if (auto *ignoredBase = dyn_cast(parentExpr)){ if (!ignoredBase->isImplicit()) return; } if (auto *calledBase = dyn_cast(parentExpr)) { if (!calledBase->isImplicit()) return; } } const auto *VD = cast(D); const TypeDecl *declParent = VD->getDeclContext()->getSelfNominalTypeDecl(); if (!declParent) { // If the declaration has been validated but not fully type-checked, // the attribute might be applied to something invalid. if (!VD->getDeclContext()->isModuleScopeContext()) return; declParent = VD->getDeclContext()->getParentModule(); } Ctx.Diags.diagnose(DRE->getLoc(), diag::warn_unqualified_access, VD, declParent); Ctx.Diags.diagnose(VD, diag::decl_declared_here, VD); if (VD->getDeclContext()->isTypeContext()) { Ctx.Diags.diagnose(DRE->getLoc(), diag::fix_unqualified_access_member) .fixItInsert(DRE->getStartLoc(), "self."); } DeclContext *topLevelSubcontext = DC->getModuleScopeContext(); auto descriptor = UnqualifiedLookupDescriptor( DeclNameRef(VD->getBaseName()), topLevelSubcontext, SourceLoc()); auto lookup = evaluateOrDefault(Ctx.evaluator, UnqualifiedLookupRequest{descriptor}, {}); // Group results by module. Pick an arbitrary result from each module. llvm::SmallDenseMap resultsByModule; for (auto &result : lookup) { const ValueDecl *value = result.getValueDecl(); resultsByModule.insert(std::make_pair(value->getModuleContext(),value)); } // Sort by module name. using ModuleValuePair = std::pair; SmallVector sortedResults{ resultsByModule.begin(), resultsByModule.end() }; llvm::array_pod_sort(sortedResults.begin(), sortedResults.end(), [](const ModuleValuePair *lhs, const ModuleValuePair *rhs) { return lhs->first->getName().compare(rhs->first->getName()); }); auto topLevelDiag = diag::fix_unqualified_access_top_level; if (sortedResults.size() > 1) topLevelDiag = diag::fix_unqualified_access_top_level_multi; for (const ModuleValuePair &pair : sortedResults) { SmallString<32> namePlusDot = pair.first->getName().str(); namePlusDot.push_back('.'); Ctx.Diags .diagnose(DRE->getLoc(), topLevelDiag, namePlusDot, pair.second, pair.first) .fixItInsert(DRE->getStartLoc(), namePlusDot); } } void checkForDeclWithSpecialTypeCheckingSemantics(const DeclRefExpr *DRE) { // Referencing type(of:) and other decls with special type-checking // behavior as functions is not implemented. Maybe we could wrap up the // special-case behavior in a closure someday... if (TypeChecker::getDeclTypeCheckingSemantics(DRE->getDecl()) != DeclTypeCheckingSemantics::Normal) { Ctx.Diags.diagnose(DRE->getLoc(), diag::unsupported_special_decl_ref, DRE->getDecl()->getBaseIdentifier()); } } enum BitcastableNumberKind { BNK_None = 0, BNK_Int8, BNK_Int16, BNK_Int32, BNK_Int64, BNK_Int, BNK_UInt8, BNK_UInt16, BNK_UInt32, BNK_UInt64, BNK_UInt, BNK_Float, BNK_Double, }; BitcastableNumberKind getBitcastableNumberKind(Type t) const { auto decl = t->getNominalOrBoundGenericNominal(); #define MATCH_DECL(type) \ if (decl == Ctx.get##type##Decl()) \ return BNK_##type; MATCH_DECL(Int8) MATCH_DECL(Int16) MATCH_DECL(Int32) MATCH_DECL(Int64) MATCH_DECL(Int) MATCH_DECL(UInt8) MATCH_DECL(UInt16) MATCH_DECL(UInt32) MATCH_DECL(UInt64) MATCH_DECL(UInt) MATCH_DECL(Float) MATCH_DECL(Double) #undef MATCH_DECL return BNK_None; } static constexpr unsigned BNKPair(BitcastableNumberKind a, BitcastableNumberKind b) { return (a << 8) | b; } void checkForSuspiciousBitCasts(DeclRefExpr *DRE, Expr *Parent = nullptr) { if (DRE->getDecl() != Ctx.getUnsafeBitCast()) return; if (DRE->getDeclRef().getSubstitutions().empty()) return; // Don't check the same use of unsafeBitCast twice. if (!AlreadyDiagnosedBitCasts.insert(DRE).second) return; auto subMap = DRE->getDeclRef().getSubstitutions(); auto fromTy = subMap.getReplacementTypes()[0]; auto toTy = subMap.getReplacementTypes()[1]; // Warn about `unsafeBitCast` formulations that are undefined behavior // or have better-defined alternative APIs that can be used instead. // If we have a parent ApplyExpr that calls bitcast, extract the argument // for fixits. Expr *subExpr = nullptr; CharSourceRange removeBeforeRange, removeAfterRange; if (auto apply = dyn_cast_or_null(Parent)) { subExpr = apply->getArgs()->getExpr(0); // Determine the fixit range from the start of the application to // the first argument, `unsafeBitCast(` removeBeforeRange = CharSourceRange(Ctx.SourceMgr, DRE->getLoc(), subExpr->getStartLoc()); // Determine the fixit range from the end of the first argument to // the end of the application, `, to: T.self)` removeAfterRange = CharSourceRange(Ctx.SourceMgr, Lexer::getLocForEndOfToken(Ctx.SourceMgr, subExpr->getEndLoc()), Lexer::getLocForEndOfToken(Ctx.SourceMgr, apply->getEndLoc())); } // Casting to the same type or a superclass is a no-op. if (toTy->isEqual(fromTy) || toTy->isExactSuperclassOf(fromTy)) { auto d = Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_is_no_op, fromTy, toTy); if (subExpr) { d.fixItRemoveChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd()) .fixItRemoveChars(removeAfterRange.getStart(), removeAfterRange.getEnd()); } return; } if (auto fromFnTy = fromTy->getAs()) { if (auto toFnTy = toTy->getAs()) { // Casting a nonescaping function to escaping is UB. // `withoutActuallyEscaping` ought to be used instead. if (fromFnTy->isNoEscape() && !toFnTy->isNoEscape()) { Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_away_noescape, fromTy, toTy); } // Changing function representation (say, to try to force a // @convention(c) function pointer to exist) is also unlikely to work. if (fromFnTy->getRepresentation() != toFnTy->getRepresentation()) { Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_change_function_rep, fromTy, toTy); } return; } } // Unchecked casting to a subclass is better done by unsafeDowncast. if (fromTy->isBindableToSuperclassOf(toTy)) { Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_downcast, fromTy, toTy) .fixItReplace(DRE->getNameLoc().getBaseNameLoc(), "unsafeDowncast"); return; } // Casting among pointer types should use the Unsafe*Pointer APIs for // rebinding typed memory or accessing raw memory instead. PointerTypeKind fromPTK, toPTK; Type fromPointee = fromTy->getAnyPointerElementType(fromPTK); Type toPointee = toTy->getAnyPointerElementType(toPTK); if (fromPointee && toPointee) { // Casting to a pointer to the same type or UnsafeRawPointer can use // normal initializers on the destination type. if (toPointee->isEqual(fromPointee) || isRawPointerKind(toPTK)) { auto d = Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_change_pointer_kind, fromTy, toTy, toTy->getStructOrBoundGenericStruct()->getName()); if (subExpr) { StringRef before, after; switch (toPTK) { case PTK_UnsafePointer: // UnsafePointer(mutablePointer) before = "UnsafePointer("; after = ")"; break; case PTK_UnsafeMutablePointer: case PTK_AutoreleasingUnsafeMutablePointer: before = "UnsafeMutablePointer(mutating: "; after = ")"; break; case PTK_UnsafeRawPointer: // UnsafeRawPointer(pointer) before = "UnsafeRawPointer("; after = ")"; break; case PTK_UnsafeMutableRawPointer: // UnsafeMutableRawPointer(mutating: rawPointer) before = fromPTK == PTK_UnsafeMutablePointer ? "UnsafeMutableRawPointer(" : "UnsafeMutableRawPointer(mutating: "; after = ")"; break; } d.fixItReplaceChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd(), before) .fixItReplaceChars(removeAfterRange.getStart(), removeAfterRange.getEnd(), after); } return; } // Casting to a different typed pointer type should use // withMemoryRebound. if (!isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK)) { Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_change_pointee_type, fromTy, toTy); return; } // Casting a raw pointer to a typed pointer should bind the memory // (or assume it's already bound). assert(isRawPointerKind(fromPTK) && !isRawPointerKind(toPTK) && "unhandled cast combo?!"); Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcasting_to_give_type_to_raw_pointer, fromTy, toTy); if (subExpr) { SmallString<64> fixitBuf; { llvm::raw_svector_ostream os(fixitBuf); os << ".assumingMemoryBound(to: "; toPointee->print(os); os << ".self)"; } Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcast_assume_memory_rebound, toPointee) .fixItRemoveChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd()) .fixItReplaceChars(removeAfterRange.getStart(), removeAfterRange.getEnd(), fixitBuf); fixitBuf.clear(); { llvm::raw_svector_ostream os(fixitBuf); os << ".bindMemory(to: "; toPointee->print(os); os << ".self, capacity: <""#capacity#"">)"; } Ctx.Diags.diagnose(DRE->getLoc(), diag::bitcast_bind_memory, toPointee) .fixItRemoveChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd()) .fixItReplaceChars(removeAfterRange.getStart(), removeAfterRange.getEnd(), fixitBuf); } return; } StringRef replaceBefore, replaceAfter; std::optional> diagID; SmallString<64> replaceBeforeBuf; // Bitcasting among numeric types should use `bitPattern:` initializers. auto fromBNK = getBitcastableNumberKind(fromTy); auto toBNK = getBitcastableNumberKind(toTy); if (fromBNK && toBNK) { switch (BNKPair(fromBNK, toBNK)) { // Combos that can be bitPattern-ed with a constructor case BNKPair(BNK_Int8, BNK_UInt8): case BNKPair(BNK_UInt8, BNK_Int8): case BNKPair(BNK_Int16, BNK_UInt16): case BNKPair(BNK_UInt16, BNK_Int16): case BNKPair(BNK_Int32, BNK_UInt32): case BNKPair(BNK_UInt32, BNK_Int32): case BNKPair(BNK_Int64, BNK_UInt64): case BNKPair(BNK_UInt64, BNK_Int64): case BNKPair(BNK_Int, BNK_UInt): case BNKPair(BNK_UInt, BNK_Int): case BNKPair(BNK_UInt32, BNK_Float): case BNKPair(BNK_UInt64, BNK_Double): diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: "; } replaceBefore = replaceBeforeBuf; replaceAfter = ")"; break; // Combos that can be bitPattern-ed with a constructor and sign flip case BNKPair(BNK_Int32, BNK_Float): case BNKPair(BNK_Int64, BNK_Double): diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: "; if (fromBNK == BNK_Int32) os << "UInt32(bitPattern: "; else os << "UInt64(bitPattern: "; } replaceBefore = replaceBeforeBuf; replaceAfter = "))"; break; // Combos that can be bitPattern-ed with a property case BNKPair(BNK_Float, BNK_UInt32): case BNKPair(BNK_Double, BNK_UInt64): diagID = diag::bitcasting_for_number_bit_pattern_property; replaceAfter = ".bitPattern"; break; // Combos that can be bitPattern-ed with a property and sign flip case BNKPair(BNK_Float, BNK_Int32): case BNKPair(BNK_Double, BNK_Int64): diagID = diag::bitcasting_for_number_bit_pattern_property; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: "; } replaceBefore = replaceBeforeBuf; replaceAfter = ")"; break; // Combos that can be bitPattern-ed with a constructor once (U)Int is // converted to a sized type. case BNKPair(BNK_UInt, BNK_Float): case BNKPair(BNK_Int, BNK_UInt32): case BNKPair(BNK_UInt, BNK_Int32): case BNKPair(BNK_Int, BNK_UInt64): case BNKPair(BNK_UInt, BNK_Int64): case BNKPair(BNK_UInt, BNK_Double): diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: "; if (fromBNK == BNK_Int) os << "Int"; else os << "UInt"; if (toBNK == BNK_Float || toBNK == BNK_Int32 || toBNK == BNK_UInt32) os << "32("; else os << "64("; } replaceBefore = replaceBeforeBuf; replaceAfter = "))"; break; case BNKPair(BNK_Int, BNK_Float): case BNKPair(BNK_Int, BNK_Double): diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: UInt"; if (toBNK == BNK_Float || toBNK == BNK_Int32 || toBNK == BNK_UInt32) os << "32(bitPattern: Int32("; else os << "64(bitPattern: Int64("; } replaceBefore = replaceBeforeBuf; replaceAfter = ")))"; break; // Combos that can be bitPattern-ed then converted from a sized type // to (U)Int. case BNKPair(BNK_Int32, BNK_UInt): case BNKPair(BNK_UInt32, BNK_Int): case BNKPair(BNK_Int64, BNK_UInt): case BNKPair(BNK_UInt64, BNK_Int): diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "("; if (toBNK == BNK_UInt) os << "UInt"; else os << "Int"; if (fromBNK == BNK_Int32 || fromBNK == BNK_UInt32) os << "32(bitPattern: "; else os << "64(bitPattern: "; } replaceBefore = replaceBeforeBuf; replaceAfter = "))"; break; case BNKPair(BNK_Float, BNK_UInt): case BNKPair(BNK_Double, BNK_UInt): diagID = diag::bitcasting_for_number_bit_pattern_property; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "("; } replaceBefore = replaceBeforeBuf; replaceAfter = ".bitPattern)"; break; case BNKPair(BNK_Float, BNK_Int): case BNKPair(BNK_Double, BNK_Int): diagID = diag::bitcasting_for_number_bit_pattern_property; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: UInt("; } replaceBefore = replaceBeforeBuf; replaceAfter = ".bitPattern))"; break; // Combos that should be done with a value-preserving initializer. case BNKPair(BNK_Int, BNK_Int32): case BNKPair(BNK_Int, BNK_Int64): case BNKPair(BNK_UInt, BNK_UInt32): case BNKPair(BNK_UInt, BNK_UInt64): case BNKPair(BNK_Int32, BNK_Int): case BNKPair(BNK_Int64, BNK_Int): case BNKPair(BNK_UInt32, BNK_UInt): case BNKPair(BNK_UInt64, BNK_UInt): diagID = diag::bitcasting_to_change_from_unsized_to_sized_int; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << '('; } replaceBefore = replaceBeforeBuf; replaceAfter = ")"; break; default: // Leave other combos alone. break; } } // Casting a pointer to an int or back should also use bitPattern // initializers. if (fromPointee && toBNK) { switch (toBNK) { case BNK_UInt: case BNK_Int: diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: "; } replaceBefore = replaceBeforeBuf; replaceAfter = ")"; break; case BNK_UInt64: case BNK_UInt32: case BNK_Int64: case BNK_Int32: diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << '('; if (toBNK == BNK_UInt32 || toBNK == BNK_UInt64) os << "UInt(bitPattern: "; else os << "Int(bitPattern: "; } replaceBefore = replaceBeforeBuf; replaceAfter = "))"; break; default: break; } } if (fromBNK && toPointee) { switch (fromBNK) { case BNK_UInt: case BNK_Int: diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: "; } replaceBefore = replaceBeforeBuf; replaceAfter = ")"; break; case BNK_UInt64: case BNK_UInt32: case BNK_Int64: case BNK_Int32: diagID = diag::bitcasting_for_number_bit_pattern_init; { llvm::raw_svector_ostream os(replaceBeforeBuf); toTy->print(os); os << "(bitPattern: "; if (fromBNK == BNK_Int32 || fromBNK == BNK_Int64) os << "Int("; else os << "UInt("; } replaceBefore = replaceBeforeBuf; replaceAfter = "))"; break; default: break; } } if (diagID) { auto d = Ctx.Diags.diagnose(DRE->getLoc(), *diagID, fromTy, toTy); if (subExpr) { d.fixItReplaceChars(removeBeforeRange.getStart(), removeBeforeRange.getEnd(), replaceBefore); d.fixItReplaceChars(removeAfterRange.getStart(), removeAfterRange.getEnd(), replaceAfter); } } } /// Return true if this is a 'nil' literal. This looks /// like this if the type is Optional: /// /// (dot_syntax_call_expr type='String?' /// (declref_expr type='(Optional.Type) -> Optional' /// decl=Swift.(file).Optional.none function_ref=unapplied) /// (argument_list implicit /// (argument /// (type_expr implicit type='String?.Type' typerepr='String?')))) /// /// Or like this if it is any other ExpressibleByNilLiteral type: /// /// (nil_literal_expr) /// bool isTypeCheckedOptionalNil(Expr *E) { if (dyn_cast(E)) return true; if (auto *DSCE = dyn_cast_or_null(E->getSemanticsProvidingExpr())) { if (auto *DRE = dyn_cast(DSCE->getSemanticFn())) return DRE->getDecl() == Ctx.getOptionalNoneDecl(); } return false; } /// Warn about surprising implicit optional promotions involving operands to /// calls. Specifically, we warn about these expressions when the 'x' /// operand is implicitly promoted to optional: /// /// x ?? y /// x == nil // also != /// void checkOptionalPromotions(ApplyExpr *call) { // We only care about binary expressions. auto *BE = dyn_cast(call); if (!BE) return; // Dig out the function we're calling. auto fnExpr = call->getSemanticFn(); if (auto dotSyntax = dyn_cast(fnExpr)) fnExpr = dotSyntax->getSemanticFn(); if (auto *FCE = dyn_cast(fnExpr)) fnExpr = FCE->getSubExpr(); auto DRE = dyn_cast(fnExpr); if (!DRE || !DRE->getDecl()->isOperator()) return; auto lhs = BE->getLHS(); auto rhs = BE->getRHS(); auto calleeName = DRE->getDecl()->getBaseName(); Expr *subExpr = nullptr; if (calleeName == "??" && (subExpr = isImplicitPromotionToOptional(lhs))) { Ctx.Diags .diagnose(DRE->getLoc(), diag::use_of_qq_on_non_optional_value, subExpr->getType()) .highlight(lhs->getSourceRange()) .fixItRemoveChars( Lexer::getLocForEndOfToken(Ctx.SourceMgr, lhs->getEndLoc()), Lexer::getLocForEndOfToken(Ctx.SourceMgr, rhs->getEndLoc())); return; } if (calleeName == "==" || calleeName == "!=" || calleeName == "===" || calleeName == "!==") { if (((subExpr = isImplicitPromotionToOptional(lhs)) && isTypeCheckedOptionalNil(rhs)) || (isTypeCheckedOptionalNil(lhs) && (subExpr = isImplicitPromotionToOptional(rhs)))) { bool isTrue = calleeName == "!=" || calleeName == "!=="; bool isNilLiteral = isa(lhs) || isa(rhs); Ctx.Diags.diagnose(DRE->getLoc(), diag::nonoptional_compare_to_nil, subExpr->getType(), isNilLiteral, isTrue) .highlight(lhs->getSourceRange()) .highlight(rhs->getSourceRange()); return; } } } }; DiagnoseWalker Walker(DC, isExprStmt); const_cast(E)->walk(Walker); // Diagnose uses of collection literals with defaulted types at the top // level. if (auto collection = dyn_cast(E->getSemanticsProvidingExpr())) { if (collection->isTypeDefaulted()) { Walker.checkTypeDefaultedCollectionExpr( const_cast(collection)); } } } DeferredDiags swift::findSyntacticErrorForConsume( ModuleDecl *module, SourceLoc loc, Expr *subExpr, llvm::function_ref getType) { assert(!isa(subExpr) && "operates on the sub-expr of a consume"); DeferredDiags result; const bool noncopyable = getType(subExpr)->isNoncopyable(); bool partial = false; Expr *current = subExpr; while (current) { if (isa(current)) { if (partial & !noncopyable) result.emplace_back(loc, diag::consume_expression_partial_copyable); // The chain of member_ref_exprs and load_exprs terminates at a // declref_expr. This is legal. break; } // Look through loads. if (auto *le = dyn_cast(current)) { current = le->getSubExpr(); continue; } auto *mre = dyn_cast(current); if (mre) { auto *vd = dyn_cast(mre->getMember().getDecl()); if (!vd) { result.emplace_back(loc, diag::consume_expression_non_storage); break; } partial = true; AccessStrategy strategy = vd->getAccessStrategy(mre->getAccessSemantics(), AccessKind::Read, module, ResilienceExpansion::Minimal, /*useOldABI=*/false); if (strategy.getKind() != AccessStrategy::Storage) { if (noncopyable) { result.emplace_back(loc, diag::consume_expression_non_storage); result.emplace_back(mre->getLoc(), diag::note_consume_expression_non_storage_property); break; } result.emplace_back(loc, diag::consume_expression_partial_copyable); break; } current = mre->getBase(); continue; } auto *ce = dyn_cast(current); if (ce) { if (noncopyable) { result.emplace_back(loc, diag::consume_expression_non_storage); result.emplace_back(ce->getLoc(), diag::note_consume_expression_non_storage_call); break; } result.emplace_back(loc, diag::consume_expression_partial_copyable); break; } auto *se = dyn_cast(current); if (se) { if (noncopyable) { result.emplace_back(loc, diag::consume_expression_non_storage); result.emplace_back(se->getLoc(), diag::note_consume_expression_non_storage_subscript); break; } result.emplace_back(loc, diag::consume_expression_partial_copyable); break; } result.emplace_back(loc, diag::consume_expression_not_passed_lvalue); break; } return result; } /// Diagnose recursive use of properties within their own accessors static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) { auto fn = dyn_cast(DC); if (!fn) return; auto var = dyn_cast(fn->getStorage()); if (!var) // Ignore subscripts return; class DiagnoseWalker : public BaseDiagnosticWalker { ASTContext &Ctx; VarDecl *Var; const AccessorDecl *Accessor; public: explicit DiagnoseWalker(VarDecl *var, const AccessorDecl *Accessor) : Ctx(var->getASTContext()), Var(var), Accessor(Accessor) {} /// Return true if this is an implicit reference to self. static bool isImplicitSelfUse(Expr *E) { auto *DRE = dyn_cast(E); return DRE && DRE->isImplicit() && isa(DRE->getDecl()) && cast(DRE->getDecl())->isSelfParameter(); } PreWalkResult walkToExprPre(Expr *E) override { Expr *subExpr; bool isStore = false; if (auto *AE = dyn_cast(E)) { subExpr = AE->getDest(); // If we couldn't flatten this expression, don't explode. if (!subExpr) return Action::Continue(E); isStore = true; } else if (auto *IOE = dyn_cast(E)) { subExpr = IOE->getSubExpr(); isStore = true; } else { subExpr = E; } if (auto *BOE = dyn_cast(subExpr)) subExpr = BOE; if (auto *DRE = dyn_cast(subExpr)) { if (DRE->getDecl() == Var) { // Handle local and top-level computed variables. if (DRE->getAccessSemantics() == AccessSemantics::Ordinary) { bool shouldDiagnose = false; // Warn about any property access in the getter. if (Accessor->isGetter()) shouldDiagnose = !isStore; // Warn about stores in the setter, but allow loads. if (Accessor->isSetter()) shouldDiagnose = isStore; // But silence the warning if the base was explicitly qualified. auto parentAsExpr = Parent.getAsExpr(); if (isa_and_nonnull(parentAsExpr)) shouldDiagnose = false; if (shouldDiagnose) { Ctx.Diags.diagnose(subExpr->getLoc(), diag::recursive_accessor_reference, Var->getName(), Accessor->isSetter()); } } // If this is a direct store in a "willSet", we reject this because // it is about to get overwritten. if (isStore && DRE->getAccessSemantics() == AccessSemantics::DirectToStorage && Accessor->getAccessorKind() == AccessorKind::WillSet) { Ctx.Diags.diagnose(E->getLoc(), diag::store_in_willset, Var->getName()); } } } else if (auto *MRE = dyn_cast(subExpr)) { // Handle instance and type computed variables. // Find MemberRefExprs that have an implicit "self" base. if (MRE->getMember().getDecl() == Var && isa(MRE->getBase()) && isImplicitSelfUse(MRE->getBase())) { if (MRE->getAccessSemantics() == AccessSemantics::Ordinary) { bool shouldDiagnose = false; // Warn about any property access in the getter. if (Accessor->isGetter()) shouldDiagnose = !isStore; // Warn about stores in the setter, but allow loads. if (Accessor->isSetter()) shouldDiagnose = isStore; if (shouldDiagnose) { Ctx.Diags.diagnose(subExpr->getLoc(), diag::recursive_accessor_reference, Var->getName(), Accessor->isSetter()); Ctx.Diags.diagnose(subExpr->getLoc(), diag::recursive_accessor_reference_silence) .fixItInsert(subExpr->getStartLoc(), "self."); } } // If this is a direct store in a "willSet", we reject this because // it is about to get overwritten. if (isStore && MRE->getAccessSemantics() == AccessSemantics::DirectToStorage && Accessor->getAccessorKind() == AccessorKind::WillSet) { Ctx.Diags.diagnose(subExpr->getLoc(), diag::store_in_willset, Var->getName()); } } } return Action::Continue(E); } }; DiagnoseWalker walker(var, fn); const_cast(E)->walk(walker); } /// The `weak self` capture of this closure if present static VarDecl *weakSelfCapture(const AbstractClosureExpr *ACE) { if (auto closureExpr = dyn_cast(ACE)) { if (auto selfDecl = closureExpr->getCapturedSelfDecl()) { if (selfDecl->getInterfaceType()->is()) { return selfDecl; } } } return nullptr; } /// Whether or not this closure captures self weakly static bool closureHasWeakSelfCapture(const AbstractClosureExpr *ACE) { return weakSelfCapture(ACE) != nullptr; } // Returns true if this is an implicit self expr static bool isImplicitSelf(const Expr *E) { auto *DRE = dyn_cast(E); if (!DRE || !DRE->isImplicit()) return false; ASTContext &Ctx = DRE->getDecl()->getASTContext(); return DRE->getDecl()->getName().isSimpleName(Ctx.Id_self); } namespace { class ImplicitSelfUsageChecker : public BaseDiagnosticWalker { ASTContext &Ctx; SmallVector Closures; /// A list of "implicit self" exprs from shorthand conditions /// like `if let self` or `guard let self`. These conditions /// have an RHS 'self' decl that is implicit, but this is not /// the sort of "implicit self" decl that should trigger /// these diagnostics. SmallPtrSet UnwrapStmtImplicitSelfExprs; /// The number of parent macros. unsigned MacroDepth = 0; public: explicit ImplicitSelfUsageChecker(ASTContext &Ctx, AbstractClosureExpr *ACE) : Ctx(Ctx), Closures() { if (ACE) Closures.push_back(ACE); } bool isInMacro() const { return MacroDepth > 0; } static bool implicitWeakSelfReferenceIsValid510(const DeclRefExpr *DRE, const AbstractClosureExpr *inClosure) { ASTContext &Ctx = DRE->getDecl()->getASTContext(); // Check if the implicit self decl refers to a var in a conditional stmt LabeledConditionalStmt *conditionalStmt = nullptr; if (auto var = dyn_cast(DRE->getDecl())) { if (auto parentStmt = var->getParentPatternStmt()) { conditionalStmt = dyn_cast(parentStmt); } } if (!conditionalStmt) { return false; } // Require `LoadExpr`s when validating the self binding. // This lets us reject invalid examples like: // // let `self` = self ?? .somethingElse // guard let self = self else { return } // method() // <- implicit self is not allowed // return conditionalStmt->rebindsSelf(Ctx, /*requiresCaptureListRef*/ false, /*requireLoadExpr*/ true); } static bool isEnclosingSelfReference510(VarDecl *var, const AbstractClosureExpr *inClosure) { if (var->isSelfParameter()) return true; // Capture variables have a DC of the parent function. if (inClosure && var->isSelfParamCapture() && var->getDeclContext() != inClosure->getParent()) return true; return false; } static bool selfDeclAllowsImplicitSelf510(DeclRefExpr *DRE, Type ty, const AbstractClosureExpr *inClosure) { // If this is an explicit `weak self` capture, then implicit self is // allowed once the closure's self param is unwrapped. We need to validate // that the unwrapped `self` decl specifically refers to an unwrapped copy // of the closure's `self` param, and not something else like in `guard // let self = .someOptionalVariable else { return }` or `let self = // someUnrelatedVariable`. If self hasn't been unwrapped yet and is still // an optional, we would have already hit an error elsewhere. if (closureHasWeakSelfCapture(inClosure)) { return implicitWeakSelfReferenceIsValid510(DRE, inClosure); } // Metatype self captures don't extend the lifetime of an object. if (ty->is()) return true; // If self does not have reference semantics, it is very unlikely that // capturing it will create a reference cycle. if (!ty->hasReferenceSemantics()) return true; if (auto closureExpr = dyn_cast(inClosure)) { if (auto selfDecl = closureExpr->getCapturedSelfDecl()) { // If this capture is using the name `self` actually referring // to some other variable (e.g. with `[self = "hello"]`) // then implicit self is not allowed. if (!selfDecl->isSelfParamCapture()) { return false; } } } if (auto var = dyn_cast(DRE->getDecl())) { if (!isEnclosingSelfReference510(var, inClosure)) { return true; } } return false; } /// Whether or not implicit self is allowed for self decl static bool selfDeclAllowsImplicitSelf(Expr *E, const AbstractClosureExpr *inClosure) { if (!isImplicitSelf(E)) { return true; } auto *DRE = cast(E); // Defensive check for type. If the expression doesn't have type here, it // should have been diagnosed somewhere else. Type ty = DRE->getType(); assert(ty && "Implicit self parameter ref without type"); if (!ty) return true; // Prior to Swift 6, use the old validation logic. auto &ctx = inClosure->getASTContext(); if (!ctx.isSwiftVersionAtLeast(6)) return selfDeclAllowsImplicitSelf510(DRE, ty, inClosure); return selfDeclAllowsImplicitSelf(DRE->getDecl(), ty, inClosure, /*isValidatingParentClosures:*/ false); } /// Whether or not implicit self is allowed for this implicit self decl static bool selfDeclAllowsImplicitSelf(const ValueDecl *selfDecl, const Type captureType, const AbstractClosureExpr *inClosure, bool isValidatingParentClosures) { ASTContext &ctx = inClosure->getASTContext(); auto requiresSelfQualification = isClosureRequiringSelfQualification(inClosure); // Metatype self captures don't extend the lifetime of an object. if (captureType->is()) { requiresSelfQualification = false; } // If self does not have reference semantics, it is very unlikely that // capturing it will create a reference cycle. if (!captureType->hasReferenceSemantics()) { requiresSelfQualification = false; } if (auto closureExpr = dyn_cast(inClosure)) { auto capturedSelfDecl = closureExpr->getCapturedSelfDecl(); // If this closure doesn't capture self explicitly, but this closure // requires self qualification, then implicit self is disallowed. if (!capturedSelfDecl && requiresSelfQualification) { return false; } // If the closure has an explicit capture using the name `self` that // actually refers to some other variable (e.g. `[self = "hello"]`) // then implicit self is not allowed. if (capturedSelfDecl && !isSimpleSelfCapture(capturedSelfDecl)) { return false; } } // If the self decl refers to a weak capture, then implicit self is not // allowed. Self must be unwrapped in a `guard let self` / `if let self` // condition first. This is usually enforced by the type checker, but // isn't in some cases (e.g. when calling a method on `Optional`). // - When validating implicit self usage in a nested closure, it's not // necessary for self to be unwrapped in this parent closure. If self // isn't unwrapped correctly in the nested closure, we would have // already noticed when validating that closure. if (selfDecl->getInterfaceType()->is() && !isValidatingParentClosures) { return false; } // If the self decl refers to an invalid unwrapping conditon like // `guard let self = somethingOtherThanSelf`, then implicit self is always // disallowed. // - Even if this closure doesn't have a `weak self` capture, it could // be a closure nested in some parent closure with a `weak self` // capture, so we should always validate the conditional statement // that defines self if present. if (auto condStmt = parentConditionalStmt(selfDecl)) { if (!hasValidSelfRebinding(condStmt, ctx)) { return false; } } if (auto autoclosure = dyn_cast(inClosure)) { // Implicit self is always allowed in autoclosure thunks generated // during type checking. An example of this is when storing an instance // method as a closure (e.g. `let closure = someInstanceMethodOnSelf`). auto thunkKind = autoclosure->getThunkKind(); if (thunkKind == AutoClosureExpr::Kind::SingleCurryThunk || thunkKind == AutoClosureExpr::Kind::DoubleCurryThunk) { return true; } // Explicit self is required in escaping autoclosures if (requiresSelfQualification) { return false; } } // Lastly, validate that there aren't any parent closures // with invalid self bindings that should disable implicit // self for all nested closures. // - We have to do this for all closures, even closures that typically // don't require self qualificationm since an invalid self capture in // a parent closure still disallows implicit self in a nested closure. if (!isValidatingParentClosures) { return !implicitSelfDisallowedDueToInvalidParent(selfDecl, captureType, inClosure); } else { return true; } } static bool implicitSelfDisallowedDueToInvalidParent( const ValueDecl *selfDecl, const Type captureType, const AbstractClosureExpr *inClosure) { return parentClosureDisallowingImplicitSelf(selfDecl, captureType, inClosure) != nullptr; } static const AbstractClosureExpr * parentClosureDisallowingImplicitSelf(const ValueDecl *selfDecl, const Type captureType, const AbstractClosureExpr *inClosure) { // Find the outer decl that determines what self refers to in this // closure. // - If this is an escaping closure that captured self, then `selfDecl` // refers to the self capture. // - If this is a nonescaping closure then there is no capture, // so selfDecl already comes from an outer context. const ValueDecl *outerSelfDecl = selfDecl; if (auto closureExpr = dyn_cast(inClosure)) { if (auto capturedSelfDecl = closureExpr->getCapturedSelfDecl()) { // Retrieve the outer decl that the self capture refers to. outerSelfDecl = getParentInitializerDecl(capturedSelfDecl); } } if (!outerSelfDecl) { return nullptr; } // Find the closest parent closure that contains the outer self decl, // potentially also validating all intermediate closures. auto outerClosure = inClosure; bool validateIntermediateParents = true; while (true) { // We have to validate all intermediate parent closures // to prevent cases like this from succeeding, which is // invalid because the outer closure doesn't have an // explicit self capture: // // withEscaping { // withNonEscaping { // x += 1 // not allowed // } // } // // On the other hand, we need to support cases like this. // As long as the inner closure has an explicit capture, // it is not necessary for outer closures to also have // an explicit self capture: // // withEscaping { // withEscaping { [self] in // x += 1 // ok // } // } // // So if we reach a closure with an explicit self capture, // we no longer need to validate each intermediate closure // (but we still have to validate the outer closure that // contains the outer self delf). if (auto closureExpr = dyn_cast(outerClosure)) { if (closureExpr->getCapturedSelfDecl()) { validateIntermediateParents = false; } } outerClosure = parentClosure(outerClosure); if (!outerClosure) { // Once we reach a parent context that isn't a closure, // the only valid self capture is the self parameter. // This disallows cases like: // // let `self` = somethingElse // withEscaping { [self] in // method() // } // auto VD = dyn_cast(outerSelfDecl); if (!VD) { return inClosure; } if (!VD->isSelfParameter()) { return inClosure; } return nullptr; } // Check if this closure contains the self decl. // - If the self decl is defined in the closure's body, its // decl context will be the closure itself. // - If the self decl is defined in the closure's capture list, // its parent capture list will reference the closure. auto selfDeclInOuterClosureContext = outerSelfDecl->getDeclContext() == outerClosure; auto selfDeclInOuterClosureCaptureList = false; if (auto selfVD = dyn_cast(outerSelfDecl)) { if (auto captureList = selfVD->getParentCaptureList()) { selfDeclInOuterClosureCaptureList = captureList->getClosureBody() == outerClosure; } } // We can stop searching because we found the first outer closure // that contains the outer self decl. Otherwise we continue searching // any parent closures in the next loop iteration. if (selfDeclInOuterClosureContext || selfDeclInOuterClosureCaptureList) { // Check whether implicit self is disallowed due to this specific // closure, or if its disallowed due to some parent of this closure, // so we can return the specific closure that is invalid. // // If this is a `weak self` capture, we don't need to validate that // that capture has been unwrapped in a `let self = self` binding // within the parent closure. A self rebinding in this inner closure // is sufficient to enable implicit self. if (!selfDeclAllowsImplicitSelf(outerSelfDecl, captureType, outerClosure, /*isValidatingParentClosures:*/ true)) { return outerClosure; } return parentClosureDisallowingImplicitSelf(outerSelfDecl, captureType, outerClosure); } // Optionally validate this intermediate closure before continuing // to search upwards. Since we're already validating the chain of // parent closures, we don't need to do that separate for this closure. if (validateIntermediateParents) { if (!selfDeclAllowsImplicitSelf(selfDecl, captureType, outerClosure, /*isValidatingParentClosures*/ true)) { return outerClosure; } } } } static bool hasValidSelfRebinding(const LabeledConditionalStmt *conditionalStmt, ASTContext &ctx) { if (!conditionalStmt) { return false; } // Require that the RHS of the `let self = self` condition // refers to a variable defined in a capture list. // This lets us reject invalid examples like: // // var `self` = self ?? .somethingElse // guard let self = self else { return } // method() // <- implicit self is not allowed // return conditionalStmt->rebindsSelf(ctx, /*requiresCaptureListRef*/ true); } /// The `LabeledConditionalStmt` that contains the given `ValueDecl` if /// present static LabeledConditionalStmt * parentConditionalStmt(const ValueDecl *selfDecl) { if (!selfDecl) { return nullptr; } if (auto var = dyn_cast(selfDecl)) { if (auto parentStmt = var->getParentPatternStmt()) { return dyn_cast(parentStmt); } } return nullptr; } /// Determines whether or not this is a simple self capture by retreiving /// the `CaptureListEntry` that contains the `selfDecl`. /// - Unlike `selfDecl->isSelfParamCapture()`, this will return true /// for a simple `[weak self]` capture. static bool isSimpleSelfCapture(const VarDecl *selfDecl) { if (!selfDecl) { return false; } auto captureList = selfDecl->getParentCaptureList(); if (!captureList) { return false; } for (auto capture : captureList->getCaptureList()) { if (capture.getVar() == selfDecl) { return capture.isSimpleSelfCapture(/*excludeWeakCaptures:*/ false); } } return false; } // Given a `self` decl that is a closure's `self` capture, // retrieves and returns the decl that the capture refers to. static ValueDecl *getParentInitializerDecl(const VarDecl *selfDecl) { if (!selfDecl) { return nullptr; } auto captureList = selfDecl->getParentCaptureList(); if (!captureList) { return nullptr; } for (auto capture : captureList->getCaptureList()) { if (capture.getVar() == selfDecl && capture.PBD) { // We've found the `CaptureListEntry` that contains the `self` // capture, now we can retrieve and inspect its parent initializer. auto index = capture.PBD->getPatternEntryIndexForVarDecl(selfDecl); auto parentInitializer = capture.PBD->getInit(index); // Look through implicit conversions like `InjectIntoOptionalExpr` if (auto implicitConversion = dyn_cast_or_null(parentInitializer)) { parentInitializer = implicitConversion->getSubExpr(); } auto DRE = dyn_cast_or_null(parentInitializer); if (!DRE) { return nullptr; } return DRE->getDecl(); } } return nullptr; } /// Return true if this is a closure expression that will require explicit /// use or capture of "self." for qualification of member references. static bool isClosureRequiringSelfQualification(const AbstractClosureExpr *CE, bool ignoreWeakSelf = false) { if (!ignoreWeakSelf && closureHasWeakSelfCapture(CE)) { return true; } // If the closure's type was inferred to be noescape, then it doesn't // need qualification. if (isNonEscaping(CE)) { return false; } if (auto autoclosure = dyn_cast(CE)) { if (autoclosure->getThunkKind() == AutoClosureExpr::Kind::AsyncLet) return false; } // If the closure was used in a context where it's explicitly stated // that it does not need "self." qualification, don't require it. if (auto closure = dyn_cast(CE)) { if (closure->allowsImplicitSelfCapture()) return false; } return true; } static bool isNonEscaping(const AbstractClosureExpr *ACE) { if (auto funcTy = ACE->getType()->getAs()) { return funcTy->isNoEscape(); } return false; } /// The closure that is a parent of this closure, if present static const ClosureExpr *parentClosure(const AbstractClosureExpr *closure) { auto parentContext = closure->getParent(); if (!parentContext) { return nullptr; } return parentContext->getInnermostClosureForCaptures(); } bool shouldRecordClosure(const AbstractClosureExpr *E) { // Record all closures in Swift 6 mode. if (Ctx.isSwiftVersionAtLeast(6)) return true; // Only record closures requiring self qualification prior to Swift 6 // mode. return isClosureRequiringSelfQualification(E); } template InFlightDiagnostic diagnoseImplicitSelfUse( SourceLoc loc, Expr *base, AbstractClosureExpr *closure, Diag ID, typename detail::PassArgument::type... Args) { std::optional warnUntilVersion; // Prior to Swift 6, we may need to downgrade to a warning for compatibility // with the 5.10 diagnostic behavior. if (!Ctx.isSwiftVersionAtLeast(6) && invalidImplicitSelfShouldOnlyWarn510(base, closure)) { warnUntilVersion.emplace(6); } // Prior to Swift 7, downgrade to a warning if we're in a macro to preserve // compatibility with the Swift 6 diagnostic behavior where we previously // skipped diagnosing. if (!Ctx.isSwiftVersionAtLeast(7) && isInMacro()) warnUntilVersion.emplace(7); auto diag = Ctx.Diags.diagnose(loc, ID, std::move(Args)...); if (warnUntilVersion) diag.warnUntilSwiftVersion(*warnUntilVersion); return diag; } PreWalkResult walkToExprPre(Expr *E) override { if (isa(E)) MacroDepth += 1; if (auto *CE = dyn_cast(E)) { if (shouldRecordClosure(CE)) Closures.push_back(CE); } // If we aren't in a closure, no diagnostics will be produced. if (Closures.size() == 0) return Action::Continue(E); // Diagnostics should correct the innermost closure auto *ACE = Closures[Closures.size() - 1]; assert(ACE); auto &Diags = Ctx.Diags; // If this is an "implicit self" expr from the RHS of a shorthand // condition like `guard let self` or `if let self`, then this is // always allowed and we shouldn't run any diagnostics. if (UnwrapStmtImplicitSelfExprs.count(E)) { return Action::Continue(E); } SourceLoc memberLoc = SourceLoc(); const DeclRefExpr *selfDRE = nullptr; if (auto *MRE = dyn_cast(E)) if (!selfDeclAllowsImplicitSelf(MRE->getBase(), ACE)) { selfDRE = dyn_cast_or_null(MRE->getBase()); auto baseName = MRE->getMember().getDecl()->getBaseName(); memberLoc = MRE->getLoc(); diagnoseImplicitSelfUse( memberLoc, MRE->getBase(), ACE, diag::property_use_in_closure_without_explicit_self, baseName.getIdentifier()); } // Handle method calls with a specific diagnostic + fixit. if (auto *DSCE = dyn_cast(E)) if (!selfDeclAllowsImplicitSelf(DSCE->getBase(), ACE) && isa(DSCE->getFn())) { selfDRE = dyn_cast_or_null(DSCE->getBase()); auto MethodExpr = cast(DSCE->getFn()); memberLoc = DSCE->getLoc(); diagnoseImplicitSelfUse( memberLoc, DSCE->getBase(), ACE, diag::method_call_in_closure_without_explicit_self, MethodExpr->getDecl()->getBaseIdentifier()); } if (memberLoc.isValid()) { const AbstractClosureExpr *parentDisallowingImplicitSelf = nullptr; if (Ctx.isSwiftVersionAtLeast(6) && selfDRE && selfDRE->getDecl()) { parentDisallowingImplicitSelf = parentClosureDisallowingImplicitSelf( selfDRE->getDecl(), selfDRE->getType(), ACE); } emitFixIts(Diags, memberLoc, parentDisallowingImplicitSelf, ACE); return Action::SkipNode(E); } if (!selfDeclAllowsImplicitSelf(E, ACE)) { diagnoseImplicitSelfUse(E->getLoc(), E, ACE, diag::implicit_use_of_self_in_closure); } return Action::Continue(E); } PostWalkResult walkToExprPost(Expr *E) override { if (isa(E)) { assert(isInMacro()); MacroDepth -= 1; } auto *ACE = dyn_cast(E); if (!ACE) { return Action::Continue(E); } if (shouldRecordClosure(ACE)) { assert(Closures.size() > 0); Closures.pop_back(); } return Action::Continue(E); } PreWalkAction walkToDeclPre(Decl *D) override { if (isa(D)) MacroDepth += 1; return BaseDiagnosticWalker::walkToDeclPre(D); } PostWalkAction walkToDeclPost(Decl *D) override { if (isa(D)) { assert(isInMacro()); MacroDepth -= 1; } return Action::Continue(); } PreWalkResult walkToStmtPre(Stmt *S) override { /// Conditions like `if let self` or `guard let self` /// have an RHS 'self' decl that is implicit, but this is not /// the sort of "implicit self" decl that should trigger /// these diagnostics. Track these DREs in a list so we can /// avoid running diagnostics on them when we see them later. auto conditionalStmt = dyn_cast(S); if (!conditionalStmt) { return Action::Continue(S); } for (auto cond : conditionalStmt->getCond()) { if (cond.getKind() != StmtConditionElement::CK_PatternBinding) { continue; } if (auto OSP = dyn_cast(cond.getPattern())) { if (OSP->getSubPattern()->getBoundName() != Ctx.Id_self) { continue; } auto E = cond.getInitializer(); // Peer through any implicit conversions like a LoadExpr if (auto *ICE = dyn_cast(E)) { E = ICE->getSubExpr(); } if (isImplicitSelf(E)) { UnwrapStmtImplicitSelfExprs.insert(E); } } } return Action::Continue(S); } /// Emit any fix-its for this error. void emitFixIts(DiagnosticEngine &Diags, SourceLoc memberLoc, const AbstractClosureExpr *parentDisallowingImplicitSelf, const AbstractClosureExpr *ACE) { // These fix-its have to be diagnosed on the closure that requires, // but is currently missing, self qualification. It's possible that // ACE doesn't require self qualification (e.g. because it's // non-escaping) but is nested inside a closure that does require self // qualification. In that case we have to emit the fixit for the parent // closure. // - Even if this closure requires self qualification, if there's an // invalid parent we emit the diagnostic on that parent first. // To enable implicit self you'd have to fix the parent anyway. // This lets us avoid bogus diagnostics on this closure when // it's actually _just_ the parent that's invalid. auto closureForDiagnostics = ACE; if (parentDisallowingImplicitSelf) { // Don't do this for escaping autoclosures, which are never allowed // to use implicit self, even after fixing any invalid parents. auto isEscapingAutoclosure = isa(ACE) && isClosureRequiringSelfQualification(ACE); if (!isEscapingAutoclosure) { closureForDiagnostics = parentDisallowingImplicitSelf; } } // This error can be fixed by either capturing self explicitly (if in an // explicit closure), or referencing self explicitly. if (auto *CE = dyn_cast(closureForDiagnostics)) { if (diagnoseAlmostMatchingCaptures(Diags, memberLoc, CE)) { // Bail on the rest of the diagnostics. Offering the option to // capture 'self' explicitly will result in an error, and using // 'self.' explicitly will be accessing something other than the // self param. return; } emitFixItsForExplicitClosure(Diags, memberLoc, CE); } else { // If this wasn't an explicit closure, just offer the fix-it to // reference self explicitly. Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) .fixItInsert(memberLoc, "self."); } } /// Diagnose any captures which might have been an attempt to capture /// \c self strongly, but do not actually enable implicit \c self. Returns /// whether there were any such captures to diagnose. bool diagnoseAlmostMatchingCaptures(DiagnosticEngine &Diags, SourceLoc memberLoc, const ClosureExpr *closureExpr) { // If we've already captured something with the name "self" other than // the actual self param, offer special diagnostics. if (auto *VD = closureExpr->getCapturedSelfDecl()) { if (!VD->getInterfaceType()->is()) { Diags.diagnose(VD->getLoc(), diag::note_other_self_capture); } return true; } return false; } /// Emit fix-its for invalid use of implicit \c self in an explicit closure. /// The error can be solved by capturing self explicitly, /// or by using \c self. explicitly. void emitFixItsForExplicitClosure(DiagnosticEngine &Diags, SourceLoc memberLoc, const ClosureExpr *closureExpr) { Diags.diagnose(memberLoc, diag::note_reference_self_explicitly) .fixItInsert(memberLoc, "self."); auto diag = Diags.diagnose(closureExpr->getLoc(), diag::note_capture_self_explicitly); // There are four different potential fix-its to offer based on the // closure signature: // 1. There is an existing capture list which already has some // entries. We need to insert 'self' into the capture list along // with a separating comma. // 2. There is an existing capture list, but it is empty (just '[]'). // We can just insert 'self'. // 3. Arguments or types are already specified in the signature, // but there is no existing capture list. We will need to insert // the capture list, but 'in' will already be present. // 4. The signature empty so far. We must insert the full capture // list as well as 'in'. const auto brackets = closureExpr->getBracketRange(); if (brackets.isValid()) { emitInsertSelfIntoCaptureListFixIt(brackets, diag); } else { emitInsertNewCaptureListFixIt(closureExpr, diag); } } /// Emit a fix-it for inserting \c self into in existing capture list, along /// with a trailing comma if needed. The fix-it will be attached to the /// provided diagnostic \c diag. void emitInsertSelfIntoCaptureListFixIt(SourceRange brackets, InFlightDiagnostic &diag) { // Look for any non-comment token. If there's anything before the // closing bracket, we assume that it is a valid capture list entry and // insert 'self,'. If it wasn't a valid entry, then we will at least not // be introducing any new errors/warnings... const auto locAfterBracket = brackets.Start.getAdvancedLoc(1); const auto nextAfterBracket = Lexer::getTokenAtLocation( Ctx.SourceMgr, locAfterBracket, CommentRetentionMode::None); if (nextAfterBracket.getLoc() != brackets.End) diag.fixItInsertAfter(brackets.Start, "self, "); else diag.fixItInsertAfter(brackets.Start, "self"); } /// Emit a fix-it for inserting a capture list into a closure that does not /// already have one, along with a trailing \c in if necessary. The fix-it /// will be attached to the provided diagnostic \c diag. void emitInsertNewCaptureListFixIt(const ClosureExpr *closureExpr, InFlightDiagnostic &diag) { if (closureExpr->getInLoc().isValid()) { diag.fixItInsertAfter(closureExpr->getLoc(), " [self]"); return; } // If there's a (non-comment) token immediately following the // opening brace of the closure, we may need to pad the fix-it // with a space. const auto nextLoc = closureExpr->getLoc().getAdvancedLoc(1); const auto next = Lexer::getTokenAtLocation(Ctx.SourceMgr, nextLoc, CommentRetentionMode::None); std::string trailing = next.getLoc() == nextLoc ? " " : ""; diag.fixItInsertAfter(closureExpr->getLoc(), " [self] in" + trailing); } /// Whether or not this invalid usage of implicit self should be a warning /// in Swift 5 mode, to preserve source compatibility. bool invalidImplicitSelfShouldOnlyWarn510(Expr *selfRef, AbstractClosureExpr *ACE) { auto DRE = dyn_cast_or_null(selfRef); if (!DRE) return false; auto selfDecl = dyn_cast_or_null(DRE->getDecl()); if (!selfDecl) return false; // If this implicit self decl is from a closure that captured self // weakly, then we should always emit an error, since implicit self was // only allowed starting in Swift 5.8 and later. if (closureHasWeakSelfCapture(ACE)) { // Implicit self was incorrectly permitted for weak self captures // in non-escaping closures in Swift 5.7, so in that case we can // only warn until Swift 6. return !isClosureRequiringSelfQualification(ACE, /*ignoreWeakSelf*/ true); } return !selfDecl->isSelfParameter(); } }; } // end anonymous namespace /// Look for any property references in closures that lack a 'self.' qualifier. /// Within a closure, we require that the source code contain 'self.' explicitly /// (or that the closure explicitly capture 'self' in the capture list) because /// 'self' is captured, not the property value. This is a common source of /// confusion, so we force an explicit self. static void diagnoseImplicitSelfUseInClosure(const Expr *E, const DeclContext *DC) { using DiagnoseWalker = ImplicitSelfUsageChecker; auto &ctx = DC->getASTContext(); AbstractClosureExpr *ACE = nullptr; if (DC->isLocalContext()) { while (DC->getParent()->isLocalContext() && !ACE) { // FIXME: The closure's type may not be set here yet since we have a // couple of calls to typeCheckExpression remaining in CSApply that can // result in running this logic before the solution has been applied to // the parent expression. Additionally, lazy locals can have their // initializers type-checked from CSGen due to the fact that that name // lookup can kick LazyStoragePropertyRequest, which currently eagerly // computes the interface type. The interface type computation there ought // to be made lazy. if (auto *closure = dyn_cast(DC)) if (closure->getType()) if (DiagnoseWalker::isClosureRequiringSelfQualification(closure)) ACE = const_cast(closure); DC = DC->getParent(); } } const_cast(E)->walk(DiagnoseWalker(ctx, ACE)); } bool TypeChecker::getDefaultGenericArgumentsString( SmallVectorImpl &buf, const swift::GenericTypeDecl *typeDecl, llvm::function_ref getPreferredType) { llvm::raw_svector_ostream genericParamText{buf}; genericParamText << "<"; auto printGenericParamSummary = [&](GenericTypeParamType *genericParamTy) { if (Type result = getPreferredType(genericParamTy)) { result.print(genericParamText); return; } auto genericSig = typeDecl->getGenericSignature(); auto concreteTy = genericSig->getConcreteType(genericParamTy); if (concreteTy) { genericParamText << concreteTy; return; } auto upperBound = genericSig->getUpperBound( genericParamTy, /*forExistentialSelf=*/false, /*withParameterizedProtocols=*/false); if (upperBound->isObjCExistentialType() || upperBound->isAny()) { genericParamText << upperBound; return; } genericParamText << "<#" << genericParamTy->getName() << ": "; genericParamText << upperBound << "#>"; }; llvm::interleave(typeDecl->getInnermostGenericParamTypes(), printGenericParamSummary, [&] { genericParamText << ", "; }); genericParamText << ">"; return true; } /// Diagnose an argument labeling issue, returning true if we successfully /// diagnosed the issue. bool swift::diagnoseArgumentLabelError(ASTContext &ctx, const ArgumentList *argList, ArrayRef newNames, ParameterContext paramContext, InFlightDiagnostic *existingDiag) { std::optional diagOpt; auto getDiag = [&]() -> InFlightDiagnostic & { if (existingDiag) return *existingDiag; return *diagOpt; }; auto &diags = ctx.Diags; argList = argList->getOriginalArgs(); // Figure out how many extraneous, missing, and wrong labels are in // the call. unsigned numExtra = 0, numMissing = 0, numWrong = 0; unsigned n = std::max(argList->size(), (unsigned)newNames.size()); llvm::SmallString<16> missingBuffer; llvm::SmallString<16> extraBuffer; for (unsigned i = 0; i != n; ++i) { // oldName and newName are // - None if i is out of bounds for the argument list // - nullptr for an argument without a label // - have a value if the argument has a label std::optional oldName; if (i < argList->size()) oldName = argList->getLabel(i); std::optional newName; if (i < newNames.size()) newName = newNames[i]; assert(oldName || newName && "We can't have oldName and newName out of " "bounds, otherwise n would be smaller"); if (oldName == newName || argList->isUnlabeledTrailingClosureIndex(i)) continue; if (!oldName.has_value() && newName.has_value()) { ++numMissing; missingBuffer += newName->str(); missingBuffer += ':'; } else if (oldName.has_value() && !newName.has_value()) { ++numExtra; extraBuffer += oldName->str(); extraBuffer += ':'; } else if (oldName->empty()) { // In the cases from here onwards oldValue and newValue are not null ++numMissing; missingBuffer += newName->str(); missingBuffer += ":"; } else if (newName->empty()) { ++numExtra; extraBuffer += oldName->str(); extraBuffer += ':'; } else { ++numWrong; } } // Emit the diagnostic. assert(numMissing > 0 || numExtra > 0 || numWrong > 0); llvm::SmallString<16> haveBuffer; // note: diagOpt has references to this llvm::SmallString<16> expectedBuffer; // note: diagOpt has references to this // If we had any wrong labels, or we have both missing and extra labels, // emit the catch-all "wrong labels" diagnostic. if (!existingDiag) { bool plural = (numMissing + numExtra + numWrong) > 1; if (numWrong > 0 || (numMissing > 0 && numExtra > 0)) { for (unsigned i = 0, n = argList->size(); i != n; ++i) { auto haveName = argList->getLabel(i); if (haveName.empty()) haveBuffer += '_'; else haveBuffer += haveName.str(); haveBuffer += ':'; } for (auto expected : newNames) { if (expected.empty()) expectedBuffer += '_'; else expectedBuffer += expected.str(); expectedBuffer += ':'; } StringRef haveStr = haveBuffer; StringRef expectedStr = expectedBuffer; diagOpt.emplace(diags.diagnose(argList->getLoc(), diag::wrong_argument_labels, plural, haveStr, expectedStr, static_cast(paramContext))); } else if (numMissing > 0) { StringRef missingStr = missingBuffer; diagOpt.emplace(diags.diagnose(argList->getLoc(), diag::missing_argument_labels, plural, missingStr, static_cast(paramContext))); } else { assert(numExtra > 0); StringRef extraStr = extraBuffer; diagOpt.emplace(diags.diagnose(argList->getLoc(), diag::extra_argument_labels, plural, extraStr, static_cast(paramContext))); } } // Emit Fix-Its to correct the names. auto &diag = getDiag(); for (unsigned i = 0, n = argList->size(); i != n; ++i) { Identifier oldName = argList->getLabel(i); Identifier newName; if (i < newNames.size()) newName = newNames[i]; if (oldName == newName || argList->isUnlabeledTrailingClosureIndex(i)) continue; if (newName.empty()) { // If this is a labeled trailing closure, we need to replace with '_'. if (argList->isLabeledTrailingClosureIndex(i)) { diag.fixItReplace(argList->getLabelLoc(i), "_"); continue; } // Otherwise, delete the old name. diag.fixItRemoveChars(argList->getLabelLoc(i), argList->getExpr(i)->getStartLoc()); continue; } bool newNameIsReserved = !canBeArgumentLabel(newName.str()); llvm::SmallString<16> newStr; if (newNameIsReserved) newStr += "`"; newStr += newName.str(); if (newNameIsReserved) newStr += "`"; // If the argument was previously unlabeled, insert the new label. Note that // we don't do this for labeled trailing closures as they write unlabeled // args as '_:', and therefore need replacement. if (oldName.empty() && !argList->isLabeledTrailingClosureIndex(i)) { // Insert the name. newStr += ": "; diag.fixItInsert(argList->getExpr(i)->getStartLoc(), newStr); continue; } // Change the name. diag.fixItReplace(argList->getLabelLoc(i), newStr); } // If the diagnostic is local, flush it before returning. // This makes sure it's emitted before the message text buffers are destroyed. diagOpt.reset(); return true; } static const Expr *lookThroughExprsToImmediateDeallocation(const Expr *E) { // Look through various expressions that don't affect the fact that the user // will be assigning a class instance that will be immediately deallocated. while (true) { E = E->getValueProvidingExpr(); // We don't currently deal with tuple destructuring. if (isa(E)) return E; // If we have a TupleElementExpr with a child TupleExpr, dig into that // element. if (auto *TEE = dyn_cast(E)) { auto *subExpr = lookThroughExprsToImmediateDeallocation(TEE->getBase()); if (auto *TE = dyn_cast(subExpr)) { auto *element = TE->getElements()[TEE->getFieldNumber()]; return lookThroughExprsToImmediateDeallocation(element); } return subExpr; } if (auto *ICE = dyn_cast(E)) { E = ICE->getSubExpr(); continue; } if (auto *CE = dyn_cast(E)) { E = CE->getSubExpr(); continue; } if (auto *OEE = dyn_cast(E)) { E = OEE->getSubExpr(); continue; } // Look through optional evaluations, we still want to diagnose on // things like initializers called through optional chaining and the // unwrapping of failable initializers. if (auto *OEE = dyn_cast(E)) { E = OEE->getSubExpr(); continue; } if (auto *OBE = dyn_cast(E)) { E = OBE->getSubExpr(); continue; } if (auto *FOE = dyn_cast(E)) { E = FOE->getSubExpr(); continue; } if (auto *ATE = dyn_cast(E)) { E = ATE->getSubExpr(); continue; } if (auto *DSBIE = dyn_cast(E)) { E = DSBIE->getRHS(); continue; } return E; } } static void diagnoseUnownedImmediateDeallocationImpl(ASTContext &ctx, const VarDecl *varDecl, const Expr *initExpr, SourceLoc diagLoc, SourceRange diagRange) { auto *ownershipAttr = varDecl->getAttrs().getAttribute(); if (!ownershipAttr || ownershipAttr->isInvalid()) return; // Only diagnose for non-owning ownerships such as 'weak' and 'unowned'. // Zero is the default/strong ownership strength. if (ReferenceOwnership::Strong == ownershipAttr->get() || isLessStrongThan(ReferenceOwnership::Strong, ownershipAttr->get())) return; // Try to find a call to a constructor. initExpr = lookThroughExprsToImmediateDeallocation(initExpr); auto *CE = dyn_cast(initExpr); if (!CE) return; auto *CRCE = dyn_cast(CE->getFn()); if (!CRCE) return; auto *DRE = dyn_cast(CRCE->getFn()); if (!DRE) return; auto *constructorDecl = dyn_cast(DRE->getDecl()); if (!constructorDecl) return; // Make sure the constructor constructs an instance that allows ownership. // This is to ensure we don't diagnose on constructors such as // Optional.init(nilLiteral:). auto selfType = constructorDecl->getDeclContext()->getSelfTypeInContext(); if (!selfType->allowsOwnership()) return; // This must stay in sync with // diag::unowned_assignment_immediate_deallocation. enum { SK_Variable = 0, SK_Property } storageKind = SK_Variable; if (varDecl->getDeclContext()->isTypeContext()) storageKind = SK_Property; // TODO: The DiagnoseLifetimeIssuesPass prints a similar warning in this // situation. We should only print one warning. ctx.Diags.diagnose(diagLoc, diag::unowned_assignment_immediate_deallocation, varDecl->getName(), ownershipAttr->get(), unsigned(storageKind)) .highlight(diagRange); ctx.Diags.diagnose(diagLoc, diag::unowned_assignment_requires_strong) .highlight(diagRange); ctx.Diags.diagnose(varDecl, diag::decl_declared_here, varDecl); } void swift::diagnoseUnownedImmediateDeallocation(ASTContext &ctx, const AssignExpr *assignExpr) { auto *destExpr = assignExpr->getDest()->getValueProvidingExpr(); auto *initExpr = assignExpr->getSrc(); // Try to find a referenced VarDecl. const VarDecl *VD = nullptr; if (auto *DRE = dyn_cast(destExpr)) { VD = dyn_cast(DRE->getDecl()); } else if (auto *MRE = dyn_cast(destExpr)) { VD = dyn_cast(MRE->getMember().getDecl()); } if (VD) diagnoseUnownedImmediateDeallocationImpl(ctx, VD, initExpr, assignExpr->getLoc(), initExpr->getSourceRange()); } void swift::diagnoseUnownedImmediateDeallocation(ASTContext &ctx, const Pattern *pattern, SourceLoc equalLoc, const Expr *initExpr) { pattern = pattern->getSemanticsProvidingPattern(); if (auto *TP = dyn_cast(pattern)) { initExpr = lookThroughExprsToImmediateDeallocation(initExpr); // If we've found a matching tuple initializer with the same number of // elements as our pattern, diagnose each element individually. auto TE = dyn_cast(initExpr); if (TE && TE->getNumElements() == TP->getNumElements()) { for (unsigned i = 0, e = TP->getNumElements(); i != e; ++i) { const TuplePatternElt &elt = TP->getElement(i); const Pattern *subPattern = elt.getPattern(); Expr *subInitExpr = TE->getElement(i); diagnoseUnownedImmediateDeallocation(ctx, subPattern, equalLoc, subInitExpr); } } } else if (auto *NP = dyn_cast(pattern)) { diagnoseUnownedImmediateDeallocationImpl(ctx, NP->getDecl(), initExpr, equalLoc, initExpr->getSourceRange()); } } namespace { enum NoteKind_t { FixItReplace, FixItInsert, }; static bool fixItOverrideDeclarationTypesImpl( ValueDecl *decl, const ValueDecl *base, SmallVectorImpl> ¬es) { // For now, just rewrite cases where the base uses a value type and the // override uses a reference type, and the value type is bridged to the // reference type. This is a way to migrate code that makes use of types // that previously were not bridged to value types. auto checkValueReferenceType = [&](Type overrideTy, ParamDecl::Specifier overrideSpec, Type baseTy, ParamDecl::Specifier baseSpec, SourceRange typeRange) -> bool { if (typeRange.isInvalid()) return false; auto normalizeType = [](Type &ty, ParamDecl::Specifier spec) -> Type { Type normalizedTy = ty; if (Type unwrappedTy = normalizedTy->getOptionalObjectType()) normalizedTy = unwrappedTy; if (spec == ParamDecl::Specifier::InOut) ty = InOutType::get(ty); return normalizedTy; }; // Is the base type bridged? Type normalizedBaseTy = normalizeType(baseTy, baseSpec); const DeclContext *DC = decl->getDeclContext(); ASTContext &ctx = decl->getASTContext(); // ...and just knowing that it's bridged isn't good enough if we don't // know what it's bridged /to/. Also, don't do this check for trivial // bridging---that doesn't count. Type bridged; if (normalizedBaseTy->isAny()) { bridged = ctx.getAnyObjectType(); } else { bridged = ctx.getBridgedToObjC(DC, normalizedBaseTy); } if (!bridged || bridged->isEqual(normalizedBaseTy)) return false; // ...and is it bridged to the overridden type? Type normalizedOverrideTy = normalizeType(overrideTy, overrideSpec); if (!bridged->isEqual(normalizedOverrideTy)) { // If both are nominal types, check again, ignoring generic arguments. auto *overrideNominal = normalizedOverrideTy->getAnyNominal(); if (!overrideNominal || bridged->getAnyNominal() != overrideNominal) { return false; } } Type newOverrideTy = baseTy; // Preserve optionality if we're dealing with a simple type. if (Type unwrappedTy = newOverrideTy->getOptionalObjectType()) newOverrideTy = unwrappedTy; if (overrideTy->getOptionalObjectType()) newOverrideTy = OptionalType::get(newOverrideTy); SmallString<32> baseTypeBuf; llvm::raw_svector_ostream baseTypeStr(baseTypeBuf); PrintOptions options; options.SynthesizeSugarOnTypes = true; newOverrideTy->print(baseTypeStr, options); notes.emplace_back(FixItReplace, typeRange, baseTypeStr.str().str()); return true; }; // Check if overriding fails because we lack @escaping attribute on the function // type repr. auto checkTypeMissingEscaping = [&](Type overrideTy, Type baseTy, SourceRange typeRange) -> bool { // Fix-it needs position to apply. if (typeRange.isInvalid()) return false; auto overrideFnTy = overrideTy->getAs(); auto baseFnTy = baseTy->getAs(); // Both types should be function. if (overrideFnTy && baseFnTy && // The overriding function type should be no escaping. overrideFnTy->getExtInfo().isNoEscape() && // The overridden function type should be escaping. !baseFnTy->getExtInfo().isNoEscape()) { notes.emplace_back(FixItInsert, typeRange, "@escaping "); return true; } return false; }; auto checkType = [&](Type overrideTy, ParamDecl::Specifier overrideSpec, Type baseTy, ParamDecl::Specifier baseSpec, SourceRange typeRange) -> bool { return checkValueReferenceType(overrideTy, overrideSpec, baseTy, baseSpec, typeRange) || checkTypeMissingEscaping(overrideTy, baseTy, typeRange); }; if (auto *param = dyn_cast(decl)) { SourceRange typeRange = param->getTypeSourceRangeForDiagnostics(); auto *baseParam = cast(base); return checkType(param->getInterfaceType(), param->getSpecifier(), baseParam->getInterfaceType(), baseParam->getSpecifier(), typeRange); } if (auto *var = dyn_cast(decl)) { SourceRange typeRange = var->getTypeSourceRangeForDiagnostics(); auto *baseVar = cast(base); return checkType(var->getInterfaceType(), ParamDecl::Specifier::Default, baseVar->getInterfaceType(), ParamDecl::Specifier::Default, typeRange); } if (auto *fn = dyn_cast(decl)) { auto *baseFn = cast(base); bool fixedAny = false; if (fn->getParameters()->size() == baseFn->getParameters()->size()) { for_each(*fn->getParameters(), *baseFn->getParameters(), [&](ParamDecl *param, const ParamDecl *baseParam) { fixedAny |= fixItOverrideDeclarationTypesImpl(param, baseParam, notes); }); } if (auto *method = dyn_cast(decl)) { auto resultType = method->mapTypeIntoContext( method->getResultInterfaceType()); auto *baseMethod = cast(base); auto baseResultType = baseMethod->mapTypeIntoContext( baseMethod->getResultInterfaceType()); fixedAny |= checkType(resultType, ParamDecl::Specifier::Default, baseResultType, ParamDecl::Specifier::Default, method->getResultTypeSourceRange()); } return fixedAny; } if (auto *subscript = dyn_cast(decl)) { auto *baseSubscript = cast(base); bool fixedAny = false; if (subscript->getIndices()->size() == baseSubscript->getIndices()->size()) { for_each(*subscript->getIndices(), *baseSubscript->getIndices(), [&](ParamDecl *param, const ParamDecl *baseParam) { fixedAny |= fixItOverrideDeclarationTypesImpl(param, baseParam, notes); }); } auto resultType = subscript->mapTypeIntoContext(subscript->getElementInterfaceType()); auto baseResultType = baseSubscript->mapTypeIntoContext( baseSubscript->getElementInterfaceType()); fixedAny |= checkType(resultType, ParamDecl::Specifier::Default, baseResultType, ParamDecl::Specifier::Default, subscript->getElementTypeSourceRange()); return fixedAny; } llvm_unreachable("unknown overridable member"); } } bool swift::computeFixitsForOverriddenDeclaration( ValueDecl *decl, const ValueDecl *base, llvm::function_ref(bool)> diag) { SmallVector, 4> Notes; bool hasNotes = ::fixItOverrideDeclarationTypesImpl(decl, base, Notes); std::optional diagnostic = diag(hasNotes); if (!diagnostic) return hasNotes; for (const auto ¬e : Notes) { if (std::get<0>(note) == FixItReplace) { diagnostic->fixItReplace(std::get<1>(note), std::get<2>(note)); } else { diagnostic->fixItInsert(std::get<1>(note).Start, std::get<2>(note)); } } return hasNotes; } //===----------------------------------------------------------------------===// // Per func/init diagnostics //===----------------------------------------------------------------------===// namespace { class VarDeclUsageChecker : public ASTWalker { DeclContext *DC; DiagnosticEngine &Diags; SourceRange FullRange; // Keep track of some information about a variable. enum { RK_Defined = 1, ///< Whether it was ever defined in this scope. RK_Read = 2, ///< Whether it was ever read. RK_Written = 4, ///< Whether it was ever written or passed inout. RK_CaptureList = 8 ///< Var is an entry in a capture list. }; /// These are all of the variables that we are tracking. VarDecls get added /// to this when the declaration is seen. We use a MapVector to keep the /// diagnostics emission in deterministic order. llvm::SmallMapVector VarDecls; /// This is a mapping from an OpaqueValue to the expression that initialized /// it. llvm::SmallDenseMap OpaqueValueMap; /// The first reference to the given property. llvm::SmallDenseMap AssociatedGetterRefExpr; /// This is a mapping from VarDecls to the if/while/guard statement that they /// occur in, when they are in a pattern in a StmtCondition. llvm::SmallDenseMap StmtConditionForVD; #ifndef NDEBUG llvm::SmallPtrSet AllExprsSeen; #endif bool sawError = false; VarDeclUsageChecker(const VarDeclUsageChecker &) = delete; void operator=(const VarDeclUsageChecker &) = delete; public: VarDeclUsageChecker(DeclContext *DC, DiagnosticEngine &Diags, SourceRange range) : DC(DC), Diags(Diags), FullRange(range) {} // After we have scanned the entire region, diagnose variables that could be // declared with a narrower usage kind. ~VarDeclUsageChecker() override; /// Check to see if the specified VarDecl is part of a larger /// PatternBindingDecl, where some other bound variable was mutated. In this /// case we don't want to generate a "variable never mutated" warning, because /// it would require splitting up the destructuring of the tuple, which is /// more code turmoil than the warning is worth. bool isVarDeclPartOfPBDThatHadSomeMutation(VarDecl *VD) { auto *PBD = VD->getParentPatternBinding(); if (!PBD) return false; bool sawMutation = false; for (auto idx : range(PBD->getNumPatternEntries())) { PBD->getPattern(idx)->forEachVariable([&](VarDecl *VD) { auto it = VarDecls.find(VD); sawMutation |= it != VarDecls.end() && (it->second & RK_Written); }); } return sawMutation; } bool shouldTrackVarDecl(VarDecl *VD) { // If the variable is implicit, ignore it. if (VD->isImplicit() || VD->getLoc().isInvalid()) return false; // If the variable is computed, ignore it. if (!VD->hasStorage()) return false; // If the variable was invalid, ignore it and notice that the code is // malformed. if (VD->isInvalid()) { sawError = true; return false; } // If the variable is already unnamed, ignore it. if (!VD->hasName() || VD->getName().str() == "_") return false; return true; } void addMark(Decl *D, unsigned Flag) { auto *vd = dyn_cast(D); if (!vd) return; VarDecls[vd] |= Flag; } void markBaseOfStorageUse(Expr *E, ConcreteDeclRef decl, unsigned flags); void markBaseOfStorageUse(Expr *E, bool isMutating); void markStoredOrInOutExpr(Expr *E, unsigned Flags); MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::ArgumentsAndExpansion; } // We generally walk into declarations, other than types and nested functions. // FIXME: peek into capture lists of nested functions. PreWalkAction walkToDeclPre(Decl *D) override { if (isa(D)) return Action::SkipNode(); // If this is a VarDecl, then add it to our list of things to track. if (auto *vd = dyn_cast(D)) { if (shouldTrackVarDecl(vd)) { unsigned flags = RK_Defined; if (vd->isCaptureList()) flags |= RK_CaptureList; if (auto childVd = vd->getCorrespondingCaseBodyVariable()) { // Child vars are never in capture lists. assert(flags == RK_Defined); addMark(childVd.get(), flags); } addMark(vd, flags); } } // Don't walk into implicit accessors, since eg. an observer's setter // references the variable, but we don't want to consider it as a real // "use". if (isa(D) && D->isImplicit()) return Action::SkipNode(); if (auto *afd = dyn_cast(D)) { // If this AFD is a setter, track the parameter and the getter for // the containing property so if newValue isn't used but the getter is used // an error can be reported. if (auto FD = dyn_cast(afd)) { if (FD->getAccessorKind() == AccessorKind::Set) { if (isa(FD->getStorage())) { auto arguments = FD->getParameters(); VarDecls[arguments->get(0)] = RK_Defined; } } } if (afd->isBodyTypeChecked()) return Action::Continue(); // Don't walk into a body that has not yet been type checked. This should // only occur for top-level code. VarDecls.clear(); return Action::SkipNode(); } if (auto *TLCD = dyn_cast(D)) { // If this is a TopLevelCodeDecl, scan for global variables auto *body = TLCD->getBody(); for (auto node : body->getElements()) { if (node.is()) { // Flag all variables in a PatternBindingDecl Decl *D = node.get(); auto *PBD = dyn_cast(D); if (!PBD) continue; for (auto idx : range(PBD->getNumPatternEntries())) { PBD->getPattern(idx)->forEachVariable([&](VarDecl *VD) { VarDecls[VD] = RK_Read|RK_Written|RK_Defined; }); } } else if (node.is()) { // Flag all variables in guard statements Stmt *S = node.get(); auto *GS = dyn_cast(S); if (!GS) continue; for (StmtConditionElement SCE : GS->getCond()) { if (auto pattern = SCE.getPatternOrNull()) { pattern->forEachVariable([&](VarDecl *VD) { VarDecls[VD] = RK_Read|RK_Written|RK_Defined; }); } } } } } // Note that we ignore the initialization behavior of PatternBindingDecls, // but we do want to walk into them, because we want to see any uses or // other things going on in the initializer expressions. return Action::Continue(); } /// The heavy lifting happens when visiting expressions. PreWalkResult walkToExprPre(Expr *E) override; /// Custom handling for statements. PreWalkResult walkToStmtPre(Stmt *S) override { // Keep track of an association between vardecls and the StmtCondition that // they are bound in for IfStmt, GuardStmt, WhileStmt, etc. if (auto LCS = dyn_cast(S)) { for (auto &cond : LCS->getCond()) { if (auto pat = cond.getPatternOrNull()) { pat->forEachVariable([&](VarDecl *VD) { StmtConditionForVD[VD] = LCS; }); } } } // A fallthrough dest case's bound variable means the source case's // var of the same name is read. if (auto *fallthroughStmt = dyn_cast(S)) { if (auto *sourceCase = fallthroughStmt->getFallthroughSource()) { SmallVector sourceVars; auto sourcePattern = sourceCase->getCaseLabelItems()[0].getPattern(); sourcePattern->collectVariables(sourceVars); auto destCase = fallthroughStmt->getFallthroughDest(); auto destPattern = destCase->getCaseLabelItems()[0].getPattern(); destPattern->forEachVariable([&](VarDecl *V) { if (!V->hasName()) return; for (auto *var : sourceVars) { if (var->hasName() && var->getName() == V->getName()) { VarDecls[var] |= RK_Read; break; } } }); } } // Make sure that we setup our case body variables. if (auto *caseStmt = dyn_cast(S)) { for (auto *vd : caseStmt->getCaseBodyVariablesOrEmptyArray()) { VarDecls[vd] |= RK_Defined; } } return Action::Continue(S); } }; /// An AST walker that determines the underlying type of an opaque return decl /// from its associated function body. class OpaqueUnderlyingTypeChecker : public ASTWalker { using Candidate = std::tuple; using AvailabilityContext = IfStmt *; ASTContext &Ctx; AbstractFunctionDecl *Implementation; OpaqueTypeDecl *OpaqueDecl; BraceStmt *Body; /// A set of all candidates with unique signatures. SmallPtrSet UniqueSignatures; /// Represents a current availability context. `nullptr` means that /// there are no restrictions. AvailabilityContext CurrentAvailability = nullptr; /// All of the candidates together with their availability. /// /// If a candidate is found in non-`if #available` context or /// `if #available` has other dynamic conditions, it covers 'all' /// versions and the context is set to `nullptr`. SmallVector, 4> Candidates; bool HasInvalidReturn = false; public: OpaqueUnderlyingTypeChecker(AbstractFunctionDecl *Implementation, OpaqueTypeDecl *OpaqueDecl, BraceStmt *Body) : Ctx(Implementation->getASTContext()), Implementation(Implementation), OpaqueDecl(OpaqueDecl), Body(Body) {} void check() { Body->walk(*this); // If given function has any invalid `return`s in the body // let's not try to validate the types, since it wouldn't // be accurate. if (HasInvalidReturn) return; // If there are no candidates, then the body has no return statements, and // we have nothing to infer the underlying type from. if (Candidates.empty()) { Implementation->diagnose(diag::opaque_type_no_underlying_type_candidates); // We try to find if the last element of the `Body` multi element // `BraceStmt` is an expression that produces a value that satisfies all // the opaque type requirements and if that is the case, it means we can // suggest a fix-it note to add an explicit `return`. if (Body->getNumElements() > 1) { auto element = Body->getLastElement(); // Let's see if the last statement would make for a valid return value. if (auto expr = element.dyn_cast()) { auto exprType = expr->getType(); // Function body might not be valid and we cannot reply on // \c typeCheckStmt here to propagate HadError because its // unreliable. if (!exprType || exprType->hasError()) return; bool conforms = llvm::all_of( OpaqueDecl->getOpaqueInterfaceGenericSignature() .getRequirements(), [&exprType](auto requirement) { if (requirement.getKind() == RequirementKind::Conformance) { auto conformance = checkConformance( exprType->getRValueType(), requirement.getProtocolDecl(), /*allowMissing=*/false); return !conformance.isInvalid(); } // If we encounter any requirements other than `Conformance`, we // do not attempt to type check the expression. return false; }); // If all requirements are fulfilled, we offer to insert `return` to // fix the issue. if (conforms) { Ctx.Diags .diagnose(expr->getStartLoc(), diag::opaque_type_missing_return_last_expr_note) .fixItInsert(expr->getStartLoc(), "return "); } } } return; } if (Candidates.size() == 1) { finalizeUnique(Candidates.front().second); return; } // Check whether all of the underlying type candidates match up. // There is a single unique signature, which means that all returns // matched. if (llvm::count_if(Candidates, [](const auto &entry) { const auto &candidate = entry.second; return std::get<2>(candidate); // isUnique field. }) == 1) { finalizeUnique(Candidates.front().second); return; } SmallVector universallyUniqueCandidates; for (const auto &entry : Candidates) { AvailabilityContext availability = entry.first; const auto &candidate = entry.second; // Unique candidate without availability context. if (!availability && std::get<2>(candidate)) universallyUniqueCandidates.push_back(candidate); } // TODO(diagnostics): Need a tailored diagnostic for this case. if (universallyUniqueCandidates.empty()) { Implementation->diagnose(diag::opaque_type_no_underlying_type_candidates); return; } // If there is a single universally available unique candidate // the underlying type would have to be determined at runtime // based on the results of availability checks. if (universallyUniqueCandidates.size() == 1) { finalizeOpaque(universallyUniqueCandidates.front()); return; } // A list of all mismatches discovered across all candidates. // If there are any mismatches in availability contexts, they // are not diagnosed but propagated to the declaration. std::optional> mismatch; auto opaqueParams = OpaqueDecl->getOpaqueGenericParams(); SubstitutionMap underlyingSubs = std::get<1>(Candidates.front().second); for (auto index : indices(opaqueParams)) { auto *genericParam = opaqueParams[index]; Type underlyingType = Type(genericParam).subst(underlyingSubs); bool found = false; for (const auto &candidate : universallyUniqueCandidates) { Type otherType = Type(genericParam).subst(std::get<1>(candidate)); if (!underlyingType->isEqual(otherType)) { mismatch.emplace(index, genericParam); found = true; break; } } if (found) break; } assert(mismatch.has_value()); if (auto genericParam = OpaqueDecl->getExplicitGenericParam(mismatch->first)) { Implementation ->diagnose( diag::opaque_type_mismatched_underlying_type_candidates_named, genericParam->getName()) .highlight(genericParam->getLoc()); } else { TypeRepr *opaqueRepr = OpaqueDecl->getOpaqueReturnTypeReprs()[mismatch->first]; Implementation ->diagnose(diag::opaque_type_mismatched_underlying_type_candidates, opaqueRepr) .highlight(opaqueRepr->getSourceRange()); } for (const auto &candidate : universallyUniqueCandidates) { Ctx.Diags.diagnose(std::get<0>(candidate)->getLoc(), diag::opaque_type_underlying_type_candidate_here, Type(mismatch->second).subst(std::get<1>(candidate))); } } bool isSelfReferencing(const Candidate &candidate) { auto substitutions = std::get<1>(candidate); // The underlying type can't be defined recursively // in terms of the opaque type itself. auto opaqueTypeInContext = Implementation->mapTypeIntoContext( OpaqueDecl->getDeclaredInterfaceType()); for (auto genericParam : OpaqueDecl->getOpaqueGenericParams()) { auto underlyingType = Type(genericParam).subst(substitutions); auto isSelfReferencing = underlyingType.findIf( [&](Type t) -> bool { return t->isEqual(opaqueTypeInContext); }); if (isSelfReferencing) { Ctx.Diags.diagnose(std::get<0>(candidate)->getLoc(), diag::opaque_type_self_referential_underlying_type, underlyingType); return true; } } return false; } // A single unique underlying substitution. void finalizeUnique(const Candidate &candidate) { // If we have one successful candidate, then save it as the underlying // substitutions of the opaque decl. OpaqueDecl->setUniqueUnderlyingTypeSubstitutions( std::get<1>(candidate).mapReplacementTypesOutOfContext()); } // There is no clear winner here since there are candidates within // limited availability contexts. void finalizeOpaque(const Candidate &universallyAvailable) { using AvailabilityCondition = OpaqueTypeDecl::AvailabilityCondition; SmallVector conditionalSubstitutions; SubstitutionMap universalSubstMap = std::get<1>(universallyAvailable); for (const auto &entry : Candidates) { auto availabilityContext = entry.first; const auto &candidate = entry.second; if (!availabilityContext) continue; unsigned neverAvailableCount = 0, alwaysAvailableCount = 0; SmallVector conditions; for (const auto &elt : availabilityContext->getCond()) { auto condition = elt.getAvailability(); auto availabilityRange = condition->getAvailableRange(); // Type checking did not set a range for this query (it may be invalid). // Treat the condition as if it were always true. if (!availabilityRange.has_value()) { alwaysAvailableCount++; continue; } // If there is no lower endpoint it means that the condition has no // OS version specification that matches the target platform. // FIXME: [availability] Handle empty ranges (never available). if (!availabilityRange->hasLowerEndpoint()) { // An inactive #unavailable condition trivially evaluates to false. if (condition->isUnavailability()) { neverAvailableCount++; continue; } // An inactive #available condition trivially evaluates to true. alwaysAvailableCount++; continue; } conditions.push_back( {*availabilityRange, condition->isUnavailability()}); } // If there were any conditions that were always false, then this // candidate is unreachable at runtime. if (neverAvailableCount > 0) continue; // If all the conditions were trivially true, then this candidate is // effectively a universally available candidate and the rest of the // candidates should be ignored since they are unreachable. if (alwaysAvailableCount == availabilityContext->getCond().size()) { universalSubstMap = std::get<1>(candidate); break; } ASSERT(conditions.size() > 0); conditionalSubstitutions.push_back( OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get( Ctx, conditions, std::get<1>(candidate).mapReplacementTypesOutOfContext())); } // Add universally available choice as the last one. conditionalSubstitutions.push_back( OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get( Ctx, {{VersionRange::all(), /*unavailable=*/false}}, universalSubstMap.mapReplacementTypesOutOfContext())); OpaqueDecl->setConditionallyAvailableSubstitutions( conditionalSubstitutions); } MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } PreWalkResult walkToExprPre(Expr *E) override { if (auto underlyingToOpaque = dyn_cast(E)) { auto subMap = underlyingToOpaque->substitutions; auto key = subMap.getCanonical().getOpaqueValue(); auto isUnique = UniqueSignatures.insert(key).second; auto candidate = std::make_tuple(underlyingToOpaque->getSubExpr(), subMap, isUnique); if (isSelfReferencing(candidate)) { HasInvalidReturn = true; return Action::Stop(); } if (subMap.getRecursiveProperties().hasDynamicSelf()) { Ctx.Diags.diagnose(E->getLoc(), diag::opaque_type_cannot_contain_dynamic_self); HasInvalidReturn = true; return Action::Stop(); } Candidates.push_back({CurrentAvailability, candidate}); return Action::SkipNode(E); } return Action::Continue(E); } PreWalkResult walkToStmtPre(Stmt *S) override { if (auto *If = dyn_cast(S)) { if (Parent.getAsStmt() != Body) { // If this is not a top-level `if`, let's drop // contextual information that has been set previously. CurrentAvailability = nullptr; return Action::Continue(S); } // If this is `if #(un)available` statement with no other dynamic // conditions, let's check if it returns opaque type directly. if (llvm::all_of(If->getCond(), [&](const auto &condition) { return condition.getKind() == StmtConditionElement::CK_Availability; })) { // Check return statement directly with availability context set. if (auto *Then = dyn_cast(If->getThenStmt())) { llvm::SaveAndRestore parent(Parent, Then); for (auto element : Then->getElements()) { auto *Return = getAsStmt(element); // If this is not a direct return statement, walk into it // without setting contextual availability because we want // to find all `return`s. if (!(Return && Return->hasResult())) { element.walk(*this); continue; } // Note that we are about to walk into a return statement // that is located in a `if #available` without any other // conditions. llvm::SaveAndRestore context( CurrentAvailability, If); Return->getResult()->walk(*this); } } // Walk the else branch directly as well. if (auto *Else = If->getElseStmt()) { llvm::SaveAndRestore parent(Parent, If); Else->walk(*this); } return Action::SkipNode(S); } } if (auto *RS = dyn_cast(S)) { if (RS->hasResult()) { auto resultTy = RS->getResult()->getType(); // If expression associated with return statement doesn't have // a type or type has an error, checking opaque types is going // to produce incorrect diagnostics. HasInvalidReturn |= resultTy.isNull() || resultTy->hasError(); } } return Action::Continue(S); } // Don't descend into nested decls. PreWalkAction walkToDeclPre(Decl *D) override { return Action::SkipNode(); } }; class ReturnTypePlaceholderReplacer : public ASTWalker { FuncDecl *Implementation; BraceStmt *Body; SmallVector Candidates; bool HasInvalidReturn = false; public: ReturnTypePlaceholderReplacer(FuncDecl *Implementation, BraceStmt *Body) : Implementation(Implementation), Body(Body) {} void check() { auto *resultRepr = Implementation->getResultTypeRepr(); if (!resultRepr) { return; } Implementation->getASTContext() .Diags .diagnose(resultRepr->getLoc(), diag::placeholder_type_not_allowed_in_return_type) .highlight(resultRepr->getSourceRange()); Body->walk(*this); // If given function has any invalid returns in the body // let's not try to validate the types, since it wouldn't // be accurate. if (HasInvalidReturn) return; auto writtenType = Implementation->getResultInterfaceType(); llvm::SmallPtrSet seenTypes; for (auto candidate : Candidates) { if (!seenTypes.insert(candidate.getPointer()).second) { continue; } TypeChecker::notePlaceholderReplacementTypes(writtenType, candidate); } } MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::ArgumentsAndExpansion; } PreWalkResult walkToExprPre(Expr *E) override { return Action::Continue(E); } PreWalkResult walkToStmtPre(Stmt *S) override { if (auto *RS = dyn_cast(S)) { if (RS->hasResult()) { auto resultTy = RS->getResult()->getType(); HasInvalidReturn |= resultTy.isNull() || resultTy->hasError(); Candidates.push_back(resultTy); } } return Action::Continue(S); } // Don't descend into nested decls. PreWalkAction walkToDeclPre(Decl *D) override { return Action::SkipNode(); } }; } // end anonymous namespace SourceLoc swift::getFixItLocForVarToLet(VarDecl *var) { // Try to find the location of the 'var' so we can produce a fixit. If // this is a simple PatternBinding, use its location. if (auto *PBD = var->getParentPatternBinding()) { if (PBD->getSingleVar() == var) return PBD->getLoc(); } else if (auto *pattern = var->getParentPattern()) { BindingPattern *foundVP = nullptr; pattern->forEachNode([&](Pattern *P) { if (auto *VP = dyn_cast(P)) if (VP->getSingleVar() == var) foundVP = VP; }); if (foundVP && foundVP->getIntroducer() != VarDecl::Introducer::Let) { return foundVP->getLoc(); } } return SourceLoc(); } extern "C" bool swift_ASTGen_inactiveCodeContainsReference( BridgedASTContext ctx, BridgedStringRef sourceFileBuffer, BridgedStringRef searchRange, BridgedStringRef name, void **cache ); extern "C" void swift_ASTGen_freeInactiveCodeContainsReferenceCache(void *cache); // After we have scanned the entire region, diagnose variables that could be // declared with a narrower usage kind. VarDeclUsageChecker::~VarDeclUsageChecker() { // If we saw an ErrorExpr somewhere in the body, then we have a malformed AST // and we know stuff got dropped. Instead of producing these diagnostics, // lets let the bigger issues get resolved first. if (sawError) return; /// Check whether the given variable might have been referenced from an /// inactive region of the source code. void *usedInInactiveCache = nullptr; auto isUsedInInactive = [&](VarDecl *var) -> bool { #if SWIFT_BUILD_SWIFT_SYNTAX // Extract the full buffer containing the source range. ASTContext &ctx = DC->getASTContext(); SourceManager &sourceMgr = ctx.SourceMgr; auto bufferID = sourceMgr.findBufferContainingLoc(FullRange.Start); StringRef sourceFileText = sourceMgr.getEntireTextForBuffer(bufferID); // Extract the search text from that buffer. auto searchTextCharRange = Lexer::getCharSourceRangeFromSourceRange( sourceMgr, FullRange); StringRef searchText = sourceMgr.extractText(searchTextCharRange, bufferID); return swift_ASTGen_inactiveCodeContainsReference( ctx, sourceFileText, searchText, var->getName().str(), &usedInInactiveCache); #else return false; #endif }; SWIFT_DEFER { #if SWIFT_BUILD_SWIFT_SYNTAX swift_ASTGen_freeInactiveCodeContainsReferenceCache(usedInInactiveCache); #endif }; for (auto p : VarDecls) { VarDecl *var; unsigned access; std::tie(var, access) = p; // If the variable was not defined in this scope, we can safely ignore it. if (!(access & RK_Defined)) continue; if (auto *caseStmt = dyn_cast_or_null(var->getRecursiveParentPatternStmt())) { // Only diagnose VarDecls from the first CaseLabelItem in CaseStmts, as // the remaining items must match it anyway. auto caseItems = caseStmt->getCaseLabelItems(); assert(!caseItems.empty() && "If we have any case stmt var decls, we should have a case item"); if (!caseItems.front().getPattern()->containsVarDecl(var)) continue; auto *childVar = var->getCorrespondingCaseBodyVariable().get(); access |= VarDecls[childVar]; } // If the setter parameter is not used, but the property is read, report // a warning. Otherwise, parameters should not generate usage warnings. It // is common to name a parameter and not use it (e.g. because you are an // override or want the named keyword, etc). Warning to rewrite it to _ is // more annoying than it is useful. if (auto param = dyn_cast(var)) { auto FD = dyn_cast(param->getDeclContext()); if (FD && FD->getAccessorKind() == AccessorKind::Set) { auto VD = dyn_cast(FD->getStorage()); if ((access & RK_Read) == 0) { auto found = AssociatedGetterRefExpr.find(VD); if (found != AssociatedGetterRefExpr.end() && !isUsedInInactive(VD)) { auto *DRE = found->second; Diags.diagnose(DRE->getLoc(), diag::unused_setter_parameter, var->getName()); Diags.diagnose(DRE->getLoc(), diag::fixit_for_unused_setter_parameter, var->getName()) .fixItReplace(DRE->getSourceRange(), var->getName().str()); } } } continue; } // If this is a 'let' value, any stores to it are actually initializations, // not mutations. auto isWrittenLet = false; if (var->isLet()) { isWrittenLet = (access & RK_Written) != 0; access &= ~RK_Written; } // If this variable has WeakStorageType, then it can be mutated in ways we // don't know. if (var->getInterfaceType()->is()) access |= RK_Written; // Diagnose variables that were never used (other than their // initialization). // if ((access & (RK_Read|RK_Written)) == 0) { // If this is a member in a capture list, just say it is unused. We could // produce a fixit hint with a parent map, but this is a lot of effort for // a narrow case. if (access & RK_CaptureList) { if (isUsedInInactive(var)) continue; Diags.diagnose(var->getLoc(), diag::capture_never_used, var->getName()); continue; } // If the source of the VarDecl is a trivial PatternBinding with only a // single binding, rewrite the whole thing into an assignment. // let x = foo() // -> // _ = foo() if (auto *pbd = var->getParentPatternBinding()) { if (pbd->getSingleVar() == var && pbd->getInit(0) != nullptr && !isa(pbd->getPattern(0))) { if (isUsedInInactive(var)) continue; unsigned varKind = var->isLet(); SourceRange replaceRange( pbd->getStartLoc(), pbd->getPattern(0)->getEndLoc()); Diags.diagnose(var->getLoc(), diag::pbd_never_used, var->getName(), varKind) .fixItReplace(replaceRange, "_"); continue; } } // If the variable is defined in a pattern in an if/while/guard statement, // see if we can produce a tuned fixit. When we have something like: // // if let x = { // // we prefer to rewrite it to: // // if != nil { // if (auto SC = StmtConditionForVD[var]) { // We only handle the "if let" case right now, since it is vastly the // most common situation that people run into. if (SC->getCond().size() == 1) { auto pattern = SC->getCond()[0].getPattern(); if (auto OSP = dyn_cast(pattern)) if (auto LP = dyn_cast(OSP->getSubPattern())) if (isa(LP->getSubPattern())) { auto initExpr = SC->getCond()[0].getInitializer(); if (initExpr->getStartLoc().isValid()) { if (isUsedInInactive(var)) continue; unsigned noParens = initExpr->canAppendPostfixExpression(); // If the subexpr is an "as?" cast, we can rewrite it to // be an "is" test. ConditionalCheckedCastExpr *CCE = nullptr; // initExpr can be wrapped inside parens or try expressions. if (auto ccExpr = dyn_cast( initExpr->getValueProvidingExpr())) { if (!ccExpr->isImplicit()) { CCE = ccExpr; noParens = true; } } // In cases where the value is optional, the cast expr is // wrapped inside OptionalEvaluationExpr. Unwrap it to get // ConditionalCheckedCastExpr. if (auto oeExpr = dyn_cast( initExpr->getValueProvidingExpr())) { if (auto ccExpr = dyn_cast( oeExpr->getSubExpr()->getValueProvidingExpr())) { if (!ccExpr->isImplicit()) { CCE = ccExpr; noParens = true; } } } auto diagIF = Diags.diagnose(var->getLoc(), diag::pbd_never_used_stmtcond, var->getName()); auto introducerLoc = SC->getCond()[0].getIntroducerLoc(); diagIF.fixItReplaceChars(introducerLoc, initExpr->getStartLoc(), &"("[noParens]); if (CCE) { // If this was an "x as? T" check, rewrite it to "x is T". diagIF.fixItReplace(SourceRange(CCE->getLoc(), CCE->getQuestionLoc()), "is"); } else { diagIF.fixItInsertAfter(initExpr->getEndLoc(), &") != nil"[noParens]); } continue; } } } } // If the variable is defined in a pattern that isn't one of the usual // conditional statements, try to detect and rewrite "simple" binding // patterns: // case .pattern(let x): // -> // case .pattern(_): if (auto *pattern = var->getParentPattern()) { BindingPattern *foundVP = nullptr; pattern->forEachNode([&](Pattern *P) { if (auto *VP = dyn_cast(P)) if (VP->getSingleVar() == var) foundVP = VP; }); if (foundVP) { if (isUsedInInactive(var)) continue; unsigned varKind = var->isLet(); Diags .diagnose(var->getLoc(), diag::variable_never_used, var->getName(), varKind) .fixItReplace(foundVP->getSourceRange(), "_"); continue; } } // Otherwise, this is something more complex, perhaps // let (a,b) = foo() if (isUsedInInactive(var)) continue; if (isWrittenLet) { Diags.diagnose(var->getLoc(), diag::immutable_value_never_used_but_assigned, var->getName()); } else { unsigned varKind = var->isLet(); // Just rewrite the one variable with a _. Diags.diagnose(var->getLoc(), diag::variable_never_used, var->getName(), varKind) .fixItReplace(var->getLoc(), "_"); } continue; } // If this is a mutable 'var', and it was never written to, suggest // upgrading to 'let'. if (var->getIntroducer() == VarDecl::Introducer::Var && (access & RK_Written) == 0 && // Don't warn if we have something like "let (x,y) = ..." and 'y' was // never mutated, but 'x' was. !isVarDeclPartOfPBDThatHadSomeMutation(var)) { SourceLoc FixItLoc = getFixItLocForVarToLet(var); if (isUsedInInactive(var)) continue; // If this is a parameter explicitly marked 'var', remove it. if (FixItLoc.isInvalid()) { bool suggestCaseLet = false; if (auto *stmt = var->getRecursiveParentPatternStmt()) { // Suggest 'var' -> 'case let' conversion // in case of 'for' loop and invalid because it's // tuple variable. suggestCaseLet = isa(stmt); } if (suggestCaseLet) Diags.diagnose(var->getLoc(), diag::variable_tuple_elt_never_mutated, var->getName(), var->getNameStr()); else Diags.diagnose(var->getLoc(), diag::variable_never_mutated, var->getName(), true); } else { bool suggestLet = true; if (auto *stmt = var->getRecursiveParentPatternStmt()) { // Don't try to suggest 'var' -> 'let' conversion // in case of 'for' loop because it's an implicitly // immutable context. suggestLet = !isa(stmt); } auto diag = Diags.diagnose(var->getLoc(), diag::variable_never_mutated, var->getName(), suggestLet); if (suggestLet) diag.fixItReplace(FixItLoc, "let"); else diag.fixItRemove(FixItLoc); continue; } } // If this is a variable that was only written to, emit a warning. if ((access & RK_Read) == 0) { if (isUsedInInactive(var)) continue; Diags.diagnose(var->getLoc(), diag::variable_never_read, var->getName()); continue; } } } /// Handle a use of "x.y" or "x[0]" where 'base' is the expression for x and /// 'decl' is the property or subscript. /// /// TODO: Rip this out and just rely on LValueAccessKind. void VarDeclUsageChecker::markBaseOfStorageUse(Expr *base, ConcreteDeclRef decl, unsigned flags) { // If the base is an rvalue, then we know that this is a non-mutating access. // Note that we can have mutating accesses even when the base has class or // metatype type due to protocols and protocol extensions. if (!base->getType()->hasLValueType() && !base->isSemanticallyInOutExpr()) { base->walk(*this); return; } // Compute whether this access is to a mutating member. auto *ASD = dyn_cast_or_null(decl.getDecl()); bool isMutating = false; if (!ASD) { // If there's no abstract storage declaration (which should hopefully // only happen with invalid code), treat the base access as mutating if // the subobject is being mutated and the base type is not a class // or metatype. if (flags & RK_Written) { Type type = base->getType()->getRValueType()->getInOutObjectType(); if (!type->isAnyClassReferenceType() && !type->is()) isMutating = true; } } else { // Otherwise, consider whether the accessors are mutating. if (flags & RK_Read) isMutating |= ASD->isGetterMutating(); if (flags & RK_Written) isMutating |= ASD->isSettable(nullptr) && ASD->isSetterMutating(); } markBaseOfStorageUse(base, isMutating); } void VarDeclUsageChecker::markBaseOfStorageUse(Expr *base, bool isMutating) { // CSApply sometimes wraps the base in an InOutExpr just because the // base is an l-value; look through that so we can get more precise // checking. if (auto *ioe = dyn_cast(base)) base = ioe->getSubExpr(); if (!isMutating) { base->walk(*this); return; } // Otherwise this is a read and write of the base. return markStoredOrInOutExpr(base, RK_Written|RK_Read); } void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) { // Sema leaves some subexpressions null, which seems really unfortunate. It // should replace them with ErrorExpr. if (E == nullptr || !E->getType() || E->getType()->hasError()) { sawError = true; return; } // Ignore parens and other easy cases. E = E->getSemanticsProvidingExpr(); // If we found a decl that is being assigned to, then mark it. if (auto *DRE = dyn_cast(E)) { addMark(DRE->getDecl(), Flags); return; } if (auto *TE = dyn_cast(E)) { for (auto &elt : TE->getElements()) markStoredOrInOutExpr(elt, Flags); return; } // If this is an assignment into a mutating subscript lvalue expr, then we // are mutating the base expression. We also need to visit the index // expressions as loads though. if (auto *SE = dyn_cast(E)) { // The arguments of a subscript are evaluated as rvalues. SE->getArgs()->walk(*this); markBaseOfStorageUse(SE->getBase(), SE->getDecl(), Flags); return; } // Likewise for key path applications. An application of a WritableKeyPath // reads and writes its base; an application of a ReferenceWritableKeyPath // only reads its base; the other KeyPath types cannot be written at all. if (auto *KPA = dyn_cast(E)) { KPA->getKeyPath()->walk(*this); bool isMutating = (Flags & RK_Written) && KPA->getKeyPath()->getType()->isWritableKeyPath(); markBaseOfStorageUse(KPA->getBase(), isMutating); return; } if (auto *ioe = dyn_cast(E)) return markStoredOrInOutExpr(ioe->getSubExpr(), RK_Written|RK_Read); if (auto *MRE = dyn_cast(E)) { markBaseOfStorageUse(MRE->getBase(), MRE->getMember(), Flags); return; } if (auto *TEE = dyn_cast(E)) return markStoredOrInOutExpr(TEE->getBase(), Flags); if (auto *FVE = dyn_cast(E)) return markStoredOrInOutExpr(FVE->getSubExpr(), Flags); if (auto *BOE = dyn_cast(E)) return markStoredOrInOutExpr(BOE->getSubExpr(), Flags); // Bind existential expressions. if (auto *OEE = dyn_cast(E)) { OpaqueValueMap[OEE->getOpaqueValue()] = OEE->getExistentialValue(); return markStoredOrInOutExpr(OEE->getSubExpr(), Flags); } // If this is an OpaqueValueExpr that we've seen a mapping for, jump to the // mapped value. if (auto *OVE = dyn_cast(E)) if (auto *expr = OpaqueValueMap[OVE]) return markStoredOrInOutExpr(expr, Flags); if (auto *ABIConv = dyn_cast(E)) return markStoredOrInOutExpr(ABIConv->getSubExpr(), Flags); // If we don't know what kind of expression this is, assume it's a reference // and mark it as a read. E->walk(*this); } /// The heavy lifting happens when visiting expressions. ASTWalker::PreWalkResult VarDeclUsageChecker::walkToExprPre(Expr *E) { STATISTIC(VarDeclUsageCheckerExprVisits, "# of times VarDeclUsageChecker::walkToExprPre is called"); ++VarDeclUsageCheckerExprVisits; // Sema leaves some subexpressions null, which seems really unfortunate. It // should replace them with ErrorExpr. if (E == nullptr || !E->getType() || E->getType()->hasError()) { sawError = true; return Action::SkipNode(E); } assert(AllExprsSeen.insert(E).second && "duplicate traversal"); // If this is a DeclRefExpr found in a random place, it is a load of the // vardecl. if (auto *DRE = dyn_cast(E)) { addMark(DRE->getDecl(), RK_Read); // If the Expression is a read of a getter, track for diagnostics if (auto VD = dyn_cast(DRE->getDecl())) { AssociatedGetterRefExpr.insert(std::make_pair(VD, DRE)); } } // If the Expression is a member reference, see if it is a read of the getter // to track for diagnostics. if (auto *MRE = dyn_cast(E)) { if (auto VD = dyn_cast(MRE->getMember().getDecl())) { AssociatedGetterRefExpr.insert(std::make_pair(VD, MRE)); markBaseOfStorageUse(MRE->getBase(), MRE->getMember(), RK_Read); return Action::SkipNode(E); } } if (auto SE = dyn_cast(E)) { SE->getArgs()->walk(*this); markBaseOfStorageUse(SE->getBase(), SE->getDecl(), RK_Read); return Action::SkipNode(E); } // If this is an AssignExpr, see if we're mutating something that we know // about. if (auto *assign = dyn_cast(E)) { markStoredOrInOutExpr(assign->getDest(), RK_Written); // Don't walk into the LHS of the assignment, only the RHS. assign->getSrc()->walk(*this); return Action::SkipNode(E); } // '&x' is a read and write of 'x'. if (auto *io = dyn_cast(E)) { markStoredOrInOutExpr(io->getSubExpr(), RK_Read|RK_Written); // Don't bother walking into this. return Action::SkipNode(E); } // If we see an OpenExistentialExpr, remember the mapping for its OpaqueValue // and only walk the subexpr. if (auto *oee = dyn_cast(E)) { OpaqueValueMap[oee->getOpaqueValue()] = oee->getExistentialValue(); oee->getSubExpr()->walk(*this); return Action::SkipNode(E); } // Visit bindings. if (auto ove = dyn_cast(E)) { if (auto mapping = OpaqueValueMap.lookup(ove)) mapping->walk(*this); return Action::SkipNode(E); } // If we saw an ErrorExpr, take note of this. if (isa(E)) sawError = true; return Action::Continue(E); } /// Apply the warnings managed by VarDeclUsageChecker to the top level /// code declarations that haven't been checked yet. void swift:: performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) { auto &ctx = TLCD->getDeclContext()->getASTContext(); VarDeclUsageChecker checker(TLCD, ctx.Diags, TLCD->getSourceRange()); TLCD->walk(checker); } /// Perform diagnostics for func/init/deinit declarations. void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) { // Don't produce these diagnostics for implicitly generated code. if (AFD->getLoc().isInvalid() || AFD->isImplicit() || AFD->isInvalid()) return; if (!AFD->getDeclContext()->isLocalContext()) { // Check for unused variables, as well as variables that are could be // declared as constants. Skip local functions though, since they will // be checked as part of their parent function or TopLevelCodeDecl. auto &ctx = AFD->getDeclContext()->getASTContext(); VarDeclUsageChecker checker(AFD, ctx.Diags, AFD->getBody()->getSourceRange()); AFD->walk(checker); } auto *body = AFD->getBody(); // If the function has an opaque return type, check the return expressions // to determine the underlying type. if (auto opaqueResultTy = AFD->getOpaqueResultTypeDecl()) { OpaqueUnderlyingTypeChecker(AFD, opaqueResultTy, body).check(); } else if (auto accessor = dyn_cast(AFD)) { if (accessor->isGetter()) { if (auto opaqueResultTy = accessor->getStorage()->getOpaqueResultTypeDecl()) { OpaqueUnderlyingTypeChecker(AFD, opaqueResultTy, body).check(); } } } else if (auto *FD = dyn_cast(AFD)) { auto resultIFaceTy = FD->getResultInterfaceType(); // If the result has a placeholder, we need to try to use the contextual // type inferred in the body to replace it. if (resultIFaceTy && resultIFaceTy->hasPlaceholder()) { ReturnTypePlaceholderReplacer(FD, body).check(); } } } static void diagnoseCaseStmtAmbiguousWhereClause(const CaseStmt *CS, ASTContext &ctx) { // The case statement can have multiple case items, each can have a where. // If we find a "where", and there is a preceding item without a where, and // if they are on the same source line, e.g // "case .Foo, .Bar where 1 != 100:" then warn since it may be unexpected. auto items = CS->getCaseLabelItems(); // Don't do any work for the vastly most common case. if (items.size() == 1) return; // Ignore the first item, since it can't have preceding ones. for (unsigned i = 1, e = items.size(); i != e; ++i) { // Must have a where clause. auto where = items[i].getGuardExpr(); if (!where) continue; // Preceding item must not. if (items[i - 1].getGuardExpr()) continue; // Must be on the same source line. auto prevLoc = items[i - 1].getStartLoc(); auto thisLoc = items[i].getStartLoc(); if (prevLoc.isInvalid() || thisLoc.isInvalid()) continue; auto &SM = ctx.SourceMgr; auto prevLineCol = SM.getLineAndColumnInBuffer(prevLoc); if (SM.getLineAndColumnInBuffer(thisLoc).first != prevLineCol.first) continue; ctx.Diags .diagnose(items[i].getWhereLoc(), diag::where_on_one_item, CS->getParentKind() == CaseParentKind::DoCatch) .highlight(items[i].getPattern()->getSourceRange()) .highlight(where->getSourceRange()); // Whitespace it out to the same column as the previous item. std::string whitespace(prevLineCol.second - 1, ' '); ctx.Diags.diagnose(thisLoc, diag::add_where_newline) .fixItInsert(thisLoc, "\n" + whitespace); auto whereRange = SourceRange(items[i].getWhereLoc(), where->getEndLoc()); auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, whereRange); auto whereText = SM.extractText(charRange); ctx.Diags.diagnose(prevLoc, diag::duplicate_where) .fixItInsertAfter(items[i - 1].getEndLoc(), " " + whereText.str()) .highlight(items[i - 1].getSourceRange()); } } void swift::fixItEncloseTrailingClosure(ASTContext &ctx, InFlightDiagnostic &diag, const CallExpr *call, Identifier closureLabel) { auto *argList = call->getArgs()->getOriginalArgs(); assert(argList->size() >= 1 && "must have at least one argument"); SmallString<32> replacement; SourceLoc lastLoc; SourceRange closureRange; if (argList->isUnary()) { closureRange = argList->getExpr(0)->getSourceRange(); lastLoc = argList->getLParenLoc(); // e.g funcName() { 1 } if (!lastLoc.isValid()) { // Bare trailing closure: e.g. funcName { 1 } replacement = "("; lastLoc = call->getFn()->getEndLoc(); } } else { // Tuple + trailing closure: e.g. funcName(x: 1) { 1 } auto numElements = argList->size(); closureRange = argList->getExpr(numElements - 1)->getSourceRange(); lastLoc = argList->getExpr(numElements - 2)->getEndLoc(); replacement = ", "; } // Add argument label of the closure. if (!closureLabel.empty()) { replacement += closureLabel.str(); replacement += ": "; } lastLoc = Lexer::getLocForEndOfToken(ctx.SourceMgr, lastLoc); diag .fixItReplaceChars(lastLoc, closureRange.Start, replacement) .fixItInsertAfter(closureRange.End, ")"); } // Perform checkStmtConditionTrailingClosure for single expression. static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) { if (E == nullptr || isa(E)) return; // Walk into expressions which might have invalid trailing closures class DiagnoseWalker : public BaseDiagnosticWalker { ASTContext &Ctx; void diagnoseIt(const CallExpr *E) { // FIXME(https://github.com/apple/swift/issues/57382): We ought to handle multiple trailing closures here. auto *args = E->getArgs()->getOriginalArgs(); if (args->getNumTrailingClosures() != 1) return; auto closureArg = *args->getFirstTrailingClosure(); auto *closureExpr = closureArg.getExpr(); auto closureTy = closureExpr->getType(); // Ignore invalid argument type. Some diagnostics are already emitted. if (!closureTy || closureTy->hasError()) return; // Figure out the label of the parameter the closure is being passed to. // This will be present in the type-checked argument list (but not the // original), so search it for the relevant argument, looking into // variadic expansions if necessary. Identifier label; for (auto arg : *E->getArgs()) { if (arg.getExpr() == closureExpr) { label = arg.getLabel(); break; } if (auto *varg = dyn_cast(arg.getExpr())) { if (auto *array = dyn_cast(varg->getSubExpr())) { if (!array->getElements().empty() && array->getElements()[0] == closureExpr) { label = arg.getLabel(); break; } } } } auto diag = Ctx.Diags.diagnose(closureExpr->getStartLoc(), diag::trailing_closure_requires_parens); fixItEncloseTrailingClosure(Ctx, diag, E, label); } public: DiagnoseWalker(ASTContext &ctx) : Ctx(ctx) { } PreWalkResult walkToArgumentListPre(ArgumentList *args) override { // Don't walk into an explicit argument list, as trailing closures that // appear in child arguments are fine. return Action::VisitNodeIf(args->isImplicit(), args); } PreWalkResult walkToExprPre(Expr *E) override { switch (E->getKind()) { case ExprKind::Paren: case ExprKind::Tuple: case ExprKind::Array: case ExprKind::Dictionary: case ExprKind::InterpolatedStringLiteral: case ExprKind::Closure: // If a trailing closure appears as a child of one of these types of // expression, don't diagnose it as there is no ambiguity. return Action::VisitNodeIf(E->isImplicit(), E); case ExprKind::Call: diagnoseIt(cast(E)); break; default: break; } return Action::Continue(E); } }; DiagnoseWalker Walker(ctx); const_cast(E)->walk(Walker); } /// Diagnose trailing closure in statement-conditions. /// /// Conditional statements, including 'for' or `switch` doesn't allow ambiguous /// trailing closures in these conditions part. Even if the parser can recover /// them, we force them to disambiguate. // /// E.g.: /// if let _ = arr?.map {$0+1} { ... } /// for _ in numbers.filter {$0 > 4} { ... } static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Stmt *S) { if (auto LCS = dyn_cast(S)) { for (auto elt : LCS->getCond()) { if (elt.getKind() == StmtConditionElement::CK_PatternBinding) { checkStmtConditionTrailingClosure(ctx, elt.getInitializer()); if (auto *exprPattern = dyn_cast(elt.getPattern())) { checkStmtConditionTrailingClosure(ctx, exprPattern->getMatchExpr()); } } else if (elt.getKind() == StmtConditionElement::CK_Boolean) checkStmtConditionTrailingClosure(ctx, elt.getBoolean()); // No trailing closure for CK_Availability: e.g. `if #available() {}`. } } else if (auto SS = dyn_cast(S)) { checkStmtConditionTrailingClosure(ctx, SS->getSubjectExpr()); } else if (auto FES = dyn_cast(S)) { checkStmtConditionTrailingClosure(ctx, FES->getParsedSequence()); checkStmtConditionTrailingClosure(ctx, FES->getWhere()); } else if (auto DCS = dyn_cast(S)) { for (auto CS : DCS->getCatches()) for (auto &LabelItem : CS->getCaseLabelItems()) checkStmtConditionTrailingClosure(ctx, LabelItem.getGuardExpr()); } } namespace { class ObjCSelectorWalker : public BaseDiagnosticWalker { ASTContext &Ctx; const DeclContext *DC; Type SelectorTy; /// Determine whether a reference to the given method via its /// enclosing class/protocol is ambiguous (and, therefore, needs to /// be disambiguated with a coercion). bool isSelectorReferenceAmbiguous(AbstractFunctionDecl *method) { // Determine the name we would search for. If there are no // argument names, our lookup will be based solely on the base // name. DeclName lookupName = method->getName(); if (lookupName.getArgumentNames().empty()) lookupName = lookupName.getBaseName(); // Look for members with the given name. auto nominal = method->getDeclContext()->getSelfNominalTypeDecl(); auto result = TypeChecker::lookupMember( const_cast(DC), nominal->getDeclaredInterfaceType(), DeclNameRef(lookupName), method->getLoc(), defaultMemberLookupOptions); // If we didn't find multiple methods, there is no ambiguity. if (result.size() < 2) return false; // If we found more than two methods, it's ambiguous. if (result.size() > 2) return true; // Dig out the methods. auto firstMethod = dyn_cast(result[0].getValueDecl()); auto secondMethod = dyn_cast(result[1].getValueDecl()); if (!firstMethod || !secondMethod) return true; // If one is a static/class method and the other is not... if (firstMethod->isStatic() == secondMethod->isStatic()) return true; // ... overload resolution will prefer the static method. Check // that it has the correct selector. We don't even care that it's // the same method we're asking for, just that it has the right // selector. FuncDecl *staticMethod = firstMethod->isStatic() ? firstMethod : secondMethod; return staticMethod->getObjCSelector() != method->getObjCSelector(); } public: ObjCSelectorWalker(const DeclContext *dc, Type selectorTy) : Ctx(dc->getASTContext()), DC(dc), SelectorTy(selectorTy) { } PreWalkResult walkToExprPre(Expr *expr) override { auto *stringLiteral = dyn_cast(expr); bool fromStringLiteral = false; bool hadParens = false; if (stringLiteral) { // Is this a string literal that has type 'Selector'. if (!stringLiteral->getType() || !stringLiteral->getType()->isEqual(SelectorTy)) return Action::Continue(expr); fromStringLiteral = true; // FIXME: hadParens } else { // Is this an initialization of 'Selector'? auto call = dyn_cast(expr); if (!call) return Action::Continue(expr); // That produce Selectors. if (!call->getType() || !call->getType()->isEqual(SelectorTy)) return Action::Continue(expr); // Via a constructor. ConstructorDecl *ctor = nullptr; if (auto ctorRefCall = dyn_cast(call->getFn())) { if (auto ctorRef = dyn_cast(ctorRefCall->getFn())) ctor = dyn_cast(ctorRef->getDecl()); else if (auto otherCtorRef = dyn_cast(ctorRefCall->getFn())) ctor = otherCtorRef->getDecl(); } if (!ctor) return Action::Continue(expr); // Make sure the constructor is within Selector. auto ctorContextType = ctor->getDeclContext() ->getSelfNominalTypeDecl() ->getDeclaredType(); if (!ctorContextType || !ctorContextType->isEqual(SelectorTy)) return Action::Continue(expr); auto argNames = ctor->getName().getArgumentNames(); if (argNames.size() != 1) return Action::Continue(expr); // Is this the init(stringLiteral:) initializer or init(_:) initializer? if (argNames[0] == Ctx.Id_stringLiteral) fromStringLiteral = true; else if (!argNames[0].empty()) return Action::Continue(expr); auto *arg = call->getArgs()->getUnaryExpr(); if (!arg) return Action::Continue(expr); // Track whether we had parentheses around the string literal. if (auto paren = dyn_cast(arg)) { hadParens = true; arg = paren->getSubExpr(); } // Check whether we have a string literal. stringLiteral = dyn_cast(arg); if (!stringLiteral) return Action::Continue(expr); } /// Retrieve the parent expression that coerces to Selector, if /// there is one. auto getParentCoercion = [&]() -> CoerceExpr * { auto parentExpr = Parent.getAsExpr(); if (!parentExpr) return nullptr; auto coerce = dyn_cast(parentExpr); if (!coerce) return nullptr; if (coerce->getType() && coerce->getType()->isEqual(SelectorTy)) return coerce; return nullptr; }; // Local function that adds the constructor syntax around string // literals implicitly treated as a Selector. auto addSelectorConstruction = [&](InFlightDiagnostic &diag) { if (!fromStringLiteral) return; // Introduce the beginning part of the Selector construction. diag.fixItInsert(stringLiteral->getLoc(), "Selector("); if (auto coerce = getParentCoercion()) { // If the string literal was coerced to Selector, replace the // coercion with the ")". SourceLoc endLoc = Lexer::getLocForEndOfToken(Ctx.SourceMgr, expr->getEndLoc()); diag.fixItReplace(SourceRange(endLoc, coerce->getEndLoc()), ")"); } else { // Otherwise, just insert the closing ")". diag.fixItInsertAfter(stringLiteral->getEndLoc(), ")"); } }; // Try to parse the string literal as an Objective-C selector, and complain // if it isn't one. auto selector = ObjCSelector::parse(Ctx, stringLiteral->getValue()); if (!selector) { auto diag = Ctx.Diags.diagnose(stringLiteral->getLoc(), diag::selector_literal_invalid); diag.highlight(stringLiteral->getSourceRange()); addSelectorConstruction(diag); return Action::Continue(expr); } // Look for methods with this selector. SmallVector allMethods; DC->lookupAllObjCMethods(*selector, allMethods); // If we didn't find any methods, complain. if (allMethods.empty()) { // If this was Selector(("selector-name")), suppress, the // diagnostic. if (!fromStringLiteral && hadParens) return Action::Continue(expr); { auto diag = Ctx.Diags.diagnose(stringLiteral->getLoc(), diag::selector_literal_undeclared, *selector); addSelectorConstruction(diag); } // If the result was from a Selector("selector-name"), add a // separate note that suggests wrapping the selector in // parentheses to silence the warning. if (!fromStringLiteral) { Ctx.Diags.diagnose(stringLiteral->getLoc(), diag::selector_construction_suppress_warning) .fixItInsert(stringLiteral->getStartLoc(), "(") .fixItInsertAfter(stringLiteral->getEndLoc(), ")"); } return Action::Continue(expr); } // Find the "best" method that has this selector, so we can report // that. AbstractFunctionDecl *bestMethod = nullptr; for (auto method : allMethods) { // If this is the first method, use it. if (!bestMethod) { bestMethod = method; continue; } // If referencing the best method would produce an ambiguity and // referencing the new method would not, we have a new "best". if (isSelectorReferenceAmbiguous(bestMethod) && !isSelectorReferenceAmbiguous(method)) { bestMethod = method; continue; } // If this method is within a protocol... if (auto proto = method->getDeclContext()->getSelfProtocolDecl()) { // If the best so far is not from a protocol, or is from a // protocol that inherits this protocol, we have a new best. auto bestProto = bestMethod->getDeclContext()->getSelfProtocolDecl(); if (!bestProto || bestProto->inheritsFrom(proto)) bestMethod = method; continue; } // This method is from a class. auto classDecl = method->getDeclContext()->getSelfClassDecl(); // If the best method was from a protocol, keep it. auto bestClassDecl = bestMethod->getDeclContext()->getSelfClassDecl(); if (!bestClassDecl) continue; // If the best method was from a subclass of the place where // this method was declared, we have a new best. if (classDecl->isSuperclassOf(bestClassDecl)) { bestMethod = method; } } // If we have a best method, reference it. if (bestMethod) { // Form the replacement #selector expression. SmallString<32> replacement; { llvm::raw_svector_ostream out(replacement); auto nominal = bestMethod->getDeclContext()->getSelfNominalTypeDecl(); out << "#selector("; DeclName name; auto bestAccessor = dyn_cast(bestMethod); if (bestAccessor) { switch (bestAccessor->getAccessorKind()) { case AccessorKind::Get: out << "getter: "; name = bestAccessor->getStorage()->getName(); break; case AccessorKind::DistributedGet: out << "_distributed_getter: "; name = bestAccessor->getStorage()->getName(); break; case AccessorKind::Set: case AccessorKind::WillSet: case AccessorKind::DidSet: out << "setter: "; name = bestAccessor->getStorage()->getName(); break; case AccessorKind::Address: case AccessorKind::MutableAddress: case AccessorKind::Read: case AccessorKind::Read2: case AccessorKind::Modify: case AccessorKind::Modify2: case AccessorKind::Init: llvm_unreachable("cannot be @objc"); } } else { name = bestMethod->getName(); } auto typeName = nominal->getName().str(); // If we're inside a type Foo (or an extension of it) and the suggestion // is going to be #selector(Foo.bar) (or #selector(SuperclassOfFoo.bar), // then suggest the more natural #selector(self.bar) instead. if (auto containingTypeContext = DC->getInnermostTypeContext()) { auto methodNominalType = nominal->getDeclaredType(); auto outerNomType = containingTypeContext->getSelfNominalTypeDecl() ->getDeclaredType(); if (methodNominalType->isEqual(outerNomType) || methodNominalType->isExactSuperclassOf(outerNomType)) typeName = "self"; } out << typeName << "." << name.getBaseName(); auto argNames = name.getArgumentNames(); // Only print the parentheses if there are some argument // names, because "()" would indicate a call. if (!argNames.empty()) { out << "("; for (auto argName : argNames) { if (argName.empty()) out << "_"; else out << argName.str(); out << ":"; } out << ")"; } // If there will be an ambiguity when referring to the method, // introduce a coercion to resolve it to the method we found. if (!bestAccessor && isSelectorReferenceAmbiguous(bestMethod)) { if (auto fnType = bestMethod->getInterfaceType()->getAs()) { // For static/class members, drop the metatype argument. if (bestMethod->isStatic()) fnType = fnType->getResult()->getAs(); // Coerce to this type. assert(fnType->hasTypeRepr() && "Objective-C methods should always have printable types"); out << " as "; fnType->print(out); } } out << ")"; } // Emit the diagnostic. SourceRange replacementRange = expr->getSourceRange(); if (auto coerce = getParentCoercion()) replacementRange.End = coerce->getEndLoc(); Ctx.Diags .diagnose(expr->getLoc(), fromStringLiteral ? diag::selector_literal_deprecated_suggest : diag::selector_construction_suggest) .fixItReplace(replacementRange, replacement); return Action::Continue(expr); } // If we couldn't pick a method to use for #selector, just wrap // the string literal in Selector(...). if (fromStringLiteral) { auto diag = Ctx.Diags.diagnose(stringLiteral->getLoc(), diag::selector_literal_deprecated); addSelectorConstruction(diag); return Action::Continue(expr); } return Action::Continue(expr); } }; } // end anonymous namespace static void diagDeprecatedObjCSelectors(const DeclContext *dc, const Expr *expr) { auto selectorTy = dc->getASTContext().getSelectorType(); if (!selectorTy) return; const_cast(expr)->walk(ObjCSelectorWalker(dc, selectorTy)); } /// Skip over syntactic patterns that aren't typed patterns. static Pattern *skipNonTypeSyntacticPatterns(Pattern *pattern) { if (auto *pp = dyn_cast(pattern)) return skipNonTypeSyntacticPatterns(pp->getSubPattern()); if (auto *vp = dyn_cast(pattern)) return skipNonTypeSyntacticPatterns(vp->getSubPattern()); return pattern; } /// Diagnose things like this, where 'i' is an Int, not an Int? /// if let x: Int = i { static void checkImplicitPromotionsInCondition(const StmtConditionElement &cond, ASTContext &ctx) { auto *p = cond.getPatternOrNull(); if (!p) return; if (auto *subExpr = isImplicitPromotionToOptional(cond.getInitializer())) { // If the subexpression was actually optional, then the pattern must be // checking for a type, which forced it to be promoted to a double optional // type. if (auto ooType = subExpr->getType()->getOptionalObjectType()) { if (auto OSP = dyn_cast(p)) { // Check for 'if let' to produce a tuned diagnostic. if (auto *TP = dyn_cast(OSP->getSubPattern())) { ctx.Diags.diagnose(cond.getIntroducerLoc(), diag::optional_check_promotion, subExpr->getType()) .highlight(subExpr->getSourceRange()) .fixItReplace(TP->getTypeRepr()->getSourceRange(), ooType->getString()); return; } } ctx.Diags.diagnose(cond.getIntroducerLoc(), diag::optional_pattern_match_promotion, subExpr->getType(), cond.getInitializer()->getType()) .highlight(subExpr->getSourceRange()); return; } // Check for 'if let' to produce a tuned diagnostic. if (isa(skipNonTypeSyntacticPatterns(p))) { ctx.Diags.diagnose( cond.getIntroducerLoc(), p->isImplicit() ? diag::condition_optional_element_pattern_not_valid_type : diag::optional_element_pattern_not_valid_type, subExpr->getType()) .highlight(subExpr->getSourceRange()); return; } ctx.Diags.diagnose(cond.getIntroducerLoc(), diag::optional_check_nonoptional, subExpr->getType()) .highlight(subExpr->getSourceRange()); } } /// Diagnoses a `if #available(...)` condition. Returns true if a diagnostic /// was emitted. static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info, DeclContext *DC) { if (info->isInvalid()) return false; auto &diags = DC->getASTContext().Diags; StringRef queryName = info->isUnavailability() ? "#unavailable" : "#available"; bool hasValidSpecs = false; bool allValidSpecsArePlatform = true; std::optional wildcardLoc; llvm::SmallSet seenDomains; for (auto spec : info->getSemanticAvailabilitySpecs(DC)) { auto parsedSpec = spec.getParsedSpec(); if (spec.isWildcard()) { wildcardLoc = parsedSpec->getStartLoc(); continue; } auto domain = spec.getDomain(); auto loc = parsedSpec->getStartLoc(); bool hasVersion = !spec.getVersion().empty(); if (!domain.supportsQueries()) { diags.diagnose(loc, diag::availability_query_not_allowed, domain, hasVersion, queryName); return true; } if (!domain.isPlatform() && info->getQueries().size() > 1) { diags.diagnose(loc, diag::availability_must_occur_alone, domain, hasVersion); return true; } if (hasVersion) { auto rawVersion = parsedSpec->getRawVersion(); if (!VersionRange::isValidVersion(rawVersion)) { diags .diagnose(loc, diag::availability_unsupported_version_number, rawVersion) .highlight(parsedSpec->getVersionSrcRange()); return true; } } if (domain.isVersioned()) { if (!hasVersion) { diags.diagnose(loc, diag::avail_query_expected_version_number); return true; } } else if (hasVersion) { diags.diagnose(loc, diag::availability_unexpected_version, domain) .highlight(parsedSpec->getVersionSrcRange()); return true; } // Diagnose duplicate domains. if (!seenDomains.insert(domain).second) { diags.diagnose(loc, diag::availability_query_already_specified, domain.isVersioned(), domain); return true; } hasValidSpecs = true; if (!domain.isPlatform()) allValidSpecsArePlatform = false; } if (info->isUnavailability()) { if (wildcardLoc) { diags .diagnose(*wildcardLoc, diag::unavailability_query_wildcard_not_required) .fixItRemove(*wildcardLoc); } } else if (!wildcardLoc && hasValidSpecs && allValidSpecsArePlatform) { if (info->getQueries().size() > 0) { auto insertLoc = info->getQueries().back()->getSourceRange().End; diags.diagnose(insertLoc, diag::availability_query_wildcard_required) .fixItInsertAfter(insertLoc, ", *"); } } // Reject inlinable code using availability macros. In order to lift this // restriction, macros would need to either be expanded when printed in // swiftinterfaces or be parsable as macros by module clients. auto fragileKind = DC->getFragileFunctionKind(); if (fragileKind.kind != FragileFunctionKind::None) { for (auto availSpec : info->getQueries()) { if (availSpec->getMacroLoc().isValid()) { diags.diagnose(availSpec->getMacroLoc(), swift::diag::availability_macro_in_inlinable, fragileKind.getSelector()); return true; } } } return false; } /// Diagnoses whether the given clang::Decl can be referenced by a /// `if #_hasSymbol(...)` condition. Returns true if a diagnostic was emitted. static bool diagnoseHasSymbolConditionClangDecl(SourceLoc loc, const clang::Decl *clangDecl, ASTContext &ctx) { if (isa(clangDecl) || isa(clangDecl)) return false; if (auto *method = dyn_cast(clangDecl)) { // FIXME: Allow objc_direct methods when supported by IRGen. ctx.Diags.diagnose(loc, diag::has_symbol_invalid_decl_use_responds_to_selector, /*isProperty*/ false, method->getNameAsString()); return true; } if (auto *property = dyn_cast(clangDecl)) { // FIXME: Allow objc_direct properties when supported by IRGen. ctx.Diags.diagnose(loc, diag::has_symbol_invalid_decl_use_responds_to_selector, /*isProperty*/ true, property->getNameAsString()); return true; } ctx.Diags.diagnose(loc, diag::has_symbol_invalid_decl); return true; } /// Diagnoses a `if #_hasSymbol(...)` condition. Returns true if a diagnostic /// was emitted. static bool diagnoseHasSymbolCondition(PoundHasSymbolInfo *info, DeclContext *DC) { // If we have an invalid info, null expression, or expression without a type // then type checking failed already for this condition. if (info->isInvalid()) return false; auto symbolExpr = info->getSymbolExpr(); if (!symbolExpr) return false; if (!symbolExpr->getType()) return false; auto &ctx = DC->getASTContext(); auto decl = info->getReferencedDecl().getDecl(); if (!decl) { // Diagnose because we weren't able to interpret the expression as one // that uniquely identifies a single declaration. ctx.Diags.diagnose(symbolExpr->getLoc(), diag::has_symbol_invalid_expr); return true; } if (auto *clangDecl = decl->getClangDecl()) { if (diagnoseHasSymbolConditionClangDecl(symbolExpr->getLoc(), clangDecl, ctx)) return true; } if (DC->getFragileFunctionKind().kind == FragileFunctionKind::None && !decl->isWeakImported(DC->getParentModule())) { // `if #_hasSymbol(someStronglyLinkedSymbol)` is functionally a no-op // and may indicate the developer has mis-identified the declaration // they want to check (or forgot to import the module weakly). ctx.Diags.diagnose(symbolExpr->getLoc(), diag::has_symbol_decl_must_be_weak, decl); return true; } return false; } /// Perform MiscDiagnostics for the conditions belonging to a \c /// LabeledConditionalStmt. static void checkLabeledStmtConditions(ASTContext &ctx, const LabeledConditionalStmt *stmt, DeclContext *DC) { for (auto elt : stmt->getCond()) { // Check for implicit optional promotions in stmt-condition patterns. checkImplicitPromotionsInCondition(elt, ctx); switch (elt.getKind()) { case StmtConditionElement::CK_Boolean: case StmtConditionElement::CK_PatternBinding: break; case StmtConditionElement::CK_Availability: { auto info = elt.getAvailability(); if (diagnoseAvailabilityCondition(info, DC)) info->setInvalid(); break; } case StmtConditionElement::CK_HasSymbol: { auto info = elt.getHasSymbolInfo(); if (diagnoseHasSymbolCondition(info, DC)) info->setInvalid(); break; } } } } static void diagnoseUnintendedOptionalBehavior(const Expr *E, const DeclContext *DC) { if (!E || isa(E) || !E->getType()) return; class UnintendedOptionalBehaviorWalker : public BaseDiagnosticWalker { ASTContext &Ctx; SmallPtrSet IgnoredExprs; class OptionalToAnyCoercion { public: Type DestType; CoerceExpr *ParentCoercion; bool shouldSuppressDiagnostic() { // If we have a parent CoerceExpr that has the same type as our // Optional-to-Any coercion, don't emit a diagnostic. return ParentCoercion && ParentCoercion->getType()->isEqual(DestType); } }; /// Returns true iff a coercion from srcType to destType is an /// Optional-to-Any coercion. bool isOptionalToAnyCoercion(Type srcType, Type destType) { size_t difference = 0; return isOptionalToAnyCoercion(srcType, destType, difference); } /// Returns true iff a coercion from srcType to destType is an /// Optional-to-Any coercion. On returning true, the value of 'difference' /// will be the difference in the levels of optionality. bool isOptionalToAnyCoercion(Type srcType, Type destType, size_t &difference) { SmallVector destOptionals; auto destValueType = destType->lookThroughAllOptionalTypes(destOptionals); if (!destValueType->isAny()) return false; SmallVector srcOptionals; srcType->lookThroughAllOptionalTypes(srcOptionals); if (srcOptionals.size() > destOptionals.size()) { difference = srcOptionals.size() - destOptionals.size(); return true; } else { return false; } } /// Returns true iff the collection upcast coercion is an Optional-to-Any /// coercion. bool isOptionalToAnyCoercion(CollectionUpcastConversionExpr::ConversionPair conversion) { if (!conversion.OrigValue || !conversion.Conversion) return false; auto srcType = conversion.OrigValue->getType(); auto destType = conversion.Conversion->getType(); return isOptionalToAnyCoercion(srcType, destType); } /// Looks through OptionalEvaluationExprs and InjectIntoOptionalExprs to /// find a child ErasureExpr, returning nullptr if no such child is found. /// Any intermediate OptionalEvaluationExprs will be marked as ignored. ErasureExpr *findErasureExprThroughOptionalInjections(Expr *E) { while (true) { if (auto *next = dyn_cast(E)) { // We don't want to re-visit any intermediate optional evaluations. IgnoredExprs.insert(next); E = next->getSubExpr(); } else if (auto *next = dyn_cast(E)) { E = next->getSubExpr(); } else { break; } } return dyn_cast(E); } void emitSilenceOptionalAnyWarningWithCoercion(Expr *E, Type destType) { assert(destType->hasTypeRepr() && "coercion to Any should always be printable"); SmallString<16> coercionString; coercionString += " as "; coercionString += destType->getString(); Ctx.Diags.diagnose(E->getLoc(), diag::silence_optional_to_any, destType, coercionString.substr(1)) .highlight(E->getSourceRange()) .fixItInsertAfter(E->getEndLoc(), coercionString); } static bool hasImplicitlyUnwrappedResult(Expr *E) { auto *decl = getDeclForImplicitlyUnwrappedExpr(E); return decl && decl->isImplicitlyUnwrappedOptional(); } static ValueDecl *getDeclForImplicitlyUnwrappedExpr(Expr *E) { E = E->getValueProvidingExpr(); // Look through implicit conversions like loads, derived-to-base // conversion, etc. if (auto *ICE = dyn_cast(E)) { E = ICE->getSubExpr(); } if (auto *subscript = dyn_cast(E)) { if (subscript->hasDecl()) return subscript->getDecl().getDecl(); return nullptr; } if (auto *memberRef = dyn_cast(E)) return memberRef->getMember().getDecl(); if (auto *declRef = dyn_cast(E)) return declRef->getDecl(); if (auto *apply = dyn_cast(E)) { auto *decl = apply->getCalledValue(/*skipFunctionConversions=*/true); if (isa_and_nonnull(decl)) return decl; } return nullptr; } void visitErasureExpr(ErasureExpr *E, OptionalToAnyCoercion coercion) { if (coercion.shouldSuppressDiagnostic()) return; auto subExpr = E->getSubExpr(); // Look through any BindOptionalExprs, as the coercion may have started // from a higher level of optionality. while (auto *bindExpr = dyn_cast(subExpr)) subExpr = bindExpr->getSubExpr(); // Do not warn on coercions from implicitly unwrapped optionals // for Swift versions less than 5. if (!Ctx.isSwiftVersionAtLeast(5) && hasImplicitlyUnwrappedResult(subExpr)) return; // We're taking the source type from the child of any BindOptionalExprs, // and the destination from the parent of any // (InjectIntoOptional/OptionalEvaluation)Exprs in order to take into // account any bindings that need to be done for nested Optional-to-Any // coercions, e.g Int??? to Any?. auto srcType = subExpr->getType(); auto destType = coercion.DestType; size_t optionalityDifference = 0; if (!isOptionalToAnyCoercion(srcType, destType, optionalityDifference)) return; // If we're implicitly unwrapping from IUO to Any then emit a custom // diagnostic if (hasImplicitlyUnwrappedResult(subExpr)) { if (auto decl = getDeclForImplicitlyUnwrappedExpr(subExpr)) { Ctx.Diags.diagnose(subExpr->getStartLoc(), diag::iuo_to_any_coercion, /* from */ srcType, /* to */ destType) .highlight(subExpr->getSourceRange()); auto noteDiag = isa(decl) ? diag::iuo_to_any_coercion_note_func_result : diag::iuo_to_any_coercion_note; Ctx.Diags.diagnose(decl->getLoc(), noteDiag, decl); } } else { Ctx.Diags.diagnose(subExpr->getStartLoc(), diag::optional_to_any_coercion, /* from */ srcType, /* to */ destType) .highlight(subExpr->getSourceRange()); } if (optionalityDifference == 1) { Ctx.Diags.diagnose(subExpr->getLoc(), diag::default_optional_to_any) .highlight(subExpr->getSourceRange()) .fixItInsertAfter(subExpr->getEndLoc(), " ?? <#default value#>"); } SmallString<4> forceUnwrapString; for (size_t i = 0; i < optionalityDifference; ++i) forceUnwrapString += "!"; Ctx.Diags.diagnose(subExpr->getLoc(), diag::force_optional_to_any) .highlight(subExpr->getSourceRange()) .fixItInsertAfter(subExpr->getEndLoc(), forceUnwrapString); emitSilenceOptionalAnyWarningWithCoercion(subExpr, destType); } void visitCollectionUpcastExpr(CollectionUpcastConversionExpr *E, OptionalToAnyCoercion coercion) { // We only need to consider the valueConversion, as the Key type of a // Dictionary cannot be implicitly coerced to Any. auto valueConversion = E->getValueConversion(); // We're handling the coercion of the entire collection, so we don't need // to re-visit a nested ErasureExpr for the value. if (auto conversionExpr = valueConversion.Conversion) if (auto *erasureExpr = findErasureExprThroughOptionalInjections(conversionExpr)) IgnoredExprs.insert(erasureExpr); if (coercion.shouldSuppressDiagnostic() || !isOptionalToAnyCoercion(valueConversion)) return; auto subExpr = E->getSubExpr(); Ctx.Diags.diagnose(subExpr->getStartLoc(), diag::optional_to_any_coercion, /* from */ subExpr->getType(), /* to */ E->getType()) .highlight(subExpr->getSourceRange()); emitSilenceOptionalAnyWarningWithCoercion(subExpr, E->getType()); } void visitPossibleOptionalToAnyExpr(Expr *E, OptionalToAnyCoercion coercion) { if (auto *upcastExpr = dyn_cast(E)) { visitCollectionUpcastExpr(upcastExpr, coercion); } else if (auto *erasureExpr = dyn_cast(E)) { visitErasureExpr(erasureExpr, coercion); } else if (auto *optionalEvalExpr = dyn_cast(E)) { // The ErasureExpr could be nested within optional injections and // bindings, such as is the case for e.g Int??? to Any?. Try and find // and visit it directly, making sure we don't re-visit it later. auto subExpr = optionalEvalExpr->getSubExpr(); if (auto *erasureExpr = findErasureExprThroughOptionalInjections(subExpr)) { visitErasureExpr(erasureExpr, coercion); IgnoredExprs.insert(erasureExpr); } } } enum class UnintendedInterpolationKind: bool { Optional, Function }; void visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E) { E->forEachSegment(Ctx, [&](bool isInterpolation, CallExpr *segment) -> void { if (isInterpolation) { diagnoseIfUnintendedInterpolation(segment, UnintendedInterpolationKind::Optional); diagnoseIfUnintendedInterpolation(segment, UnintendedInterpolationKind::Function); } }); } void diagnoseIfUnintendedInterpolation(CallExpr *segment, UnintendedInterpolationKind kind) { if (interpolationWouldBeUnintended( segment->getCalledValue(/*skipFunctionConversions=*/true), kind)) if (auto firstArg = getFirstArgIfUnintendedInterpolation(segment->getArgs(), kind)) diagnoseUnintendedInterpolation(firstArg, kind); } bool interpolationWouldBeUnintended(ConcreteDeclRef appendMethod, UnintendedInterpolationKind kind) { ValueDecl * fnDecl = appendMethod.getDecl(); // If things aren't set up right, just hope for the best. if (!fnDecl || fnDecl->isInvalid()) return false; // If the decl expects an optional, that's fine. auto uncurriedType = fnDecl->getInterfaceType()->getAs(); auto curriedType = uncurriedType->getResult()->getAs(); // I don't know why you'd use a zero-arg interpolator, but it obviously // doesn't interpolate an optional. if (curriedType->getNumParams() == 0) return false; // If the first parameter explicitly accepts the type, this method // presumably doesn't want us to warn about optional use. auto firstParamType = curriedType->getParams().front().getPlainType()->getRValueType(); if (kind == UnintendedInterpolationKind::Optional) { if (firstParamType->getOptionalObjectType()) return false; } else { if (firstParamType->is()) return false; } return true; } Expr * getFirstArgIfUnintendedInterpolation(ArgumentList *args, UnintendedInterpolationKind kind) { // Just check the first argument, which is usually the value // being interpolated. if (args->empty()) return nullptr; auto *firstArg = args->getExpr(0); // Allow explicit casts. if (isa(firstArg->getSemanticsProvidingExpr())) return nullptr; // If we don't have a type, assume the best. if (!firstArg->getType() || firstArg->getType()->hasError()) return nullptr; // Bail out if we don't have an optional. if (kind == UnintendedInterpolationKind::Optional) { if (!firstArg->getType()->getRValueType()->getOptionalObjectType()) return nullptr; } else if (kind == UnintendedInterpolationKind::Function) { if (!firstArg->getType()->getRValueType()->is()) return nullptr; } return firstArg; } void diagnoseUnintendedInterpolation(Expr * arg, UnintendedInterpolationKind kind) { Ctx.Diags .diagnose(arg->getStartLoc(), diag::debug_description_in_string_interpolation_segment, (bool)kind) .highlight(arg->getSourceRange()); // Suggest 'String(describing: )'. auto argStart = arg->getStartLoc(); Ctx.Diags .diagnose( arg->getLoc(), diag::silence_debug_description_in_interpolation_segment_call) .highlight(arg->getSourceRange()) .fixItInsert(argStart, "String(describing: ") .fixItInsertAfter(arg->getEndLoc(), ")"); if (kind == UnintendedInterpolationKind::Optional) { // Suggest inserting a default value. Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_to_any) .highlight(arg->getSourceRange()) .fixItInsertAfter(arg->getEndLoc(), " ?? <#default value#>"); } } PreWalkResult walkToExprPre(Expr *E) override { if (!E || isa(E) || !E->getType()) return Action::SkipNode(E); if (IgnoredExprs.count(E)) return Action::Continue(E); if (auto *literal = dyn_cast(E)) { visitInterpolatedStringLiteralExpr(literal); } else if (auto *coercion = dyn_cast(E)) { // If we come across a CoerceExpr, visit its subExpr with the coercion // as the parent, making sure we don't re-visit the subExpr later. auto subExpr = coercion->getSubExpr(); visitPossibleOptionalToAnyExpr(subExpr, { subExpr->getType(), coercion }); IgnoredExprs.insert(subExpr); } else { visitPossibleOptionalToAnyExpr(E, { E->getType(), nullptr }); } return Action::Continue(E); } public: UnintendedOptionalBehaviorWalker(ASTContext &ctx) : Ctx(ctx) { } }; UnintendedOptionalBehaviorWalker Walker(DC->getASTContext()); const_cast(E)->walk(Walker); } static void diagnoseDeprecatedWritableKeyPath(const Expr *E, const DeclContext *DC) { if (!E || isa(E) || !E->getType()) return; class DeprecatedWritableKeyPathWalker : public BaseDiagnosticWalker { ASTContext &Ctx; const DeclContext *DC; void visitKeyPathApplicationExpr(KeyPathApplicationExpr *E) { bool isWrite = false; if (auto *P = Parent.getAsExpr()) if (auto *AE = dyn_cast(P)) if (AE->getDest() == E) isWrite = true; if (!isWrite) return; if (auto *keyPathExpr = dyn_cast(E->getKeyPath())) { if (!keyPathExpr->getType()->isWritableKeyPath() && !keyPathExpr->getType()->isReferenceWritableKeyPath()) return; assert(keyPathExpr->getComponents().size() > 0); auto &component = keyPathExpr->getComponents().back(); if (component.getKind() == KeyPathExpr::Component::Kind::Member) { auto *storage = cast(component.getDeclRef().getDecl()); if (!storage->isSettable(nullptr) || !storage->isSetterAccessibleFrom(DC)) { Ctx.Diags.diagnose(keyPathExpr->getLoc(), swift::diag::expr_deprecated_writable_keypath, storage); } } } } PreWalkResult walkToExprPre(Expr *E) override { if (!E || isa(E) || !E->getType()) return Action::SkipNode(E); if (auto *KPAE = dyn_cast(E)) { visitKeyPathApplicationExpr(KPAE); return Action::Continue(E); } return Action::Continue(E); } public: DeprecatedWritableKeyPathWalker(const DeclContext *DC) : Ctx(DC->getASTContext()), DC(DC) {} }; DeprecatedWritableKeyPathWalker Walker(DC); const_cast(E)->walk(Walker); } static void maybeDiagnoseCallToKeyValueObserveMethod(const Expr *E, const DeclContext *DC) { class KVOObserveCallWalker : public BaseDiagnosticWalker { const ASTContext &C; public: KVOObserveCallWalker(ASTContext &ctx) : C(ctx) {} void maybeDiagnoseCallExpr(CallExpr *expr) { auto fn = expr->getCalledValue(/*skipFunctionConversions=*/true); if (!fn) return; SmallVector keyPathArgs; auto *args = expr->getArgs(); auto isKeyPathLiteral = [&](Expr *argExpr) -> KeyPathExpr * { if (auto *DTBE = getAsExpr(argExpr)) argExpr = DTBE->getSubExpr(); // Sendable key path literals are represented as an existential // protocol composition with `Sendable` protocol which has to be // opened in certain scenarios i.e. to pass it to non-Sendable version. if (auto *OEE = getAsExpr(argExpr)) argExpr = OEE->getExistentialValue(); return getAsExpr(argExpr); }; if (fn->getModuleContext()->getName() == C.Id_Foundation && fn->getName().isCompoundName("observe", {"", "options", "changeHandler"})) { if (auto keyPathArg = isKeyPathLiteral(args->getExpr(0))) { keyPathArgs.push_back(keyPathArg); } } else if (fn->getAttrs() .hasSemanticsAttr(semantics::KEYPATH_MUST_BE_VALID_FOR_KVO)) { for (auto *argExpr : args->getArgExprs()) { if (auto keyPathArg = isKeyPathLiteral(argExpr)) { keyPathArgs.push_back(keyPathArg); } } } for (auto *keyPathArg : keyPathArgs) { auto lastComponent = keyPathArg->getComponents().back(); if (lastComponent.getKind() != KeyPathExpr::Component::Kind::Member) continue; auto property = lastComponent.getDeclRef().getDecl(); if (!property) continue; auto propertyVar = cast(property); if (propertyVar->shouldUseObjCDispatch() || (propertyVar->isObjC() && propertyVar->getParsedAccessor(AccessorKind::Set))) continue; C.Diags .diagnose(expr->getLoc(), diag::observe_keypath_property_not_objc_dynamic, property->getName(), fn->getName()) .highlight(lastComponent.getLoc()); } } PreWalkResult walkToExprPre(Expr *E) override { if (!E || isa(E) || !E->getType()) return Action::SkipNode(E); if (auto *CE = dyn_cast(E)) { maybeDiagnoseCallExpr(CE); return Action::SkipNode(E); } return Action::Continue(E); } }; KVOObserveCallWalker Walker(DC->getASTContext()); const_cast(E)->walk(Walker); } static void diagnoseExplicitUseOfLazyVariableStorage(const Expr *E, const DeclContext *DC) { class ExplicitLazyVarStorageAccessFinder : public BaseDiagnosticWalker { const ASTContext &C; public: ExplicitLazyVarStorageAccessFinder(ASTContext &ctx) : C(ctx) {} void tryDiagnoseExplicitLazyStorageVariableUse(MemberRefExpr *MRE) { if (MRE->isImplicit()) { return; } auto VD = dyn_cast(MRE->getMember().getDecl()); if (!VD) { return; } auto sourceFileKind = VD->getDeclContext()->getParentSourceFile(); if (!sourceFileKind) { return; } if (sourceFileKind->Kind != SourceFileKind::Library && sourceFileKind->Kind != SourceFileKind::Main) { return; } if (VD->isLazyStorageProperty()) { C.Diags.diagnose(MRE->getLoc(), diag::lazy_var_storage_access); } } PreWalkResult walkToExprPre(Expr *E) override { if (!E || isa(E) || !E->getType()) return Action::SkipNode(E); if (auto *MRE = dyn_cast(E)) { tryDiagnoseExplicitLazyStorageVariableUse(MRE); return Action::SkipNode(E); } return Action::Continue(E); } }; ExplicitLazyVarStorageAccessFinder Walker(DC->getASTContext()); const_cast(E)->walk(Walker); } static void diagnoseComparisonWithNaN(const Expr *E, const DeclContext *DC) { class ComparisonWithNaNFinder : public BaseDiagnosticWalker { const ASTContext &C; public: ComparisonWithNaNFinder(const DeclContext *dc) : C(dc->getASTContext()) {} void tryDiagnoseComparisonWithNaN(BinaryExpr *BE) { ValueDecl *comparisonDecl = nullptr; // Dig out the function declaration. if (auto Fn = BE->getFn()) { if (auto DSCE = dyn_cast(Fn)) { comparisonDecl = DSCE->getCalledValue(/*skipFunctionConversions=*/true); } else { comparisonDecl = BE->getCalledValue(/*skipFunctionConversions=*/true); } } // Bail out if it isn't a function. if (!comparisonDecl || !isa(comparisonDecl)) { return; } // We're only interested in comparison functions like == or <=. auto comparisonDeclName = comparisonDecl->getBaseIdentifier(); if (!comparisonDeclName.isStandardComparisonOperator()) { return; } auto *firstArg = BE->getLHS(); auto *secondArg = BE->getRHS(); // Make sure that both arguments are valid before doing anything else, // this helps us to debug reports of crashes in `conformsToKnownProtocol` // referencing arguments (rdar://78920375). // // Since this diagnostic should only be run on type-checked AST, // it's unclear what caused one of the arguments to have null type. assert(firstArg->getType() && "Expected valid type for first argument"); assert(secondArg->getType() && "Expected valid type for second argument"); // Both arguments must conform to FloatingPoint protocol. if (!TypeChecker::conformsToKnownProtocol(firstArg->getType(), KnownProtocolKind::FloatingPoint) || !TypeChecker::conformsToKnownProtocol(secondArg->getType(), KnownProtocolKind::FloatingPoint)) { return; } // Convenience utility to extract argument decl. auto extractArgumentDecl = [&](Expr *arg) -> ValueDecl * { if (auto DRE = dyn_cast(arg)) { return DRE->getDecl(); } else if (auto MRE = dyn_cast(arg)) { return MRE->getMember().getDecl(); } return nullptr; }; // Dig out the declarations for the arguments. auto *firstVal = extractArgumentDecl(firstArg); auto *secondVal = extractArgumentDecl(secondArg); // If we can't find declarations for both arguments, bail out, // because one of them has to be '.nan'. if (!firstArg && !secondArg) { return; } // Convenience utility to check if this is a 'nan' variable. auto isNanDecl = [&](ValueDecl *VD) { return VD && isa(VD) && VD->getBaseIdentifier().is("nan"); }; // Diagnose comparison with '.nan'. // // If the comparison is done using '<=', '<', '==', '>', '>=', then // the result is always false. If the comparison is done using '!=', // then the result is always true. // // Emit a different diagnostic which doesn't mention using '.isNaN' if // the comparison isn't done using '==' or '!=' or if both sides are // '.nan'. if (isNanDecl(firstVal) && isNanDecl(secondVal)) { C.Diags.diagnose(BE->getLoc(), diag::nan_comparison_both_nan, comparisonDeclName.str(), comparisonDeclName.is("!=")); } else if (isNanDecl(firstVal) || isNanDecl(secondVal)) { if (comparisonDeclName.is("==") || comparisonDeclName.is("!=")) { auto exprStr = C.SourceMgr .extractText(Lexer::getCharSourceRangeFromSourceRange( C.SourceMgr, firstArg->getSourceRange())) .str(); auto prefix = exprStr; if (comparisonDeclName.is("!=")) { prefix = "!" + prefix; } C.Diags.diagnose(BE->getLoc(), diag::nan_comparison, comparisonDeclName, comparisonDeclName.is("!="), prefix, exprStr); } else { C.Diags.diagnose(BE->getLoc(), diag::nan_comparison_without_isnan, comparisonDeclName, comparisonDeclName.is("!=")); } } } PreWalkResult walkToExprPre(Expr *E) override { if (!E || isa(E) || !E->getType()) return Action::SkipNode(E); if (auto *BE = dyn_cast(E)) { tryDiagnoseComparisonWithNaN(BE); return Action::SkipNode(E); } return Action::Continue(E); } }; ComparisonWithNaNFinder Walker(DC); const_cast(E)->walk(Walker); } static void diagUnqualifiedAccessToMethodNamedSelf(const Expr *E, const DeclContext *DC) { if (!E || isa(E) || !E->getType()) return; class DiagnoseWalker : public BaseDiagnosticWalker { ASTContext &Ctx; const DeclContext *DC; public: DiagnoseWalker(const DeclContext *DC) : Ctx(DC->getASTContext()), DC(DC) {} PreWalkResult walkToExprPre(Expr *E) override { if (!E || isa(E) || !E->getType()) return Action::SkipNode(E); auto *DRE = dyn_cast(E); // If this is not an explicit 'self' reference, let's keep searching. if (!DRE || DRE->isImplicit()) return Action::Continue(E); // If this not 'self' or it's not a function reference, it's unrelated. if (!(DRE->getDecl()->getBaseName() == Ctx.Id_self && DRE->getType()->is())) return Action::Continue(E); auto typeContext = DC->getInnermostTypeContext(); // Use of 'self' in enums is not confusable. if (!typeContext || typeContext->getSelfEnumDecl()) return Action::Continue(E); // self(...) is not easily confusable. if (auto *parentExpr = Parent.getAsExpr()) { if (isa(parentExpr)) return Action::Continue(E); // Explicit call to a static method 'self' of some type is not // confusable. if (isa(parentExpr) && !parentExpr->isImplicit()) return Action::Continue(E); } auto baseType = typeContext->getDeclaredInterfaceType(); auto baseTypeString = baseType.getString(); Ctx.Diags.diagnose(E->getLoc(), diag::self_refers_to_method, baseTypeString); Ctx.Diags .diagnose(E->getLoc(), diag::fix_unqualified_access_member_named_self, baseTypeString) .fixItInsert(E->getLoc(), diag::insert_type_qualification, baseType); return Action::Continue(E); } }; DiagnoseWalker Walker(DC); const_cast(E)->walk(Walker); } static void diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E, const DeclContext *DC) { class DiagnoseWalker : public BaseDiagnosticWalker { ASTContext &Ctx; private: std::string getKeyStringValue(const LiteralExpr *keyExpr) { if (auto *MLE = dyn_cast(keyExpr)) { return getMagicLiteralKeyValue(MLE); } std::string out; llvm::raw_string_ostream OS(out); keyExpr->printConstExprValue(&OS, /*additionalCheck=*/nullptr); return out; } std::string getMagicLiteralKeyValue(const MagicIdentifierLiteralExpr *MLE) { auto magicLiteralValue = MLE->getLiteralKindDescription().str(); switch (MLE->getKind()) { case MagicIdentifierLiteralExpr::DSOHandle: case MagicIdentifierLiteralExpr::FileID: case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: case MagicIdentifierLiteralExpr::FilePath: case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: case MagicIdentifierLiteralExpr::Function: break; // Those are literals that can evaluate to different values in a // dictionary literal declaration context based on source position // so we need to consider that position as part of the literal value. case MagicIdentifierLiteralExpr::Column: { unsigned int column; std::tie(std::ignore, column) = Ctx.SourceMgr.getPresumedLineAndColumnForLoc(MLE->getStartLoc()); magicLiteralValue += ":" + std::to_string(column); break; } case MagicIdentifierLiteralExpr::Line: { unsigned int line; std::tie(line, std::ignore) = Ctx.SourceMgr.getPresumedLineAndColumnForLoc(MLE->getStartLoc()); magicLiteralValue += ":" + std::to_string(line); break; } } return magicLiteralValue; } std::string getKeyStringValueForDiagnostic(const LiteralExpr *keyExpr) { std::string out; switch (keyExpr->getKind()) { case ExprKind::NilLiteral: case ExprKind::MagicIdentifierLiteral: return out; case ExprKind::StringLiteral: { const auto *SL = cast(keyExpr); out = SL->getValue().str(); break; } default: llvm::raw_string_ostream OS(out); keyExpr->printConstExprValue(&OS, /*additionalCheck=*/nullptr); break; } return "'" + out + "'"; } bool shouldDiagnoseLiteral(const LiteralExpr *LE) { switch (LE->getKind()) { case ExprKind::IntegerLiteral: case ExprKind::FloatLiteral: case ExprKind::BooleanLiteral: case ExprKind::StringLiteral: case ExprKind::MagicIdentifierLiteral: case ExprKind::NilLiteral: return true; // Skip interpolated literals because they // can contain expressions that although equal // maybe be evaluated to different values. e.g. // "\(a) \(a)" where 'a' is a computed variable. case ExprKind::InterpolatedStringLiteral: // Also skip object literals as most of them takes paramenters that can // contain expressions that altough equal may evaluate to different // values e.g. #fileLiteral(resourceName: a) where 'a' is a computed // property is valid. case ExprKind::ObjectLiteral: // Literal expressions produce Regex type result, // which cannot be keys due to not conforming to hashable. case ExprKind::RegexLiteral: return false; // If a new literal is added in the future, the compiler // will warn that a case is missing from this switch. #define LITERAL_EXPR(Id, Parent) #define EXPR(Id, Parent) case ExprKind::Id: #include "swift/AST/ExprNodes.def" llvm_unreachable("Not a literal expression"); } llvm_unreachable("Unhandled literal"); } public: DiagnoseWalker(const DeclContext *DC) : Ctx(DC->getASTContext()) {} PreWalkResult walkToExprPre(Expr *E) override { const auto *DLE = dyn_cast_or_null(E); if (!DLE) return Action::Continue(E); auto type = DLE->getType(); // For other types conforming with `ExpressibleByDictionaryLiteral` // protocol, duplicated keys may be allowed. if (!(type && type->isDictionary())) { return Action::Continue(E); } using LiteralKey = std::pair; using Element = std::pair; std::map> groupedLiteralKeys; for (size_t i = 0; i < DLE->getElements().size(); ++i) { const auto *elt = DLE->getElement(i); const auto *tupleElt = cast(elt); const auto *keyExpr = tupleElt->getElement(0)->getSemanticsProvidingExpr(); auto *LE = dyn_cast(keyExpr); if (!LE) continue; if (!shouldDiagnoseLiteral(LE)) continue; auto keyStringValue = getKeyStringValue(LE); auto literalKey = std::make_pair(keyStringValue, keyExpr->getKind()); groupedLiteralKeys[literalKey].push_back({tupleElt, i}); } // All keys are unique. if (groupedLiteralKeys.size() == DLE->getNumElements()) { return Action::Continue(E); } auto &DE = Ctx.Diags; auto emitNoteWithFixit = [&](const Element &duplicated) { auto note = DE.diagnose(duplicated.first->getLoc(), diag::duplicated_key_declared_here); auto duplicatedEltIdx = duplicated.second; const auto commanLocs = DLE->getCommaLocs(); note.fixItRemove(duplicated.first->getSourceRange()); if (duplicatedEltIdx < commanLocs.size()) { note.fixItRemove(commanLocs[duplicatedEltIdx]); } else { // For the last element remove the previous comma. note.fixItRemove(commanLocs[duplicatedEltIdx - 1]); } }; for (auto &entry : groupedLiteralKeys) { auto &keyValuePairs = entry.second; if (keyValuePairs.size() == 1) { continue; } auto elt = keyValuePairs.front(); const auto keyValue = entry.first.first; const auto keyExpr = cast( elt.first->getElement(0)->getSemanticsProvidingExpr()); const auto value = getKeyStringValueForDiagnostic(keyExpr); DE.diagnose(elt.first->getLoc(), diag::duplicated_literal_keys_in_dictionary_literal, type, keyExpr->getLiteralKindDescription(), value.empty(), value); for (auto &duplicated : keyValuePairs) { emitNoteWithFixit(duplicated); } } return Action::Continue(E); } }; DiagnoseWalker Walker(DC); const_cast(E)->walk(Walker); } // Verifies that references to members are valid under the restrictions of the // MemberImportVisibility feature. static void diagnoseMissingMemberImports(const Expr *E, const DeclContext *DC) { class DiagnoseWalker : public BaseDiagnosticWalker { const DeclContext *dc; public: DiagnoseWalker(const DeclContext *dc) : dc(dc) {} PreWalkResult walkToExprPre(Expr *E) override { if (auto declRef = E->getReferencedDecl()) checkDecl(declRef.getDecl(), E->getLoc()); return Action::Continue(E); } void checkDecl(const ValueDecl *decl, SourceLoc loc) { // Only diagnose uses of members. if (!decl->getDeclContext()->isTypeContext()) return; if (!dc->isDeclImported(decl)) maybeDiagnoseMissingImportForMember(decl, dc, loc); } }; auto &ctx = DC->getASTContext(); if (!ctx.LangOpts.hasFeature(Feature::MemberImportVisibility)) return; DiagnoseWalker walker(DC); const_cast(E)->walk(walker); } //===----------------------------------------------------------------------===// // High-level entry points. //===----------------------------------------------------------------------===// /// Emit diagnostics for syntactic restrictions on a given expression. void swift::performSyntacticExprDiagnostics(const Expr *E, const DeclContext *DC, bool isExprStmt) { auto &ctx = DC->getASTContext(); TypeChecker::diagnoseSelfAssignment(E); diagSyntacticUseRestrictions(E, DC, isExprStmt); diagRecursivePropertyAccess(E, DC); diagnoseImplicitSelfUseInClosure(E, DC); diagnoseUnintendedOptionalBehavior(E, DC); maybeDiagnoseCallToKeyValueObserveMethod(E, DC); diagnoseExplicitUseOfLazyVariableStorage(E, DC); diagnoseComparisonWithNaN(E, DC); if (!ctx.isSwiftVersionAtLeast(5)) diagnoseDeprecatedWritableKeyPath(E, DC); if (!ctx.LangOpts.DisableAvailabilityChecking) diagnoseExprAvailability(E, const_cast(DC)); if (ctx.LangOpts.EnableObjCInterop) diagDeprecatedObjCSelectors(DC, E); diagnoseConstantArgumentRequirement(E, DC); diagUnqualifiedAccessToMethodNamedSelf(E, DC); diagnoseDictionaryLiteralDuplicateKeyEntries(E, DC); diagnoseMissingMemberImports(E, DC); } void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) { auto &ctx = DC->getASTContext(); TypeChecker::checkExistentialTypes(ctx, const_cast(S), DC); if (auto *CS = dyn_cast(S)) diagnoseCaseStmtAmbiguousWhereClause(CS, ctx); checkStmtConditionTrailingClosure(ctx, S); if (auto *lcs = dyn_cast(S)) checkLabeledStmtConditions(ctx, lcs, DC); if (!ctx.LangOpts.DisableAvailabilityChecking) diagnoseStmtAvailability(S, const_cast(DC)); } //===----------------------------------------------------------------------===// // Utility functions //===----------------------------------------------------------------------===// void swift::fixItAccess(InFlightDiagnostic &diag, ValueDecl *VD, AccessLevel desiredAccess, bool isForSetter, bool shouldUseDefaultAccess) { StringRef fixItString; switch (desiredAccess) { case AccessLevel::Private: fixItString = "private "; break; case AccessLevel::FilePrivate: fixItString = "fileprivate "; break; case AccessLevel::Internal: fixItString = "internal "; break; case AccessLevel::Package: fixItString = "package "; break; case AccessLevel::Public: fixItString = "public "; break; case AccessLevel::Open: fixItString = "open "; break; } DeclAttributes &attrs = VD->getAttrs(); AbstractAccessControlAttr *attr; if (isForSetter) { attr = attrs.getAttribute(); cast(VD)->overwriteSetterAccess(desiredAccess); } else { attr = attrs.getAttribute(); VD->overwriteAccess(desiredAccess); if (auto *ASD = dyn_cast(VD)) { if (auto *getter = ASD->getAccessor(AccessorKind::Get)) getter->overwriteAccess(desiredAccess); if (auto *setterAttr = attrs.getAttribute()) { if (setterAttr->getAccess() > desiredAccess) fixItAccess(diag, VD, desiredAccess, true); } else { ASD->overwriteSetterAccess(desiredAccess); } } } if (isForSetter && VD->getFormalAccess() == desiredAccess) { assert(attr); attr->setInvalid(); // Remove the setter attribute. diag.fixItRemove(attr->Range); } else if (attr) { // If the formal access already matches the desired access, the problem // must be in a parent scope. Don't emit a fix-it. // FIXME: It's also possible for access to already be /broader/ than what's // desired, in which case the problem is also in a parent scope. However, // this function is sometimes called to make access narrower, so assuming // that a broader scope is acceptable breaks some diagnostics. if (attr->getAccess() != desiredAccess) { if (shouldUseDefaultAccess) { // Remove the attribute if replacement is not preferred. diag.fixItRemove(attr->getRange()); } else { // This uses getLocation() instead of getRange() because we don't want to // replace the "(set)" part of a setter attribute. diag.fixItReplace(attr->getLocation(), fixItString.drop_back()); } attr->setInvalid(); } } else if (auto *override = VD->getAttrs().getAttribute()) { // Insert the access in front of 'override', if it exists, in order to // match the same keyword order as produced by method autocompletion. diag.fixItInsert(override->getLocation(), fixItString); } else if (auto var = dyn_cast(VD)) { if (auto PBD = var->getParentPatternBinding()) diag.fixItInsert(PBD->getStartLoc(), fixItString); } else { diag.fixItInsert(VD->getStartLoc(), fixItString); } } /// Retrieve the type name to be used for determining whether we can /// omit needless words. static OmissionTypeName getTypeNameForOmission(Type type) { if (!type) return ""; ASTContext &ctx = type->getASTContext(); auto objcBoolType = ctx.getObjCBoolType(); /// Determine the options associated with the given type. auto getOptions = [&](Type type) { // Look for Boolean types. OmissionTypeOptions options; // Look for Boolean types. if (type->isBool()) { // Swift.Bool options |= OmissionTypeFlags::Boolean; } else if (objcBoolType && type->isEqual(objcBoolType)) { // ObjectiveC.ObjCBool options |= OmissionTypeFlags::Boolean; } return options; }; do { // Look through typealiases. if (auto aliasTy = dyn_cast(type.getPointer())) { type = aliasTy->getSinglyDesugaredType(); continue; } // Strip off lvalue/inout types. Type newType = type->getWithoutSpecifierType(); if (newType.getPointer() != type.getPointer()) { type = newType; continue; } // Look through reference-storage types. newType = type->getReferenceStorageReferent(); if (newType.getPointer() != type.getPointer()) { type = newType; continue; } // Look through optionals. if (auto optObjectTy = type->getOptionalObjectType()) { type = optObjectTy; continue; } break; } while (true); // Nominal types. if (auto nominal = type->getAnyNominal()) { // If we have a collection, get the element type. if (auto bound = type->getAs()) { auto args = bound->getGenericArgs(); if (!args.empty() && (bound->isArray() || bound->isSet())) { return OmissionTypeName(nominal->getName().str(), getOptions(bound), getTypeNameForOmission(args[0]).Name); } } // AnyObject -> "Object". if (type->isAnyObject()) return "Object"; return OmissionTypeName(nominal->getName().str(), getOptions(type)); } // Generic type parameters. if (auto genericParamTy = type->getAs()) { if (!genericParamTy->isCanonical()) return genericParamTy->getName().str(); return ""; } // Dependent members. if (auto dependentMemberTy = type->getAs()) { return dependentMemberTy->getName().str(); } // Archetypes. if (auto archetypeTy = type->getAs()) { return archetypeTy->getName().str(); } // Function types. if (auto funcTy = type->getAs()) { if (funcTy->getRepresentation() == AnyFunctionType::Representation::Block) return "Block"; return "Function"; } return ""; } std::optional TypeChecker::omitNeedlessWords(AbstractFunctionDecl *afd) { auto &Context = afd->getASTContext(); if (afd->isInvalid() || isa(afd)) return std::nullopt; const DeclName name = afd->getName(); if (!name) return std::nullopt; // String'ify the arguments. StringRef baseNameStr = name.getBaseName().userFacingName(); SmallVector argNameStrs; for (auto arg : name.getArgumentNames()) { if (arg.empty()) argNameStrs.push_back(""); else argNameStrs.push_back(arg.str()); } // String'ify the parameter types. SmallVector paramTypes; // Always look at the parameters in the last parameter list. for (auto param : *afd->getParameters()) { paramTypes.push_back(getTypeNameForOmission(param->getInterfaceType()) .withDefaultArgument(param->isDefaultArgument())); } // Handle contextual type, result type, and returnsSelf. Type contextType = afd->getDeclContext()->getDeclaredInterfaceType(); Type resultType; bool returnsSelf = afd->hasDynamicSelfResult(); if (auto func = dyn_cast(afd)) { resultType = func->getResultInterfaceType(); resultType = func->mapTypeIntoContext(resultType); } else if (isa(afd)) { resultType = contextType; } // Figure out the first parameter name. StringRef firstParamName; auto params = afd->getParameters(); if (params->size() != 0 && !params->get(0)->getName().empty()) firstParamName = params->get(0)->getName().str(); StringScratchSpace scratch; if (!swift::omitNeedlessWords( baseNameStr, argNameStrs, firstParamName, getTypeNameForOmission(resultType), getTypeNameForOmission(contextType), paramTypes, returnsSelf, false, /*allPropertyNames=*/nullptr, std::nullopt, std::nullopt, scratch)) return std::nullopt; /// Retrieve a replacement identifier. auto getReplacementIdentifier = [&](StringRef name, DeclBaseName old) -> DeclBaseName{ if (name.empty()) return Identifier(); if (!old.empty() && name == old.userFacingName()) return old; return Context.getIdentifier(name); }; auto newBaseName = getReplacementIdentifier( baseNameStr, name.getBaseName()); SmallVector newArgNames; auto oldArgNames = name.getArgumentNames(); for (unsigned i = 0, n = argNameStrs.size(); i != n; ++i) { auto argBaseName = getReplacementIdentifier(argNameStrs[i], oldArgNames[i]); newArgNames.push_back(argBaseName.getIdentifier()); } return DeclName(Context, newBaseName, newArgNames); } std::optional TypeChecker::omitNeedlessWords(VarDecl *var) { auto &Context = var->getASTContext(); if (var->isInvalid()) return std::nullopt; if (var->getName().empty()) return std::nullopt; auto name = var->getName().str(); // Dig out the context type. Type contextType = var->getDeclContext()->getDeclaredInterfaceType(); if (!contextType) return std::nullopt; // Dig out the type of the variable. Type type = var->getValueInterfaceType(); while (auto optObjectTy = type->getOptionalObjectType()) type = optObjectTy; // Omit needless words. StringScratchSpace scratch; OmissionTypeName typeName = getTypeNameForOmission(var->getInterfaceType()); OmissionTypeName contextTypeName = getTypeNameForOmission(contextType); if (::omitNeedlessWords(name, {}, "", typeName, contextTypeName, {}, /*returnsSelf=*/false, true, /*allPropertyNames=*/nullptr, std::nullopt, std::nullopt, scratch)) { return Context.getIdentifier(name); } return std::nullopt; } bool swift::diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc, ForEachStmt *forEach) { auto &ctx = dc->getASTContext(); if (auto thrownError = TypeChecker::canThrow(ctx, forEach)) { if (forEach->getTryLoc().isInvalid()) { ctx.Diags .diagnose(forEach->getAwaitLoc(), diag::throwing_call_unhandled, "call") .fixItInsert(forEach->getAwaitLoc(), "try"); return true; } } return false; } void DeferredDiag::emit(swift::ASTContext &ctx) { assert(loc && "no loc... already emitted?"); ctx.Diags.diagnose(loc, diag); loc = SourceLoc(); }