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);
}
Exemple #7
0
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);
}