void PassByValueCheck::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; Finder->addMatcher( cxxConstructorDecl( forEachConstructorInitializer( cxxCtorInitializer( // Clang builds a CXXConstructExpr only whin it knows which // constructor will be called. In dependent contexts a // ParenListExpr is generated instead of a CXXConstructExpr, // filtering out templates automatically for us. withInitializer(cxxConstructExpr( has(ignoringParenImpCasts(declRefExpr(to( parmVarDecl( hasType(qualType( // Match only const-ref or a non-const value // parameters. Rvalues and const-values // shouldn't be modified. anyOf(constRefType(), nonConstValueType())))) .bind("Param"))))), hasDeclaration(cxxConstructorDecl( isCopyConstructor(), unless(isDeleted()), hasDeclContext( cxxRecordDecl(isMoveConstructible()))))))) .bind("Initializer"))) .bind("Ctor"), this); }
void AssignOperatorSignatureCheck::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) { const auto HasGoodReturnType = methodDecl(returns(lValueReferenceType( pointee(unless(isConstQualified()), hasDeclaration(equalsBoundNode("class")))))); const auto IsSelf = qualType(anyOf( hasDeclaration(equalsBoundNode("class")), referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))); const auto IsSelfAssign = methodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())), hasName("operator="), ofClass(recordDecl().bind("class")), hasParameter(0, parmVarDecl(hasType(IsSelf)))) .bind("method"); Finder->addMatcher( methodDecl(IsSelfAssign, unless(HasGoodReturnType)).bind("ReturnType"), this); const auto BadSelf = referenceType( anyOf(lValueReferenceType(pointee(unless(isConstQualified()))), rValueReferenceType(pointee(isConstQualified())))); Finder->addMatcher( methodDecl(IsSelfAssign, hasParameter(0, parmVarDecl(hasType(BadSelf)))) .bind("ArgumentType"), this); Finder->addMatcher(methodDecl(IsSelfAssign, isConst()).bind("Const"), this); } }
void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) { // Forwarding references require C++11 or later. if (!getLangOpts().CPlusPlus11) return; auto ForwardingRefParm = parmVarDecl( hasType(qualType(rValueReferenceType(), references(templateTypeParmType(hasDeclaration( templateTypeParmDecl().bind("type-parm-decl")))), unless(references(isConstQualified()))))) .bind("parm-var"); DeclarationMatcher findOverload = cxxConstructorDecl( hasParameter(0, ForwardingRefParm), unless(hasAnyParameter( // No warning: enable_if as constructor parameter. parmVarDecl(hasType(isEnableIf())))), unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl( // No warning: enable_if as type parameter. hasDefaultArgument(isEnableIf()))))))) .bind("ctor"); Finder->addMatcher(findOverload, this); }
void FoldInitTypeCheck::registerMatchers(MatchFinder *Finder) { // We match functions of interest and bind the iterator and init value types. // Note: Right now we check only builtin types. const auto BuiltinTypeWithId = [](const char *ID) { return hasCanonicalType(builtinType().bind(ID)); }; const auto IteratorWithValueType = [&BuiltinTypeWithId](const char *ID) { return anyOf( // Pointer types. pointsTo(BuiltinTypeWithId(ID)), // Iterator types. recordType(hasDeclaration(has(typedefNameDecl( hasName("value_type"), hasType(BuiltinTypeWithId(ID))))))); }; const auto IteratorParam = parmVarDecl( hasType(hasCanonicalType(IteratorWithValueType("IterValueType")))); const auto Iterator2Param = parmVarDecl( hasType(hasCanonicalType(IteratorWithValueType("Iter2ValueType")))); const auto InitParam = parmVarDecl(hasType(BuiltinTypeWithId("InitType"))); // std::accumulate, std::reduce. Finder->addMatcher( callExpr(callee(functionDecl( hasAnyName("::std::accumulate", "::std::reduce"), hasParameter(0, IteratorParam), hasParameter(2, InitParam))), argumentCountIs(3)) .bind("Call"), this); // std::inner_product. Finder->addMatcher( callExpr(callee(functionDecl(hasName("::std::inner_product"), hasParameter(0, IteratorParam), hasParameter(2, Iterator2Param), hasParameter(3, InitParam))), argumentCountIs(4)) .bind("Call"), this); // std::reduce with a policy. Finder->addMatcher( callExpr(callee(functionDecl(hasName("::std::reduce"), hasParameter(1, IteratorParam), hasParameter(3, InitParam))), argumentCountIs(4)) .bind("Call"), this); // std::inner_product with a policy. Finder->addMatcher( callExpr(callee(functionDecl(hasName("::std::inner_product"), hasParameter(1, IteratorParam), hasParameter(3, Iterator2Param), hasParameter(4, InitParam))), argumentCountIs(5)) .bind("Call"), this); }
void UnconventionalAssignOperatorCheck::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; const auto HasGoodReturnType = cxxMethodDecl(returns(lValueReferenceType( pointee(unless(isConstQualified()), anyOf(autoType(), hasDeclaration(equalsBoundNode("class"))))))); const auto IsSelf = qualType( anyOf(hasDeclaration(equalsBoundNode("class")), referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))); const auto IsAssign = cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())), hasName("operator="), ofClass(recordDecl().bind("class"))) .bind("method"); const auto IsSelfAssign = cxxMethodDecl(IsAssign, hasParameter(0, parmVarDecl(hasType(IsSelf)))) .bind("method"); Finder->addMatcher( cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"), this); const auto BadSelf = referenceType( anyOf(lValueReferenceType(pointee(unless(isConstQualified()))), rValueReferenceType(pointee(isConstQualified())))); Finder->addMatcher( cxxMethodDecl(IsSelfAssign, hasParameter(0, parmVarDecl(hasType(BadSelf)))) .bind("ArgumentType"), this); Finder->addMatcher( cxxMethodDecl(IsSelfAssign, anyOf(isConst(), isVirtual())).bind("cv"), this); const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts( anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())), cxxOperatorCallExpr(argumentCountIs(1), callee(unresolvedLookupExpr()), hasArgument(0, cxxThisExpr()))))))); const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType); Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign)) .bind("returnStmt"), this); }
void UseNodiscardCheck::registerMatchers(MatchFinder *Finder) { // If we use ``[[nodiscard]]`` attribute, we require at least C++17. Use a // macro or ``__attribute__`` with pre c++17 compilers by using // ReplacementString option. if ((NoDiscardMacro == "[[nodiscard]]" && !getLangOpts().CPlusPlus17) || !getLangOpts().CPlusPlus) return; auto FunctionObj = cxxRecordDecl(hasAnyName("::std::function", "::boost::function")); // Find all non-void const methods which have not already been marked to // warn on unused result. Finder->addMatcher( cxxMethodDecl( allOf(isConst(), isDefinitionOrInline(), unless(anyOf( returns(voidType()), isNoReturn(), isOverloadedOperator(), isVariadic(), hasTemplateReturnType(), hasClassMutableFields(), isConversionOperator(), hasAttr(clang::attr::WarnUnusedResult), hasType(isInstantiationDependentType()), hasAnyParameter(anyOf( parmVarDecl(anyOf(hasType(FunctionObj), hasType(references(FunctionObj)))), hasType(isNonConstReferenceOrPointer()), hasParameterPack())))))) .bind("no_discard"), this); }
void ScopeChecker::registerMatchers(MatchFinder *AstMatcher) { AstMatcher->addMatcher(varDecl().bind("node"), this); AstMatcher->addMatcher(cxxNewExpr().bind("node"), this); AstMatcher->addMatcher(materializeTemporaryExpr().bind("node"), this); AstMatcher->addMatcher( callExpr(callee(functionDecl(heapAllocator()))).bind("node"), this); AstMatcher->addMatcher(parmVarDecl().bind("parm_vardecl"), this); }
void NonConstReferences::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( parmVarDecl( unless(isInstantiated()), hasType(references( qualType(unless(isConstQualified())).bind("referenced_type"))), unless(hasType(rValueReferenceType()))) .bind("param"), this); }
void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) { const auto ExpensiveValueParamDecl = parmVarDecl(hasType(hasCanonicalType(allOf(matchers::isExpensiveToCopy(), unless(referenceType())))), decl().bind("param")); Finder->addMatcher( functionDecl(isDefinition(), unless(cxxMethodDecl(isOverride())), unless(isInstantiated()), has(typeLoc(forEach(ExpensiveValueParamDecl))), decl().bind("functionDecl")), this); }
void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) { const auto ConstParamDecl = parmVarDecl(hasType(qualType(isConstQualified()))).bind("param"); Finder->addMatcher( functionDecl(unless(isDefinition()), // Lambdas are always their own definition, but they // generate a non-definition FunctionDecl too. Ignore those. // Class template instantiations have a non-definition // CXXMethodDecl for methods that aren't used in this // translation unit. Ignore those, as the template will have // already been checked. unless(cxxMethodDecl(ofClass(cxxRecordDecl(anyOf( isLambda(), ast_matchers::isTemplateInstantiation()))))), has(typeLoc(forEach(ConstParamDecl)))) .bind("func"), this); }
void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) { // 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( cxxConstructorDecl( unless(isImplicit()), allOf(isMoveConstructor(), hasAnyConstructorInitializer( cxxCtorInitializer( withInitializer(cxxConstructExpr(hasDeclaration( cxxConstructorDecl(isCopyConstructor()) .bind("ctor"))))) .bind("move-init")))), this); auto NonConstValueMovableAndExpensiveToCopy = qualType(allOf(unless(pointerType()), unless(isConstQualified()), hasDeclaration(cxxRecordDecl(hasMethod(cxxConstructorDecl( isMoveConstructor(), unless(isDeleted()))))), matchers::isExpensiveToCopy())); // This checker is also used to implement cert-oop11-cpp, but when using that // form of the checker, we do not want to diagnose movable parameters. if (!UseCERTSemantics) { Finder->addMatcher( cxxConstructorDecl( allOf( unless(isMoveConstructor()), hasAnyConstructorInitializer(withInitializer(cxxConstructExpr( hasDeclaration(cxxConstructorDecl(isCopyConstructor())), hasArgument( 0, declRefExpr( to(parmVarDecl( hasType( NonConstValueMovableAndExpensiveToCopy)) .bind("movable-param"))) .bind("init-arg"))))))) .bind("ctor-decl"), 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 NonConstParameterCheck::registerMatchers(MatchFinder *Finder) { // Add parameters to Parameters. Finder->addMatcher(parmVarDecl(unless(isInstantiated())).bind("Parm"), this); // C++ constructor. Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this); // Track unused parameters, there is Wunused-parameter about unused // parameters. Finder->addMatcher(declRefExpr().bind("Ref"), this); // Analyse parameter usage in function. Finder->addMatcher(stmt(anyOf(unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--"))), binaryOperator(), callExpr(), returnStmt(), cxxConstructExpr())) .bind("Mark"), this); Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this); }
void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) { // There are two ways to get into trouble with objects like FILE *: // dereferencing the pointer type to be a non-pointer type, and declaring // the type as a non-pointer type in the first place. While the declaration // itself could technically be well-formed in the case where the type is not // an opaque type, it's highly suspicious behavior. // // POSIX types are a bit different in that it's reasonable to declare a // non-pointer variable or data member of the type, but it is not reasonable // to dereference a pointer to the type, or declare a parameter of non-pointer // type. auto BadFILEType = hasType(namedDecl(isFILEType()).bind("type_decl")); auto BadPOSIXType = hasType(namedDecl(isPOSIXType()).bind("type_decl")); auto BadEitherType = anyOf(BadFILEType, BadPOSIXType); Finder->addMatcher( namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType))) .bind("decl"), this); Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this); Finder->addMatcher( expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"), this); }
void CanRunScriptChecker::registerMatchers(MatchFinder *AstMatcher) { auto InvalidArg = // We want to find any expression, ignoreTrivials(expr( // which has a refcounted pointer type, hasType(pointerType( pointee(hasDeclaration(cxxRecordDecl(isRefCounted()))))), // and which is not this, unless(cxxThisExpr()), // and which is not a method call on a smart ptr, unless(cxxMemberCallExpr(on(hasType(isSmartPtrToRefCounted())))), // and which is not a parameter of the parent function, unless(declRefExpr(to(parmVarDecl()))), // and which is not a MOZ_KnownLive wrapped value. unless(callExpr(callee(functionDecl(hasName("MOZ_KnownLive"))))), expr().bind("invalidArg"))); auto OptionalInvalidExplicitArg = anyOf( // We want to find any argument which is invalid. hasAnyArgument(InvalidArg), // This makes this matcher optional. anything()); // Please not that the hasCanRunScriptAnnotation() matchers are not present // directly in the cxxMemberCallExpr, callExpr and constructExpr matchers // because we check that the corresponding functions can run script later in // the checker code. AstMatcher->addMatcher( expr( anyOf( // We want to match a method call expression, cxxMemberCallExpr( // which optionally has an invalid arg, OptionalInvalidExplicitArg, // or which optionally has an invalid implicit this argument, anyOf( // which derefs into an invalid arg, on(cxxOperatorCallExpr( anyOf(hasAnyArgument(InvalidArg), anything()))), // or is an invalid arg. on(InvalidArg), anything()), expr().bind("callExpr")), // or a regular call expression, callExpr( // which optionally has an invalid arg. OptionalInvalidExplicitArg, expr().bind("callExpr")), // or a construct expression, cxxConstructExpr( // which optionally has an invalid arg. OptionalInvalidExplicitArg, expr().bind("constructExpr"))), anyOf( // We want to match the parent function. forFunction(functionDecl().bind("nonCanRunScriptParentFunction")), // ... optionally. anything())), this); }
void DefaultArgumentsCheck::registerMatchers(MatchFinder *Finder) { // Calling a function which uses default arguments is disallowed. Finder->addMatcher(cxxDefaultArgExpr().bind("stmt"), this); // Declaring default parameters is disallowed. Finder->addMatcher(parmVarDecl(hasDefaultArgument()).bind("decl"), this); }