diff options
author | Ilya Biryukov <ibiryukov@google.com> | 2018-12-03 13:29:17 +0000 |
---|---|---|
committer | Ilya Biryukov <ibiryukov@google.com> | 2018-12-03 13:29:17 +0000 |
commit | f1822ec431cded5e50b69152346794d79976a8b2 (patch) | |
tree | 367d6815eb69d834b7d7236ed170ddc9b5cb6d56 /clang/lib/Sema | |
parent | 6ef089d21c8853d4623980617f18490ab64c8548 (diff) | |
download | llvm-f1822ec431cded5e50b69152346794d79976a8b2.zip llvm-f1822ec431cded5e50b69152346794d79976a8b2.tar.gz llvm-f1822ec431cded5e50b69152346794d79976a8b2.tar.bz2 |
[CodeComplete] Cleanup access checking in code completion
Summary: Also fixes a crash (see the added 'accessibility-crash.cpp' test).
Reviewers: ioeric, kadircet
Reviewed By: kadircet
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D55124
llvm-svn: 348135
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/CodeCompleteConsumer.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaAccess.cpp | 29 | ||||
-rw-r--r-- | clang/lib/Sema/SemaCodeComplete.cpp | 71 |
3 files changed, 63 insertions, 40 deletions
diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp index f6a959c..40db849 100644 --- a/clang/lib/Sema/CodeCompleteConsumer.cpp +++ b/clang/lib/Sema/CodeCompleteConsumer.cpp @@ -555,6 +555,9 @@ void PrintingCodeCompleteConsumer::ProcessCodeCompleteResults( Tags.push_back("Hidden"); if (Results[I].InBaseClass) Tags.push_back("InBase"); + if (Results[I].Availability == + CXAvailabilityKind::CXAvailability_NotAccessible) + Tags.push_back("Inaccessible"); if (!Tags.empty()) OS << " (" << llvm::join(Tags, ",") << ")"; } diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index cf33231..0432e70 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -1877,22 +1877,33 @@ void Sema::CheckLookupAccess(const LookupResult &R) { /// specifiers into account, but no member access expressions and such. /// /// \param Target the declaration to check if it can be accessed -/// \param Ctx the class/context from which to start the search +/// \param NamingClass the class in which the lookup was started. +/// \param BaseType type of the left side of member access expression. +/// \p BaseType and \p NamingClass are used for C++ access control. +/// Depending on the lookup case, they should be set to the following: +/// - lhs.target (member access without a qualifier): +/// \p BaseType and \p NamingClass are both the type of 'lhs'. +/// - lhs.X::target (member access with a qualifier): +/// BaseType is the type of 'lhs', NamingClass is 'X' +/// - X::target (qualified lookup without member access): +/// BaseType is null, NamingClass is 'X'. +/// - target (unqualified lookup). +/// BaseType is null, NamingClass is the parent class of 'target'. /// \return true if the Target is accessible from the Class, false otherwise. -bool Sema::IsSimplyAccessible(NamedDecl *Target, DeclContext *Ctx) { - if (CXXRecordDecl *Class = dyn_cast<CXXRecordDecl>(Ctx)) { - if (!Target->isCXXClassMember()) - return true; - +bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass, + QualType BaseType) { + // Perform the C++ accessibility checks first. + if (Target->isCXXClassMember() && NamingClass) { + if (!getLangOpts().CPlusPlus) + return false; if (Target->getAccess() == AS_public) return true; - QualType qType = Class->getTypeForDecl()->getCanonicalTypeInternal(); // The unprivileged access is AS_none as we don't know how the member was // accessed, which is described by the access in DeclAccessPair. // `IsAccessible` will examine the actual access of Target (i.e. // Decl->getAccess()) when calculating the access. - AccessTarget Entity(Context, AccessedEntity::Member, Class, - DeclAccessPair::make(Target, AS_none), qType); + AccessTarget Entity(Context, AccessedEntity::Member, NamingClass, + DeclAccessPair::make(Target, AS_none), BaseType); EffectiveContext EC(CurContext); return ::IsAccessible(*this, EC, Entity) != ::AR_inaccessible; } diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index aeb7aee..cb66a01 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1278,38 +1278,53 @@ bool ResultBuilder::IsObjCIvar(const NamedDecl *ND) const { } namespace { + /// Visible declaration consumer that adds a code-completion result /// for each visible declaration. class CodeCompletionDeclConsumer : public VisibleDeclConsumer { ResultBuilder &Results; - DeclContext *CurContext; + DeclContext *InitialLookupCtx; + // NamingClass and BaseType are used for access-checking. See + // Sema::IsSimplyAccessible for details. + CXXRecordDecl *NamingClass; + QualType BaseType; std::vector<FixItHint> FixIts; - // This is set to the record where the search starts, if this is a record - // member completion. - RecordDecl *MemberCompletionRecord = nullptr; public: CodeCompletionDeclConsumer( - ResultBuilder &Results, DeclContext *CurContext, - std::vector<FixItHint> FixIts = std::vector<FixItHint>(), - RecordDecl *MemberCompletionRecord = nullptr) - : Results(Results), CurContext(CurContext), FixIts(std::move(FixIts)), - MemberCompletionRecord(MemberCompletionRecord) {} + ResultBuilder &Results, DeclContext *InitialLookupCtx, + QualType BaseType = QualType(), + std::vector<FixItHint> FixIts = std::vector<FixItHint>()) + : Results(Results), InitialLookupCtx(InitialLookupCtx), + FixIts(std::move(FixIts)) { + NamingClass = llvm::dyn_cast<CXXRecordDecl>(InitialLookupCtx); + // If BaseType was not provided explicitly, emulate implicit 'this->'. + if (BaseType.isNull()) { + auto ThisType = Results.getSema().getCurrentThisType(); + if (!ThisType.isNull()) { + assert(ThisType->isPointerType()); + BaseType = ThisType->getPointeeType(); + if (!NamingClass) + NamingClass = BaseType->getAsCXXRecordDecl(); + } + } + this->BaseType = BaseType; + } void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx, bool InBaseClass) override { - bool Accessible = true; - if (Ctx) { - // Set the actual accessing context (i.e. naming class) to the record - // context where the search starts. When `InBaseClass` is true, `Ctx` - // will be the base class, which is not the actual naming class. - DeclContext *AccessingCtx = - MemberCompletionRecord ? MemberCompletionRecord : Ctx; - Accessible = Results.getSema().IsSimplyAccessible(ND, AccessingCtx); - } + // Naming class to use for access check. In most cases it was provided + // explicitly (e.g. member access (lhs.foo) or qualified lookup (X::)), for + // unqualified lookup we fallback to the \p Ctx in which we found the + // member. + auto *NamingClass = this->NamingClass; + if (!NamingClass) + NamingClass = llvm::dyn_cast_or_null<CXXRecordDecl>(Ctx); + bool Accessible = + Results.getSema().IsSimplyAccessible(ND, NamingClass, BaseType); ResultBuilder::Result Result(ND, Results.getBasePriority(ND), nullptr, false, Accessible, FixIts); - Results.AddResult(Result, CurContext, Hiding, InBaseClass); + Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass); } void EnteredContext(DeclContext *Ctx) override { @@ -3699,20 +3714,15 @@ void Sema::CodeCompleteOrdinaryName(Scope *S, } // If we are in a C++ non-static member function, check the qualifiers on - // the member function to filter/prioritize the results list and set the - // context to the record context so that accessibility check in base class - // works correctly. - RecordDecl *MemberCompletionRecord = nullptr; + // the member function to filter/prioritize the results list. if (CXXMethodDecl *CurMethod = dyn_cast<CXXMethodDecl>(CurContext)) { if (CurMethod->isInstance()) { Results.setObjectTypeQualifiers( Qualifiers::fromCVRMask(CurMethod->getTypeQualifiers())); - MemberCompletionRecord = CurMethod->getParent(); } } - CodeCompletionDeclConsumer Consumer(Results, CurContext, /*FixIts=*/{}, - MemberCompletionRecord); + CodeCompletionDeclConsumer Consumer(Results, CurContext); LookupVisibleDecls(S, LookupOrdinaryName, Consumer, CodeCompleter->includeGlobals(), CodeCompleter->loadExternal()); @@ -4152,8 +4162,7 @@ AddRecordMembersCompletionResults(Sema &SemaRef, ResultBuilder &Results, std::vector<FixItHint> FixIts; if (AccessOpFixIt) FixIts.emplace_back(AccessOpFixIt.getValue()); - CodeCompletionDeclConsumer Consumer(Results, SemaRef.CurContext, - std::move(FixIts), RD); + CodeCompletionDeclConsumer Consumer(Results, RD, BaseType, std::move(FixIts)); SemaRef.LookupVisibleDecls(RD, Sema::LookupMemberName, Consumer, SemaRef.CodeCompleter->includeGlobals(), /*IncludeDependentBases=*/true, @@ -4283,7 +4292,7 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base, // Add all ivars from this class and its superclasses. if (Class) { - CodeCompletionDeclConsumer Consumer(Results, CurContext); + CodeCompletionDeclConsumer Consumer(Results, Class, BaseType); Results.setFilter(&ResultBuilder::IsObjCIvar); LookupVisibleDecls( Class, LookupMemberName, Consumer, CodeCompleter->includeGlobals(), @@ -4856,7 +4865,7 @@ void Sema::CodeCompleteAssignmentRHS(Scope *S, Expr *LHS) { } void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS, - bool EnteringContext) { + bool EnteringContext, QualType BaseType) { if (SS.isEmpty() || !CodeCompleter) return; @@ -4903,7 +4912,7 @@ void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS, if (CodeCompleter->includeNamespaceLevelDecls() || (!Ctx->isNamespace() && !Ctx->isTranslationUnit())) { - CodeCompletionDeclConsumer Consumer(Results, CurContext); + CodeCompletionDeclConsumer Consumer(Results, Ctx, BaseType); LookupVisibleDecls(Ctx, LookupOrdinaryName, Consumer, /*IncludeGlobalScope=*/true, /*IncludeDependentBases=*/true, |