/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/include/clang/Sema/DelayedDiagnostic.h
Line | Count | Source (jump to first uncovered line) |
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 | 1.26M | 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 | 12.2k | NamingClass(DerivedClass), Diag(0, Allocator) {} |
72 | | |
73 | 3.44M | bool isMemberAccess() const { return IsMember; } |
74 | | |
75 | 1.28M | bool isQuiet() const { return Diag.getDiagID() == 0; } |
76 | | |
77 | 3.78M | AccessSpecifier getAccess() const { return AccessSpecifier(Access); } |
78 | | |
79 | | // These apply to member decls... |
80 | 2.61M | NamedDecl *getTargetDecl() const { return Target; } |
81 | 1.26M | CXXRecordDecl *getNamingClass() const { return NamingClass; } |
82 | | |
83 | | // ...and these apply to hierarchy conversions. |
84 | 12.2k | CXXRecordDecl *getBaseClass() const { |
85 | 12.2k | assert(!IsMember); return cast<CXXRecordDecl>(Target); |
86 | 12.2k | } |
87 | 0 | CXXRecordDecl *getDerivedClass() const { return NamingClass; } |
88 | | |
89 | | /// Retrieves the base object type, important when accessing |
90 | | /// an instance member. |
91 | 1.62M | 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 | 85.6k | void setDiag(const PartialDiagnostic &PDiag) { |
100 | 85.6k | assert(isQuiet() && "partial diagnostic already defined"); |
101 | 0 | Diag = PDiag; |
102 | 85.6k | } |
103 | 1.18M | PartialDiagnostic &setDiag(unsigned DiagID) { |
104 | 1.18M | assert(isQuiet() && "partial diagnostic already defined"); |
105 | 0 | assert(DiagID && "creating null diagnostic"); |
106 | 0 | Diag.Reset(DiagID); |
107 | 1.18M | return Diag; |
108 | 1.18M | } |
109 | 5.47k | const PartialDiagnostic &getDiag() const { |
110 | 5.47k | return Diag; |
111 | 5.47k | } |
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 | 328k | const AccessedEntity &Entity) { |
146 | 328k | DelayedDiagnostic DD; |
147 | 328k | DD.Kind = Access; |
148 | 328k | DD.Triggered = false; |
149 | 328k | DD.Loc = Loc; |
150 | 328k | new (&DD.getAccessData()) AccessedEntity(Entity); |
151 | 328k | return DD; |
152 | 328k | } |
153 | | |
154 | | static DelayedDiagnostic makeForbiddenType(SourceLocation loc, |
155 | | unsigned diagnostic, |
156 | | QualType type, |
157 | 61 | unsigned argument) { |
158 | 61 | DelayedDiagnostic DD; |
159 | 61 | DD.Kind = ForbiddenType; |
160 | 61 | DD.Triggered = false; |
161 | 61 | DD.Loc = loc; |
162 | 61 | DD.ForbiddenTypeData.Diagnostic = diagnostic; |
163 | 61 | DD.ForbiddenTypeData.OperandType = type.getAsOpaquePtr(); |
164 | 61 | DD.ForbiddenTypeData.Argument = argument; |
165 | 61 | return DD; |
166 | 61 | } |
167 | | |
168 | 963k | AccessedEntity &getAccessData() { |
169 | 963k | assert(Kind == Access && "Not an access diagnostic."); |
170 | 0 | return *reinterpret_cast<AccessedEntity*>(AccessData); |
171 | 963k | } |
172 | 0 | const AccessedEntity &getAccessData() const { |
173 | 0 | assert(Kind == Access && "Not an access diagnostic."); |
174 | 0 | return *reinterpret_cast<const AccessedEntity*>(AccessData); |
175 | 0 | } |
176 | | |
177 | 257k | const NamedDecl *getAvailabilityReferringDecl() const { |
178 | 257k | assert(Kind == Availability && "Not an availability diagnostic."); |
179 | 0 | return AvailabilityData.ReferringDecl; |
180 | 257k | } |
181 | | |
182 | 257k | const NamedDecl *getAvailabilityOffendingDecl() const { |
183 | 257k | return AvailabilityData.OffendingDecl; |
184 | 257k | } |
185 | | |
186 | 257k | StringRef getAvailabilityMessage() const { |
187 | 257k | assert(Kind == Availability && "Not an availability diagnostic."); |
188 | 0 | return StringRef(AvailabilityData.Message, AvailabilityData.MessageLen); |
189 | 257k | } |
190 | | |
191 | 257k | ArrayRef<SourceLocation> getAvailabilitySelectorLocs() const { |
192 | 257k | assert(Kind == Availability && "Not an availability diagnostic."); |
193 | 0 | return llvm::makeArrayRef(AvailabilityData.SelectorLocs, |
194 | 257k | AvailabilityData.NumSelectorLocs); |
195 | 257k | } |
196 | | |
197 | 257k | AvailabilityResult getAvailabilityResult() const { |
198 | 257k | assert(Kind == Availability && "Not an availability diagnostic."); |
199 | 0 | return AvailabilityData.AR; |
200 | 257k | } |
201 | | |
202 | | /// The diagnostic ID to emit. Used like so: |
203 | | /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) |
204 | | /// << diag.getForbiddenTypeOperand() |
205 | | /// << diag.getForbiddenTypeArgument(); |
206 | 67 | unsigned getForbiddenTypeDiagnostic() const { |
207 | 67 | assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); |
208 | 0 | return ForbiddenTypeData.Diagnostic; |
209 | 67 | } |
210 | | |
211 | 9 | unsigned getForbiddenTypeArgument() const { |
212 | 9 | assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); |
213 | 0 | return ForbiddenTypeData.Argument; |
214 | 9 | } |
215 | | |
216 | 9 | QualType getForbiddenTypeOperand() const { |
217 | 9 | assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); |
218 | 0 | return QualType::getFromOpaquePtr(ForbiddenTypeData.OperandType); |
219 | 9 | } |
220 | | |
221 | 257k | const ObjCInterfaceDecl *getUnknownObjCClass() const { |
222 | 257k | return AvailabilityData.UnknownObjCClass; |
223 | 257k | } |
224 | | |
225 | 257k | const ObjCPropertyDecl *getObjCProperty() const { |
226 | 257k | return AvailabilityData.ObjCProperty; |
227 | 257k | } |
228 | | |
229 | 0 | bool getObjCPropertyAccess() const { |
230 | 0 | return AvailabilityData.ObjCPropertyAccess; |
231 | 0 | } |
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 | 116M | DelayedDiagnosticPool(const DelayedDiagnosticPool *parent) : Parent(parent) {} |
269 | | |
270 | | DelayedDiagnosticPool(const DelayedDiagnosticPool &) = delete; |
271 | | DelayedDiagnosticPool &operator=(const DelayedDiagnosticPool &) = delete; |
272 | | |
273 | | DelayedDiagnosticPool(DelayedDiagnosticPool &&Other) |
274 | 1.04M | : Parent(Other.Parent), Diagnostics(std::move(Other.Diagnostics)) { |
275 | 1.04M | Other.Diagnostics.clear(); |
276 | 1.04M | } |
277 | | |
278 | 0 | DelayedDiagnosticPool &operator=(DelayedDiagnosticPool &&Other) { |
279 | 0 | Parent = Other.Parent; |
280 | 0 | Diagnostics = std::move(Other.Diagnostics); |
281 | 0 | Other.Diagnostics.clear(); |
282 | 0 | return *this; |
283 | 0 | } |
284 | | |
285 | 117M | ~DelayedDiagnosticPool() { |
286 | 117M | for (SmallVectorImpl<DelayedDiagnostic>::iterator |
287 | 117M | i = Diagnostics.begin(), e = Diagnostics.end(); i != e; ++i586k ) |
288 | 586k | i->Destroy(); |
289 | 117M | } |
290 | | |
291 | 74.1M | const DelayedDiagnosticPool *getParent() const { return Parent; } |
292 | | |
293 | | /// Does this pool, or any of its ancestors, contain any diagnostics? |
294 | 0 | bool empty() const { |
295 | 0 | return (Diagnostics.empty() && (!Parent || Parent->empty())); |
296 | 0 | } |
297 | | |
298 | | /// Add a diagnostic to this pool. |
299 | 586k | void add(const DelayedDiagnostic &diag) { |
300 | 586k | Diagnostics.push_back(diag); |
301 | 586k | } |
302 | | |
303 | | /// Steal the diagnostics from the given pool. |
304 | 1.56M | void steal(DelayedDiagnosticPool &pool) { |
305 | 1.56M | if (pool.Diagnostics.empty()) return1.55M ; |
306 | | |
307 | 13.5k | if (Diagnostics.empty()) { |
308 | 13.5k | Diagnostics = std::move(pool.Diagnostics); |
309 | 13.5k | } else { |
310 | 0 | Diagnostics.append(pool.pool_begin(), pool.pool_end()); |
311 | 0 | } |
312 | 13.5k | pool.Diagnostics.clear(); |
313 | 13.5k | } |
314 | | |
315 | | using pool_iterator = SmallVectorImpl<DelayedDiagnostic>::const_iterator; |
316 | | |
317 | 72.5M | pool_iterator pool_begin() const { return Diagnostics.begin(); } |
318 | 72.5M | pool_iterator pool_end() const { return Diagnostics.end(); } |
319 | 7.68M | bool pool_empty() const { return Diagnostics.empty(); } |
320 | | }; |
321 | | |
322 | | } // namespace clang |
323 | | |
324 | | /// Add a diagnostic to the current delay pool. |
325 | 586k | inline void Sema::DelayedDiagnostics::add(const sema::DelayedDiagnostic &diag) { |
326 | 586k | assert(shouldDelayDiagnostics() && "trying to delay without pool"); |
327 | 0 | CurPool->add(diag); |
328 | 586k | } |
329 | | |
330 | | } // namespace clang |
331 | | |
332 | | #endif // LLVM_CLANG_SEMA_DELAYEDDIAGNOSTIC_H |