| //===- DelayedDiagnostic.h - Delayed declarator diagnostics -----*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| /// \file |
| /// Defines the classes clang::DelayedDiagnostic and |
| /// clang::AccessedEntity. |
| /// |
| /// DelayedDiangostic is used to record diagnostics that are being |
| /// conditionally produced during declarator parsing. Certain kinds of |
| /// diagnostics -- notably deprecation and access control -- are suppressed |
| /// based on semantic properties of the parsed declaration that aren't known |
| /// until it is fully parsed. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H |
| #define LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H |
| |
| #include "clang/AST/DeclAccessPair.h" |
| #include "clang/AST/DeclBase.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/Type.h" |
| #include "clang/Basic/LLVM.h" |
| #include "clang/Basic/PartialDiagnostic.h" |
| #include "clang/Basic/SourceLocation.h" |
| #include "clang/Basic/Specifiers.h" |
| #include "clang/Sema/Sema.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Casting.h" |
| #include <cassert> |
| #include <cstddef> |
| #include <utility> |
| |
| namespace clang { |
| |
| class ObjCInterfaceDecl; |
| class ObjCPropertyDecl; |
| |
| namespace sema { |
| |
| /// A declaration being accessed, together with information about how |
| /// it was accessed. |
| class AccessedEntity { |
| public: |
| /// A member declaration found through lookup. The target is the |
| /// member. |
| enum MemberNonce { Member }; |
| |
| /// A hierarchy (base-to-derived or derived-to-base) conversion. |
| /// The target is the base class. |
| enum BaseNonce { Base }; |
| |
| AccessedEntity(PartialDiagnostic::DiagStorageAllocator &Allocator, |
| MemberNonce _, CXXRecordDecl *NamingClass, |
| DeclAccessPair FoundDecl, QualType BaseObjectType) |
| : Access(FoundDecl.getAccess()), IsMember(true), |
| Target(FoundDecl.getDecl()), NamingClass(NamingClass), |
| BaseObjectType(BaseObjectType), Diag(0, Allocator) {} |
| |
| AccessedEntity(PartialDiagnostic::DiagStorageAllocator &Allocator, |
| BaseNonce _, CXXRecordDecl *BaseClass, |
| CXXRecordDecl *DerivedClass, AccessSpecifier Access) |
| : Access(Access), IsMember(false), Target(BaseClass), |
| NamingClass(DerivedClass), Diag(0, Allocator) {} |
| |
| bool isMemberAccess() const { return IsMember; } |
| |
| bool isQuiet() const { return Diag.getDiagID() == 0; } |
| |
| AccessSpecifier getAccess() const { return AccessSpecifier(Access); } |
| |
| // These apply to member decls... |
| NamedDecl *getTargetDecl() const { return Target; } |
| CXXRecordDecl *getNamingClass() const { return NamingClass; } |
| |
| // ...and these apply to hierarchy conversions. |
| CXXRecordDecl *getBaseClass() const { |
| assert(!IsMember); return cast<CXXRecordDecl>(Target); |
| } |
| CXXRecordDecl *getDerivedClass() const { return NamingClass; } |
| |
| /// Retrieves the base object type, important when accessing |
| /// an instance member. |
| QualType getBaseObjectType() const { return BaseObjectType; } |
| |
| /// Sets a diagnostic to be performed. The diagnostic is given |
| /// four (additional) arguments: |
| /// %0 - 0 if the entity was private, 1 if protected |
| /// %1 - the DeclarationName of the entity |
| /// %2 - the TypeDecl type of the naming class |
| /// %3 - the TypeDecl type of the declaring class |
| void setDiag(const PartialDiagnostic &PDiag) { |
| assert(isQuiet() && "partial diagnostic already defined"); |
| Diag = PDiag; |
| } |
| PartialDiagnostic &setDiag(unsigned DiagID) { |
| assert(isQuiet() && "partial diagnostic already defined"); |
| assert(DiagID && "creating null diagnostic"); |
| Diag.Reset(DiagID); |
| return Diag; |
| } |
| const PartialDiagnostic &getDiag() const { |
| return Diag; |
| } |
| |
| private: |
| unsigned Access : 2; |
| unsigned IsMember : 1; |
| NamedDecl *Target; |
| CXXRecordDecl *NamingClass; |
| QualType BaseObjectType; |
| PartialDiagnostic Diag; |
| }; |
| |
| /// A diagnostic message which has been conditionally emitted pending |
| /// the complete parsing of the current declaration. |
| class DelayedDiagnostic { |
| public: |
| enum DDKind : unsigned char { Availability, Access, ForbiddenType }; |
| |
| DDKind Kind; |
| bool Triggered; |
| |
| SourceLocation Loc; |
| |
| void Destroy(); |
| |
| static DelayedDiagnostic makeAvailability(AvailabilityResult AR, |
| ArrayRef<SourceLocation> Locs, |
| const NamedDecl *ReferringDecl, |
| const NamedDecl *OffendingDecl, |
| const ObjCInterfaceDecl *UnknownObjCClass, |
| const ObjCPropertyDecl *ObjCProperty, |
| StringRef Msg, |
| bool ObjCPropertyAccess); |
| |
| static DelayedDiagnostic makeAccess(SourceLocation Loc, |
| const AccessedEntity &Entity) { |
| DelayedDiagnostic DD; |
| DD.Kind = Access; |
| DD.Triggered = false; |
| DD.Loc = Loc; |
| new (&DD.getAccessData()) AccessedEntity(Entity); |
| return DD; |
| } |
| |
| static DelayedDiagnostic makeForbiddenType(SourceLocation loc, |
| unsigned diagnostic, |
| QualType type, |
| unsigned argument) { |
| DelayedDiagnostic DD; |
| DD.Kind = ForbiddenType; |
| DD.Triggered = false; |
| DD.Loc = loc; |
| DD.ForbiddenTypeData.Diagnostic = diagnostic; |
| DD.ForbiddenTypeData.OperandType = type.getAsOpaquePtr(); |
| DD.ForbiddenTypeData.Argument = argument; |
| return DD; |
| } |
| |
| AccessedEntity &getAccessData() { |
| assert(Kind == Access && "Not an access diagnostic."); |
| return *reinterpret_cast<AccessedEntity*>(AccessData); |
| } |
| const AccessedEntity &getAccessData() const { |
| assert(Kind == Access && "Not an access diagnostic."); |
| return *reinterpret_cast<const AccessedEntity*>(AccessData); |
| } |
| |
| const NamedDecl *getAvailabilityReferringDecl() const { |
| assert(Kind == Availability && "Not an availability diagnostic."); |
| return AvailabilityData.ReferringDecl; |
| } |
| |
| const NamedDecl *getAvailabilityOffendingDecl() const { |
| return AvailabilityData.OffendingDecl; |
| } |
| |
| StringRef getAvailabilityMessage() const { |
| assert(Kind == Availability && "Not an availability diagnostic."); |
| return StringRef(AvailabilityData.Message, AvailabilityData.MessageLen); |
| } |
| |
| ArrayRef<SourceLocation> getAvailabilitySelectorLocs() const { |
| assert(Kind == Availability && "Not an availability diagnostic."); |
| return llvm::makeArrayRef(AvailabilityData.SelectorLocs, |
| AvailabilityData.NumSelectorLocs); |
| } |
| |
| AvailabilityResult getAvailabilityResult() const { |
| assert(Kind == Availability && "Not an availability diagnostic."); |
| return AvailabilityData.AR; |
| } |
| |
| /// The diagnostic ID to emit. Used like so: |
| /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) |
| /// << diag.getForbiddenTypeOperand() |
| /// << diag.getForbiddenTypeArgument(); |
| unsigned getForbiddenTypeDiagnostic() const { |
| assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); |
| return ForbiddenTypeData.Diagnostic; |
| } |
| |
| unsigned getForbiddenTypeArgument() const { |
| assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); |
| return ForbiddenTypeData.Argument; |
| } |
| |
| QualType getForbiddenTypeOperand() const { |
| assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); |
| return QualType::getFromOpaquePtr(ForbiddenTypeData.OperandType); |
| } |
| |
| const ObjCInterfaceDecl *getUnknownObjCClass() const { |
| return AvailabilityData.UnknownObjCClass; |
| } |
| |
| const ObjCPropertyDecl *getObjCProperty() const { |
| return AvailabilityData.ObjCProperty; |
| } |
| |
| bool getObjCPropertyAccess() const { |
| return AvailabilityData.ObjCPropertyAccess; |
| } |
| |
| private: |
| struct AD { |
| const NamedDecl *ReferringDecl; |
| const NamedDecl *OffendingDecl; |
| const ObjCInterfaceDecl *UnknownObjCClass; |
| const ObjCPropertyDecl *ObjCProperty; |
| const char *Message; |
| size_t MessageLen; |
| SourceLocation *SelectorLocs; |
| size_t NumSelectorLocs; |
| AvailabilityResult AR; |
| bool ObjCPropertyAccess; |
| }; |
| |
| struct FTD { |
| unsigned Diagnostic; |
| unsigned Argument; |
| void *OperandType; |
| }; |
| |
| union { |
| struct AD AvailabilityData; |
| struct FTD ForbiddenTypeData; |
| |
| /// Access control. |
| char AccessData[sizeof(AccessedEntity)]; |
| }; |
| }; |
| |
| /// A collection of diagnostics which were delayed. |
| class DelayedDiagnosticPool { |
| const DelayedDiagnosticPool *Parent; |
| SmallVector<DelayedDiagnostic, 4> Diagnostics; |
| |
| public: |
| DelayedDiagnosticPool(const DelayedDiagnosticPool *parent) : Parent(parent) {} |
| |
| DelayedDiagnosticPool(const DelayedDiagnosticPool &) = delete; |
| DelayedDiagnosticPool &operator=(const DelayedDiagnosticPool &) = delete; |
| |
| DelayedDiagnosticPool(DelayedDiagnosticPool &&Other) |
| : Parent(Other.Parent), Diagnostics(std::move(Other.Diagnostics)) { |
| Other.Diagnostics.clear(); |
| } |
| |
| DelayedDiagnosticPool &operator=(DelayedDiagnosticPool &&Other) { |
| Parent = Other.Parent; |
| Diagnostics = std::move(Other.Diagnostics); |
| Other.Diagnostics.clear(); |
| return *this; |
| } |
| |
| ~DelayedDiagnosticPool() { |
| for (SmallVectorImpl<DelayedDiagnostic>::iterator |
| i = Diagnostics.begin(), e = Diagnostics.end(); i != e; ++i) |
| i->Destroy(); |
| } |
| |
| const DelayedDiagnosticPool *getParent() const { return Parent; } |
| |
| /// Does this pool, or any of its ancestors, contain any diagnostics? |
| bool empty() const { |
| return (Diagnostics.empty() && (!Parent || Parent->empty())); |
| } |
| |
| /// Add a diagnostic to this pool. |
| void add(const DelayedDiagnostic &diag) { |
| Diagnostics.push_back(diag); |
| } |
| |
| /// Steal the diagnostics from the given pool. |
| void steal(DelayedDiagnosticPool &pool) { |
| if (pool.Diagnostics.empty()) return; |
| |
| if (Diagnostics.empty()) { |
| Diagnostics = std::move(pool.Diagnostics); |
| } else { |
| Diagnostics.append(pool.pool_begin(), pool.pool_end()); |
| } |
| pool.Diagnostics.clear(); |
| } |
| |
| using pool_iterator = SmallVectorImpl<DelayedDiagnostic>::const_iterator; |
| |
| pool_iterator pool_begin() const { return Diagnostics.begin(); } |
| pool_iterator pool_end() const { return Diagnostics.end(); } |
| bool pool_empty() const { return Diagnostics.empty(); } |
| }; |
| |
| } // namespace clang |
| |
| /// Add a diagnostic to the current delay pool. |
| inline void Sema::DelayedDiagnostics::add(const sema::DelayedDiagnostic &diag) { |
| assert(shouldDelayDiagnostics() && "trying to delay without pool"); |
| CurPool->add(diag); |
| } |
| |
| } // namespace clang |
| |
| #endif // LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H |