void InaccurateEraseCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus) return; const auto CheckForEndCall = hasArgument( 1, anyOf(cxxConstructExpr(has(ignoringParenImpCasts( cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end")))) .bind("InaccEndCall")))), anything())); Finder->addMatcher( cxxMemberCallExpr( on(hasType(namedDecl(matchesName("^::std::")))), callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1), hasArgument(0, has(ignoringParenImpCasts( callExpr(callee(functionDecl(matchesName( "^::std::(remove(_if)?|unique)$"))), CheckForEndCall) .bind("InaccAlgCall")))), unless(isInTemplateInstantiation())) .bind("InaccErase"), this); }
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); }
void InaccurateEraseCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus) return; const auto EndCall = callExpr( callee(functionDecl(hasAnyName("remove", "remove_if", "unique"))), hasArgument( 1, anyOf(cxxConstructExpr(has(ignoringImplicit( cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end")))) .bind("end")))), anything()))) .bind("alg"); const auto DeclInStd = type(hasUnqualifiedDesugaredType( tagType(hasDeclaration(decl(isInStdNamespace()))))); Finder->addMatcher( cxxMemberCallExpr( on(anyOf(hasType(DeclInStd), hasType(pointsTo(DeclInStd)))), callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1), hasArgument(0, has(ignoringImplicit( anyOf(EndCall, has(ignoringImplicit(EndCall)))))), unless(isInTemplateInstantiation())) .bind("erase"), this); }
void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { if (!isLanguageVersionSupported(getLangOpts())) return; // Calling make_smart_ptr from within a member function of a type with a // private or protected constructor would be ill-formed. auto CanCallCtor = unless(has(ignoringImpCasts( cxxConstructExpr(hasDeclaration(decl(unless(isPublic()))))))); Finder->addMatcher( cxxBindTemporaryExpr(has(ignoringParenImpCasts( cxxConstructExpr( hasType(getSmartPointerTypeMatcher()), argumentCountIs(1), hasArgument(0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType( equalsBoundNode(PointerType))))), CanCallCtor) .bind(NewExpression)), unless(isInTemplateInstantiation())) .bind(ConstructorCall)))), this); Finder->addMatcher( cxxMemberCallExpr( thisPointerType(getSmartPointerTypeMatcher()), callee(cxxMethodDecl(hasName("reset"))), hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)), unless(isInTemplateInstantiation())) .bind(ResetCall), this); }
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 ProTypeCstyleCastCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( cStyleCastExpr(unless(isInTemplateInstantiation())).bind("cast"), this); }
void InefficientAlgorithmCheck::registerMatchers(MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus) return; const std::string Algorithms = "^::std::(find|count|equal_range|lower_bound|upper_bound)$"; const auto ContainerMatcher = classTemplateSpecializationDecl( matchesName("^::std::(unordered_)?(multi)?(set|map)$")); const auto Matcher = callExpr( callee(functionDecl(matchesName(Algorithms))), hasArgument( 0, constructExpr(has(memberCallExpr( callee(methodDecl(hasName("begin"))), on(declRefExpr( hasDeclaration(decl().bind("IneffContObj")), anyOf(hasType(ContainerMatcher.bind("IneffCont")), hasType(pointsTo( ContainerMatcher.bind("IneffContPtr"))))) .bind("IneffContExpr")))))), hasArgument(1, constructExpr(has(memberCallExpr( callee(methodDecl(hasName("end"))), on(declRefExpr(hasDeclaration( equalsBoundNode("IneffContObj")))))))), hasArgument(2, expr().bind("AlgParam")), unless(isInTemplateInstantiation())) .bind("IneffAlg"); Finder->addMatcher(Matcher, this); }
void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStaticStorageClass()), varDecl(hasStaticStorageDuration()))), unless(isInTemplateInstantiation())) .bind("memberExpression"), this); }
void MemsetZeroLengthCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { // Look for memset(x, y, 0) as those is most likely an argument swap. // TODO: Also handle other standard functions that suffer from the same // problem, e.g. memchr. Finder->addMatcher( callExpr(callee(functionDecl(hasName("::memset"))), argumentCountIs(3), unless(isInTemplateInstantiation())).bind("decl"), this); }
void AvoidCStyleCastsCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { Finder->addMatcher( cStyleCastExpr( // Filter out (EnumType)IntegerLiteral construct, which is generated // for non-type template arguments of enum types. // FIXME: Remove this once this is fixed in the AST. unless(hasParent(substNonTypeTemplateParmExpr())), // Avoid matches in template instantiations. unless(isInTemplateInstantiation())).bind("cast"), this); }
void UseBoolLiteralsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( implicitCastExpr( has(ignoringParenImpCasts(integerLiteral().bind("literal"))), hasImplicitDestinationType(qualType(booleanType())), unless(isInTemplateInstantiation()), anyOf(hasParent(explicitCastExpr().bind("cast")), anything())), this); Finder->addMatcher( conditionalOperator( hasParent(implicitCastExpr( hasImplicitDestinationType(qualType(booleanType())), unless(isInTemplateInstantiation()))), eachOf(hasTrueExpression( ignoringParenImpCasts(integerLiteral().bind("literal"))), hasFalseExpression( ignoringParenImpCasts(integerLiteral().bind("literal"))))), this); }
void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( expr(unless(isInTemplateInstantiation()), expr(sizeOfExpr(has(ignoringParenImpCasts( expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl( matchesName("^(::std::|::string)"), unless(matchesName("^::std::(bitset|array)$")), hasMethod(cxxMethodDecl(hasName("size"), isPublic(), isConst()))))))))))) .bind("sizeof"), // Ignore ARRAYSIZE(<array of containers>) pattern. unless(hasAncestor(binaryOperator( anyOf(hasOperatorName("/"), hasOperatorName("%")), hasLHS(ignoringParenCasts(sizeOfExpr(expr()))), hasRHS(ignoringParenCasts(equalsBoundNode("sizeof"))))))), this); }
void ExplicitMakePairCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { // Only register the matchers for C++; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus) return; // Look for std::make_pair with explicit template args. Ignore calls in // templates. Finder->addMatcher( callExpr(unless(isInTemplateInstantiation()), callee(expr(ignoringParenImpCasts( declRefExpr(hasExplicitTemplateArgs(), to(functionDecl(hasName("::std::make_pair")))) .bind("declref"))))).bind("call"), this); }
void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( cxxOperatorCallExpr( anyOf(hasOverloadedOperatorName("="), hasOverloadedOperatorName("+=")), callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl( hasName("::std::basic_string"), hasTemplateArgument(0, refersToType(qualType().bind("type"))))))), hasArgument(1, ignoringImpCasts(expr(hasType(isInteger()), unless(hasType(isAnyCharacter()))) .bind("expr"))), unless(isInTemplateInstantiation())), this); }
void UseToStringCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Finder->addMatcher( callExpr( hasDeclaration(functionDecl( returns(hasDeclaration(classTemplateSpecializationDecl( hasName("std::basic_string"), hasTemplateArgument(0, templateArgument().bind("char_type"))))), hasName("boost::lexical_cast"), hasParameter(0, hasType(qualType(has(substTemplateTypeParmType( isStrictlyInteger()))))))), argumentCountIs(1), unless(isInTemplateInstantiation())) .bind("to_string"), this); }
void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; auto MoveCallMatcher = callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1), unless(isInTemplateInstantiation())) .bind("call-move"); Finder->addMatcher(MoveCallMatcher, this); auto ConstParamMatcher = forEachArgumentWithParam( MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified())))); Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this); Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"), this); }
void UpgradeDurationConversionsCheck::check( const MatchFinder::MatchResult &Result) { const llvm::StringRef Message = "implicit conversion to 'int64_t' is deprecated in this context; use an " "explicit cast instead"; const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg"); SourceLocation Loc = ArgExpr->getBeginLoc(); if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) { if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) { // For each location matched in a template instantiation, we check if the // location can also be found in `MatchedTemplateLocations`. If it is not // found, that means the expression did not create a match without the // instantiation and depends on template parameters. A manual fix is // probably required so we provide only a warning. diag(Loc, Message); } return; } // We gather source locations from template matches not in template // instantiations for future matches. internal::Matcher<Stmt> IsInsideTemplate = hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl()))); if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty()) MatchedTemplateLocations.insert(Loc.getRawEncoding()); DiagnosticBuilder Diag = diag(Loc, Message); CharSourceRange SourceRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(ArgExpr->getSourceRange()), *Result.SourceManager, Result.Context->getLangOpts()); if (SourceRange.isInvalid()) // An invalid source range likely means we are inside a macro body. A manual // fix is likely needed so we do not create a fix-it hint. return; Diag << FixItHint::CreateInsertion(SourceRange.getBegin(), "static_cast<int64_t>(") << FixItHint::CreateInsertion(SourceRange.getEnd(), ")"); }
void UndelegatedConstructorCheck::registerMatchers(MatchFinder *Finder) { // We look for calls to constructors of the same type in constructors. To do // this we have to look through a variety of nodes that occur in the path, // depending on the type's destructor and the number of arguments on the // constructor call, this is handled by ignoringTemporaryExpr. Ignore template // instantiations to reduce the number of duplicated warnings. // // Only register the matchers for C++11; the functionality currently does not // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus11) return; Finder->addMatcher( compoundStmt( hasParent( cxxConstructorDecl(ofClass(cxxRecordDecl().bind("parent")))), forEach(ignoringTemporaryExpr( cxxConstructExpr(hasDeclaration(cxxConstructorDecl(ofClass( cxxRecordDecl(baseOfBoundNode("parent")))))) .bind("construct"))), unless(isInTemplateInstantiation())), 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 UseEmplaceCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus11) return; // FIXME: Bunch of functionality that could be easily added: // + add handling of `push_front` for std::forward_list, std::list // and std::deque. // + add handling of `push` for std::stack, std::queue, std::priority_queue // + add handling of `insert` for stl associative container, but be careful // because this requires special treatment (it could cause performance // regression) // + match for emplace calls that should be replaced with insertion // + match for make_pair calls. auto callPushBack = cxxMemberCallExpr( hasDeclaration(functionDecl(hasName("push_back"))), on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>( ContainersWithPushBack.begin(), ContainersWithPushBack.end())))))); // We can't replace push_backs of smart pointer because // if emplacement fails (f.e. bad_alloc in vector) we will have leak of // passed pointer because smart pointer won't be constructed // (and destructed) as in push_back case. auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName( SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end()))))); // Bitfields binds only to consts and emplace_back take it by universal ref. auto bitFieldAsArgument = hasAnyArgument( ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField()))))); // Initializer list can't be passed to universal reference. auto initializerListAsArgument = hasAnyArgument( ignoringImplicit(cxxConstructExpr(isListInitialization()))); // We could have leak of resource. auto newExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr())); // We would call another constructor. auto constructingDerived = hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase))); // emplace_back can't access private constructor. auto isPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate())); auto hasInitList = has(ignoringImplicit(initListExpr())); // FIXME: Discard 0/NULL (as nullptr), static inline const data members, // overloaded functions and template names. auto soughtConstructExpr = cxxConstructExpr( unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument, initializerListAsArgument, newExprAsArgument, constructingDerived, isPrivateCtor))) .bind("ctor"); auto hasConstructExpr = has(ignoringImplicit(soughtConstructExpr)); auto ctorAsArgument = materializeTemporaryExpr( anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr)))); Finder->addMatcher(cxxMemberCallExpr(callPushBack, has(ctorAsArgument), unless(isInTemplateInstantiation())) .bind("call"), this); }