/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef Utils_h__ #define Utils_h__ #include "CustomAttributes.h" #include "ThirdPartyPaths.h" #include "ThreadAllows.h" #include "plugin.h" #if CLANG_VERSION_FULL >= 1300 // Starting with clang-13 some functions from StringRef have been renamed #define compare_lower compare_insensitive #endif inline StringRef getFilename(const SourceManager &SM, SourceLocation Loc) { // We use the presumed location to handle #line directives and such, so the // plugin is friendly to icecc / sccache users. auto PL = SM.getPresumedLoc(Loc); if (PL.isValid()) { return StringRef(PL.getFilename()); } return SM.getFilename(Loc); } // Check if the given expression contains an assignment expression. // This can either take the form of a Binary Operator or a // Overloaded Operator Call. inline bool hasSideEffectAssignment(const Expr *Expression) { if (auto OpCallExpr = dyn_cast_or_null(Expression)) { auto BinOp = OpCallExpr->getOperator(); if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) { return true; } } else if (auto BinOpExpr = dyn_cast_or_null(Expression)) { if (BinOpExpr->isAssignmentOp()) { return true; } } // Recurse to children. for (const Stmt *SubStmt : Expression->children()) { auto ChildExpr = dyn_cast_or_null(SubStmt); if (ChildExpr && hasSideEffectAssignment(ChildExpr)) { return true; } } return false; } template inline bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) { auto &SourceManager = AC.getSourceManager(); auto ExpansionLoc = SourceManager.getExpansionLoc(D.getBeginLoc()); if (ExpansionLoc.isInvalid()) { return false; } return SourceManager.isInSystemHeader(ExpansionLoc); } template inline StringRef getNameChecked(const T &D) { return D->getIdentifier() ? D->getName() : ""; } /// A cached data of whether classes are refcounted or not. typedef DenseMap> RefCountedMap; extern RefCountedMap RefCountedClasses; inline bool classHasAddRefRelease(const CXXRecordDecl *D) { const RefCountedMap::iterator &It = RefCountedClasses.find(D); if (It != RefCountedClasses.end()) { return It->second.second; } bool SeenAddRef = false; bool SeenRelease = false; for (CXXRecordDecl::method_iterator Method = D->method_begin(); Method != D->method_end(); ++Method) { const auto &Name = getNameChecked(Method); if (Name == "AddRef") { SeenAddRef = true; } else if (Name == "Release") { SeenRelease = true; } } RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease); return SeenAddRef && SeenRelease; } inline bool isClassRefCounted(QualType T); inline bool isClassRefCounted(const CXXRecordDecl *D) { // Normalize so that D points to the definition if it exists. if (!D->hasDefinition()) return false; D = D->getDefinition(); // Base class: anyone with AddRef/Release is obviously a refcounted class. if (classHasAddRefRelease(D)) return true; // Look through all base cases to figure out if the parent is a refcounted // class. for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(); Base != D->bases_end(); ++Base) { bool Super = isClassRefCounted(Base->getType()); if (Super) { return true; } } return false; } inline bool isClassRefCounted(QualType T) { while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) T = ArrTy->getElementType(); CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); return Clazz ? isClassRefCounted(Clazz) : false; } inline const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) { for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end(); Field != E; ++Field) { if (getNameChecked(Field) == "mRefCnt") { return *Field; } } return 0; } inline bool typeHasVTable(QualType T) { while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) T = ArrTy->getElementType(); CXXRecordDecl *Offender = T->getAsCXXRecordDecl(); return Offender && Offender->hasDefinition() && Offender->isDynamicClass(); } inline StringRef getDeclarationNamespace(const Decl *Declaration) { const DeclContext *DC = Declaration->getDeclContext()->getEnclosingNamespaceContext(); const NamespaceDecl *ND = dyn_cast(DC); if (!ND) { return ""; } while (const DeclContext *ParentDC = ND->getParent()) { if (!isa(ParentDC)) { break; } ND = cast(ParentDC); } const auto &Name = ND->getName(); return Name; } inline bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) { StringRef Name = getDeclarationNamespace(Declaration); if (Name == "") { return false; } return Name == "std" || // standard C++ lib Name == "__gnu_cxx" || // gnu C++ lib Name == "boost" || // boost Name == "webrtc" || // upstream webrtc Name == "rtc" || // upstream webrtc 'base' package Name.startswith("icu_") || // icu Name == "google" || // protobuf Name == "google_breakpad" || // breakpad Name == "soundtouch" || // libsoundtouch Name == "stagefright" || // libstagefright Name == "MacFileUtilities" || // MacFileUtilities Name == "dwarf2reader" || // dwarf2reader Name == "arm_ex_to_module" || // arm_ex_to_module Name == "testing" || // gtest Name == "Json" || // jsoncpp Name == "rlbox" || // rlbox Name == "v8"; // irregexp } inline bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) { StringRef Name = getDeclarationNamespace(Declaration); if (Name == "") { return false; } return Name == "std" || // standard C++ lib Name == "__gnu_cxx" || // gnu C++ lib Name == "google_breakpad" || // breakpad Name == "webrtc" || // libwebrtc Name == "testing" || // gtest Name == "rlbox"; // rlbox } inline bool isIgnoredPathForImplicitConversion(const Decl *Declaration) { Declaration = Declaration->getCanonicalDecl(); SourceLocation Loc = Declaration->getLocation(); const SourceManager &SM = Declaration->getASTContext().getSourceManager(); SmallString<1024> FileName = getFilename(SM, Loc); llvm::sys::fs::make_absolute(FileName); llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), End = llvm::sys::path::rend(FileName); for (; Begin != End; ++Begin) { if (Begin->compare_lower(StringRef("graphite2")) == 0) { return true; } if (Begin->compare_lower(StringRef("chromium")) == 0) { // Ignore security/sandbox/chromium but not ipc/chromium. ++Begin; return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0; } } return false; } inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, const SourceManager &SM) { SourceLocation Loc = Call->getBeginLoc(); SmallString<1024> FileName = getFilename(SM, Loc); llvm::sys::fs::make_absolute(FileName); llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), End = llvm::sys::path::rend(FileName); for (; Begin != End; ++Begin) { if (Begin->compare_lower(StringRef("angle")) == 0 || Begin->compare_lower(StringRef("chromium")) == 0 || Begin->compare_lower(StringRef("crashreporter")) == 0 || Begin->compare_lower(StringRef("google-breakpad")) == 0 || Begin->compare_lower(StringRef("gflags")) == 0 || Begin->compare_lower(StringRef("harfbuzz")) == 0 || Begin->compare_lower(StringRef("icu")) == 0 || Begin->compare_lower(StringRef("jsoncpp")) == 0 || Begin->compare_lower(StringRef("libstagefright")) == 0 || Begin->compare_lower(StringRef("transport")) == 0 || Begin->compare_lower(StringRef("protobuf")) == 0 || Begin->compare_lower(StringRef("skia")) == 0 || Begin->compare_lower(StringRef("sfntly")) == 0 || // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof Begin->compare_lower(StringRef("testing")) == 0) { return true; } if (Begin->compare_lower(StringRef("webrtc")) == 0) { // Ignore trunk/webrtc, but not media/webrtc ++Begin; return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0; } } return false; } inline bool isInterestingDeclForImplicitConversion(const Decl *Declaration) { return !isInIgnoredNamespaceForImplicitConversion(Declaration) && !isIgnoredPathForImplicitConversion(Declaration); } inline bool isIgnoredExprForMustUse(const Expr *E) { if (const CXXOperatorCallExpr *OpCall = dyn_cast(E)) { switch (OpCall->getOperator()) { case OO_Equal: case OO_PlusEqual: case OO_MinusEqual: case OO_StarEqual: case OO_SlashEqual: case OO_PercentEqual: case OO_CaretEqual: case OO_AmpEqual: case OO_PipeEqual: case OO_LessLessEqual: case OO_GreaterGreaterEqual: return true; default: return false; } } if (const BinaryOperator *Op = dyn_cast(E)) { return Op->isAssignmentOp(); } return false; } inline bool typeIsRefPtr(QualType Q) { CXXRecordDecl *D = Q->getAsCXXRecordDecl(); if (!D || !D->getIdentifier()) { return false; } StringRef name = D->getName(); if (name == "RefPtr" || name == "nsCOMPtr") { return true; } return false; } // The method defined in clang for ignoring implicit nodes doesn't work with // some AST trees. To get around this, we define our own implementation of // IgnoreTrivials. inline const Stmt *MaybeSkipOneTrivial(const Stmt *s) { if (!s) { return nullptr; } if (auto *ewc = dyn_cast(s)) { return ewc->getSubExpr(); } if (auto *mte = dyn_cast(s)) { // With clang 10 and up `getTemporary` has been replaced with the more // versatile `getSubExpr`. #if CLANG_VERSION_FULL >= 1000 return mte->getSubExpr(); #else return mte->GetTemporaryExpr(); #endif } if (auto *bte = dyn_cast(s)) { return bte->getSubExpr(); } if (auto *ce = dyn_cast(s)) { s = ce->getSubExpr(); } if (auto *pe = dyn_cast(s)) { s = pe->getSubExpr(); } // Not a trivial. return s; } inline const Stmt *IgnoreTrivials(const Stmt *s) { while (true) { const Stmt *newS = MaybeSkipOneTrivial(s); if (newS == s) { return newS; } s = newS; } // Unreachable return nullptr; } inline const Expr *IgnoreTrivials(const Expr *e) { return cast_or_null(IgnoreTrivials(static_cast(e))); } // Returns the input if the input is not a trivial. inline const Expr *MaybeSkipOneTrivial(const Expr *e) { return cast_or_null(MaybeSkipOneTrivial(static_cast(e))); } const FieldDecl *getBaseRefCntMember(QualType T); inline const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) { const FieldDecl *RefCntMember = getClassRefCntMember(D); if (RefCntMember && isClassRefCounted(D)) { return RefCntMember; } for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(), E = D->bases_end(); Base != E; ++Base) { RefCntMember = getBaseRefCntMember(Base->getType()); if (RefCntMember) { return RefCntMember; } } return 0; } inline const FieldDecl *getBaseRefCntMember(QualType T) { while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) T = ArrTy->getElementType(); CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); return Clazz ? getBaseRefCntMember(Clazz) : 0; } inline bool isPlacementNew(const CXXNewExpr *Expression) { // Regular new expressions aren't placement new if (Expression->getNumPlacementArgs() == 0) return false; const FunctionDecl *Declaration = Expression->getOperatorNew(); if (Declaration && hasCustomAttribute(Declaration)) { return false; } return true; } extern DenseMap InThirdPartyPathCache; inline bool inThirdPartyPath(SourceLocation Loc, const SourceManager &SM) { StringRef OriginalFileName = getFilename(SM, Loc); auto pair = InThirdPartyPathCache.find(OriginalFileName); if (pair != InThirdPartyPathCache.end()) { return pair->second; } SmallString<1024> FileName = OriginalFileName; llvm::sys::fs::make_absolute(FileName); for (uint32_t i = 0; i < MOZ_THIRD_PARTY_PATHS_COUNT; ++i) { auto PathB = sys::path::begin(FileName); auto PathE = sys::path::end(FileName); auto ThirdPartyB = sys::path::begin(MOZ_THIRD_PARTY_PATHS[i]); auto ThirdPartyE = sys::path::end(MOZ_THIRD_PARTY_PATHS[i]); for (; PathB != PathE; ++PathB) { // Perform an inner loop to compare path segments, checking if the current // segment is the start of the current third party path. auto IPathB = PathB; auto IThirdPartyB = ThirdPartyB; for (; IPathB != PathE && IThirdPartyB != ThirdPartyE; ++IPathB, ++IThirdPartyB) { if (IPathB->compare_lower(*IThirdPartyB) != 0) { break; } } // We found a match! if (IThirdPartyB == ThirdPartyE) { InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, true)); return true; } } } InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, false)); return false; } inline bool inThirdPartyPath(const Decl *D, ASTContext *context) { D = D->getCanonicalDecl(); SourceLocation Loc = D->getLocation(); const SourceManager &SM = context->getSourceManager(); return inThirdPartyPath(Loc, SM); } inline CXXRecordDecl *getNonTemplateSpecializedCXXRecordDecl(QualType Q) { auto *D = Q->getAsCXXRecordDecl(); if (!D) { auto TemplateQ = Q->getAs(); if (!TemplateQ) { return nullptr; } auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl(); if (!TemplateDecl) { return nullptr; } D = dyn_cast_or_null(TemplateDecl->getTemplatedDecl()); if (!D) { return nullptr; } } return D; } inline bool inThirdPartyPath(const Decl *D) { return inThirdPartyPath(D, &D->getASTContext()); } inline bool inThirdPartyPath(const Stmt *S, ASTContext *context) { SourceLocation Loc = S->getBeginLoc(); const SourceManager &SM = context->getSourceManager(); auto ExpansionLoc = SM.getExpansionLoc(Loc); if (ExpansionLoc.isInvalid()) { return inThirdPartyPath(Loc, SM); } return inThirdPartyPath(ExpansionLoc, SM); } /// Polyfill for CXXOperatorCallExpr::isInfixBinaryOp() inline bool isInfixBinaryOp(const CXXOperatorCallExpr *OpCall) { #if CLANG_VERSION_FULL >= 400 return OpCall->isInfixBinaryOp(); #else // Taken from clang source. if (OpCall->getNumArgs() != 2) return false; switch (OpCall->getOperator()) { case OO_Call: case OO_Subscript: return false; default: return true; } #endif } #undef compare_lower #endif