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::StorageAllocator &Allocator, 61 MemberNonce _, 62 CXXRecordDecl *NamingClass, 63 DeclAccessPair FoundDecl, 64 QualType BaseObjectType) 65 : Access(FoundDecl.getAccess()), IsMember(true), 66 Target(FoundDecl.getDecl()), NamingClass(NamingClass), 67 BaseObjectType(BaseObjectType), Diag(0, Allocator) { 68 } 69 70 AccessedEntity(PartialDiagnostic::StorageAllocator &Allocator, 71 BaseNonce _, 72 CXXRecordDecl *BaseClass, 73 CXXRecordDecl *DerivedClass, 74 AccessSpecifier Access) 75 : Access(Access), IsMember(false), Target(BaseClass), 76 NamingClass(DerivedClass), Diag(0, Allocator) {} 77 78 bool isMemberAccess() const { return IsMember; } 79 80 bool isQuiet() const { return Diag.getDiagID() == 0; } 81 82 AccessSpecifier getAccess() const { return AccessSpecifier(Access); } 83 84 // These apply to member decls... 85 NamedDecl *getTargetDecl() const { return Target; } 86 CXXRecordDecl *getNamingClass() const { return NamingClass; } 87 88 // ...and these apply to hierarchy conversions. 89 CXXRecordDecl *getBaseClass() const { 90 assert(!IsMember); return cast<CXXRecordDecl>(Target); 91 } 92 CXXRecordDecl *getDerivedClass() const { return NamingClass; } 93 94 /// Retrieves the base object type, important when accessing 95 /// an instance member. 96 QualType getBaseObjectType() const { return BaseObjectType; } 97 98 /// Sets a diagnostic to be performed. The diagnostic is given 99 /// four (additional) arguments: 100 /// %0 - 0 if the entity was private, 1 if protected 101 /// %1 - the DeclarationName of the entity 102 /// %2 - the TypeDecl type of the naming class 103 /// %3 - the TypeDecl type of the declaring class 104 void setDiag(const PartialDiagnostic &PDiag) { 105 assert(isQuiet() && "partial diagnostic already defined"); 106 Diag = PDiag; 107 } 108 PartialDiagnostic &setDiag(unsigned DiagID) { 109 assert(isQuiet() && "partial diagnostic already defined"); 110 assert(DiagID && "creating null diagnostic"); 111 Diag.Reset(DiagID); 112 return Diag; 113 } 114 const PartialDiagnostic &getDiag() const { 115 return Diag; 116 } 117 118 private: 119 unsigned Access : 2; 120 unsigned IsMember : 1; 121 NamedDecl *Target; 122 CXXRecordDecl *NamingClass; 123 QualType BaseObjectType; 124 PartialDiagnostic Diag; 125 }; 126 127 /// A diagnostic message which has been conditionally emitted pending 128 /// the complete parsing of the current declaration. 129 class DelayedDiagnostic { 130 public: 131 enum DDKind : unsigned char { Availability, Access, ForbiddenType }; 132 133 DDKind Kind; 134 bool Triggered; 135 136 SourceLocation Loc; 137 138 void Destroy(); 139 140 static DelayedDiagnostic makeAvailability(AvailabilityResult AR, 141 ArrayRef<SourceLocation> Locs, 142 const NamedDecl *ReferringDecl, 143 const NamedDecl *OffendingDecl, 144 const ObjCInterfaceDecl *UnknownObjCClass, 145 const ObjCPropertyDecl *ObjCProperty, 146 StringRef Msg, 147 bool ObjCPropertyAccess); 148 149 static DelayedDiagnostic makeAccess(SourceLocation Loc, 150 const AccessedEntity &Entity) { 151 DelayedDiagnostic DD; 152 DD.Kind = Access; 153 DD.Triggered = false; 154 DD.Loc = Loc; 155 new (&DD.getAccessData()) AccessedEntity(Entity); 156 return DD; 157 } 158 159 static DelayedDiagnostic makeForbiddenType(SourceLocation loc, 160 unsigned diagnostic, 161 QualType type, 162 unsigned argument) { 163 DelayedDiagnostic DD; 164 DD.Kind = ForbiddenType; 165 DD.Triggered = false; 166 DD.Loc = loc; 167 DD.ForbiddenTypeData.Diagnostic = diagnostic; 168 DD.ForbiddenTypeData.OperandType = type.getAsOpaquePtr(); 169 DD.ForbiddenTypeData.Argument = argument; 170 return DD; 171 } 172 173 AccessedEntity &getAccessData() { 174 assert(Kind == Access && "Not an access diagnostic."); 175 return *reinterpret_cast<AccessedEntity*>(AccessData); 176 } 177 const AccessedEntity &getAccessData() const { 178 assert(Kind == Access && "Not an access diagnostic."); 179 return *reinterpret_cast<const AccessedEntity*>(AccessData); 180 } 181 182 const NamedDecl *getAvailabilityReferringDecl() const { 183 assert(Kind == Availability && "Not an availability diagnostic."); 184 return AvailabilityData.ReferringDecl; 185 } 186 187 const NamedDecl *getAvailabilityOffendingDecl() const { 188 return AvailabilityData.OffendingDecl; 189 } 190 191 StringRef getAvailabilityMessage() const { 192 assert(Kind == Availability && "Not an availability diagnostic."); 193 return StringRef(AvailabilityData.Message, AvailabilityData.MessageLen); 194 } 195 196 ArrayRef<SourceLocation> getAvailabilitySelectorLocs() const { 197 assert(Kind == Availability && "Not an availability diagnostic."); 198 return llvm::makeArrayRef(AvailabilityData.SelectorLocs, 199 AvailabilityData.NumSelectorLocs); 200 } 201 202 AvailabilityResult getAvailabilityResult() const { 203 assert(Kind == Availability && "Not an availability diagnostic."); 204 return AvailabilityData.AR; 205 } 206 207 /// The diagnostic ID to emit. Used like so: 208 /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) 209 /// << diag.getForbiddenTypeOperand() 210 /// << diag.getForbiddenTypeArgument(); 211 unsigned getForbiddenTypeDiagnostic() const { 212 assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); 213 return ForbiddenTypeData.Diagnostic; 214 } 215 216 unsigned getForbiddenTypeArgument() const { 217 assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); 218 return ForbiddenTypeData.Argument; 219 } 220 221 QualType getForbiddenTypeOperand() const { 222 assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); 223 return QualType::getFromOpaquePtr(ForbiddenTypeData.OperandType); 224 } 225 226 const ObjCInterfaceDecl *getUnknownObjCClass() const { 227 return AvailabilityData.UnknownObjCClass; 228 } 229 230 const ObjCPropertyDecl *getObjCProperty() const { 231 return AvailabilityData.ObjCProperty; 232 } 233 234 bool getObjCPropertyAccess() const { 235 return AvailabilityData.ObjCPropertyAccess; 236 } 237 238 private: 239 struct AD { 240 const NamedDecl *ReferringDecl; 241 const NamedDecl *OffendingDecl; 242 const ObjCInterfaceDecl *UnknownObjCClass; 243 const ObjCPropertyDecl *ObjCProperty; 244 const char *Message; 245 size_t MessageLen; 246 SourceLocation *SelectorLocs; 247 size_t NumSelectorLocs; 248 AvailabilityResult AR; 249 bool ObjCPropertyAccess; 250 }; 251 252 struct FTD { 253 unsigned Diagnostic; 254 unsigned Argument; 255 void *OperandType; 256 }; 257 258 union { 259 struct AD AvailabilityData; 260 struct FTD ForbiddenTypeData; 261 262 /// Access control. 263 char AccessData[sizeof(AccessedEntity)]; 264 }; 265 }; 266 267 /// A collection of diagnostics which were delayed. 268 class DelayedDiagnosticPool { 269 const DelayedDiagnosticPool *Parent; 270 SmallVector<DelayedDiagnostic, 4> Diagnostics; 271 272 public: 273 DelayedDiagnosticPool(const DelayedDiagnosticPool *parent) : Parent(parent) {} 274 275 DelayedDiagnosticPool(const DelayedDiagnosticPool &) = delete; 276 DelayedDiagnosticPool &operator=(const DelayedDiagnosticPool &) = delete; 277 278 DelayedDiagnosticPool(DelayedDiagnosticPool &&Other) 279 : Parent(Other.Parent), Diagnostics(std::move(Other.Diagnostics)) { 280 Other.Diagnostics.clear(); 281 } 282 283 DelayedDiagnosticPool &operator=(DelayedDiagnosticPool &&Other) { 284 Parent = Other.Parent; 285 Diagnostics = std::move(Other.Diagnostics); 286 Other.Diagnostics.clear(); 287 return *this; 288 } 289 290 ~DelayedDiagnosticPool() { 291 for (SmallVectorImpl<DelayedDiagnostic>::iterator 292 i = Diagnostics.begin(), e = Diagnostics.end(); i != e; ++i) 293 i->Destroy(); 294 } 295 296 const DelayedDiagnosticPool *getParent() const { return Parent; } 297 298 /// Does this pool, or any of its ancestors, contain any diagnostics? 299 bool empty() const { 300 return (Diagnostics.empty() && (!Parent || Parent->empty())); 301 } 302 303 /// Add a diagnostic to this pool. 304 void add(const DelayedDiagnostic &diag) { 305 Diagnostics.push_back(diag); 306 } 307 308 /// Steal the diagnostics from the given pool. 309 void steal(DelayedDiagnosticPool &pool) { 310 if (pool.Diagnostics.empty()) return; 311 312 if (Diagnostics.empty()) { 313 Diagnostics = std::move(pool.Diagnostics); 314 } else { 315 Diagnostics.append(pool.pool_begin(), pool.pool_end()); 316 } 317 pool.Diagnostics.clear(); 318 } 319 320 using pool_iterator = SmallVectorImpl<DelayedDiagnostic>::const_iterator; 321 322 pool_iterator pool_begin() const { return Diagnostics.begin(); } 323 pool_iterator pool_end() const { return Diagnostics.end(); } 324 bool pool_empty() const { return Diagnostics.empty(); } 325 }; 326 327 } // namespace clang 328 329 /// Add a diagnostic to the current delay pool. 330 inline void Sema::DelayedDiagnostics::add(const sema::DelayedDiagnostic &diag) { 331 assert(shouldDelayDiagnostics() && "trying to delay without pool"); 332 CurPool->add(diag); 333 } 334 335 } // namespace clang 336 337 #endif // LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H 338