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("std::vector", "llvm::SmallVector", "std::list", "std::deque"))))); // 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("std::shared_ptr", "std::unique_ptr", "std::auto_ptr", "std::weak_ptr")))); // Bitfields binds only to consts and emplace_back take it by universal ref. auto bitFieldAsArgument = hasAnyArgument(ignoringParenImpCasts( memberExpr(hasDeclaration(fieldDecl(matchers::isBitfield()))))); // We could have leak of resource. auto newExprAsArgument = hasAnyArgument(ignoringParenImpCasts(cxxNewExpr())); auto constructingDerived = hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase))); auto hasInitList = has(ignoringParenImpCasts(initListExpr())); auto soughtConstructExpr = cxxConstructExpr( unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument, newExprAsArgument, constructingDerived, has(materializeTemporaryExpr(hasInitList))))) .bind("ctor"); auto hasConstructExpr = has(ignoringParenImpCasts(soughtConstructExpr)); auto ctorAsArgument = materializeTemporaryExpr( anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr)))); Finder->addMatcher( cxxMemberCallExpr(callPushBack, has(ctorAsArgument)).bind("call"), 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 StringReferenceMemberCheck::registerMatchers( ast_matchers::MatchFinder *Finder) { // Look for const references to std::string or ::string. auto String = anyOf(recordDecl(hasName("::std::basic_string")), recordDecl(hasName("::string"))); auto ConstString = qualType(isConstQualified(), hasDeclaration(String)); // Ignore members in template instantiations. auto InTemplateInstantiation = hasAncestor( decl(anyOf(recordDecl(ast_matchers::isTemplateInstantiation()), functionDecl(ast_matchers::isTemplateInstantiation())))); Finder->addMatcher(fieldDecl(hasType(references(ConstString)), unless(InTemplateInstantiation)).bind("member"), this); }
bool QoreTypeInfo::isOutputIdentical(const QoreTypeInfo* typeInfo) const { bool thisnt = (!hasType()); bool typent = (!typeInfo->hasType()); if (thisnt && typent) return true; if (thisnt || typent) return false; // from this point on, we know that both have types and are not NULL if ((returns_mult && !typeInfo->returns_mult) || (!returns_mult && typeInfo->returns_mult)) { //printd(5, "QoreTypeInfo::isOutputIdentical() lrm: %d rrm: %d\n", returns_mult, typeInfo->returns_mult); return false; } // from here on, we know either both accept single types or both accept multiple types if (!returns_mult) return isTypeIdenticalIntern(typeInfo); const type_vec_t &my_rt = getReturnTypeList(); const type_vec_t &their_rt = typeInfo->getReturnTypeList(); if (my_rt.size() != their_rt.size()) { //printd(5, "QoreTypeInfo::isOutputIdentical() lrts: %d rrts: %d\n", my_rt.size(), their_rt.size()); return false; } // check all types to see if there is an identical type // FIXME: this is not very efficient (also only works properly if all types are unique in the list, which they should be) for (type_vec_t::const_iterator i = my_rt.begin(), e = my_rt.end(); i != e; ++i) { bool ident = false; for (type_vec_t::const_iterator j = their_rt.begin(), je = their_rt.end(); j != je; ++j) { if ((*i)->isOutputIdentical(*j)) { ident = true; break; } } if (!ident) { //printd(5, "QoreTypeInfo::isOutputIdentical() cannot find match for %s in rhs\n", (*i)->getName()); return false; } } return true; }
bool QoreTypeInfo::isInputIdentical(const QoreTypeInfo* typeInfo) const { bool thisnt = (!hasType()); bool typent = (!typeInfo->hasType()); if (thisnt && typent) return true; if (thisnt || typent) return false; // from this point on, we know that both have types and are not NULL if ((accepts_mult && !typeInfo->accepts_mult) || (!accepts_mult && typeInfo->accepts_mult)) return false; // from here on, we know either both accept single types or both accept multiple types if (!accepts_mult) return isTypeIdenticalIntern(typeInfo); const type_vec_t &my_at = getAcceptTypeList(); const type_vec_t &their_at = typeInfo->getAcceptTypeList(); if (my_at.size() != their_at.size()) return false; // check all types to see if there is an identical type for (type_vec_t::const_iterator i = my_at.begin(), e = my_at.end(); i != e; ++i) { bool ident = false; for (type_vec_t::const_iterator j = their_at.begin(), je = their_at.end(); j != je; ++j) { //printd(5, "QoreTypeInfo::isInputIdentical() this=%p i=%p %s j=%p %s\n", this, *i, (*i)->getName(), *j, (*j)->getName()); // if the second type is the original type, skip it if (*j == this) continue; if ((*i) == (*j) || (*i)->isInputIdentical(*j)) { ident = true; break; } } if (!ident) return false; } return true; }
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 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 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 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 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 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); }
void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) { auto Construct = cxxConstructExpr( hasDeclaration(cxxConstructorDecl(hasParent( cxxRecordDecl(unless(isTriviallyDefaultConstructible())))))) .bind("construct"); Finder->addMatcher( cxxConstructorDecl( unless(isDelegatingConstructor()), ofClass(unless( anyOf(isUnion(), ast_matchers::isTemplateInstantiation()))), forEachConstructorInitializer( cxxCtorInitializer(isWritten(), withInitializer(ignoringImplicit(Construct)), unless(forField(hasType(isConstQualified())))) .bind("init"))), this); }
int QoreTypeInfo::acceptInputDefault(bool& priv_error, QoreValue& n) const { //printd(5, "QoreTypeInfo::acceptInputDefault() this=%p hasType=%d (%s) n=%p (%s)\n", this, hasType(), getName(), n, get_type_name(n)); if (!hasType()) return 0; if (!accepts_mult) return runtimeAcceptInputIntern(priv_error, n); const type_vec_t &at = getAcceptTypeList(); // check all types until one accepts the input // priv_error can be set to false more than once; this is OK for error reporting for (type_vec_t::const_iterator i = at.begin(), e = at.end(); i != e; ++i) { assert((*i)->acceptsSingle()); if (!(*i)->runtimeAcceptInputIntern(priv_error, n)) return 0; } return runtimeAcceptInputIntern(priv_error, n); }
bool NumericRepresentationSetter::canUse(const QString &id) const { try { bool ok; quint64 objid = id.toULongLong(&ok); if ( !ok) return false; Resource resource = mastercatalog()->id2Resource(objid); if ( !resource.isValid()) return false; if ( resource.ilwisType() != itREPRESENTATION) return false; IRepresentation representation(resource); return hasType(representation->domain()->ilwisType(), itNUMERICDOMAIN); } catch(const ErrorObject& err){ } return false; }
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 FasterStringFindCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; const auto SingleChar = expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal"))); const auto StringFindFunctions = hasAnyName("find", "rfind", "find_first_of", "find_first_not_of", "find_last_of", "find_last_not_of"); Finder->addMatcher( cxxMemberCallExpr( callee(functionDecl(StringFindFunctions).bind("func")), anyOf(argumentCountIs(1), argumentCountIs(2)), hasArgument(0, SingleChar), on(expr( hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( recordDecl(hasAnyName(SmallVector<StringRef, 4>( StringLikeClasses.begin(), StringLikeClasses.end()))))))), unless(hasSubstitutedType())))), this); }
// if the argument's return type is compatible with "this"'s return type bool QoreTypeInfo::isOutputCompatible(const QoreTypeInfo* typeInfo) const { if (!hasType()) return true; if (!typeInfo->hasType()) return false; // from this point on, we know that both have types and are not NULL if (!typeInfo->returns_mult) { //printd(5, "QoreTypeInfo::isOutputCompatible(%p '%s') this: %p '%s' (qc: %p qt: %d) ti->qc: %p ti->qt: %d\n", typeInfo, typeInfo->getName(), this, getName(), qc, qt, typeInfo->qc, typeInfo->qt); return typeInfo->qc ? parseReturnsClass(typeInfo->qc) : parseReturnsType(typeInfo->qt); } const type_vec_t &their_rt = typeInfo->getReturnTypeList(); for (type_vec_t::const_iterator j = their_rt.begin(), je = their_rt.end(); j != je; ++j) { if (!isOutputCompatible(*j)) return false; } return true; }
void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) { const auto SignedIntegerOperand = expr(ignoringImpCasts(hasType(isSignedInteger()))).bind("signed-operand"); // The standard [bitmask.types] allows some integral types to be implemented // as signed types. Exclude these types from diagnosing for bitwise or(|) and // bitwise and(&). Shifting and complementing such values is still not // allowed. const auto BitmaskType = namedDecl(anyOf( hasName("::std::locale::category"), hasName("::std::ctype_base::mask"), hasName("::std::ios_base::fmtflags"), hasName("::std::ios_base::iostate"), hasName("::std::ios_base::openmode"))); const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType))); // Match binary bitwise operations on signed integer arguments. Finder->addMatcher( binaryOperator( allOf(anyOf(hasOperatorName("^"), hasOperatorName("|"), hasOperatorName("&"), hasOperatorName("^="), hasOperatorName("|="), hasOperatorName("&=")), unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))), hasEitherOperand(SignedIntegerOperand), hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger())))) .bind("binary-no-sign-interference"), this); // Shifting and complement is not allowed for any signed integer type because // the sign bit may corrupt the result. Finder->addMatcher( binaryOperator( allOf(anyOf(hasOperatorName("<<"), hasOperatorName(">>"), hasOperatorName("<<="), hasOperatorName(">>=")), hasEitherOperand(SignedIntegerOperand), hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger())))) .bind("binary-sign-interference"), this); // Match unary operations on signed integer types. Finder->addMatcher(unaryOperator(allOf(hasOperatorName("~"), hasUnaryOperand(SignedIntegerOperand))) .bind("unary-signed"), this); }
void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) { const auto Calc = expr(anyOf(binaryOperator( anyOf(hasOperatorName("+"), hasOperatorName("-"), hasOperatorName("*"), hasOperatorName("<<"))), unaryOperator(hasOperatorName("~"))), hasType(isInteger())) .bind("Calc"); const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()), has(ignoringParenImpCasts(Calc))); const auto ImplicitCast = implicitCastExpr(hasImplicitDestinationType(isInteger()), has(ignoringParenImpCasts(Calc))); const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast"); Finder->addMatcher(varDecl(hasInitializer(Cast)), this); Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this); Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this); Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this); Finder->addMatcher( binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)), this); }
void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; // For the arithmetic calls, we match only the uses of the templated operators // where the template parameter is not a built-in type. This means the // instantiation makes use of an available user defined conversion to // `int64_t`. // // The implementation of these templates will be updated to fail SFINAE for // non-integral types. We match them to suggest an explicit cast. // Match expressions like `a *= b` and `a /= b` where `a` has type // `absl::Duration` and `b` is not of a built-in type. Finder->addMatcher( cxxOperatorCallExpr( argumentCountIs(2), hasArgument( 0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))), hasArgument(1, expr().bind("arg")), callee(functionDecl( hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasAnyName("operator*=", "operator/=")))), this); // Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a` // has type `absl::Duration` and `b` is not of a built-in type. Finder->addMatcher( cxxMemberCallExpr( callee(cxxMethodDecl( ofClass(cxxRecordDecl(hasName("::absl::Duration"))), hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasAnyName("operator*=", "operator/="))), argumentCountIs(1), hasArgument(0, expr().bind("arg"))), this); // Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and // `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a // built-in type. Finder->addMatcher( callExpr(callee(functionDecl( hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasAnyName("::absl::operator*", "::absl::operator/"))), argumentCountIs(2), hasArgument(0, expr(hasType( cxxRecordDecl(hasName("::absl::Duration"))))), hasArgument(1, expr().bind("arg"))), this); // Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a // built-in type and `b` has type `absl::Duration`. Finder->addMatcher( callExpr(callee(functionDecl( hasParent(functionTemplateDecl()), unless(hasTemplateArgument(0, refersToType(builtinType()))), hasName("::absl::operator*"))), argumentCountIs(2), hasArgument(0, expr().bind("arg")), hasArgument(1, expr(hasType(cxxRecordDecl( hasName("::absl::Duration")))))), this); // For the factory functions, we match only the non-templated overloads that // take an `int64_t` parameter. Within these calls, we care about implicit // casts through a user defined conversion to `int64_t`. // // The factory functions will be updated to be templated and SFINAE on whether // the template parameter is an integral type. This complements the already // existing templated overloads that only accept floating point types. // Match calls like: // `absl::Nanoseconds(x)` // `absl::Microseconds(x)` // `absl::Milliseconds(x)` // `absl::Seconds(x)` // `absl::Minutes(x)` // `absl::Hours(x)` // where `x` is not of a built-in type. Finder->addMatcher( implicitCastExpr( anyOf(hasCastKind(CK_UserDefinedConversion), has(implicitCastExpr(hasCastKind(CK_UserDefinedConversion)))), hasParent(callExpr( callee(functionDecl(DurationFactoryFunction(), unless(hasParent(functionTemplateDecl())))), hasArgument(0, expr().bind("arg"))))), this); }
void SuspiciousStringCompareCheck::registerMatchers(MatchFinder *Finder) { // Match relational operators. const auto ComparisonUnaryOperator = unaryOperator(hasOperatorName("!")); const auto ComparisonBinaryOperator = binaryOperator(matchers::isComparisonOperator()); const auto ComparisonOperator = expr(anyOf(ComparisonUnaryOperator, ComparisonBinaryOperator)); // Add the list of known string compare-like functions and add user-defined // functions. std::vector<std::string> FunctionNames = utils::options::parseStringList( (llvm::Twine(KnownStringCompareFunctions) + StringCompareLikeFunctions) .str()); // Match a call to a string compare functions. const auto FunctionCompareDecl = functionDecl(hasAnyName(std::vector<StringRef>(FunctionNames.begin(), FunctionNames.end()))) .bind("decl"); const auto DirectStringCompareCallExpr = callExpr(hasDeclaration(FunctionCompareDecl)).bind("call"); const auto MacroStringCompareCallExpr = conditionalOperator(anyOf( hasTrueExpression(ignoringParenImpCasts(DirectStringCompareCallExpr)), hasFalseExpression(ignoringParenImpCasts(DirectStringCompareCallExpr)))); // The implicit cast is not present in C. const auto StringCompareCallExpr = ignoringParenImpCasts( anyOf(DirectStringCompareCallExpr, MacroStringCompareCallExpr)); if (WarnOnImplicitComparison) { // Detect suspicious calls to string compare: // 'if (strcmp())' -> 'if (strcmp() != 0)' Finder->addMatcher( stmt(anyOf(ifStmt(hasCondition(StringCompareCallExpr)), whileStmt(hasCondition(StringCompareCallExpr)), doStmt(hasCondition(StringCompareCallExpr)), forStmt(hasCondition(StringCompareCallExpr)), binaryOperator( anyOf(hasOperatorName("&&"), hasOperatorName("||")), hasEitherOperand(StringCompareCallExpr)))) .bind("missing-comparison"), this); } if (WarnOnLogicalNotComparison) { // Detect suspicious calls to string compared with '!' operator: // 'if (!strcmp())' -> 'if (strcmp() == 0)' Finder->addMatcher(unaryOperator(hasOperatorName("!"), hasUnaryOperand(ignoringParenImpCasts( StringCompareCallExpr))) .bind("logical-not-comparison"), this); } // Detect suspicious cast to an inconsistant type (i.e. not integer type). Finder->addMatcher( implicitCastExpr(unless(hasType(isInteger())), hasSourceExpression(StringCompareCallExpr)) .bind("invalid-conversion"), this); // Detect suspicious operator with string compare function as operand. Finder->addMatcher( binaryOperator( unless(anyOf(matchers::isComparisonOperator(), hasOperatorName("&&"), hasOperatorName("||"), hasOperatorName("="))), hasEitherOperand(StringCompareCallExpr)) .bind("suspicious-operator"), this); // Detect comparison to invalid constant: 'strcmp() == -1'. const auto InvalidLiteral = ignoringParenImpCasts( anyOf(integerLiteral(unless(equals(0))), unaryOperator( hasOperatorName("-"), has(ignoringParenImpCasts(integerLiteral(unless(equals(0)))))), characterLiteral(), cxxBoolLiteral())); Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(StringCompareCallExpr), hasEitherOperand(InvalidLiteral)) .bind("invalid-comparison"), 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); }
#include "ASTUtility.h" //StatementMatcher callExprMatcher = callExpr().bind("callexpr"); StatementMatcher mallocMatcher = callExpr(callee(functionDecl(hasName("malloc")).bind("m"))).bind("mallocCall"); StatementMatcher freeMatcher = callExpr(callee(functionDecl(hasName("free")).bind("f"))).bind("freeCall"); StatementMatcher reallocMatcher = callExpr(callee(functionDecl(hasName("realloc")).bind("r"))).bind("reallocCall"); //memcpy arrayType non-builtin = pointer or user-defined StatementMatcher memcpyAryMatcher = callExpr( callee(functionDecl(hasName("memcpy")).bind("cpy")), hasAnyArgument(ignoringImpCasts(declRefExpr( to(declaratorDecl(hasType(arrayType( unless(hasElementType(builtinType()))).bind("cpyParm"))))))) ).bind("memcpyCall"); StatementMatcher memcpyPtrMatcher = callExpr( callee(functionDecl(hasName("memcpy")).bind("cpy")), hasAnyArgument(ignoringImpCasts(declRefExpr( to(declaratorDecl(hasType(pointerType( pointee(unless(builtinType()))).bind("cpyParmPtr"))))))) ).bind("memcpyCall"); StatementMatcher memcmpAryMatcher = callExpr( callee(functionDecl(hasName("memcmp")).bind("cmp")), hasAnyArgument(ignoringImpCasts(declRefExpr( to(declaratorDecl(hasType(arrayType( unless(hasElementType(builtinType()))).bind("cmpParm"))))))) ).bind("memcmpCall"); StatementMatcher memcmpPtrMatcher = callExpr( callee(functionDecl(hasName("memcmp")).bind("cmp")), hasAnyArgument(ignoringImpCasts(declRefExpr(
bool NumericRepresentationSetter::canUse(const IIlwisObject &obj, const ColumnDefinition &coldef) const { if ( coldef.isValid()) return hasType(coldef.datadef().domain()->ilwisType(), itNUMERICDOMAIN); return false; }
void CloexecDupCheck::registerMatchers(MatchFinder *Finder) { registerMatchersImpl(Finder, functionDecl(returns(isInteger()), hasName("dup"), hasParameter(0, hasType(isInteger())))); }
#include "ASTUtility.h" // int *x = NULL or int *x = 0 DeclarationMatcher nullPointerMatcher = varDecl(hasType(pointerType()), hasInitializer(implicitCastExpr().bind("cast"))).bind("var"); // x == NULL or x == 0 StatementMatcher biOpMatcher1 = binaryOperator(hasRHS(implicitCastExpr().bind("castR1")), hasOperatorName("==")).bind("bo1"); // x != NULL or x != 0 StatementMatcher biOpMatcher2 = binaryOperator(hasRHS(implicitCastExpr().bind("castR2")), hasOperatorName("!=")).bind("bo2"); // x != NULL or x != 0 StatementMatcher biOpMatcher3 = binaryOperator(hasRHS(implicitCastExpr().bind("castR3")), hasOperatorName("=")).bind("bo3"); class NullPointerPrinter : public MatchFinder::MatchCallback { public: virtual void run(const MatchFinder::MatchResult &Result) { //get the node clang::ASTContext *Context = Result.Context; const clang::ImplicitCastExpr *cast = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("cast"); const clang::ImplicitCastExpr *castR1 = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("castR1"); const clang::ImplicitCastExpr *castR2 = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("castR2"); const clang::ImplicitCastExpr *castR3 = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("castR3"); const clang::BinaryOperator *bo1 = Result.Nodes.getNodeAs<clang::BinaryOperator>("bo1"); const clang::BinaryOperator *bo2 = Result.Nodes.getNodeAs<clang::BinaryOperator>("bo2"); const clang::BinaryOperator *bo3 = Result.Nodes.getNodeAs<clang::BinaryOperator>("bo3");
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 DanglingOnTemporaryChecker::registerMatchers(MatchFinder *AstMatcher) { //////////////////////////////////////// // Quick annotation conflict checkers // //////////////////////////////////////// AstMatcher->addMatcher( // This is a matcher on a method declaration, cxxMethodDecl( // which is marked as no dangling on temporaries, noDanglingOnTemporaries(), // and which is && ref-qualified. isRValueRefQualified(), decl().bind("invalidMethodRefQualified")), this); AstMatcher->addMatcher( // This is a matcher on a method declaration, cxxMethodDecl( // which is marked as no dangling on temporaries, noDanglingOnTemporaries(), // which returns a primitive type, returns(builtinType()), // and which doesn't return a pointer. unless(returns(pointerType())), decl().bind("invalidMethodPointer")), this); ////////////////// // Main checker // ////////////////// auto hasParentCall = hasParent(expr(anyOf( cxxOperatorCallExpr( // If we're in a lamda, we may have an operator call expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // constructor call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentOperatorCallExpr")), callExpr( // If we're in a lamda, we may have a call expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // function call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentCallExpr")), objcMessageExpr( // If we're in a lamda, we may have an objc message expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // function call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentObjCMessageExpr")), cxxConstructExpr( // If we're in a lamda, we may have a construct expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // constructor call. unless(has(expr(ignoreTrivials(lambdaExpr())))), expr().bind("parentConstructExpr"))))); AstMatcher->addMatcher( // This is a matcher on a method call, cxxMemberCallExpr( // which is in first party code, isFirstParty(), // and which is performed on a temporary, on(allOf( unless(hasType(pointerType())), isTemporary(), // but which is not `this`. unless(cxxThisExpr()))), // and which is marked as no dangling on temporaries. callee(cxxMethodDecl(noDanglingOnTemporaries())), expr().bind("memberCallExpr"), // We optionally match a parent call expression or a parent construct // expression because using a temporary inside a call is fine as long // as the pointer doesn't escape the function call. anyOf( // This is the case where the call is the direct parent, so we // know that the member call expression is the argument. allOf(hasParentCall, expr().bind("parentCallArg")), // This is the case where the call is not the direct parent, so we // get its child to know in which argument tree we are. hasAncestor(expr( hasParentCall, expr().bind("parentCallArg"))), // To make it optional. anything())), this); }
bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &attributes) { // add to elements stack: m_elementStack.append(new ElementData(qName.utf8())); // First we need a node. if (DBUS("node")) { CONDITION(!m_currentNode.isEmpty(), "Node inside a node."); const int idx(indexOf(attributes, "name")); COND_DOC_ERROR(idx < 0, QCString("Anonymous node found.")); m_currentNode = attributes.value(idx).utf8(); // A node is actually of little interest, so do nothing here. return true; } // Then we need an interface. if (DBUS("interface")) { // We need a nodeName for interfaces: CONDITION(m_currentNode.isEmpty(), "Interface without a node."); CONDITION(m_currentInterface, "Interface within another interface."); const int idx(indexOf(attributes, "name")); COND_DOC_ERROR(idx < 0, QString("Interface without a name found.")); // A interface is roughly equivalent to a class: m_currentInterface = createEntry(); m_currentInterface->section = Entry::CLASS_SEC; m_currentInterface->spec |= Entry::Interface; m_currentInterface->type = "Interface"; m_currentInterface->name = substitute(attributes.value(idx).utf8(), ".", "::"); openScopes(m_currentInterface); return true; } if (DBUS("method") || DBUS("signal")) { // We need a interfaceName for methods and signals: CONDITION(!m_currentInterface, "Method or signal found outside a interface."); CONDITION(m_currentMethod, "Method or signal found inside another method or signal."); CONDITION(m_currentProperty, "Methor or signal found inside a property."); CONDITION(!m_structStack.isEmpty(), "Method or signal found inside a struct."); CONDITION(m_currentEnum, "Methor or signal found inside a enum."); const int idx(indexOf(attributes, "name")); COND_DOC_ERROR(idx < 0, QString("Method or signal without a name found.")); m_currentMethod = createEntry(); m_currentMethod->section = Entry::FUNCTION_SEC; m_currentMethod->name = attributes.value(idx).utf8(); m_currentMethod->mtype = Method; m_currentMethod->type = "void"; if (DBUS("signal")) { m_currentMethod->mtype = Signal; } } if (DBUS("arg")) { // We need a method for arguments: CONDITION(!m_currentMethod, "Argument found outside a method or signal."); CONDITION(m_currentArgument, "Argument found inside another argument."); const int name_idx(indexOf(attributes, "name")); COND_DOC_ERROR(name_idx < 0, QString("Argument without a name found.")); COND_DOC_ERROR(!hasType(attributes), QString("Argument without a type found.")); const int direction_idx(indexOf(attributes, "direction")); if ((m_currentMethod->mtype == Signal && direction_idx >= 0 && attributes.value(direction_idx) != "in") || (m_currentMethod->mtype == Method && direction_idx >= 0 && attributes.value(direction_idx) != "in" && attributes.value(direction_idx) != "out")) { m_errorString = "Invalid direction found."; return false; } m_currentArgument = new Argument; m_currentArgument->type = getType(attributes).utf8(); m_currentArgument->name = attributes.value(name_idx).utf8(); if (direction_idx >= 0) { m_currentArgument->attrib = attributes.value(direction_idx).utf8(); } else { if (m_currentMethod->mtype == Signal) { m_currentArgument->attrib = "in"; } else { m_currentArgument->attrib = "out"; } } } if (DBUS("property")) { CONDITION(m_currentMethod, "Property found inside a method or signal."); CONDITION(!m_currentInterface, "Property found outside an interface."); CONDITION(m_currentProperty, "Property found inside another property."); CONDITION(!m_structStack.isEmpty(), "Property found inside a struct."); CONDITION(m_currentEnum, "Property found inside a enum."); const int name_idx(indexOf(attributes, "name")); COND_DOC_ERROR(name_idx < 0, QString("Anonymous property found.")); COND_DOC_ERROR(!hasType(attributes), QString("Property without a type found.")); const int access_idx(indexOf(attributes, "access")); COND_DOC_ERROR(access_idx < 0, QString("Property without a access attribute found.")); COND_DOC_ERROR(attributes.value(access_idx) != "read" && attributes.value(access_idx) != "write" && attributes.value(access_idx) != "readwrite", QString("Property with invalid access attribute \"%1\" found."). arg(attributes.value(access_idx))); m_currentProperty = createEntry(); m_currentProperty->section = Entry::FUNCTION_SEC; if (attributes.value(access_idx) == "read" || attributes.value(access_idx) == "readwrite") { m_currentProperty->spec |= Entry::Readable; } if (attributes.value(access_idx) == "write" || attributes.value(access_idx) == "readwrite") { m_currentProperty->spec |= Entry::Writable; } m_currentProperty->name = attributes.value(name_idx).utf8(); m_currentProperty->mtype = Property; m_currentProperty->type = getType(attributes).utf8(); } if (EXTENSION("namespace")) { CONDITION(m_currentNode.isEmpty(), "Namespace found outside a node."); CONDITION(m_currentInterface, "Namespace found inside an interface."); const int idx(indexOf(attributes, "name")); COND_DOC_ERROR(idx < 0, QString("Anonymous namespace found.")); m_namespaceStack.append(openNamespace(attributes.value(idx))); openScopes(m_namespaceStack.last()); } if (EXTENSION("struct")) { CONDITION(m_currentMethod, "Struct found inside a method or signal."); CONDITION(m_currentProperty, "Struct found inside a property."); CONDITION(m_currentEnum, "Struct found inside an enum."); const int idx(indexOf(attributes, "name")); COND_DOC_ERROR(idx < 0, QString("Anonymous struct found.")); Entry * current_struct = createEntry(); current_struct->section = Entry::CLASS_SEC; current_struct->spec = Entry::Struct; current_struct->name = attributes.value(idx).utf8(); openScopes(current_struct); current_struct->type = current_struct->name + " struct"; m_structStack.append(new StructData(current_struct)); } if (EXTENSION("member")) { CONDITION(m_structStack.isEmpty(), "Member found outside of struct."); const int name_idx(indexOf(attributes, "name")); COND_DOC_ERROR(name_idx < 0, QString("Anonymous member found.")); COND_DOC_ERROR(!hasType(attributes), QString("Member without a type found.")); createEntry(); m_currentEntry->section = Entry::VARIABLE_SEC; m_currentEntry->name = attributes.value(name_idx).utf8(); m_currentEntry->type = getType(attributes).utf8(); QString type(getDBusType(m_currentEntry->type)); m_structStack.last()->type.append(type.utf8()); } if (EXTENSION("enum") || EXTENSION("flagset")) { CONDITION(m_currentMethod, "Enum found inside a method or signal."); CONDITION(m_currentProperty, "Enum found inside a property."); const int name_idx(indexOf(attributes, "name")); COND_DOC_ERROR(name_idx < 0, QString("Anonymous enum found.")); const int type_idx(indexOf(attributes, "type")); QString type = "u"; if (type_idx >= 0) { type = attributes.value(type_idx); } if (type != "y" && type != "q" && type != "u" && type != "t") { DOC_ERROR(QString("Invalid enum type \"%1\" found.").arg(type)); } m_currentEnum = createEntry(); m_currentEnum->section = Entry::ENUM_SEC; m_currentEnum->name = attributes.value(name_idx).utf8(); openScopes(m_currentEnum); m_currentEnum->type = m_currentEntry->name + " enum"; addNamedType(type.utf8()); } if (EXTENSION("value")) { CONDITION(!m_currentEnum, "Value found outside an enum."); const int name_idx(indexOf(attributes, "name")); COND_DOC_ERROR(name_idx < 0, QString("Anonymous value found.")); const int value_idx(indexOf(attributes, "value")); createEntry(); m_currentEntry->section = Entry::VARIABLE_SEC; m_currentEntry->name = attributes.value(name_idx).utf8(); m_currentEntry->type = m_currentEnum->name; // "@"; // enum marker! if (value_idx >= 0) { m_currentEntry->initializer = attributes.value(value_idx).utf8(); } } return true; }