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/. */
8 #include "CustomAttributes.h"
9 #include "ThirdPartyPaths.h"
10 #include "ThreadAllows.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
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
);
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
)) {
37 } else if (auto BinOpExpr
= dyn_cast_or_null
<BinaryOperator
>(Expression
)) {
38 if (BinOpExpr
->isAssignmentOp()) {
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
)) {
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()) {
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>>
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") {
86 } else if (Name
== "Release") {
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())
100 D
= D
->getDefinition();
101 // Base class: anyone with AddRef/Release is obviously a refcounted class.
102 if (classHasAddRefRelease(D
))
105 // Look through all base cases to figure out if the parent is a refcounted
107 for (CXXRecordDecl::base_class_const_iterator Base
= D
->bases_begin();
108 Base
!= D
->bases_end(); ++Base
) {
109 bool Super
= isClassRefCounted(Base
->getType());
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") {
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
);
150 while (const DeclContext
*ParentDC
= ND
->getParent()) {
151 if (!isa
<NamespaceDecl
>(ParentDC
)) {
154 ND
= cast
<NamespaceDecl
>(ParentDC
);
157 const auto &Name
= ND
->getName();
161 inline bool isInIgnoredNamespaceForImplicitCtor(const Decl
*Declaration
) {
162 StringRef Name
= getDeclarationNamespace(Declaration
);
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
175 Name
.startswith("icu_") || // icu
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
);
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) {
216 if (Begin
->compare_lower(StringRef("chromium")) == 0) {
217 // Ignore security/sandbox/chromium but not ipc/chromium.
219 return Begin
!= End
&& Begin
->compare_lower(StringRef("sandbox")) == 0;
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) {
250 if (Begin
->compare_lower(StringRef("webrtc")) == 0) {
251 // Ignore trunk/webrtc, but not media/webrtc
253 return Begin
!= End
&& Begin
->compare_lower(StringRef("trunk")) == 0;
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()) {
272 case OO_PercentEqual
:
276 case OO_LessLessEqual
:
277 case OO_GreaterGreaterEqual
:
284 if (const BinaryOperator
*Op
= dyn_cast
<BinaryOperator
>(E
)) {
285 return Op
->isAssignmentOp();
291 inline bool typeIsRefPtr(QualType Q
) {
292 CXXRecordDecl
*D
= Q
->getAsCXXRecordDecl();
293 if (!D
|| !D
->getIdentifier()) {
297 StringRef name
= D
->getName();
298 if (name
== "RefPtr" || name
== "nsCOMPtr") {
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
307 inline const Stmt
*MaybeSkipOneTrivial(const Stmt
*s
) {
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();
320 return mte
->GetTemporaryExpr();
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();
336 inline const Stmt
*IgnoreTrivials(const Stmt
*s
) {
338 const Stmt
*newS
= MaybeSkipOneTrivial(s
);
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
)) {
366 for (CXXRecordDecl::base_class_const_iterator Base
= D
->bases_begin(),
369 RefCntMember
= getBaseRefCntMember(Base
->getType());
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)
388 const FunctionDecl
*Declaration
= Expression
->getOperatorNew();
389 if (Declaration
&& hasCustomAttribute
<moz_heap_allocator
>(Declaration
)) {
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()) {
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.
418 auto IThirdPartyB
= ThirdPartyB
;
419 for (; IPathB
!= PathE
&& IThirdPartyB
!= ThirdPartyE
;
420 ++IPathB
, ++IThirdPartyB
) {
421 if (IPathB
->compare_lower(*IThirdPartyB
) != 0) {
427 if (IThirdPartyB
== ThirdPartyE
) {
428 InThirdPartyPathCache
.insert(std::make_pair(OriginalFileName
, true));
434 InThirdPartyPathCache
.insert(std::make_pair(OriginalFileName
, 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();
450 auto TemplateQ
= Q
->getAs
<TemplateSpecializationType
>();
455 auto TemplateDecl
= TemplateQ
->getTemplateName().getAsTemplateDecl();
460 D
= dyn_cast_or_null
<CXXRecordDecl
>(TemplateDecl
->getTemplatedDecl());
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();
488 // Taken from clang source.
489 if (OpCall
->getNumArgs() != 2)
492 switch (OpCall
->getOperator()) {