1 //===- DelayedDiagnostic.h - Delayed declarator diagnostics -----*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 /// \file 10 /// Defines the classes clang::DelayedDiagnostic and 11 /// clang::AccessedEntity. 12 /// 13 /// DelayedDiangostic is used to record diagnostics that are being 14 /// conditionally produced during declarator parsing. Certain kinds of 15 /// diagnostics -- notably deprecation and access control -- are suppressed 16 /// based on semantic properties of the parsed declaration that aren't known 17 /// until it is fully parsed. 18 // 19 //===----------------------------------------------------------------------===// 20 21 #ifndef LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H 22 #define LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H 23 24 #include "clang/AST/DeclAccessPair.h" 25 #include "clang/AST/DeclBase.h" 26 #include "clang/AST/DeclCXX.h" 27 #include "clang/AST/Type.h" 28 #include "clang/Basic/LLVM.h" 29 #include "clang/Basic/PartialDiagnostic.h" 30 #include "clang/Basic/SourceLocation.h" 31 #include "clang/Basic/Specifiers.h" 32 #include "clang/Sema/Sema.h" 33 #include "llvm/ADT/ArrayRef.h" 34 #include "llvm/ADT/SmallVector.h" 35 #include "llvm/ADT/StringRef.h" 36 #include "llvm/Support/Casting.h" 37 #include <cassert> 38 #include <cstddef> 39 #include <utility> 40 41 namespace clang { 42 43 class ObjCInterfaceDecl; 44 class ObjCPropertyDecl; 45 46 namespace sema { 47 48 /// A declaration being accessed, together with information about how 49 /// it was accessed. 50 class AccessedEntity { 51 public: 52 /// A member declaration found through lookup. The target is the 53 /// member. 54 enum MemberNonce { Member }; 55 56 /// A hierarchy (base-to-derived or derived-to-base) conversion. 57 /// The target is the base class. 58 enum BaseNonce { Base }; 59 60 AccessedEntity(PartialDiagnostic::DiagStorageAllocator &Allocator, 61 MemberNonce _, CXXRecordDecl *NamingClass, 62 DeclAccessPair FoundDecl, QualType BaseObjectType) 63 : Access(FoundDecl.getAccess()), IsMember(true), 64 Target(FoundDecl.getDecl()), NamingClass(NamingClass), 65 BaseObjectType(BaseObjectType), Diag(0, Allocator) {} 66 67 AccessedEntity(PartialDiagnostic::DiagStorageAllocator &Allocator, 68 BaseNonce _, CXXRecordDecl *BaseClass, 69 CXXRecordDecl *DerivedClass, AccessSpecifier Access) 70 : Access(Access), IsMember(false), Target(BaseClass), 71 NamingClass(DerivedClass), Diag(0, Allocator) {} 72 73 bool isMemberAccess() const { return IsMember; } 74 75 bool isQuiet() const { return Diag.getDiagID() == 0; } 76 77 AccessSpecifier getAccess() const { return AccessSpecifier(Access); } 78 79 // These apply to member decls... 80 NamedDecl *getTargetDecl() const { return Target; } 81 CXXRecordDecl *getNamingClass() const { return NamingClass; } 82 83 // ...and these apply to hierarchy conversions. 84 CXXRecordDecl *getBaseClass() const { 85 assert(!IsMember); return cast<CXXRecordDecl>(Target); 86 } 87 CXXRecordDecl *getDerivedClass() const { return NamingClass; } 88 89 /// Retrieves the base object type, important when accessing 90 /// an instance member. 91 QualType getBaseObjectType() const { return BaseObjectType; } 92 93 /// Sets a diagnostic to be performed. The diagnostic is given 94 /// four (additional) arguments: 95 /// %0 - 0 if the entity was private, 1 if protected 96 /// %1 - the DeclarationName of the entity 97 /// %2 - the TypeDecl type of the naming class 98 /// %3 - the TypeDecl type of the declaring class 99 void setDiag(const PartialDiagnostic &PDiag) { 100 assert(isQuiet() && "partial diagnostic already defined"); 101 Diag = PDiag; 102 } 103 PartialDiagnostic &setDiag(unsigned DiagID) { 104 assert(isQuiet() && "partial diagnostic already defined"); 105 assert(DiagID && "creating null diagnostic"); 106 Diag.Reset(DiagID); 107 return Diag; 108 } 109 const PartialDiagnostic &getDiag() const { 110 return Diag; 111 } 112 113 private: 114 unsigned Access : 2; 115 unsigned IsMember : 1; 116 NamedDecl *Target; 117 CXXRecordDecl *NamingClass; 118 QualType BaseObjectType; 119 PartialDiagnostic Diag; 120 }; 121 122 /// A diagnostic message which has been conditionally emitted pending 123 /// the complete parsing of the current declaration. 124 class DelayedDiagnostic { 125 public: 126 enum DDKind : unsigned char { Availability, Access, ForbiddenType }; 127 128 DDKind Kind; 129 bool Triggered; 130 131 SourceLocation Loc; 132 133 void Destroy(); 134 135 static DelayedDiagnostic makeAvailability(AvailabilityResult AR, 136 ArrayRef<SourceLocation> Locs, 137 const NamedDecl *ReferringDecl, 138 const NamedDecl *OffendingDecl, 139 const ObjCInterfaceDecl *UnknownObjCClass, 140 const ObjCPropertyDecl *ObjCProperty, 141 StringRef Msg, 142 bool ObjCPropertyAccess); 143 144 static DelayedDiagnostic makeAccess(SourceLocation Loc, 145 const AccessedEntity &Entity) { 146 DelayedDiagnostic DD; 147 DD.Kind = Access; 148 DD.Triggered = false; 149 DD.Loc = Loc; 150 new (&DD.getAccessData()) AccessedEntity(Entity); 151 return DD; 152 } 153 154 static DelayedDiagnostic makeForbiddenType(SourceLocation loc, 155 unsigned diagnostic, 156 QualType type, 157 unsigned argument) { 158 DelayedDiagnostic DD; 159 DD.Kind = ForbiddenType; 160 DD.Triggered = false; 161 DD.Loc = loc; 162 DD.ForbiddenTypeData.Diagnostic = diagnostic; 163 DD.ForbiddenTypeData.OperandType = type.getAsOpaquePtr(); 164 DD.ForbiddenTypeData.Argument = argument; 165 return DD; 166 } 167 168 AccessedEntity &getAccessData() { 169 assert(Kind == Access && "Not an access diagnostic."); 170 return *reinterpret_cast<AccessedEntity*>(AccessData); 171 } 172 const AccessedEntity &getAccessData() const { 173 assert(Kind == Access && "Not an access diagnostic."); 174 return *reinterpret_cast<const AccessedEntity*>(AccessData); 175 } 176 177 const NamedDecl *getAvailabilityReferringDecl() const { 178 assert(Kind == Availability && "Not an availability diagnostic."); 179 return AvailabilityData.ReferringDecl; 180 } 181 182 const NamedDecl *getAvailabilityOffendingDecl() const { 183 return AvailabilityData.OffendingDecl; 184 } 185 186 StringRef getAvailabilityMessage() const { 187 assert(Kind == Availability && "Not an availability diagnostic."); 188 return StringRef(AvailabilityData.Message, AvailabilityData.MessageLen); 189 } 190 191 ArrayRef<SourceLocation> getAvailabilitySelectorLocs() const { 192 assert(Kind == Availability && "Not an availability diagnostic."); 193 return llvm::makeArrayRef(AvailabilityData.SelectorLocs, 194 AvailabilityData.NumSelectorLocs); 195 } 196 197 AvailabilityResult getAvailabilityResult() const { 198 assert(Kind == Availability && "Not an availability diagnostic."); 199 return AvailabilityData.AR; 200 } 201 202 /// The diagnostic ID to emit. Used like so: 203 /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) 204 /// << diag.getForbiddenTypeOperand() 205 /// << diag.getForbiddenTypeArgument(); 206 unsigned getForbiddenTypeDiagnostic() const { 207 assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); 208 return ForbiddenTypeData.Diagnostic; 209 } 210 211 unsigned getForbiddenTypeArgument() const { 212 assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); 213 return ForbiddenTypeData.Argument; 214 } 215 216 QualType getForbiddenTypeOperand() const { 217 assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); 218 return QualType::getFromOpaquePtr(ForbiddenTypeData.OperandType); 219 } 220 221 const ObjCInterfaceDecl *getUnknownObjCClass() const { 222 return AvailabilityData.UnknownObjCClass; 223 } 224 225 const ObjCPropertyDecl *getObjCProperty() const { 226 return AvailabilityData.ObjCProperty; 227 } 228 229 bool getObjCPropertyAccess() const { 230 return AvailabilityData.ObjCPropertyAccess; 231 } 232 233 private: 234 struct AD { 235 const NamedDecl *ReferringDecl; 236 const NamedDecl *OffendingDecl; 237 const ObjCInterfaceDecl *UnknownObjCClass; 238 const ObjCPropertyDecl *ObjCProperty; 239 const char *Message; 240 size_t MessageLen; 241 SourceLocation *SelectorLocs; 242 size_t NumSelectorLocs; 243 AvailabilityResult AR; 244 bool ObjCPropertyAccess; 245 }; 246 247 struct FTD { 248 unsigned Diagnostic; 249 unsigned Argument; 250 void *OperandType; 251 }; 252 253 union { 254 struct AD AvailabilityData; 255 struct FTD ForbiddenTypeData; 256 257 /// Access control. 258 char AccessData[sizeof(AccessedEntity)]; 259 }; 260 }; 261 262 /// A collection of diagnostics which were delayed. 263 class DelayedDiagnosticPool { 264 const DelayedDiagnosticPool *Parent; 265 SmallVector<DelayedDiagnostic, 4> Diagnostics; 266 267 public: 268 DelayedDiagnosticPool(const DelayedDiagnosticPool *parent) : Parent(parent) {} 269 270 DelayedDiagnosticPool(const DelayedDiagnosticPool &) = delete; 271 DelayedDiagnosticPool &operator=(const DelayedDiagnosticPool &) = delete; 272 273 DelayedDiagnosticPool(DelayedDiagnosticPool &&Other) 274 : Parent(Other.Parent), Diagnostics(std::move(Other.Diagnostics)) { 275 Other.Diagnostics.clear(); 276 } 277 278 DelayedDiagnosticPool &operator=(DelayedDiagnosticPool &&Other) { 279 Parent = Other.Parent; 280 Diagnostics = std::move(Other.Diagnostics); 281 Other.Diagnostics.clear(); 282 return *this; 283 } 284 285 ~DelayedDiagnosticPool() { 286 for (SmallVectorImpl<DelayedDiagnostic>::iterator 287 i = Diagnostics.begin(), e = Diagnostics.end(); i != e; ++i) 288 i->Destroy(); 289 } 290 291 const DelayedDiagnosticPool *getParent() const { return Parent; } 292 293 /// Does this pool, or any of its ancestors, contain any diagnostics? 294 bool empty() const { 295 return (Diagnostics.empty() && (!Parent || Parent->empty())); 296 } 297 298 /// Add a diagnostic to this pool. 299 void add(const DelayedDiagnostic &diag) { 300 Diagnostics.push_back(diag); 301 } 302 303 /// Steal the diagnostics from the given pool. 304 void steal(DelayedDiagnosticPool &pool) { 305 if (pool.Diagnostics.empty()) return; 306 307 if (Diagnostics.empty()) { 308 Diagnostics = std::move(pool.Diagnostics); 309 } else { 310 Diagnostics.append(pool.pool_begin(), pool.pool_end()); 311 } 312 pool.Diagnostics.clear(); 313 } 314 315 using pool_iterator = SmallVectorImpl<DelayedDiagnostic>::const_iterator; 316 317 pool_iterator pool_begin() const { return Diagnostics.begin(); } 318 pool_iterator pool_end() const { return Diagnostics.end(); } 319 bool pool_empty() const { return Diagnostics.empty(); } 320 }; 321 322 } // namespace clang 323 324 /// Add a diagnostic to the current delay pool. 325 inline void Sema::DelayedDiagnostics::add(const sema::DelayedDiagnostic &diag) { 326 assert(shouldDelayDiagnostics() && "trying to delay without pool"); 327 CurPool->add(diag); 328 } 329 330 } // namespace clang 331 332 #endif // LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H 333