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 Name
.startswith("icu_") || // icu
173 Name
== "google" || // protobuf
174 Name
== "google_breakpad" || // breakpad
175 Name
== "soundtouch" || // libsoundtouch
176 Name
== "stagefright" || // libstagefright
177 Name
== "MacFileUtilities" || // MacFileUtilities
178 Name
== "dwarf2reader" || // dwarf2reader
179 Name
== "arm_ex_to_module" || // arm_ex_to_module
180 Name
== "testing" || // gtest
181 Name
== "Json" || // jsoncpp
182 Name
== "rlbox" || // rlbox
183 Name
== "v8"; // irregexp
186 inline bool isInIgnoredNamespaceForImplicitConversion(const Decl
*Declaration
) {
187 StringRef Name
= getDeclarationNamespace(Declaration
);
192 return Name
== "std" || // standard C++ lib
193 Name
== "__gnu_cxx" || // gnu C++ lib
194 Name
== "google_breakpad" || // breakpad
195 Name
== "webrtc" || // libwebrtc
196 Name
== "testing" || // gtest
197 Name
== "rlbox"; // rlbox
200 inline bool isIgnoredPathForImplicitConversion(const Decl
*Declaration
) {
201 Declaration
= Declaration
->getCanonicalDecl();
202 SourceLocation Loc
= Declaration
->getLocation();
203 const SourceManager
&SM
= Declaration
->getASTContext().getSourceManager();
204 SmallString
<1024> FileName
= getFilename(SM
, Loc
);
205 llvm::sys::fs::make_absolute(FileName
);
206 llvm::sys::path::reverse_iterator Begin
= llvm::sys::path::rbegin(FileName
),
207 End
= llvm::sys::path::rend(FileName
);
208 for (; Begin
!= End
; ++Begin
) {
209 if (Begin
->compare_lower(StringRef("graphite2")) == 0) {
212 if (Begin
->compare_lower(StringRef("chromium")) == 0) {
213 // Ignore security/sandbox/chromium but not ipc/chromium.
215 return Begin
!= End
&& Begin
->compare_lower(StringRef("sandbox")) == 0;
221 inline bool isIgnoredPathForSprintfLiteral(const CallExpr
*Call
,
222 const SourceManager
&SM
) {
223 SourceLocation Loc
= Call
->getBeginLoc();
224 SmallString
<1024> FileName
= getFilename(SM
, Loc
);
225 llvm::sys::fs::make_absolute(FileName
);
226 llvm::sys::path::reverse_iterator Begin
= llvm::sys::path::rbegin(FileName
),
227 End
= llvm::sys::path::rend(FileName
);
228 for (; Begin
!= End
; ++Begin
) {
229 if (Begin
->compare_lower(StringRef("angle")) == 0 ||
230 Begin
->compare_lower(StringRef("chromium")) == 0 ||
231 Begin
->compare_lower(StringRef("crashreporter")) == 0 ||
232 Begin
->compare_lower(StringRef("google-breakpad")) == 0 ||
233 Begin
->compare_lower(StringRef("gflags")) == 0 ||
234 Begin
->compare_lower(StringRef("harfbuzz")) == 0 ||
235 Begin
->compare_lower(StringRef("icu")) == 0 ||
236 Begin
->compare_lower(StringRef("jsoncpp")) == 0 ||
237 Begin
->compare_lower(StringRef("libstagefright")) == 0 ||
238 Begin
->compare_lower(StringRef("transport")) == 0 ||
239 Begin
->compare_lower(StringRef("protobuf")) == 0 ||
240 Begin
->compare_lower(StringRef("skia")) == 0 ||
241 Begin
->compare_lower(StringRef("sfntly")) == 0 ||
242 // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
243 Begin
->compare_lower(StringRef("testing")) == 0) {
246 if (Begin
->compare_lower(StringRef("webrtc")) == 0) {
247 // Ignore trunk/webrtc, but not media/webrtc
249 return Begin
!= End
&& Begin
->compare_lower(StringRef("trunk")) == 0;
255 inline bool isInterestingDeclForImplicitConversion(const Decl
*Declaration
) {
256 return !isInIgnoredNamespaceForImplicitConversion(Declaration
) &&
257 !isIgnoredPathForImplicitConversion(Declaration
);
260 inline bool isIgnoredExprForMustUse(const Expr
*E
) {
261 if (const CXXOperatorCallExpr
*OpCall
= dyn_cast
<CXXOperatorCallExpr
>(E
)) {
262 switch (OpCall
->getOperator()) {
268 case OO_PercentEqual
:
272 case OO_LessLessEqual
:
273 case OO_GreaterGreaterEqual
:
280 if (const BinaryOperator
*Op
= dyn_cast
<BinaryOperator
>(E
)) {
281 return Op
->isAssignmentOp();
287 inline bool typeIsRefPtr(QualType Q
) {
288 CXXRecordDecl
*D
= Q
->getAsCXXRecordDecl();
289 if (!D
|| !D
->getIdentifier()) {
293 StringRef name
= D
->getName();
294 if (name
== "RefPtr" || name
== "nsCOMPtr") {
300 // The method defined in clang for ignoring implicit nodes doesn't work with
301 // some AST trees. To get around this, we define our own implementation of
303 inline const Stmt
*MaybeSkipOneTrivial(const Stmt
*s
) {
307 if (auto *ewc
= dyn_cast
<ExprWithCleanups
>(s
)) {
308 return ewc
->getSubExpr();
310 if (auto *mte
= dyn_cast
<MaterializeTemporaryExpr
>(s
)) {
311 // With clang 10 and up `getTemporary` has been replaced with the more
312 // versatile `getSubExpr`.
313 #if CLANG_VERSION_FULL >= 1000
314 return mte
->getSubExpr();
316 return mte
->GetTemporaryExpr();
319 if (auto *bte
= dyn_cast
<CXXBindTemporaryExpr
>(s
)) {
320 return bte
->getSubExpr();
322 if (auto *ce
= dyn_cast
<CastExpr
>(s
)) {
323 s
= ce
->getSubExpr();
325 if (auto *pe
= dyn_cast
<ParenExpr
>(s
)) {
326 s
= pe
->getSubExpr();
332 inline const Stmt
*IgnoreTrivials(const Stmt
*s
) {
334 const Stmt
*newS
= MaybeSkipOneTrivial(s
);
345 inline const Expr
*IgnoreTrivials(const Expr
*e
) {
346 return cast_or_null
<Expr
>(IgnoreTrivials(static_cast<const Stmt
*>(e
)));
349 // Returns the input if the input is not a trivial.
350 inline const Expr
*MaybeSkipOneTrivial(const Expr
*e
) {
351 return cast_or_null
<Expr
>(MaybeSkipOneTrivial(static_cast<const Stmt
*>(e
)));
354 const FieldDecl
*getBaseRefCntMember(QualType T
);
356 inline const FieldDecl
*getBaseRefCntMember(const CXXRecordDecl
*D
) {
357 const FieldDecl
*RefCntMember
= getClassRefCntMember(D
);
358 if (RefCntMember
&& isClassRefCounted(D
)) {
362 for (CXXRecordDecl::base_class_const_iterator Base
= D
->bases_begin(),
365 RefCntMember
= getBaseRefCntMember(Base
->getType());
373 inline const FieldDecl
*getBaseRefCntMember(QualType T
) {
374 while (const clang::ArrayType
*ArrTy
= T
->getAsArrayTypeUnsafe())
375 T
= ArrTy
->getElementType();
376 CXXRecordDecl
*Clazz
= T
->getAsCXXRecordDecl();
377 return Clazz
? getBaseRefCntMember(Clazz
) : 0;
380 inline bool isPlacementNew(const CXXNewExpr
*Expression
) {
381 // Regular new expressions aren't placement new
382 if (Expression
->getNumPlacementArgs() == 0)
384 const FunctionDecl
*Declaration
= Expression
->getOperatorNew();
385 if (Declaration
&& hasCustomAttribute
<moz_heap_allocator
>(Declaration
)) {
391 extern DenseMap
<StringRef
, bool> InThirdPartyPathCache
;
393 inline bool inThirdPartyPath(SourceLocation Loc
, const SourceManager
&SM
) {
394 StringRef OriginalFileName
= getFilename(SM
, Loc
);
395 auto pair
= InThirdPartyPathCache
.find(OriginalFileName
);
396 if (pair
!= InThirdPartyPathCache
.end()) {
400 SmallString
<1024> FileName
= OriginalFileName
;
401 llvm::sys::fs::make_absolute(FileName
);
403 for (uint32_t i
= 0; i
< MOZ_THIRD_PARTY_PATHS_COUNT
; ++i
) {
404 auto PathB
= sys::path::begin(FileName
);
405 auto PathE
= sys::path::end(FileName
);
407 auto ThirdPartyB
= sys::path::begin(MOZ_THIRD_PARTY_PATHS
[i
]);
408 auto ThirdPartyE
= sys::path::end(MOZ_THIRD_PARTY_PATHS
[i
]);
410 for (; PathB
!= PathE
; ++PathB
) {
411 // Perform an inner loop to compare path segments, checking if the current
412 // segment is the start of the current third party path.
414 auto IThirdPartyB
= ThirdPartyB
;
415 for (; IPathB
!= PathE
&& IThirdPartyB
!= ThirdPartyE
;
416 ++IPathB
, ++IThirdPartyB
) {
417 if (IPathB
->compare_lower(*IThirdPartyB
) != 0) {
423 if (IThirdPartyB
== ThirdPartyE
) {
424 InThirdPartyPathCache
.insert(std::make_pair(OriginalFileName
, true));
430 InThirdPartyPathCache
.insert(std::make_pair(OriginalFileName
, false));
434 inline bool inThirdPartyPath(const Decl
*D
, ASTContext
*context
) {
435 D
= D
->getCanonicalDecl();
436 SourceLocation Loc
= D
->getLocation();
437 const SourceManager
&SM
= context
->getSourceManager();
439 return inThirdPartyPath(Loc
, SM
);
442 inline CXXRecordDecl
*getNonTemplateSpecializedCXXRecordDecl(QualType Q
) {
443 auto *D
= Q
->getAsCXXRecordDecl();
446 auto TemplateQ
= Q
->getAs
<TemplateSpecializationType
>();
451 auto TemplateDecl
= TemplateQ
->getTemplateName().getAsTemplateDecl();
456 D
= dyn_cast_or_null
<CXXRecordDecl
>(TemplateDecl
->getTemplatedDecl());
465 inline bool inThirdPartyPath(const Decl
*D
) {
466 return inThirdPartyPath(D
, &D
->getASTContext());
469 inline bool inThirdPartyPath(const Stmt
*S
, ASTContext
*context
) {
470 SourceLocation Loc
= S
->getBeginLoc();
471 const SourceManager
&SM
= context
->getSourceManager();
472 auto ExpansionLoc
= SM
.getExpansionLoc(Loc
);
473 if (ExpansionLoc
.isInvalid()) {
474 return inThirdPartyPath(Loc
, SM
);
476 return inThirdPartyPath(ExpansionLoc
, SM
);
479 /// Polyfill for CXXOperatorCallExpr::isInfixBinaryOp()
480 inline bool isInfixBinaryOp(const CXXOperatorCallExpr
*OpCall
) {
481 #if CLANG_VERSION_FULL >= 400
482 return OpCall
->isInfixBinaryOp();
484 // Taken from clang source.
485 if (OpCall
->getNumArgs() != 2)
488 switch (OpCall
->getOperator()) {