void IncorrectRoundings::registerMatchers(MatchFinder *MatchFinder) {
  // Match a floating literal with value 0.5.
  auto FloatHalf = floatLiteral(floatHalf());

  // Match a floating point expression.
  auto FloatType = expr(hasType(realFloatingPointType()));

  // Match a floating literal of 0.5 or a floating literal of 0.5 implicitly.
  // cast to floating type.
  auto FloatOrCastHalf =
      anyOf(FloatHalf, implicitCastExpr(FloatType, has(FloatHalf)));

  // Match if either the LHS or RHS is a floating literal of 0.5 or a floating
  // literal of 0.5 and the other is of type double or vice versa.
  auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)),
                           allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType)));

  // Find expressions of cast to int of the sum of a floating point expression
  // and 0.5.
  MatchFinder->addMatcher(
      implicitCastExpr(
          hasImplicitDestinationType(isInteger()),
          ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf)))
          .bind("CastExpr"),
      this);
}
void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
  const auto WrongUse = anyOf(
      hasParent(
          binaryOperator(
              anyOf(has(integerLiteral(equals(0))),
                    allOf(anyOf(hasOperatorName("<"), hasOperatorName(">="),
                                hasOperatorName(">"), hasOperatorName("<=")),
                          hasEitherOperand(integerLiteral(equals(1))))))
              .bind("SizeBinaryOp")),
      hasParent(implicitCastExpr(
          hasImplicitDestinationType(isBoolType()),
          anyOf(
              hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
              anything()))),
      hasParent(explicitCastExpr(hasDestinationType(isBoolType()))));

  Finder->addMatcher(
      memberCallExpr(
          on(expr(anyOf(hasType(namedDecl(stlContainer())),
                        hasType(pointsTo(namedDecl(stlContainer()))),
                        hasType(references(namedDecl(stlContainer())))))
                 .bind("STLObject")),
          callee(methodDecl(hasName("size"))), WrongUse).bind("SizeCallExpr"),
      this);
}
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 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 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 ProBoundsArrayToPointerDecayCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  // The only allowed array to pointer decay
  // 1) just before array subscription
  // 2) inside a range-for over an array
  // 3) if it converts a string literal to a pointer
  Finder->addMatcher(
      implicitCastExpr(unless(hasParent(arraySubscriptExpr())),
                       unless(hasParentIgnoringImpCasts(explicitCastExpr())),
                       unless(isInsideOfRangeBeginEndStmt()),
                       unless(hasSourceExpression(stringLiteral())))
          .bind("cast"),
      this);
}
void ContainerSizeEmptyCheck::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 ValidContainer = cxxRecordDecl(isSameOrDerivedFrom(
      namedDecl(
          has(cxxMethodDecl(
                  isConst(), parameterCountIs(0), isPublic(), hasName("size"),
                  returns(qualType(isInteger(), unless(booleanType()))))
                  .bind("size")),
          has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
                            hasName("empty"), returns(booleanType()))
                  .bind("empty")))
          .bind("container")));

  const auto WrongUse = anyOf(
      hasParent(binaryOperator(
                    matchers::isComparisonOperator(),
                    hasEitherOperand(ignoringImpCasts(anyOf(
                        integerLiteral(equals(1)), integerLiteral(equals(0))))))
                    .bind("SizeBinaryOp")),
      hasParent(implicitCastExpr(
          hasImplicitDestinationType(booleanType()),
          anyOf(
              hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
              anything()))),
      hasParent(explicitCastExpr(hasDestinationType(booleanType()))));

  Finder->addMatcher(
      cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
                                      hasType(pointsTo(ValidContainer)),
                                      hasType(references(ValidContainer))))
                               .bind("STLObject")),
                        callee(cxxMethodDecl(hasName("size"))), WrongUse)
          .bind("SizeCallExpr"),
      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);
}
예제 #9
0
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);
}
예제 #10
0
#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");
namespace ProgrammingLanguage {

    //only match implicit cast
    StatementMatcher impCastExprMatcherPL = implicitCastExpr().bind("cast");
}
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);
}