diff options
Diffstat (limited to '')
-rw-r--r-- | build/clang-plugin/Utils.h | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/build/clang-plugin/Utils.h b/build/clang-plugin/Utils.h new file mode 100644 index 0000000000..c25caf43ee --- /dev/null +++ b/build/clang-plugin/Utils.h @@ -0,0 +1,499 @@ +/* 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<CXXOperatorCallExpr>(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<BinaryOperator>(Expression)) { + if (BinOpExpr->isAssignmentOp()) { + return true; + } + } + + // Recurse to children. + for (const Stmt *SubStmt : Expression->children()) { + auto ChildExpr = dyn_cast_or_null<Expr>(SubStmt); + if (ChildExpr && hasSideEffectAssignment(ChildExpr)) { + return true; + } + } + + return false; +} + +template <class T> +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 <typename T> inline StringRef getNameChecked(const T &D) { + return D->getIdentifier() ? D->getName() : ""; +} + +/// A cached data of whether classes are refcounted or not. +typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>> + 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<NamespaceDecl>(DC); + if (!ND) { + return ""; + } + + while (const DeclContext *ParentDC = ND->getParent()) { + if (!isa<NamespaceDecl>(ParentDC)) { + break; + } + ND = cast<NamespaceDecl>(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<CXXOperatorCallExpr>(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<BinaryOperator>(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<ExprWithCleanups>(s)) { + return ewc->getSubExpr(); + } + if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(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<CXXBindTemporaryExpr>(s)) { + return bte->getSubExpr(); + } + if (auto *ce = dyn_cast<CastExpr>(s)) { + s = ce->getSubExpr(); + } + if (auto *pe = dyn_cast<ParenExpr>(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<Expr>(IgnoreTrivials(static_cast<const Stmt *>(e))); +} + +// Returns the input if the input is not a trivial. +inline const Expr *MaybeSkipOneTrivial(const Expr *e) { + return cast_or_null<Expr>(MaybeSkipOneTrivial(static_cast<const Stmt *>(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<moz_heap_allocator>(Declaration)) { + return false; + } + return true; +} + +extern DenseMap<StringRef, bool> 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<TemplateSpecializationType>(); + if (!TemplateQ) { + return nullptr; + } + + auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl(); + if (!TemplateDecl) { + return nullptr; + } + + D = dyn_cast_or_null<CXXRecordDecl>(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 |