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);
}
Esempio n. 10
0
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);
}
Esempio n. 20
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);
}