Bug 1921522: Mark WPTs gradient-external-reference.svg and pattern-external-reference...
[gecko.git] / build / clang-plugin / Utils.h
blobd252eeda153feb47d4797e8c6287b9ad9406a569
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifndef Utils_h__
6 #define Utils_h__
8 #include "CustomAttributes.h"
9 #include "ThirdPartyPaths.h"
10 #include "ThreadAllows.h"
11 #include "plugin.h"
13 #if CLANG_VERSION_FULL >= 1300
14 // Starting with clang-13 some functions from StringRef have been renamed
15 #define compare_lower compare_insensitive
16 #endif
18 inline StringRef getFilename(const SourceManager &SM, SourceLocation Loc) {
19 // We use the presumed location to handle #line directives and such, so the
20 // plugin is friendly to icecc / sccache users.
21 auto PL = SM.getPresumedLoc(Loc);
22 if (PL.isValid()) {
23 return StringRef(PL.getFilename());
25 return SM.getFilename(Loc);
28 // Check if the given expression contains an assignment expression.
29 // This can either take the form of a Binary Operator or a
30 // Overloaded Operator Call.
31 inline bool hasSideEffectAssignment(const Expr *Expression) {
32 if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) {
33 auto BinOp = OpCallExpr->getOperator();
34 if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) {
35 return true;
37 } else if (auto BinOpExpr = dyn_cast_or_null<BinaryOperator>(Expression)) {
38 if (BinOpExpr->isAssignmentOp()) {
39 return true;
43 // Recurse to children.
44 for (const Stmt *SubStmt : Expression->children()) {
45 auto ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
46 if (ChildExpr && hasSideEffectAssignment(ChildExpr)) {
47 return true;
51 return false;
54 template <class T>
55 inline bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) {
56 auto &SourceManager = AC.getSourceManager();
57 auto ExpansionLoc = SourceManager.getExpansionLoc(D.getBeginLoc());
58 if (ExpansionLoc.isInvalid()) {
59 return false;
61 return SourceManager.isInSystemHeader(ExpansionLoc);
64 template <typename T> inline StringRef getNameChecked(const T &D) {
65 return D->getIdentifier() ? D->getName() : "";
68 /// A cached data of whether classes are refcounted or not.
69 typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>>
70 RefCountedMap;
71 extern RefCountedMap RefCountedClasses;
73 inline bool classHasAddRefRelease(const CXXRecordDecl *D) {
74 const RefCountedMap::iterator &It = RefCountedClasses.find(D);
75 if (It != RefCountedClasses.end()) {
76 return It->second.second;
79 bool SeenAddRef = false;
80 bool SeenRelease = false;
81 for (CXXRecordDecl::method_iterator Method = D->method_begin();
82 Method != D->method_end(); ++Method) {
83 const auto &Name = getNameChecked(Method);
84 if (Name == "AddRef") {
85 SeenAddRef = true;
86 } else if (Name == "Release") {
87 SeenRelease = true;
90 RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease);
91 return SeenAddRef && SeenRelease;
94 inline bool isClassRefCounted(QualType T);
96 inline bool isClassRefCounted(const CXXRecordDecl *D) {
97 // Normalize so that D points to the definition if it exists.
98 if (!D->hasDefinition())
99 return false;
100 D = D->getDefinition();
101 // Base class: anyone with AddRef/Release is obviously a refcounted class.
102 if (classHasAddRefRelease(D))
103 return true;
105 // Look through all base cases to figure out if the parent is a refcounted
106 // class.
107 for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin();
108 Base != D->bases_end(); ++Base) {
109 bool Super = isClassRefCounted(Base->getType());
110 if (Super) {
111 return true;
115 return false;
118 inline bool isClassRefCounted(QualType T) {
119 while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
120 T = ArrTy->getElementType();
121 CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
122 return Clazz ? isClassRefCounted(Clazz) : false;
125 inline const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) {
126 for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end();
127 Field != E; ++Field) {
128 if (getNameChecked(Field) == "mRefCnt") {
129 return *Field;
132 return 0;
135 inline bool typeHasVTable(QualType T) {
136 while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
137 T = ArrTy->getElementType();
138 CXXRecordDecl *Offender = T->getAsCXXRecordDecl();
139 return Offender && Offender->hasDefinition() && Offender->isDynamicClass();
142 inline StringRef getDeclarationNamespace(const Decl *Declaration) {
143 const DeclContext *DC =
144 Declaration->getDeclContext()->getEnclosingNamespaceContext();
145 const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
146 if (!ND) {
147 return "";
150 while (const DeclContext *ParentDC = ND->getParent()) {
151 if (!isa<NamespaceDecl>(ParentDC)) {
152 break;
154 ND = cast<NamespaceDecl>(ParentDC);
157 const auto &Name = ND->getName();
158 return Name;
161 inline bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) {
162 StringRef Name = getDeclarationNamespace(Declaration);
163 if (Name == "") {
164 return false;
167 return Name == "std" || // standard C++ lib
168 Name == "__gnu_cxx" || // gnu C++ lib
169 Name == "boost" || // boost
170 Name == "webrtc" || // upstream webrtc
171 Name == "rtc" || // upstream webrtc 'base' package
172 #if CLANG_VERSION_MAJOR >= 16
173 Name.starts_with("icu_") || // icu
174 #else
175 Name.startswith("icu_") || // icu
176 #endif
177 Name == "google" || // protobuf
178 Name == "google_breakpad" || // breakpad
179 Name == "soundtouch" || // libsoundtouch
180 Name == "stagefright" || // libstagefright
181 Name == "MacFileUtilities" || // MacFileUtilities
182 Name == "dwarf2reader" || // dwarf2reader
183 Name == "arm_ex_to_module" || // arm_ex_to_module
184 Name == "testing" || // gtest
185 Name == "Json" || // jsoncpp
186 Name == "rlbox" || // rlbox
187 Name == "v8"; // irregexp
190 inline bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) {
191 StringRef Name = getDeclarationNamespace(Declaration);
192 if (Name == "") {
193 return false;
196 return Name == "std" || // standard C++ lib
197 Name == "__gnu_cxx" || // gnu C++ lib
198 Name == "google_breakpad" || // breakpad
199 Name == "webrtc" || // libwebrtc
200 Name == "testing" || // gtest
201 Name == "rlbox"; // rlbox
204 inline bool isIgnoredPathForImplicitConversion(const Decl *Declaration) {
205 Declaration = Declaration->getCanonicalDecl();
206 SourceLocation Loc = Declaration->getLocation();
207 const SourceManager &SM = Declaration->getASTContext().getSourceManager();
208 SmallString<1024> FileName = getFilename(SM, Loc);
209 llvm::sys::fs::make_absolute(FileName);
210 llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
211 End = llvm::sys::path::rend(FileName);
212 for (; Begin != End; ++Begin) {
213 if (Begin->compare_lower(StringRef("graphite2")) == 0) {
214 return true;
216 if (Begin->compare_lower(StringRef("chromium")) == 0) {
217 // Ignore security/sandbox/chromium but not ipc/chromium.
218 ++Begin;
219 return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0;
222 return false;
225 inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call,
226 const SourceManager &SM) {
227 SourceLocation Loc = Call->getBeginLoc();
228 SmallString<1024> FileName = getFilename(SM, Loc);
229 llvm::sys::fs::make_absolute(FileName);
230 llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
231 End = llvm::sys::path::rend(FileName);
232 for (; Begin != End; ++Begin) {
233 if (Begin->compare_lower(StringRef("angle")) == 0 ||
234 Begin->compare_lower(StringRef("chromium")) == 0 ||
235 Begin->compare_lower(StringRef("crashreporter")) == 0 ||
236 Begin->compare_lower(StringRef("google-breakpad")) == 0 ||
237 Begin->compare_lower(StringRef("gflags")) == 0 ||
238 Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
239 Begin->compare_lower(StringRef("icu")) == 0 ||
240 Begin->compare_lower(StringRef("jsoncpp")) == 0 ||
241 Begin->compare_lower(StringRef("libstagefright")) == 0 ||
242 Begin->compare_lower(StringRef("transport")) == 0 ||
243 Begin->compare_lower(StringRef("protobuf")) == 0 ||
244 Begin->compare_lower(StringRef("skia")) == 0 ||
245 Begin->compare_lower(StringRef("sfntly")) == 0 ||
246 // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
247 Begin->compare_lower(StringRef("testing")) == 0) {
248 return true;
250 if (Begin->compare_lower(StringRef("webrtc")) == 0) {
251 // Ignore trunk/webrtc, but not media/webrtc
252 ++Begin;
253 return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0;
256 return false;
259 inline bool isInterestingDeclForImplicitConversion(const Decl *Declaration) {
260 return !isInIgnoredNamespaceForImplicitConversion(Declaration) &&
261 !isIgnoredPathForImplicitConversion(Declaration);
264 inline bool isIgnoredExprForMustUse(const Expr *E) {
265 if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
266 switch (OpCall->getOperator()) {
267 case OO_Equal:
268 case OO_PlusEqual:
269 case OO_MinusEqual:
270 case OO_StarEqual:
271 case OO_SlashEqual:
272 case OO_PercentEqual:
273 case OO_CaretEqual:
274 case OO_AmpEqual:
275 case OO_PipeEqual:
276 case OO_LessLessEqual:
277 case OO_GreaterGreaterEqual:
278 return true;
279 default:
280 return false;
284 if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) {
285 return Op->isAssignmentOp();
288 return false;
291 inline bool typeIsRefPtr(QualType Q) {
292 CXXRecordDecl *D = Q->getAsCXXRecordDecl();
293 if (!D || !D->getIdentifier()) {
294 return false;
297 StringRef name = D->getName();
298 if (name == "RefPtr" || name == "nsCOMPtr") {
299 return true;
301 return false;
304 // The method defined in clang for ignoring implicit nodes doesn't work with
305 // some AST trees. To get around this, we define our own implementation of
306 // IgnoreTrivials.
307 inline const Stmt *MaybeSkipOneTrivial(const Stmt *s) {
308 if (!s) {
309 return nullptr;
311 if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) {
312 return ewc->getSubExpr();
314 if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) {
315 // With clang 10 and up `getTemporary` has been replaced with the more
316 // versatile `getSubExpr`.
317 #if CLANG_VERSION_FULL >= 1000
318 return mte->getSubExpr();
319 #else
320 return mte->GetTemporaryExpr();
321 #endif
323 if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) {
324 return bte->getSubExpr();
326 if (auto *ce = dyn_cast<CastExpr>(s)) {
327 s = ce->getSubExpr();
329 if (auto *pe = dyn_cast<ParenExpr>(s)) {
330 s = pe->getSubExpr();
332 // Not a trivial.
333 return s;
336 inline const Stmt *IgnoreTrivials(const Stmt *s) {
337 while (true) {
338 const Stmt *newS = MaybeSkipOneTrivial(s);
339 if (newS == s) {
340 return newS;
342 s = newS;
345 // Unreachable
346 return nullptr;
349 inline const Expr *IgnoreTrivials(const Expr *e) {
350 return cast_or_null<Expr>(IgnoreTrivials(static_cast<const Stmt *>(e)));
353 // Returns the input if the input is not a trivial.
354 inline const Expr *MaybeSkipOneTrivial(const Expr *e) {
355 return cast_or_null<Expr>(MaybeSkipOneTrivial(static_cast<const Stmt *>(e)));
358 const FieldDecl *getBaseRefCntMember(QualType T);
360 inline const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) {
361 const FieldDecl *RefCntMember = getClassRefCntMember(D);
362 if (RefCntMember && isClassRefCounted(D)) {
363 return RefCntMember;
366 for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(),
367 E = D->bases_end();
368 Base != E; ++Base) {
369 RefCntMember = getBaseRefCntMember(Base->getType());
370 if (RefCntMember) {
371 return RefCntMember;
374 return 0;
377 inline const FieldDecl *getBaseRefCntMember(QualType T) {
378 while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
379 T = ArrTy->getElementType();
380 CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
381 return Clazz ? getBaseRefCntMember(Clazz) : 0;
384 inline bool isPlacementNew(const CXXNewExpr *Expression) {
385 // Regular new expressions aren't placement new
386 if (Expression->getNumPlacementArgs() == 0)
387 return false;
388 const FunctionDecl *Declaration = Expression->getOperatorNew();
389 if (Declaration && hasCustomAttribute<moz_heap_allocator>(Declaration)) {
390 return false;
392 return true;
395 extern DenseMap<StringRef, bool> InThirdPartyPathCache;
397 inline bool inThirdPartyPath(SourceLocation Loc, const SourceManager &SM) {
398 StringRef OriginalFileName = getFilename(SM, Loc);
399 auto pair = InThirdPartyPathCache.find(OriginalFileName);
400 if (pair != InThirdPartyPathCache.end()) {
401 return pair->second;
404 SmallString<1024> FileName = OriginalFileName;
405 llvm::sys::fs::make_absolute(FileName);
407 for (uint32_t i = 0; i < MOZ_THIRD_PARTY_PATHS_COUNT; ++i) {
408 auto PathB = sys::path::begin(FileName);
409 auto PathE = sys::path::end(FileName);
411 auto ThirdPartyB = sys::path::begin(MOZ_THIRD_PARTY_PATHS[i]);
412 auto ThirdPartyE = sys::path::end(MOZ_THIRD_PARTY_PATHS[i]);
414 for (; PathB != PathE; ++PathB) {
415 // Perform an inner loop to compare path segments, checking if the current
416 // segment is the start of the current third party path.
417 auto IPathB = PathB;
418 auto IThirdPartyB = ThirdPartyB;
419 for (; IPathB != PathE && IThirdPartyB != ThirdPartyE;
420 ++IPathB, ++IThirdPartyB) {
421 if (IPathB->compare_lower(*IThirdPartyB) != 0) {
422 break;
426 // We found a match!
427 if (IThirdPartyB == ThirdPartyE) {
428 InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, true));
429 return true;
434 InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, false));
435 return false;
438 inline bool inThirdPartyPath(const Decl *D, ASTContext *context) {
439 D = D->getCanonicalDecl();
440 SourceLocation Loc = D->getLocation();
441 const SourceManager &SM = context->getSourceManager();
443 return inThirdPartyPath(Loc, SM);
446 inline CXXRecordDecl *getNonTemplateSpecializedCXXRecordDecl(QualType Q) {
447 auto *D = Q->getAsCXXRecordDecl();
449 if (!D) {
450 auto TemplateQ = Q->getAs<TemplateSpecializationType>();
451 if (!TemplateQ) {
452 return nullptr;
455 auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl();
456 if (!TemplateDecl) {
457 return nullptr;
460 D = dyn_cast_or_null<CXXRecordDecl>(TemplateDecl->getTemplatedDecl());
461 if (!D) {
462 return nullptr;
466 return D;
469 inline bool inThirdPartyPath(const Decl *D) {
470 return inThirdPartyPath(D, &D->getASTContext());
473 inline bool inThirdPartyPath(const Stmt *S, ASTContext *context) {
474 SourceLocation Loc = S->getBeginLoc();
475 const SourceManager &SM = context->getSourceManager();
476 auto ExpansionLoc = SM.getExpansionLoc(Loc);
477 if (ExpansionLoc.isInvalid()) {
478 return inThirdPartyPath(Loc, SM);
480 return inThirdPartyPath(ExpansionLoc, SM);
483 /// Polyfill for CXXOperatorCallExpr::isInfixBinaryOp()
484 inline bool isInfixBinaryOp(const CXXOperatorCallExpr *OpCall) {
485 #if CLANG_VERSION_FULL >= 400
486 return OpCall->isInfixBinaryOp();
487 #else
488 // Taken from clang source.
489 if (OpCall->getNumArgs() != 2)
490 return false;
492 switch (OpCall->getOperator()) {
493 case OO_Call:
494 case OO_Subscript:
495 return false;
496 default:
497 return true;
499 #endif
502 #undef compare_lower
503 #endif