void StrCatAppendCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto StrCat = functionDecl(hasName("::absl::StrCat")); // The arguments of absl::StrCat are implicitly converted to AlphaNum. This // matches to the arguments because of that behavior. const auto AlphaNum = IgnoringTemporaries(cxxConstructExpr( argumentCountIs(1), hasType(cxxRecordDecl(hasName("::absl::AlphaNum"))), hasArgument(0, ignoringImpCasts(declRefExpr(to(equalsBoundNode("LHS")), expr().bind("Arg0")))))); const auto HasAnotherReferenceToLhs = callExpr(hasAnyArgument(expr(hasDescendant(declRefExpr( to(equalsBoundNode("LHS")), unless(equalsBoundNode("Arg0"))))))); // Now look for calls to operator= with an object on the LHS and a call to // StrCat on the RHS. The first argument of the StrCat call should be the same // as the LHS. Ignore calls from template instantiations. Finder->addMatcher( cxxOperatorCallExpr( unless(isInTemplateInstantiation()), hasOverloadedOperatorName("="), hasArgument(0, declRefExpr(to(decl().bind("LHS")))), hasArgument(1, IgnoringTemporaries( callExpr(callee(StrCat), hasArgument(0, AlphaNum), unless(HasAnotherReferenceToLhs)) .bind("Call")))) .bind("Op"), this); }
namespace EffectiveCPP { StatementMatcher ctorCallVtlMatcherEC = compoundStmt( hasParent(constructorDecl().bind("cDecl")), hasDescendant(callExpr(callee(methodDecl(isVirtual())))) ); StatementMatcher dtorCallVtlMatcherEC = compoundStmt( hasParent(destructorDecl().bind("dDecl")), hasDescendant(callExpr(callee(methodDecl(isVirtual())))) ); }
void ShrinkToFitCheck::registerMatchers(MatchFinder *Finder) { // Swap as a function need not to be considered, because rvalue can not // be bound to a non-const reference. const auto ShrinkableAsMember = memberExpr(member(valueDecl().bind("ContainerDecl"))); const auto ShrinkableAsDecl = declRefExpr(hasDeclaration(valueDecl().bind("ContainerDecl"))); const auto CopyCtorCall = constructExpr( hasArgument(0, anyOf(ShrinkableAsMember, ShrinkableAsDecl, unaryOperator(has(ShrinkableAsMember)), unaryOperator(has(ShrinkableAsDecl))))); const auto SwapParam = expr(anyOf( memberExpr(member(equalsBoundNode("ContainerDecl"))), declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))), unaryOperator(has(memberExpr(member(equalsBoundNode("ContainerDecl"))))), unaryOperator( has(declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))))))); Finder->addMatcher( memberCallExpr(on(hasType(namedDecl(stlShrinkableContainer()))), callee(methodDecl(hasName("swap"))), has(memberExpr(hasDescendant(CopyCtorCall))), hasArgument(0, SwapParam.bind("ContainerToShrink")), unless(isInTemplateInstantiation())) .bind("CopyAndSwapTrick"), this); }
void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) { auto ConditionWithSideEffect = hasCondition(hasDescendant(expr(hasSideEffect(CheckFunctionCalls)))); Finder->addMatcher( stmt(anyOf(conditionalOperator(ConditionWithSideEffect), ifStmt(ConditionWithSideEffect))).bind("condStmt"), this); }
void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) { if ((!getLangOpts().CPlusPlus) || (!getLangOpts().CXXExceptions)) return; // Match any static or thread_local variable declaration that has an // initializer that can throw. Finder->addMatcher( varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()), unless(anyOf(isConstexpr(), hasType(cxxRecordDecl(isLambda())), hasAncestor(functionDecl()))), anyOf(hasDescendant(cxxConstructExpr(hasDeclaration( cxxConstructorDecl(unless(isNoThrow())).bind("func")))), hasDescendant(cxxNewExpr(hasDeclaration( functionDecl(unless(isNoThrow())).bind("func")))), hasDescendant(callExpr(hasDeclaration( functionDecl(unless(isNoThrow())).bind("func")))))) .bind("var"), this); }
bool KMFolder::hasDescendant( KMFolder *fld ) const { if ( !fld ) return false; KMFolderDir * pdir = fld->parent(); if ( !pdir ) return false; fld = pdir->owner(); if ( fld == this ) return true; return hasDescendant( fld ); }
void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( cxxMemberCallExpr( callee(memberExpr(hasDescendant(implicitCastExpr( hasImplicitDestinationType(pointsTo( type(anything()).bind("castToType"))), hasSourceExpression(cxxThisExpr(hasType( type(anything()).bind("thisType"))))))) .bind("member")), callee(cxxMethodDecl(isVirtual()))), this); }
void InefficientStringConcatenationCheck::registerMatchers( MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto BasicStringType = hasType(qualType(hasUnqualifiedDesugaredType(recordType( hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))))); const auto BasicStringPlusOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("+"), hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType)))); const auto PlusOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("+"), hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))), hasDescendant(BasicStringPlusOperator)) .bind("plusOperator"); const auto AssignOperator = cxxOperatorCallExpr( hasOverloadedOperatorName("="), hasArgument(0, declRefExpr(BasicStringType, hasDeclaration(decl().bind("lhsStrT"))) .bind("lhsStr")), hasArgument(1, stmt(hasDescendant(declRefExpr( hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))), hasDescendant(BasicStringPlusOperator)); if (StrictMode) { Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)), this); } else { Finder->addMatcher( cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator), hasAncestor(stmt(anyOf(cxxForRangeStmt(), whileStmt(), forStmt())))), this); } }
void MisplacedOperatorInStrlenInAllocCheck::registerMatchers( MatchFinder *Finder) { const auto StrLenFunc = functionDecl(anyOf( hasName("::strlen"), hasName("::std::strlen"), hasName("::strnlen"), hasName("::std::strnlen"), hasName("::strnlen_s"), hasName("::std::strnlen_s"), hasName("::wcslen"), hasName("::std::wcslen"), hasName("::wcsnlen"), hasName("::std::wcsnlen"), hasName("::wcsnlen_s"), hasName("std::wcsnlen_s"))); const auto BadUse = callExpr(callee(StrLenFunc), hasAnyArgument(ignoringImpCasts( binaryOperator( hasOperatorName("+"), hasRHS(ignoringParenImpCasts(integerLiteral(equals(1))))) .bind("BinOp")))) .bind("StrLen"); const auto BadArg = anyOf( allOf(unless(binaryOperator( hasOperatorName("+"), hasLHS(BadUse), hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))), hasDescendant(BadUse)), BadUse); const auto Alloc0Func = functionDecl(anyOf(hasName("::malloc"), hasName("std::malloc"), hasName("::alloca"), hasName("std::alloca"))); const auto Alloc1Func = functionDecl(anyOf(hasName("::calloc"), hasName("std::calloc"), hasName("::realloc"), hasName("std::realloc"))); const auto Alloc0FuncPtr = varDecl(hasType(isConstQualified()), hasInitializer(ignoringParenImpCasts( declRefExpr(hasDeclaration(Alloc0Func))))); const auto Alloc1FuncPtr = varDecl(hasType(isConstQualified()), hasInitializer(ignoringParenImpCasts( declRefExpr(hasDeclaration(Alloc1Func))))); Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))), hasArgument(0, BadArg)) .bind("Alloc"), this); Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))), hasArgument(1, BadArg)) .bind("Alloc"), this); Finder->addMatcher( cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc"), this); }
void ReturnBracedInitListCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++. if (!getLangOpts().CPlusPlus11) return; // Skip list initialization and constructors with an initializer list. auto ConstructExpr = cxxConstructExpr( unless(anyOf(hasDeclaration(cxxConstructorDecl(isExplicit())), isListInitialization(), hasDescendant(initListExpr()), isInTemplateInstantiation()))) .bind("ctor"); auto CtorAsArgument = materializeTemporaryExpr(anyOf( has(ConstructExpr), has(cxxFunctionalCastExpr(has(ConstructExpr))))); Finder->addMatcher( functionDecl(isDefinition(), // Declarations don't have return statements. returns(unless(anyOf(builtinType(), autoType()))), hasDescendant(returnStmt(hasReturnValue( has(cxxConstructExpr(has(CtorAsArgument))))))) .bind("fn"), this); }
void InterfacesGlobalInitCheck::registerMatchers(MatchFinder *Finder) { const auto IsGlobal = allOf(hasGlobalStorage(), hasDeclContext(anyOf(translationUnitDecl(), // Global scope. namespaceDecl(), // Namespace scope. recordDecl())), // Class scope. unless(isConstexpr())); const auto ReferencesUndefinedGlobalVar = declRefExpr(hasDeclaration( varDecl(IsGlobal, unless(isDefinition())).bind("referencee"))); Finder->addMatcher( varDecl(IsGlobal, isDefinition(), hasInitializer(expr(hasDescendant(ReferencesUndefinedGlobalVar)))) .bind("var"), this); }
void UniqueptrDeleteReleaseCheck::registerMatchers(MatchFinder *Finder) { auto IsSusbstituted = qualType(anyOf( substTemplateTypeParmType(), hasDescendant(substTemplateTypeParmType()))); auto UniquePtrWithDefaultDelete = classTemplateSpecializationDecl( hasName("std::unique_ptr"), hasTemplateArgument(1, refersToType(qualType(hasDeclaration(cxxRecordDecl( hasName("std::default_delete"))))))); Finder->addMatcher( cxxDeleteExpr(has(ignoringParenImpCasts(cxxMemberCallExpr( on(expr(hasType(UniquePtrWithDefaultDelete), unless(hasType(IsSusbstituted))) .bind("uptr")), callee(cxxMethodDecl(hasName("release"))))))) .bind("delete"), this); }
void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) { auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName( "::std::linear_congruential_engine", "::std::mersenne_twister_engine", "::std::subtract_with_carry_engine", "::std::discard_block_engine", "::std::independent_bits_engine", "::std::shuffle_order_engine")); auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType( recordType(hasDeclaration(RandomGeneratorEngineDecl)))); // std::mt19937 engine; // engine.seed(); // ^ // engine.seed(1); // ^ // const int x = 1; // engine.seed(x); // ^ Finder->addMatcher( cxxMemberCallExpr( has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)), member(hasName("seed")), unless(hasDescendant(cxxThisExpr()))))) .bind("seed"), this); // std::mt19937 engine; // ^ // std::mt19937 engine(1); // ^ // const int x = 1; // std::mt19937 engine(x); // ^ Finder->addMatcher( cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor"), this); // srand(); // ^ // const int x = 1; // srand(x); // ^ Finder->addMatcher( callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand")))) .bind("srand"), this); }
void UnusedParametersCheck::warnOnUnusedParameter( const MatchFinder::MatchResult &Result, const FunctionDecl *Function, unsigned ParamIndex) { const auto *Param = Function->getParamDecl(ParamIndex); auto MyDiag = diag(Param->getLocation(), "parameter '%0' is unused") << Param->getName(); auto UsedByRef = [&] { return !ast_matchers::match( decl(hasDescendant( declRefExpr(to(equalsNode(Function)), unless(hasAncestor( callExpr(callee(equalsNode(Function)))))))), *Result.Context->getTranslationUnitDecl(), *Result.Context) .empty(); }; // Comment out parameter name for non-local functions. if (Function->isExternallyVisible() || !Result.SourceManager->isInMainFile(Function->getLocation()) || UsedByRef()) { SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd()); // Note: We always add a space before the '/*' to not accidentally create a // '*/*' for pointer types, which doesn't start a comment. clang-format will // clean this up afterwards. MyDiag << FixItHint::CreateReplacement( RemovalRange, (Twine(" /*") + Param->getName() + "*/").str()); return; } // Fix all redeclarations. for (const FunctionDecl *FD : Function->redecls()) if (FD->param_size()) MyDiag << removeParameter(FD, ParamIndex); // Fix all call sites. auto CallMatches = ast_matchers::match( decl(forEachDescendant( callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))), *Result.Context->getTranslationUnitDecl(), *Result.Context); for (const auto &Match : CallMatches) MyDiag << removeArgument(Match.getNodeAs<CallExpr>("x"), ParamIndex); }
void StaticallyConstructedObjectsCheck::registerMatchers(MatchFinder *Finder) { // Constructing global, non-trivial objects with static storage is // disallowed, unless the object is statically initialized with a constexpr // constructor or has no explicit constructor. // Constexpr requires C++11 or later. if (!getLangOpts().CPlusPlus11) return; Finder->addMatcher(varDecl( // Match global, statically stored objects... isGlobalStatic(), // ... that have C++ constructors... hasDescendant(cxxConstructExpr(unless(allOf( // ... unless it is constexpr ... hasDeclaration(cxxConstructorDecl(isConstexpr())), // ... and is statically initialized. isConstantInitializer()))))) .bind("decl"), this); }
void UnusedParametersCheck::warnOnUnusedParameter( const MatchFinder::MatchResult &Result, const FunctionDecl *Function, unsigned ParamIndex) { const auto *Param = Function->getParamDecl(ParamIndex); auto MyDiag = diag(Param->getLocation(), "parameter '%0' is unused") << Param->getName(); auto UsedByRef = [&] { return !ast_matchers::match( decl(hasDescendant( declRefExpr(to(equalsNode(Function)), unless(hasAncestor( callExpr(callee(equalsNode(Function)))))))), *Result.Context->getTranslationUnitDecl(), *Result.Context) .empty(); }; // Comment out parameter name for non-local functions. if ((Function->isExternallyVisible() && Function->getStorageClass() != StorageClass::SC_Static) || UsedByRef()) { SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd()); MyDiag << FixItHint::CreateReplacement( RemovalRange, (Twine(" /*") + Param->getName() + "*/").str()); return; } // Fix all redeclarations. for (const FunctionDecl *FD : Function->redecls()) MyDiag << removeParameter(FD, ParamIndex); // Fix all call sites. auto CallMatches = ast_matchers::match( decl(forEachDescendant( callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))), *Result.Context->getTranslationUnitDecl(), *Result.Context); for (const auto &Match : CallMatches) MyDiag << removeArgument(Match.getNodeAs<CallExpr>("x"), ParamIndex); }
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt) { return stmt(hasAncestor(cxxForRangeStmt( hasRangeBeginEndStmt(hasDescendant(equalsNode(&Node)))))) .matches(Node, Finder, Builder); }
#include "ASTUtility.h" StatementMatcher FuncStmtMatcher = compoundStmt( hasParent(functionDecl(returns(pointerType())).bind("functiondecl")), hasDescendant(returnStmt( hasDescendant(declRefExpr().bind("decl")))) ).bind("funcstmt"); class ReturnChecker : public MatchFinder::MatchCallback { public: virtual void run(const MatchFinder::MatchResult &Result) { clang::ASTContext *Context = Result.Context; const clang::CompoundStmt *cs = Result.Nodes.getNodeAs<clang::CompoundStmt>("funcstmt"); const clang::FunctionDecl *fd = Result.Nodes.getNodeAs<clang::FunctionDecl>("functiondecl"); const clang::DeclRefExpr *decl = Result.Nodes.getNodeAs<clang::DeclRefExpr>("decl"); if(!cs || !fd || !decl || ASTUtility::IsStmtInSTDFile(cs, Context)) return; if (!clang::VarDecl::classof(decl -> getDecl())) return; const clang::VarDecl *var = static_cast<const clang::VarDecl*>(decl -> getDecl()); if (var -> hasLocalStorage()) ASTUtility::Print(decl, Context, "Rule65"); } }; //@TestNeed static llvm::cl::OptionCategory MyToolCategory("options");