| //===--- TypeCheckAccess.cpp - Type Checking for Access Control -----------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 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 access control checking. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "TypeCheckAccess.h" |
| #include "TypeChecker.h" |
| #include "TypeCheckAvailability.h" |
| #include "TypeAccessScopeChecker.h" |
| #include "swift/AST/ASTVisitor.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/DiagnosticsSema.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/Import.h" |
| #include "swift/AST/Pattern.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/TypeCheckRequests.h" |
| |
| using namespace swift; |
| |
| #define DEBUG_TYPE "TypeCheckAccess" |
| |
| namespace { |
| |
| /// Calls \p callback for each type in each requirement provided by |
| /// \p source. |
| static void forAllRequirementTypes( |
| WhereClauseOwner &&source, |
| llvm::function_ref<void(Type, TypeRepr *)> callback) { |
| std::move(source).visitRequirements(TypeResolutionStage::Interface, |
| [&](const Requirement &req, RequirementRepr *reqRepr) { |
| switch (req.getKind()) { |
| case RequirementKind::Conformance: |
| case RequirementKind::SameType: |
| case RequirementKind::Superclass: |
| callback(req.getFirstType(), |
| RequirementRepr::getFirstTypeRepr(reqRepr)); |
| callback(req.getSecondType(), |
| RequirementRepr::getSecondTypeRepr(reqRepr)); |
| break; |
| |
| case RequirementKind::Layout: |
| callback(req.getFirstType(), |
| RequirementRepr::getFirstTypeRepr(reqRepr)); |
| break; |
| } |
| return false; |
| }); |
| } |
| |
| /// \see checkTypeAccess |
| using CheckTypeAccessCallback = |
| void(AccessScope, const TypeRepr *, DowngradeToWarning); |
| |
| class AccessControlCheckerBase { |
| protected: |
| bool checkUsableFromInline; |
| |
| void checkTypeAccessImpl( |
| Type type, TypeRepr *typeRepr, AccessScope contextAccessScope, |
| const DeclContext *useDC, bool mayBeInferred, |
| llvm::function_ref<CheckTypeAccessCallback> diagnose); |
| |
| void checkTypeAccess( |
| Type type, TypeRepr *typeRepr, const ValueDecl *context, |
| bool mayBeInferred, |
| llvm::function_ref<CheckTypeAccessCallback> diagnose); |
| |
| void checkTypeAccess( |
| const TypeLoc &TL, const ValueDecl *context, bool mayBeInferred, |
| llvm::function_ref<CheckTypeAccessCallback> diagnose) { |
| return checkTypeAccess(TL.getType(), TL.getTypeRepr(), context, |
| mayBeInferred, diagnose); |
| } |
| |
| void checkRequirementAccess( |
| WhereClauseOwner &&source, |
| AccessScope accessScope, |
| const DeclContext *useDC, |
| llvm::function_ref<CheckTypeAccessCallback> diagnose) { |
| forAllRequirementTypes(std::move(source), [&](Type type, TypeRepr *typeRepr) { |
| checkTypeAccessImpl(type, typeRepr, accessScope, useDC, |
| /*mayBeInferred*/false, diagnose); |
| }); |
| } |
| |
| AccessControlCheckerBase(bool checkUsableFromInline) |
| : checkUsableFromInline(checkUsableFromInline) {} |
| |
| public: |
| void checkGenericParamAccess( |
| const GenericContext *ownerCtx, |
| const Decl *ownerDecl, |
| AccessScope accessScope, |
| AccessLevel contextAccess); |
| |
| void checkGenericParamAccess( |
| const GenericContext *ownerCtx, |
| const ValueDecl *ownerDecl); |
| }; |
| |
| class TypeAccessScopeDiagnoser : private ASTWalker { |
| AccessScope accessScope; |
| const DeclContext *useDC; |
| bool treatUsableFromInlineAsPublic; |
| const ComponentIdentTypeRepr *offendingType = nullptr; |
| |
| bool walkToTypeReprPre(TypeRepr *TR) override { |
| // Exit early if we've already found a problem type. |
| if (offendingType) |
| return false; |
| |
| auto CITR = dyn_cast<ComponentIdentTypeRepr>(TR); |
| if (!CITR) |
| return true; |
| |
| const ValueDecl *VD = CITR->getBoundDecl(); |
| if (!VD) |
| return true; |
| |
| if (VD->getFormalAccessScope(useDC, treatUsableFromInlineAsPublic) |
| != accessScope) |
| return true; |
| |
| offendingType = CITR; |
| return false; |
| } |
| |
| bool walkToTypeReprPost(TypeRepr *T) override { |
| // Exit early if we've already found a problem type. |
| return offendingType != nullptr; |
| } |
| |
| explicit TypeAccessScopeDiagnoser(AccessScope accessScope, |
| const DeclContext *useDC, |
| bool treatUsableFromInlineAsPublic) |
| : accessScope(accessScope), useDC(useDC), |
| treatUsableFromInlineAsPublic(treatUsableFromInlineAsPublic) {} |
| |
| public: |
| static const TypeRepr *findTypeWithScope(TypeRepr *TR, |
| AccessScope accessScope, |
| const DeclContext *useDC, |
| bool treatUsableFromInlineAsPublic) { |
| if (TR == nullptr) |
| return nullptr; |
| TypeAccessScopeDiagnoser diagnoser(accessScope, useDC, |
| treatUsableFromInlineAsPublic); |
| TR->walk(diagnoser); |
| return diagnoser.offendingType; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| /// Checks if the access scope of the type described by \p TL contains |
| /// \p contextAccessScope. If it isn't, calls \p diagnose with a TypeRepr |
| /// representing the offending part of \p TL. |
| /// |
| /// The TypeRepr passed to \p diagnose may be null, in which case a particular |
| /// part of the type that caused the problem could not be found. The DeclContext |
| /// is never null. |
| /// |
| /// If \p type might be partially inferred even when \p typeRepr is present |
| /// (such as for properties), pass \c true for \p mayBeInferred. (This does not |
| /// include implicitly providing generic parameters for the Self type, such as |
| /// using `Array` to mean `Array<Element>` in an extension of Array.) If |
| /// \p typeRepr is known to be absent, it's okay to pass \c false for |
| /// \p mayBeInferred. |
| void AccessControlCheckerBase::checkTypeAccessImpl( |
| Type type, TypeRepr *typeRepr, AccessScope contextAccessScope, |
| const DeclContext *useDC, bool mayBeInferred, |
| llvm::function_ref<CheckTypeAccessCallback> diagnose) { |
| |
| auto &Context = useDC->getASTContext(); |
| if (Context.isAccessControlDisabled()) |
| return; |
| // Don't spend time checking local declarations; this is always valid by the |
| // time we get to this point. |
| if (!contextAccessScope.isPublic() && |
| contextAccessScope.getDeclContext()->isLocalContext()) |
| return; |
| |
| AccessScope problematicAccessScope = AccessScope::getPublic(); |
| if (type) { |
| Optional<AccessScope> typeAccessScope = |
| TypeAccessScopeChecker::getAccessScope(type, useDC, |
| checkUsableFromInline); |
| |
| // Note: This means that the type itself is invalid for this particular |
| // context, because it references declarations from two incompatible scopes. |
| // In this case we should have diagnosed the bad reference already. |
| if (!typeAccessScope.hasValue()) |
| return; |
| problematicAccessScope = *typeAccessScope; |
| } |
| |
| auto downgradeToWarning = DowngradeToWarning::No; |
| |
| if (contextAccessScope.hasEqualDeclContextWith(problematicAccessScope) || |
| contextAccessScope.isChildOf(problematicAccessScope)) { |
| |
| // /Also/ check the TypeRepr, if present. This can be important when we're |
| // unable to preserve typealias sugar that's present in the TypeRepr. |
| if (!typeRepr) |
| return; |
| |
| Optional<AccessScope> typeReprAccessScope = |
| TypeAccessScopeChecker::getAccessScope(typeRepr, useDC, |
| checkUsableFromInline); |
| if (!typeReprAccessScope.hasValue()) |
| return; |
| |
| if (contextAccessScope.hasEqualDeclContextWith(*typeReprAccessScope) || |
| contextAccessScope.isChildOf(*typeReprAccessScope)) { |
| // Only if both the Type and the TypeRepr follow the access rules can |
| // we exit; otherwise we have to emit a diagnostic. |
| return; |
| } |
| problematicAccessScope = *typeReprAccessScope; |
| |
| } else { |
| // The type violates the rules of access control (with or without taking the |
| // TypeRepr into account). |
| |
| if (typeRepr && mayBeInferred && |
| !Context.LangOpts.isSwiftVersionAtLeast(5) && |
| !useDC->getParentModule()->isResilient()) { |
| // Swift 4.2 and earlier didn't check the Type when a TypeRepr was |
| // present. However, this is a major hole when generic parameters are |
| // inferred: |
| // |
| // public let foo: Optional = VeryPrivateStruct() |
| // |
| // Downgrade the error to a warning in this case for source compatibility. |
| Optional<AccessScope> typeReprAccessScope = |
| TypeAccessScopeChecker::getAccessScope(typeRepr, useDC, |
| checkUsableFromInline); |
| assert(typeReprAccessScope && "valid Type but not valid TypeRepr?"); |
| if (contextAccessScope.hasEqualDeclContextWith(*typeReprAccessScope) || |
| contextAccessScope.isChildOf(*typeReprAccessScope)) { |
| downgradeToWarning = DowngradeToWarning::Yes; |
| } |
| } |
| } |
| |
| const TypeRepr *complainRepr = TypeAccessScopeDiagnoser::findTypeWithScope( |
| typeRepr, problematicAccessScope, useDC, checkUsableFromInline); |
| |
| diagnose(problematicAccessScope, complainRepr, downgradeToWarning); |
| } |
| |
| /// Checks if the access scope of the type described by \p TL is valid for the |
| /// type to be the type of \p context. If it isn't, calls \p diagnose with a |
| /// TypeRepr representing the offending part of \p TL. |
| /// |
| /// The TypeRepr passed to \p diagnose may be null, in which case a particular |
| /// part of the type that caused the problem could not be found. |
| /// |
| /// If \p type might be partially inferred even when \p typeRepr is present |
| /// (such as for properties), pass \c true for \p mayBeInferred. (This does not |
| /// include implicitly providing generic parameters for the Self type, such as |
| /// using `Array` to mean `Array<Element>` in an extension of Array.) If |
| /// \p typeRepr is known to be absent, it's okay to pass \c false for |
| /// \p mayBeInferred. |
| void AccessControlCheckerBase::checkTypeAccess( |
| Type type, TypeRepr *typeRepr, const ValueDecl *context, bool mayBeInferred, |
| llvm::function_ref<CheckTypeAccessCallback> diagnose) { |
| assert(!isa<ParamDecl>(context)); |
| const DeclContext *DC = context->getDeclContext(); |
| AccessScope contextAccessScope = |
| context->getFormalAccessScope( |
| context->getDeclContext(), checkUsableFromInline); |
| |
| checkTypeAccessImpl(type, typeRepr, contextAccessScope, DC, mayBeInferred, |
| diagnose); |
| } |
| |
| /// Highlights the given TypeRepr, and adds a note pointing to the type's |
| /// declaration if possible. |
| /// |
| /// Just flushes \p diag as is if \p complainRepr is null. |
| static void highlightOffendingType(InFlightDiagnostic &diag, |
| const TypeRepr *complainRepr) { |
| if (!complainRepr) { |
| diag.flush(); |
| return; |
| } |
| |
| diag.highlight(complainRepr->getSourceRange()); |
| diag.flush(); |
| |
| if (auto CITR = dyn_cast<ComponentIdentTypeRepr>(complainRepr)) { |
| const ValueDecl *VD = CITR->getBoundDecl(); |
| VD->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type); |
| } |
| } |
| |
| void AccessControlCheckerBase::checkGenericParamAccess( |
| const GenericContext *ownerCtx, |
| const Decl *ownerDecl, |
| AccessScope accessScope, |
| AccessLevel contextAccess) { |
| if (!ownerCtx->isGenericContext()) |
| return; |
| |
| // This must stay in sync with diag::generic_param_access. |
| enum class ACEK { |
| Parameter = 0, |
| Requirement |
| } accessControlErrorKind; |
| auto minAccessScope = AccessScope::getPublic(); |
| const TypeRepr *complainRepr = nullptr; |
| auto downgradeToWarning = DowngradeToWarning::Yes; |
| |
| auto callbackACEK = ACEK::Parameter; |
| |
| auto callback = [&](AccessScope typeAccessScope, |
| const TypeRepr *thisComplainRepr, |
| DowngradeToWarning thisDowngrade) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (thisDowngrade == DowngradeToWarning::No && |
| downgradeToWarning == DowngradeToWarning::Yes) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| accessControlErrorKind = callbackACEK; |
| downgradeToWarning = thisDowngrade; |
| } |
| }; |
| |
| auto *DC = ownerDecl->getDeclContext(); |
| |
| if (auto params = ownerCtx->getGenericParams()) { |
| for (auto param : *params) { |
| if (param->getInherited().empty()) |
| continue; |
| assert(param->getInherited().size() == 1); |
| checkTypeAccessImpl(param->getInherited().front().getType(), |
| param->getInherited().front().getTypeRepr(), |
| accessScope, DC, /*mayBeInferred*/false, |
| callback); |
| } |
| } |
| |
| callbackACEK = ACEK::Requirement; |
| |
| if (ownerCtx->getTrailingWhereClause()) { |
| checkRequirementAccess(WhereClauseOwner( |
| const_cast<GenericContext *>(ownerCtx)), |
| accessScope, DC, callback); |
| } |
| |
| if (minAccessScope.isPublic()) |
| return; |
| |
| // FIXME: Promote these to an error in the next -swift-version break. |
| if (isa<SubscriptDecl>(ownerDecl) || isa<TypeAliasDecl>(ownerDecl)) |
| downgradeToWarning = DowngradeToWarning::Yes; |
| |
| auto &Context = ownerDecl->getASTContext(); |
| if (checkUsableFromInline) { |
| if (!Context.isSwiftVersionAtLeast(5)) |
| downgradeToWarning = DowngradeToWarning::Yes; |
| |
| auto diagID = diag::generic_param_usable_from_inline; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::generic_param_usable_from_inline_warn; |
| auto diag = |
| Context.Diags.diagnose(ownerDecl, diagID, ownerDecl->getDescriptiveKind(), |
| accessControlErrorKind == ACEK::Requirement); |
| highlightOffendingType(diag, complainRepr); |
| return; |
| } |
| |
| auto minAccess = minAccessScope.accessLevelForDiagnostics(); |
| |
| bool isExplicit = |
| ownerDecl->getAttrs().hasAttribute<AccessControlAttr>() || |
| isa<ProtocolDecl>(DC); |
| auto diagID = diag::generic_param_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::generic_param_access_warn; |
| auto diag = Context.Diags.diagnose( |
| ownerDecl, diagID, ownerDecl->getDescriptiveKind(), isExplicit, |
| contextAccess, minAccess, isa<FileUnit>(DC), |
| accessControlErrorKind == ACEK::Requirement); |
| highlightOffendingType(diag, complainRepr); |
| } |
| |
| void AccessControlCheckerBase::checkGenericParamAccess( |
| const GenericContext *ownerCtx, |
| const ValueDecl *ownerDecl) { |
| checkGenericParamAccess(ownerCtx, ownerDecl, |
| ownerDecl->getFormalAccessScope( |
| nullptr, checkUsableFromInline), |
| ownerDecl->getFormalAccess()); |
| } |
| |
| namespace { |
| class AccessControlChecker : public AccessControlCheckerBase, |
| public DeclVisitor<AccessControlChecker> { |
| public: |
| |
| AccessControlChecker(bool allowUsableFromInline) |
| : AccessControlCheckerBase(allowUsableFromInline) {} |
| |
| AccessControlChecker() |
| : AccessControlCheckerBase(/*checkUsableFromInline=*/false) {} |
| |
| void visit(Decl *D) { |
| if (D->isInvalid() || D->isImplicit()) |
| return; |
| |
| DeclVisitor<AccessControlChecker>::visit(D); |
| } |
| |
| // Force all kinds to be handled at a lower level. |
| void visitDecl(Decl *D) = delete; |
| void visitValueDecl(ValueDecl *D) = delete; |
| |
| #define UNREACHABLE(KIND, REASON) \ |
| void visit##KIND##Decl(KIND##Decl *D) { \ |
| llvm_unreachable(REASON); \ |
| } |
| UNREACHABLE(Import, "cannot appear in a type context") |
| UNREACHABLE(Extension, "cannot appear in a type context") |
| UNREACHABLE(TopLevelCode, "cannot appear in a type context") |
| UNREACHABLE(Operator, "cannot appear in a type context") |
| UNREACHABLE(PrecedenceGroup, "cannot appear in a type context") |
| UNREACHABLE(Module, "cannot appear in a type context") |
| |
| UNREACHABLE(IfConfig, "does not have access control") |
| UNREACHABLE(PoundDiagnostic, "does not have access control") |
| UNREACHABLE(Param, "does not have access control") |
| UNREACHABLE(GenericTypeParam, "does not have access control") |
| UNREACHABLE(MissingMember, "does not have access control") |
| #undef UNREACHABLE |
| |
| #define UNINTERESTING(KIND) \ |
| void visit##KIND##Decl(KIND##Decl *D) {} |
| |
| UNINTERESTING(EnumCase) // Handled at the EnumElement level. |
| UNINTERESTING(Var) // Handled at the PatternBinding level. |
| UNINTERESTING(Destructor) // Always correct. |
| UNINTERESTING(Accessor) // Handled by the Var or Subscript. |
| |
| /// \see visitPatternBindingDecl |
| void checkNamedPattern(const NamedPattern *NP, bool isTypeContext, |
| const llvm::DenseSet<const VarDecl *> &seenVars) { |
| const VarDecl *theVar = NP->getDecl(); |
| if (seenVars.count(theVar) || theVar->isInvalid()) |
| return; |
| |
| checkTypeAccess(theVar->getInterfaceType(), nullptr, theVar, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = theVar->getAttrs().hasAttribute<AccessControlAttr>() || |
| isa<ProtocolDecl>(theVar->getDeclContext()); |
| auto theVarAccess = |
| isExplicit ? theVar->getFormalAccess() |
| : typeAccessScope.requiredAccessForDiagnostics(); |
| auto diagID = diag::pattern_type_access_inferred; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::pattern_type_access_inferred_warn; |
| auto &DE = theVar->getASTContext().Diags; |
| auto diag = DE.diagnose(NP->getLoc(), diagID, theVar->isLet(), |
| isTypeContext, isExplicit, theVarAccess, |
| isa<FileUnit>(theVar->getDeclContext()), |
| typeAccess, theVar->getInterfaceType()); |
| }); |
| } |
| |
| void checkTypedPattern(const TypedPattern *TP, bool isTypeContext, |
| llvm::DenseSet<const VarDecl *> &seenVars) { |
| VarDecl *anyVar = nullptr; |
| TP->forEachVariable([&](VarDecl *V) { |
| seenVars.insert(V); |
| anyVar = V; |
| }); |
| if (!anyVar) |
| return; |
| |
| checkTypeAccess(TP->hasType() ? TP->getType() : Type(), |
| TP->getTypeRepr(), anyVar, /*mayBeInferred*/true, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = anyVar->getAttrs().hasAttribute<AccessControlAttr>() || |
| isa<ProtocolDecl>(anyVar->getDeclContext()); |
| auto diagID = diag::pattern_type_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::pattern_type_access_warn; |
| auto anyVarAccess = |
| isExplicit ? anyVar->getFormalAccess() |
| : typeAccessScope.requiredAccessForDiagnostics(); |
| auto &DE = anyVar->getASTContext().Diags; |
| auto diag = DE.diagnose( |
| TP->getLoc(), diagID, anyVar->isLet(), isTypeContext, isExplicit, |
| anyVarAccess, isa<FileUnit>(anyVar->getDeclContext()), typeAccess); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| |
| // Check the property wrapper types. |
| for (auto attr : anyVar->getAttachedPropertyWrappers()) { |
| checkTypeAccess(attr->getType(), attr->getTypeRepr(), anyVar, |
| /*mayBeInferred=*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = |
| anyVar->getAttrs().hasAttribute<AccessControlAttr>() || |
| isa<ProtocolDecl>(anyVar->getDeclContext()); |
| auto anyVarAccess = |
| isExplicit ? anyVar->getFormalAccess() |
| : typeAccessScope.requiredAccessForDiagnostics(); |
| auto diag = anyVar->diagnose(diag::property_wrapper_type_access, |
| anyVar->isLet(), |
| isTypeContext, |
| isExplicit, |
| anyVarAccess, |
| isa<FileUnit>(anyVar->getDeclContext()), |
| typeAccess); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitPatternBindingDecl(PatternBindingDecl *PBD) { |
| bool isTypeContext = PBD->getDeclContext()->isTypeContext(); |
| |
| llvm::DenseSet<const VarDecl *> seenVars; |
| for (auto idx : range(PBD->getNumPatternEntries())) { |
| PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { |
| if (auto *NP = dyn_cast<NamedPattern>(P)) { |
| // Only check individual variables if we didn't check an enclosing |
| // TypedPattern. |
| checkNamedPattern(NP, isTypeContext, seenVars); |
| return; |
| } |
| |
| auto *TP = dyn_cast<TypedPattern>(P); |
| if (!TP) |
| return; |
| checkTypedPattern(TP, isTypeContext, seenVars); |
| }); |
| seenVars.clear(); |
| } |
| } |
| |
| void visitTypeAliasDecl(TypeAliasDecl *TAD) { |
| checkGenericParamAccess(TAD, TAD); |
| |
| checkTypeAccess(TAD->getUnderlyingType(), |
| TAD->getUnderlyingTypeRepr(), TAD, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = |
| TAD->getAttrs().hasAttribute<AccessControlAttr>() || |
| isa<ProtocolDecl>(TAD->getDeclContext()); |
| auto diagID = diag::type_alias_underlying_type_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::type_alias_underlying_type_access_warn; |
| auto aliasAccess = isExplicit |
| ? TAD->getFormalAccess() |
| : typeAccessScope.requiredAccessForDiagnostics(); |
| auto diag = TAD->diagnose(diagID, isExplicit, aliasAccess, typeAccess, |
| isa<FileUnit>(TAD->getDeclContext())); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| |
| void visitOpaqueTypeDecl(OpaqueTypeDecl *OTD) { |
| // TODO(opaque): The constraint class/protocols on the opaque interface, as |
| // well as the naming decl for the opaque type, need to be accessible. |
| } |
| |
| void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { |
| // This must stay in sync with diag::associated_type_access. |
| enum { |
| ACEK_DefaultDefinition = 0, |
| ACEK_Requirement |
| } accessControlErrorKind; |
| auto minAccessScope = AccessScope::getPublic(); |
| const TypeRepr *complainRepr = nullptr; |
| auto downgradeToWarning = DowngradeToWarning::No; |
| |
| std::for_each(assocType->getInherited().begin(), |
| assocType->getInherited().end(), |
| [&](TypeLoc requirement) { |
| checkTypeAccess(requirement, assocType, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| accessControlErrorKind = ACEK_Requirement; |
| downgradeToWarning = downgradeDiag; |
| } |
| }); |
| }); |
| checkTypeAccess(assocType->getDefaultDefinitionType(), |
| assocType->getDefaultDefinitionTypeRepr(), assocType, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| accessControlErrorKind = ACEK_DefaultDefinition; |
| downgradeToWarning = downgradeDiag; |
| } |
| }); |
| |
| checkRequirementAccess(assocType, |
| assocType->getFormalAccessScope(), |
| assocType->getDeclContext(), |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| accessControlErrorKind = ACEK_Requirement; |
| downgradeToWarning = downgradeDiag; |
| |
| // Swift versions before 5.0 did not check requirements on the |
| // protocol's where clause, so emit a warning. |
| if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) |
| downgradeToWarning = DowngradeToWarning::Yes; |
| } |
| }); |
| |
| if (!minAccessScope.isPublic()) { |
| auto minAccess = minAccessScope.accessLevelForDiagnostics(); |
| auto diagID = diag::associated_type_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::associated_type_access_warn; |
| auto diag = assocType->diagnose(diagID, assocType->getFormalAccess(), |
| minAccess, accessControlErrorKind); |
| highlightOffendingType(diag, complainRepr); |
| } |
| } |
| |
| void visitEnumDecl(EnumDecl *ED) { |
| checkGenericParamAccess(ED, ED); |
| |
| if (ED->hasRawType()) { |
| Type rawType = ED->getRawType(); |
| auto rawTypeLocIter = std::find_if(ED->getInherited().begin(), |
| ED->getInherited().end(), |
| [&](TypeLoc inherited) { |
| if (!inherited.wasValidated()) |
| return false; |
| return inherited.getType().getPointer() == rawType.getPointer(); |
| }); |
| if (rawTypeLocIter == ED->getInherited().end()) |
| return; |
| checkTypeAccess(rawType, rawTypeLocIter->getTypeRepr(), ED, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = ED->getAttrs().hasAttribute<AccessControlAttr>(); |
| auto diagID = diag::enum_raw_type_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::enum_raw_type_access_warn; |
| auto enumDeclAccess = isExplicit |
| ? ED->getFormalAccess() |
| : typeAccessScope.requiredAccessForDiagnostics(); |
| auto diag = ED->diagnose(diagID, isExplicit, enumDeclAccess, typeAccess, |
| isa<FileUnit>(ED->getDeclContext())); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitStructDecl(StructDecl *SD) { |
| checkGenericParamAccess(SD, SD); |
| } |
| |
| void visitClassDecl(ClassDecl *CD) { |
| checkGenericParamAccess(CD, CD); |
| |
| if (const NominalTypeDecl *superclassDecl = CD->getSuperclassDecl()) { |
| // Be slightly defensive here in the presence of badly-ordered |
| // inheritance clauses. |
| auto superclassLocIter = std::find_if(CD->getInherited().begin(), |
| CD->getInherited().end(), |
| [&](TypeLoc inherited) { |
| if (!inherited.wasValidated()) |
| return false; |
| Type ty = inherited.getType(); |
| if (ty->is<ProtocolCompositionType>()) |
| if (auto superclass = ty->getExistentialLayout().explicitSuperclass) |
| ty = superclass; |
| return ty->getAnyNominal() == superclassDecl; |
| }); |
| // Sanity check: we couldn't find the superclass for whatever reason |
| // (possibly because it's synthetic or something), so don't bother |
| // checking it. |
| if (superclassLocIter == CD->getInherited().end()) |
| return; |
| |
| auto outerDowngradeToWarning = DowngradeToWarning::No; |
| if (superclassDecl->isGenericContext() && |
| !CD->getASTContext().isSwiftVersionAtLeast(5)) { |
| // Swift 4 failed to properly check this if the superclass was generic, |
| // because the above loop was too strict. |
| outerDowngradeToWarning = DowngradeToWarning::Yes; |
| } |
| |
| checkTypeAccess(CD->getSuperclass(), superclassLocIter->getTypeRepr(), CD, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = CD->getAttrs().hasAttribute<AccessControlAttr>(); |
| auto diagID = diag::class_super_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes || |
| outerDowngradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::class_super_access_warn; |
| auto classDeclAccess = isExplicit |
| ? CD->getFormalAccess() |
| : typeAccessScope.requiredAccessForDiagnostics(); |
| |
| auto diag = |
| CD->diagnose(diagID, isExplicit, classDeclAccess, typeAccess, |
| isa<FileUnit>(CD->getDeclContext()), |
| superclassLocIter->getTypeRepr() != complainRepr); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitProtocolDecl(ProtocolDecl *proto) { |
| // This must stay in sync with diag::protocol_access. |
| enum { |
| PCEK_Refine = 0, |
| PCEK_Requirement |
| } protocolControlErrorKind; |
| |
| auto minAccessScope = AccessScope::getPublic(); |
| const TypeRepr *complainRepr = nullptr; |
| auto downgradeToWarning = DowngradeToWarning::No; |
| DescriptiveDeclKind declKind = DescriptiveDeclKind::Protocol; |
| |
| // FIXME: Hack to ensure that we've computed the types involved here. |
| ASTContext &ctx = proto->getASTContext(); |
| for (unsigned i : indices(proto->getInherited())) { |
| (void)evaluateOrDefault(ctx.evaluator, |
| InheritedTypeRequest{ |
| proto, i, TypeResolutionStage::Interface}, |
| Type()); |
| } |
| |
| auto declKindForType = [](Type type) { |
| if (isa<TypeAliasType>(type.getPointer())) |
| return DescriptiveDeclKind::TypeAlias; |
| else if (auto nominal = type->getAnyNominal()) |
| return nominal->getDescriptiveKind(); |
| else |
| return DescriptiveDeclKind::Type; |
| }; |
| |
| std::for_each(proto->getInherited().begin(), |
| proto->getInherited().end(), |
| [&](TypeLoc requirement) { |
| checkTypeAccess(requirement, proto, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| protocolControlErrorKind = PCEK_Refine; |
| downgradeToWarning = downgradeDiag; |
| declKind = declKindForType(requirement.getType()); |
| } |
| }); |
| }); |
| |
| forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { |
| checkTypeAccess( |
| type, typeRepr, proto, |
| /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| protocolControlErrorKind = PCEK_Requirement; |
| downgradeToWarning = downgradeDiag; |
| declKind = declKindForType(type); |
| // Swift versions before 5.0 did not check requirements on the |
| // protocol's where clause, so emit a warning. |
| if (!proto->getASTContext().isSwiftVersionAtLeast(5)) |
| downgradeToWarning = DowngradeToWarning::Yes; |
| } |
| }); |
| }); |
| |
| if (!minAccessScope.isPublic()) { |
| auto minAccess = minAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = proto->getAttrs().hasAttribute<AccessControlAttr>(); |
| auto protoAccess = isExplicit |
| ? proto->getFormalAccess() |
| : minAccessScope.requiredAccessForDiagnostics(); |
| auto diagID = diag::protocol_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::protocol_access_warn; |
| auto diag = proto->diagnose( |
| diagID, isExplicit, protoAccess, protocolControlErrorKind, minAccess, |
| isa<FileUnit>(proto->getDeclContext()), declKind); |
| highlightOffendingType(diag, complainRepr); |
| } |
| } |
| |
| void visitSubscriptDecl(SubscriptDecl *SD) { |
| checkGenericParamAccess(SD, SD); |
| |
| auto minAccessScope = AccessScope::getPublic(); |
| const TypeRepr *complainRepr = nullptr; |
| auto downgradeToWarning = DowngradeToWarning::No; |
| bool problemIsElement = false; |
| |
| for (auto &P : *SD->getIndices()) { |
| checkTypeAccess( |
| P->getInterfaceType(), P->getTypeRepr(), SD, /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| downgradeToWarning = downgradeDiag; |
| } |
| }); |
| } |
| |
| checkTypeAccess(SD->getElementInterfaceType(), SD->getElementTypeRepr(), |
| SD, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| downgradeToWarning = downgradeDiag; |
| problemIsElement = true; |
| } |
| }); |
| |
| if (!minAccessScope.isPublic()) { |
| auto minAccess = minAccessScope.accessLevelForDiagnostics(); |
| bool isExplicit = |
| SD->getAttrs().hasAttribute<AccessControlAttr>() || |
| isa<ProtocolDecl>(SD->getDeclContext()); |
| auto diagID = diag::subscript_type_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::subscript_type_access_warn; |
| auto subscriptDeclAccess = isExplicit |
| ? SD->getFormalAccess() |
| : minAccessScope.requiredAccessForDiagnostics(); |
| auto diag = SD->diagnose(diagID, isExplicit, subscriptDeclAccess, |
| minAccess, problemIsElement); |
| highlightOffendingType(diag, complainRepr); |
| } |
| } |
| |
| void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { |
| bool isTypeContext = fn->getDeclContext()->isTypeContext(); |
| |
| checkGenericParamAccess(fn, fn); |
| |
| // This must stay in sync with diag::function_type_access. |
| enum { |
| FK_Function = 0, |
| FK_Method, |
| FK_Initializer |
| }; |
| |
| auto minAccessScope = AccessScope::getPublic(); |
| const TypeRepr *complainRepr = nullptr; |
| auto downgradeToWarning = DowngradeToWarning::No; |
| |
| for (auto *P : *fn->getParameters()) { |
| checkTypeAccess( |
| P->getInterfaceType(), P->getTypeRepr(), fn, /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| downgradeToWarning = downgradeDiag; |
| } |
| }); |
| } |
| |
| bool problemIsResult = false; |
| if (auto FD = dyn_cast<FuncDecl>(fn)) { |
| checkTypeAccess(FD->getResultInterfaceType(), FD->getResultTypeRepr(), |
| FD, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *thisComplainRepr, |
| DowngradeToWarning downgradeDiag) { |
| if (typeAccessScope.isChildOf(minAccessScope) || |
| (!complainRepr && |
| typeAccessScope.hasEqualDeclContextWith(minAccessScope))) { |
| minAccessScope = typeAccessScope; |
| complainRepr = thisComplainRepr; |
| downgradeToWarning = downgradeDiag; |
| problemIsResult = true; |
| } |
| }); |
| } |
| |
| if (!minAccessScope.isPublic()) { |
| auto minAccess = minAccessScope.accessLevelForDiagnostics(); |
| auto functionKind = isa<ConstructorDecl>(fn) |
| ? FK_Initializer |
| : isTypeContext ? FK_Method : FK_Function; |
| bool isExplicit = |
| fn->getAttrs().hasAttribute<AccessControlAttr>() || |
| isa<ProtocolDecl>(fn->getDeclContext()); |
| auto fnAccess = isExplicit |
| ? fn->getFormalAccess() |
| : minAccessScope.requiredAccessForDiagnostics(); |
| |
| auto diagID = diag::function_type_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::function_type_access_warn; |
| auto diag = fn->diagnose(diagID, isExplicit, fnAccess, |
| isa<FileUnit>(fn->getDeclContext()), minAccess, |
| functionKind, problemIsResult); |
| highlightOffendingType(diag, complainRepr); |
| } |
| } |
| |
| void visitEnumElementDecl(EnumElementDecl *EED) { |
| if (!EED->hasAssociatedValues()) |
| return; |
| for (auto &P : *EED->getParameterList()) { |
| checkTypeAccess( |
| P->getInterfaceType(), P->getTypeRepr(), EED, /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto typeAccess = typeAccessScope.accessLevelForDiagnostics(); |
| auto diagID = diag::enum_case_access; |
| if (downgradeToWarning == DowngradeToWarning::Yes) |
| diagID = diag::enum_case_access_warn; |
| auto diag = |
| EED->diagnose(diagID, EED->getFormalAccess(), typeAccess); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| }; |
| |
| class UsableFromInlineChecker : public AccessControlCheckerBase, |
| public DeclVisitor<UsableFromInlineChecker> { |
| public: |
| UsableFromInlineChecker() |
| : AccessControlCheckerBase(/*checkUsableFromInline=*/true) {} |
| |
| static bool shouldSkipChecking(const ValueDecl *VD) { |
| if (VD->getFormalAccess() != AccessLevel::Internal) |
| return true; |
| return !VD->isUsableFromInline(); |
| }; |
| |
| void visit(Decl *D) { |
| if (!D->getASTContext().isSwiftVersionAtLeast(4, 2)) |
| return; |
| |
| if (D->isInvalid() || D->isImplicit()) |
| return; |
| |
| if (auto *VD = dyn_cast<ValueDecl>(D)) |
| if (shouldSkipChecking(VD)) |
| return; |
| |
| DeclVisitor<UsableFromInlineChecker>::visit(D); |
| } |
| |
| // Force all kinds to be handled at a lower level. |
| void visitDecl(Decl *D) = delete; |
| void visitValueDecl(ValueDecl *D) = delete; |
| |
| #define UNREACHABLE(KIND, REASON) \ |
| void visit##KIND##Decl(KIND##Decl *D) { \ |
| llvm_unreachable(REASON); \ |
| } |
| UNREACHABLE(Import, "cannot appear in a type context") |
| UNREACHABLE(Extension, "cannot appear in a type context") |
| UNREACHABLE(TopLevelCode, "cannot appear in a type context") |
| UNREACHABLE(Operator, "cannot appear in a type context") |
| UNREACHABLE(PrecedenceGroup, "cannot appear in a type context") |
| UNREACHABLE(Module, "cannot appear in a type context") |
| |
| UNREACHABLE(Param, "does not have access control") |
| UNREACHABLE(GenericTypeParam, "does not have access control") |
| UNREACHABLE(MissingMember, "does not have access control") |
| #undef UNREACHABLE |
| |
| #define UNINTERESTING(KIND) \ |
| void visit##KIND##Decl(KIND##Decl *D) {} |
| |
| UNINTERESTING(IfConfig) // Does not have access control. |
| UNINTERESTING(PoundDiagnostic) // Does not have access control. |
| UNINTERESTING(EnumCase) // Handled at the EnumElement level. |
| UNINTERESTING(Var) // Handled at the PatternBinding level. |
| UNINTERESTING(Destructor) // Always correct. |
| UNINTERESTING(Accessor) // Handled by the Var or Subscript. |
| UNINTERESTING(OpaqueType) // Handled by the Var or Subscript. |
| |
| /// If \p VD's layout is exposed by a @frozen struct or class, return said |
| /// struct or class. |
| /// |
| /// Stored instance properties in @frozen structs and classes must always use |
| /// public/@usableFromInline types. In these cases, check the access against |
| /// the struct instead of the VarDecl, and customize the diagnostics. |
| static const ValueDecl * |
| getFixedLayoutStructContext(const VarDecl *VD) { |
| if (VD->isLayoutExposedToClients()) |
| return dyn_cast<NominalTypeDecl>(VD->getDeclContext()); |
| |
| return nullptr; |
| } |
| |
| /// \see visitPatternBindingDecl |
| void checkNamedPattern(const NamedPattern *NP, |
| bool isTypeContext, |
| const llvm::DenseSet<const VarDecl *> &seenVars) { |
| const VarDecl *theVar = NP->getDecl(); |
| auto *fixedLayoutStructContext = getFixedLayoutStructContext(theVar); |
| if (!fixedLayoutStructContext && shouldSkipChecking(theVar)) |
| return; |
| // Only check individual variables if we didn't check an enclosing |
| // TypedPattern. |
| if (seenVars.count(theVar) || theVar->isInvalid()) |
| return; |
| |
| checkTypeAccess( |
| theVar->getInterfaceType(), nullptr, |
| fixedLayoutStructContext ? fixedLayoutStructContext : theVar, |
| /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto &Ctx = theVar->getASTContext(); |
| auto diagID = diag::pattern_type_not_usable_from_inline_inferred; |
| if (fixedLayoutStructContext) { |
| diagID = diag::pattern_type_not_usable_from_inline_inferred_frozen; |
| } else if (!Ctx.isSwiftVersionAtLeast(5)) { |
| diagID = diag::pattern_type_not_usable_from_inline_inferred_warn; |
| } |
| Ctx.Diags.diagnose(NP->getLoc(), diagID, theVar->isLet(), |
| isTypeContext, theVar->getInterfaceType()); |
| }); |
| } |
| |
| /// \see visitPatternBindingDecl |
| void checkTypedPattern(const TypedPattern *TP, |
| bool isTypeContext, |
| llvm::DenseSet<const VarDecl *> &seenVars) { |
| // FIXME: We need an access level to check against, so we pull one out |
| // of some random VarDecl in the pattern. They're all going to be the |
| // same, but still, ick. |
| VarDecl *anyVar = nullptr; |
| TP->forEachVariable([&](VarDecl *V) { |
| seenVars.insert(V); |
| anyVar = V; |
| }); |
| if (!anyVar) |
| return; |
| auto *fixedLayoutStructContext = getFixedLayoutStructContext(anyVar); |
| if (!fixedLayoutStructContext && shouldSkipChecking(anyVar)) |
| return; |
| |
| checkTypeAccess( |
| TP->hasType() ? TP->getType() : Type(), |
| TP->getTypeRepr(), |
| fixedLayoutStructContext ? fixedLayoutStructContext : anyVar, |
| /*mayBeInferred*/ true, |
| [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto &Ctx = anyVar->getASTContext(); |
| auto diagID = diag::pattern_type_not_usable_from_inline; |
| if (fixedLayoutStructContext) |
| diagID = diag::pattern_type_not_usable_from_inline_frozen; |
| else if (!Ctx.isSwiftVersionAtLeast(5)) |
| diagID = diag::pattern_type_not_usable_from_inline_warn; |
| auto diag = Ctx.Diags.diagnose(TP->getLoc(), diagID, anyVar->isLet(), |
| isTypeContext); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| |
| for (auto attr : anyVar->getAttachedPropertyWrappers()) { |
| checkTypeAccess(attr->getType(), attr->getTypeRepr(), |
| fixedLayoutStructContext ? fixedLayoutStructContext |
| : anyVar, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto diag = anyVar->diagnose( |
| diag::property_wrapper_type_not_usable_from_inline, |
| anyVar->isLet(), isTypeContext); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitPatternBindingDecl(PatternBindingDecl *PBD) { |
| bool isTypeContext = PBD->getDeclContext()->isTypeContext(); |
| |
| llvm::DenseSet<const VarDecl *> seenVars; |
| for (auto idx : range(PBD->getNumPatternEntries())) { |
| PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { |
| if (auto *NP = dyn_cast<NamedPattern>(P)) { |
| checkNamedPattern(NP, isTypeContext, seenVars); |
| return; |
| } |
| |
| auto *TP = dyn_cast<TypedPattern>(P); |
| if (!TP) |
| return; |
| checkTypedPattern(TP, isTypeContext, seenVars); |
| }); |
| seenVars.clear(); |
| } |
| } |
| |
| void visitTypeAliasDecl(TypeAliasDecl *TAD) { |
| checkGenericParamAccess(TAD, TAD); |
| |
| checkTypeAccess(TAD->getUnderlyingType(), |
| TAD->getUnderlyingTypeRepr(), TAD, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto diagID = diag::type_alias_underlying_type_not_usable_from_inline; |
| if (!TAD->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::type_alias_underlying_type_not_usable_from_inline_warn; |
| auto diag = TAD->diagnose(diagID); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| |
| void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { |
| // This must stay in sync with diag::associated_type_not_usable_from_inline. |
| enum { |
| ACEK_DefaultDefinition = 0, |
| ACEK_Requirement |
| }; |
| |
| std::for_each(assocType->getInherited().begin(), |
| assocType->getInherited().end(), |
| [&](TypeLoc requirement) { |
| checkTypeAccess(requirement, assocType, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::associated_type_not_usable_from_inline; |
| if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::associated_type_not_usable_from_inline_warn; |
| auto diag = assocType->diagnose(diagID, ACEK_Requirement); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| }); |
| checkTypeAccess(assocType->getDefaultDefinitionType(), |
| assocType->getDefaultDefinitionTypeRepr(), assocType, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::associated_type_not_usable_from_inline; |
| if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::associated_type_not_usable_from_inline_warn; |
| auto diag = assocType->diagnose(diagID, ACEK_DefaultDefinition); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| |
| if (assocType->getTrailingWhereClause()) { |
| auto accessScope = |
| assocType->getFormalAccessScope(nullptr); |
| checkRequirementAccess(assocType, |
| accessScope, |
| assocType->getDeclContext(), |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::associated_type_not_usable_from_inline; |
| if (!assocType->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::associated_type_not_usable_from_inline_warn; |
| auto diag = assocType->diagnose(diagID, ACEK_Requirement); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitEnumDecl(const EnumDecl *ED) { |
| checkGenericParamAccess(ED, ED); |
| |
| if (ED->hasRawType()) { |
| Type rawType = ED->getRawType(); |
| auto rawTypeLocIter = std::find_if(ED->getInherited().begin(), |
| ED->getInherited().end(), |
| [&](TypeLoc inherited) { |
| if (!inherited.wasValidated()) |
| return false; |
| return inherited.getType().getPointer() == rawType.getPointer(); |
| }); |
| if (rawTypeLocIter == ED->getInherited().end()) |
| return; |
| checkTypeAccess(rawType, rawTypeLocIter->getTypeRepr(), ED, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto diagID = diag::enum_raw_type_not_usable_from_inline; |
| if (!ED->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::enum_raw_type_not_usable_from_inline_warn; |
| auto diag = ED->diagnose(diagID); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitStructDecl(StructDecl *SD) { |
| checkGenericParamAccess(SD, SD); |
| } |
| |
| void visitClassDecl(ClassDecl *CD) { |
| checkGenericParamAccess(CD, CD); |
| |
| if (CD->hasSuperclass()) { |
| const NominalTypeDecl *superclassDecl = CD->getSuperclassDecl(); |
| // Be slightly defensive here in the presence of badly-ordered |
| // inheritance clauses. |
| auto superclassLocIter = std::find_if(CD->getInherited().begin(), |
| CD->getInherited().end(), |
| [&](TypeLoc inherited) { |
| if (!inherited.wasValidated()) |
| return false; |
| Type ty = inherited.getType(); |
| if (ty->is<ProtocolCompositionType>()) |
| if (auto superclass = ty->getExistentialLayout().explicitSuperclass) |
| ty = superclass; |
| return ty->getAnyNominal() == superclassDecl; |
| }); |
| // Sanity check: we couldn't find the superclass for whatever reason |
| // (possibly because it's synthetic or something), so don't bother |
| // checking it. |
| if (superclassLocIter == CD->getInherited().end()) |
| return; |
| |
| checkTypeAccess(CD->getSuperclass(), superclassLocIter->getTypeRepr(), CD, |
| /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto diagID = diag::class_super_not_usable_from_inline; |
| if (!CD->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::class_super_not_usable_from_inline_warn; |
| auto diag = CD->diagnose(diagID, superclassLocIter->getTypeRepr() != |
| complainRepr); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitProtocolDecl(ProtocolDecl *proto) { |
| // This must stay in sync with diag::protocol_usable_from_inline. |
| enum { |
| PCEK_Refine = 0, |
| PCEK_Requirement |
| }; |
| |
| std::for_each(proto->getInherited().begin(), |
| proto->getInherited().end(), |
| [&](TypeLoc requirement) { |
| checkTypeAccess(requirement, proto, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::protocol_usable_from_inline; |
| if (!proto->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::protocol_usable_from_inline_warn; |
| auto diag = proto->diagnose(diagID, PCEK_Refine); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| }); |
| |
| if (proto->getTrailingWhereClause()) { |
| auto accessScope = proto->getFormalAccessScope(nullptr, |
| /*checkUsableFromInline*/true); |
| checkRequirementAccess(proto, |
| accessScope, |
| proto->getDeclContext(), |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::protocol_usable_from_inline; |
| if (!proto->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::protocol_usable_from_inline_warn; |
| auto diag = proto->diagnose(diagID, PCEK_Requirement); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitSubscriptDecl(SubscriptDecl *SD) { |
| checkGenericParamAccess(SD, SD); |
| |
| for (auto &P : *SD->getIndices()) { |
| checkTypeAccess( |
| P->getInterfaceType(), P->getTypeRepr(), SD, /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::subscript_type_usable_from_inline; |
| if (!SD->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::subscript_type_usable_from_inline_warn; |
| auto diag = SD->diagnose(diagID, /*problemIsElement=*/false); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| |
| checkTypeAccess(SD->getElementInterfaceType(), SD->getElementTypeRepr(), |
| SD, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::subscript_type_usable_from_inline; |
| if (!SD->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::subscript_type_usable_from_inline_warn; |
| auto diag = SD->diagnose(diagID, /*problemIsElement=*/true); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| |
| void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { |
| bool isTypeContext = fn->getDeclContext()->isTypeContext(); |
| |
| checkGenericParamAccess(fn, fn); |
| |
| // This must stay in sync with diag::function_type_usable_from_inline. |
| enum { |
| FK_Function = 0, |
| FK_Method, |
| FK_Initializer |
| }; |
| |
| auto functionKind = isa<ConstructorDecl>(fn) |
| ? FK_Initializer |
| : isTypeContext ? FK_Method : FK_Function; |
| |
| for (auto *P : *fn->getParameters()) { |
| checkTypeAccess( |
| P->getInterfaceType(), P->getTypeRepr(), fn, /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::function_type_usable_from_inline; |
| if (!fn->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::function_type_usable_from_inline_warn; |
| auto diag = fn->diagnose(diagID, functionKind, |
| /*problemIsResult=*/false); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| |
| if (auto FD = dyn_cast<FuncDecl>(fn)) { |
| checkTypeAccess(FD->getResultInterfaceType(), FD->getResultTypeRepr(), |
| FD, /*mayBeInferred*/false, |
| [&](AccessScope typeAccessScope, |
| const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeDiag) { |
| auto diagID = diag::function_type_usable_from_inline; |
| if (!fn->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::function_type_usable_from_inline_warn; |
| auto diag = fn->diagnose(diagID, functionKind, |
| /*problemIsResult=*/true); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| |
| void visitEnumElementDecl(EnumElementDecl *EED) { |
| if (!EED->hasAssociatedValues()) |
| return; |
| for (auto &P : *EED->getParameterList()) { |
| checkTypeAccess( |
| P->getInterfaceType(), P->getTypeRepr(), EED, /*mayBeInferred*/ false, |
| [&](AccessScope typeAccessScope, const TypeRepr *complainRepr, |
| DowngradeToWarning downgradeToWarning) { |
| auto diagID = diag::enum_case_usable_from_inline; |
| if (!EED->getASTContext().isSwiftVersionAtLeast(5)) |
| diagID = diag::enum_case_usable_from_inline_warn; |
| auto diag = EED->diagnose(diagID); |
| highlightOffendingType(diag, complainRepr); |
| }); |
| } |
| } |
| }; |
| } // end anonymous namespace |
| |
| /// Returns the kind of origin, implementation-only import or SPI declaration, |
| /// that restricts exporting \p decl from the given file and context. |
| /// |
| /// Local variant to swift::getDisallowedOriginKind for downgrade to warnings. |
| DisallowedOriginKind |
| swift::getDisallowedOriginKind(const Decl *decl, |
| const ExportContext &where, |
| DowngradeToWarning &downgradeToWarning) { |
| downgradeToWarning = DowngradeToWarning::No; |
| ModuleDecl *M = decl->getModuleContext(); |
| auto *SF = where.getDeclContext()->getParentSourceFile(); |
| if (SF->isImportedImplementationOnly(M)) { |
| // Temporarily downgrade implementation-only exportability in SPI to |
| // a warning. |
| if (where.isSPI()) |
| downgradeToWarning = DowngradeToWarning::Yes; |
| |
| // Even if the current module is @_implementationOnly, Swift should |
| // not report an error in the cases where the decl is also exported from |
| // a non @_implementationOnly module. Thus, we check to see if there is |
| // a visible access path to the Clang decl, and only error out in case |
| // there is none. |
| auto filter = ModuleDecl::ImportFilter( |
| {ModuleDecl::ImportFilterKind::Exported, |
| ModuleDecl::ImportFilterKind::Default, |
| ModuleDecl::ImportFilterKind::SPIAccessControl, |
| ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay}); |
| SmallVector<ImportedModule, 4> sfImportedModules; |
| SF->getImportedModules(sfImportedModules, filter); |
| if (auto clangDecl = decl->getClangDecl()) { |
| for (auto redecl : clangDecl->redecls()) { |
| if (auto tagReDecl = dyn_cast<clang::TagDecl>(redecl)) { |
| // This is a forward declaration. We ignore visibility of those. |
| if (tagReDecl->getBraceRange().isInvalid()) { |
| continue; |
| } |
| } |
| auto moduleWrapper = |
| decl->getASTContext().getClangModuleLoader()->getWrapperForModule( |
| redecl->getOwningModule()); |
| auto visibleAccessPath = |
| find_if(sfImportedModules, [&moduleWrapper](auto importedModule) { |
| return importedModule.importedModule == moduleWrapper || |
| !importedModule.importedModule |
| ->isImportedImplementationOnly(moduleWrapper); |
| }); |
| if (visibleAccessPath != sfImportedModules.end()) { |
| return DisallowedOriginKind::None; |
| } |
| } |
| } |
| // Implementation-only imported, cannot be reexported. |
| return DisallowedOriginKind::ImplementationOnly; |
| } else if (decl->isSPI() && !where.isSPI()) { |
| // SPI can only be exported in SPI. |
| return where.getDeclContext()->getParentModule() == M ? |
| DisallowedOriginKind::SPILocal : |
| DisallowedOriginKind::SPIImported; |
| } |
| |
| return DisallowedOriginKind::None; |
| }; |
| |
| namespace { |
| |
| /// Diagnose declarations whose signatures refer to unavailable types. |
| class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> { |
| ExportContext Where; |
| |
| void checkType(Type type, const TypeRepr *typeRepr, const Decl *context, |
| ExportabilityReason reason=ExportabilityReason::General, |
| bool allowUnavailableProtocol=false) { |
| // Don't bother checking errors. |
| if (type && type->hasError()) |
| return; |
| |
| DeclAvailabilityFlags flags = None; |
| |
| // We allow a type to conform to a protocol that is less available than |
| // the type itself. This enables a type to retroactively model or directly |
| // conform to a protocol only available on newer OSes and yet still be used on |
| // older OSes. |
| // |
| // To support this, inside inheritance clauses we allow references to |
| // protocols that are unavailable in the current type refinement context. |
| if (allowUnavailableProtocol) |
| flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailableProtocol; |
| |
| diagnoseTypeAvailability(typeRepr, type, context->getLoc(), |
| Where.withReason(reason), flags); |
| } |
| |
| void checkGenericParams(const GenericContext *ownerCtx, |
| const ValueDecl *ownerDecl) { |
| if (!ownerCtx->isGenericContext()) |
| return; |
| |
| if (auto params = ownerCtx->getGenericParams()) { |
| for (auto param : *params) { |
| if (param->getInherited().empty()) |
| continue; |
| assert(param->getInherited().size() == 1); |
| auto inherited = param->getInherited().front(); |
| checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl); |
| } |
| } |
| |
| if (ownerCtx->getTrailingWhereClause()) { |
| forAllRequirementTypes(WhereClauseOwner( |
| const_cast<GenericContext *>(ownerCtx)), |
| [&](Type type, TypeRepr *typeRepr) { |
| checkType(type, typeRepr, ownerDecl); |
| }); |
| } |
| } |
| |
| public: |
| explicit DeclAvailabilityChecker(ExportContext where) |
| : Where(where) {} |
| |
| // Force all kinds to be handled at a lower level. |
| void visitDecl(Decl *D) = delete; |
| void visitValueDecl(ValueDecl *D) = delete; |
| |
| #define UNREACHABLE(KIND, REASON) \ |
| void visit##KIND##Decl(KIND##Decl *D) { \ |
| llvm_unreachable(REASON); \ |
| } |
| UNREACHABLE(Import, "not applicable") |
| UNREACHABLE(TopLevelCode, "not applicable") |
| UNREACHABLE(Module, "not applicable") |
| |
| UNREACHABLE(Param, "handled by the enclosing declaration") |
| UNREACHABLE(GenericTypeParam, "handled by the enclosing declaration") |
| UNREACHABLE(MissingMember, "handled by the enclosing declaration") |
| #undef UNREACHABLE |
| |
| #define UNINTERESTING(KIND) \ |
| void visit##KIND##Decl(KIND##Decl *D) {} |
| |
| UNINTERESTING(PrefixOperator) // Does not reference other decls. |
| UNINTERESTING(PostfixOperator) // Does not reference other decls. |
| UNINTERESTING(IfConfig) // Not applicable. |
| UNINTERESTING(PoundDiagnostic) // Not applicable. |
| UNINTERESTING(EnumCase) // Handled at the EnumElement level. |
| UNINTERESTING(Destructor) // Always correct. |
| UNINTERESTING(Accessor) // Handled by the Var or Subscript. |
| UNINTERESTING(OpaqueType) // TODO |
| |
| // Handled at the PatternBinding level; if the pattern has a simple |
| // "name: TheType" form, we can get better results by diagnosing the TypeRepr. |
| UNINTERESTING(Var) |
| |
| /// \see visitPatternBindingDecl |
| void checkNamedPattern(const NamedPattern *NP, |
| const llvm::DenseSet<const VarDecl *> &seenVars) { |
| const VarDecl *theVar = NP->getDecl(); |
| |
| // Only check the type of individual variables if we didn't check an |
| // enclosing TypedPattern. |
| if (seenVars.count(theVar)) |
| return; |
| |
| checkType(theVar->getValueInterfaceType(), /*typeRepr*/nullptr, theVar); |
| } |
| |
| /// \see visitPatternBindingDecl |
| void checkTypedPattern(PatternBindingDecl *PBD, |
| const TypedPattern *TP, |
| llvm::DenseSet<const VarDecl *> &seenVars) { |
| // FIXME: We need to figure out if this is a stored or computed property, |
| // so we pull out some random VarDecl in the pattern. They're all going to |
| // be the same, but still, ick. |
| const VarDecl *anyVar = nullptr; |
| TP->forEachVariable([&](VarDecl *V) { |
| seenVars.insert(V); |
| anyVar = V; |
| }); |
| |
| checkType(TP->hasType() ? TP->getType() : Type(), |
| TP->getTypeRepr(), anyVar ? (Decl *)anyVar : (Decl *)PBD); |
| |
| // Check the property wrapper types. |
| if (anyVar) { |
| for (auto attr : anyVar->getAttachedPropertyWrappers()) { |
| checkType(attr->getType(), attr->getTypeRepr(), anyVar, |
| ExportabilityReason::PropertyWrapper); |
| } |
| |
| if (auto attr = anyVar->getAttachedResultBuilder()) { |
| checkType(anyVar->getResultBuilderType(), |
| attr->getTypeRepr(), anyVar, |
| ExportabilityReason::ResultBuilder); |
| } |
| } |
| } |
| |
| void visitPatternBindingDecl(PatternBindingDecl *PBD) { |
| llvm::DenseSet<const VarDecl *> seenVars; |
| for (auto idx : range(PBD->getNumPatternEntries())) { |
| PBD->getPattern(idx)->forEachNode([&](const Pattern *P) { |
| if (auto *NP = dyn_cast<NamedPattern>(P)) { |
| checkNamedPattern(NP, seenVars); |
| return; |
| } |
| |
| auto *TP = dyn_cast<TypedPattern>(P); |
| if (!TP) |
| return; |
| checkTypedPattern(PBD, TP, seenVars); |
| }); |
| seenVars.clear(); |
| } |
| } |
| |
| void visitTypeAliasDecl(TypeAliasDecl *TAD) { |
| checkGenericParams(TAD, TAD); |
| checkType(TAD->getUnderlyingType(), |
| TAD->getUnderlyingTypeRepr(), TAD); |
| } |
| |
| void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { |
| llvm::for_each(assocType->getInherited(), |
| [&](TypeLoc requirement) { |
| checkType(requirement.getType(), requirement.getTypeRepr(), |
| assocType); |
| }); |
| checkType(assocType->getDefaultDefinitionType(), |
| assocType->getDefaultDefinitionTypeRepr(), assocType); |
| |
| if (assocType->getTrailingWhereClause()) { |
| forAllRequirementTypes(assocType, |
| [&](Type type, TypeRepr *typeRepr) { |
| checkType(type, typeRepr, assocType); |
| }); |
| } |
| } |
| |
| void visitNominalTypeDecl(const NominalTypeDecl *nominal) { |
| checkGenericParams(nominal, nominal); |
| |
| llvm::for_each(nominal->getInherited(), |
| [&](TypeLoc inherited) { |
| checkType(inherited.getType(), inherited.getTypeRepr(), |
| nominal, ExportabilityReason::General, |
| /*allowUnavailableProtocol=*/true); |
| }); |
| } |
| |
| void visitProtocolDecl(ProtocolDecl *proto) { |
| llvm::for_each(proto->getInherited(), |
| [&](TypeLoc requirement) { |
| checkType(requirement.getType(), requirement.getTypeRepr(), proto, |
| ExportabilityReason::General, |
| /*allowUnavailableProtocol=*/false); |
| }); |
| |
| if (proto->getTrailingWhereClause()) { |
| forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { |
| checkType(type, typeRepr, proto); |
| }); |
| } |
| } |
| |
| void visitSubscriptDecl(SubscriptDecl *SD) { |
| checkGenericParams(SD, SD); |
| |
| for (auto &P : *SD->getIndices()) { |
| checkType(P->getInterfaceType(), P->getTypeRepr(), SD); |
| } |
| checkType(SD->getElementInterfaceType(), SD->getElementTypeRepr(), SD); |
| } |
| |
| void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { |
| checkGenericParams(fn, fn); |
| |
| for (auto *P : *fn->getParameters()) |
| checkType(P->getInterfaceType(), P->getTypeRepr(), fn); |
| } |
| |
| void visitFuncDecl(FuncDecl *FD) { |
| visitAbstractFunctionDecl(FD); |
| checkType(FD->getResultInterfaceType(), FD->getResultTypeRepr(), FD); |
| |
| if (auto attr = FD->getAttachedResultBuilder()) { |
| checkType(FD->getResultBuilderType(), |
| attr->getTypeRepr(), FD, |
| ExportabilityReason::ResultBuilder); |
| } |
| } |
| |
| void visitEnumElementDecl(EnumElementDecl *EED) { |
| if (!EED->hasAssociatedValues()) |
| return; |
| for (auto &P : *EED->getParameterList()) |
| checkType(P->getInterfaceType(), P->getTypeRepr(), EED); |
| } |
| |
| void checkConstrainedExtensionRequirements(ExtensionDecl *ED, |
| bool hasExportedMembers) { |
| if (!ED->getTrailingWhereClause()) |
| return; |
| |
| ExportabilityReason reason = |
| hasExportedMembers ? ExportabilityReason::ExtensionWithPublicMembers |
| : ExportabilityReason::ExtensionWithConditionalConformances; |
| |
| forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { |
| checkType(type, typeRepr, ED, reason); |
| }); |
| } |
| |
| void visitExtensionDecl(ExtensionDecl *ED) { |
| auto extendedType = ED->getExtendedNominal(); |
| assert(extendedType && "valid extension with no extended type?"); |
| if (!extendedType) |
| return; |
| |
| // The rules here are tricky. |
| // |
| // 1) If the extension defines conformances, the conformed-to protocols |
| // must be exported. |
| llvm::for_each(ED->getInherited(), |
| [&](TypeLoc inherited) { |
| checkType(inherited.getType(), inherited.getTypeRepr(), |
| ED, ExportabilityReason::General, |
| /*allowUnavailableProtocol=*/true); |
| }); |
| |
| auto wasWhere = Where; |
| |
| // 2) If the extension contains exported members, the as-written |
| // extended type should be exportable. |
| bool hasExportedMembers = llvm::any_of(ED->getMembers(), |
| [](const Decl *member) -> bool { |
| auto *valueMember = dyn_cast<ValueDecl>(member); |
| if (!valueMember) |
| return false; |
| return isExported(valueMember); |
| }); |
| |
| Where = wasWhere.withExported(hasExportedMembers); |
| checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, |
| ExportabilityReason::ExtensionWithPublicMembers); |
| |
| // 3) If the extension contains exported members or defines conformances, |
| // the 'where' clause must only name exported types. |
| Where = wasWhere.withExported(hasExportedMembers || |
| !ED->getInherited().empty()); |
| checkConstrainedExtensionRequirements(ED, hasExportedMembers); |
| } |
| |
| void checkPrecedenceGroup(const PrecedenceGroupDecl *PGD, |
| const Decl *refDecl, SourceLoc diagLoc, |
| SourceRange refRange) { |
| const SourceFile *SF = refDecl->getDeclContext()->getParentSourceFile(); |
| ModuleDecl *M = PGD->getModuleContext(); |
| if (!SF->isImportedImplementationOnly(M)) |
| return; |
| |
| auto &DE = PGD->getASTContext().Diags; |
| auto diag = |
| DE.diagnose(diagLoc, diag::decl_from_hidden_module, |
| PGD->getDescriptiveKind(), PGD->getName(), |
| static_cast<unsigned>(ExportabilityReason::General), M->getName(), |
| static_cast<unsigned>(DisallowedOriginKind::ImplementationOnly) |
| ); |
| if (refRange.isValid()) |
| diag.highlight(refRange); |
| diag.flush(); |
| PGD->diagnose(diag::decl_declared_here, PGD->getName()); |
| } |
| |
| void visitInfixOperatorDecl(InfixOperatorDecl *IOD) { |
| // FIXME: Handle operator designated types (which also applies to prefix |
| // and postfix operators). |
| if (auto *precedenceGroup = IOD->getPrecedenceGroup()) { |
| if (!IOD->getIdentifiers().empty()) { |
| checkPrecedenceGroup(precedenceGroup, IOD, IOD->getLoc(), |
| IOD->getIdentifiers().front().Loc); |
| } |
| } |
| } |
| |
| void visitPrecedenceGroupDecl(PrecedenceGroupDecl *PGD) { |
| llvm::for_each(PGD->getLowerThan(), |
| [&](const PrecedenceGroupDecl::Relation &relation) { |
| checkPrecedenceGroup(relation.Group, PGD, PGD->getLowerThanLoc(), |
| relation.NameLoc); |
| }); |
| llvm::for_each(PGD->getHigherThan(), |
| [&](const PrecedenceGroupDecl::Relation &relation) { |
| checkPrecedenceGroup(relation.Group, PGD, PGD->getHigherThanLoc(), |
| relation.NameLoc); |
| }); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| static void checkExtensionGenericParamAccess(const ExtensionDecl *ED) { |
| auto *AA = ED->getAttrs().getAttribute<AccessControlAttr>(); |
| if (!AA) |
| return; |
| AccessLevel userSpecifiedAccess = AA->getAccess(); |
| |
| AccessScope desiredAccessScope = AccessScope::getPublic(); |
| switch (userSpecifiedAccess) { |
| case AccessLevel::Private: |
| assert((ED->isInvalid() || |
| ED->getDeclContext()->isModuleScopeContext()) && |
| "non-top-level extensions make 'private' != 'fileprivate'"); |
| LLVM_FALLTHROUGH; |
| case AccessLevel::FilePrivate: { |
| const DeclContext *DC = ED->getModuleScopeContext(); |
| bool isPrivate = (userSpecifiedAccess == AccessLevel::Private); |
| desiredAccessScope = AccessScope(DC, isPrivate); |
| break; |
| } |
| case AccessLevel::Internal: |
| desiredAccessScope = AccessScope(ED->getModuleContext()); |
| break; |
| case AccessLevel::Public: |
| case AccessLevel::Open: |
| break; |
| } |
| |
| AccessControlChecker().checkGenericParamAccess( |
| ED, ED, desiredAccessScope, userSpecifiedAccess); |
| } |
| |
| DisallowedOriginKind swift::getDisallowedOriginKind(const Decl *decl, |
| const ExportContext &where) { |
| auto downgradeToWarning = DowngradeToWarning::No; |
| return getDisallowedOriginKind(decl, where, downgradeToWarning); |
| } |
| |
| void swift::checkAccessControl(Decl *D) { |
| if (isa<ValueDecl>(D) || isa<PatternBindingDecl>(D)) { |
| bool allowInlineable = |
| D->getDeclContext()->isInSpecializeExtensionContext(); |
| AccessControlChecker(allowInlineable).visit(D); |
| UsableFromInlineChecker().visit(D); |
| } else if (auto *ED = dyn_cast<ExtensionDecl>(D)) { |
| checkExtensionGenericParamAccess(ED); |
| } |
| |
| if (isa<AccessorDecl>(D)) |
| return; |
| |
| auto where = ExportContext::forDeclSignature(D); |
| if (where.isImplicit()) |
| return; |
| |
| DeclAvailabilityChecker(where).visit(D); |
| } |