diff --git a/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.cpp b/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.cpp --- a/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.cpp +++ b/clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.cpp @@ -114,7 +114,7 @@ hasLHS(TimeInverseMatcher)) .bind("binop"))) .bind("outer_call"); - Finder->addMatcher(CallMatcher, this); + Finder->addMatcher(traverse(TK_AsIs, CallMatcher), this); // Match cases where we know the second operand is a 'Time'. Since // subtracting a 'Time' from a 'Duration' is not defined, in these cases, @@ -122,7 +122,7 @@ auto OperandMatcher = binaryOperator(hasOperatorName("-"), hasRHS(TimeInverseMatcher)) .bind("binop"); - Finder->addMatcher(OperandMatcher, this); + Finder->addMatcher(traverse(TK_AsIs, OperandMatcher), this); } } diff --git a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp --- a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp +++ b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp @@ -29,57 +29,66 @@ // 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/=")))) - .bind("OuterExpr"), + traverse( + ast_type_traits::TK_AsIs, + 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/=")))) + .bind("OuterExpr")), 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"))) - .bind("OuterExpr"), + traverse( + ast_type_traits::TK_AsIs, + 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"))) + .bind("OuterExpr")), 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"))) - .bind("OuterExpr"), + traverse( + ast_type_traits::TK_AsIs, + 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"))) + .bind("OuterExpr")), 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")))))) - .bind("OuterExpr"), + traverse( + ast_type_traits::TK_AsIs, + 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")))))) + .bind("OuterExpr")), this); // For the factory functions, we match only the non-templated overloads that diff --git a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp @@ -97,9 +97,10 @@ // the sizeof size_t. if (WarnOnSizeOfConstant) { Finder->addMatcher( - expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))), - unless(SizeOfZero)) - .bind("sizeof-constant"), + traverse(TK_AsIs, + expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))), + unless(SizeOfZero)) + .bind("sizeof-constant")), this); } @@ -186,14 +187,15 @@ const auto DenomType = qualType(hasCanonicalType(type().bind("denom-type"))); Finder->addMatcher( - binaryOperator(hasOperatorName("/"), - hasLHS(expr(ignoringParenImpCasts( - anyOf(sizeOfExpr(has(NumType)), - sizeOfExpr(has(expr(hasType(NumType)))))))), - hasRHS(expr(ignoringParenImpCasts( - anyOf(sizeOfExpr(has(DenomType)), - sizeOfExpr(has(expr(hasType(DenomType))))))))) - .bind("sizeof-divide-expr"), + traverse(TK_AsIs, + binaryOperator(hasOperatorName("/"), + hasLHS(expr(ignoringParenImpCasts(anyOf( + sizeOfExpr(has(NumType)), + sizeOfExpr(has(expr(hasType(NumType)))))))), + hasRHS(expr(ignoringParenImpCasts(anyOf( + sizeOfExpr(has(DenomType)), + sizeOfExpr(has(expr(hasType(DenomType))))))))) + .bind("sizeof-divide-expr")), this); // Detect expression like: sizeof(...) * sizeof(...)); most likely an error. diff --git a/clang-tools-extra/clang-tidy/bugprone/TooSmallLoopVariableCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TooSmallLoopVariableCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/TooSmallLoopVariableCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/TooSmallLoopVariableCheck.cpp @@ -79,20 +79,22 @@ expr(ignoringParenImpCasts(hasType(isInteger()))).bind(LoopIncrementName); Finder->addMatcher( - forStmt( - hasCondition(anyOf( - binaryOperator(hasOperatorName("<"), - hasLHS(LoopVarConversionMatcher), - hasRHS(LoopBoundMatcher)), - binaryOperator(hasOperatorName("<="), - hasLHS(LoopVarConversionMatcher), - hasRHS(LoopBoundMatcher)), - binaryOperator(hasOperatorName(">"), hasLHS(LoopBoundMatcher), - hasRHS(LoopVarConversionMatcher)), - binaryOperator(hasOperatorName(">="), hasLHS(LoopBoundMatcher), - hasRHS(LoopVarConversionMatcher)))), - hasIncrement(IncrementMatcher)) - .bind(LoopName), + traverse(TK_AsIs, + forStmt(hasCondition(anyOf( + binaryOperator(hasOperatorName("<"), + hasLHS(LoopVarConversionMatcher), + hasRHS(LoopBoundMatcher)), + binaryOperator(hasOperatorName("<="), + hasLHS(LoopVarConversionMatcher), + hasRHS(LoopBoundMatcher)), + binaryOperator(hasOperatorName(">"), + hasLHS(LoopBoundMatcher), + hasRHS(LoopVarConversionMatcher)), + binaryOperator(hasOperatorName(">="), + hasLHS(LoopBoundMatcher), + hasRHS(LoopVarConversionMatcher)))), + hasIncrement(IncrementMatcher)) + .bind(LoopName)), this); } diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp @@ -30,18 +30,20 @@ // Check whether destination object is not TriviallyCopyable. // Applicable to all three memory manipulation functions. - Finder->addMatcher(callExpr(callee(functionDecl(hasAnyName( - "::memset", "::memcpy", "::memmove"))), - hasArgument(0, NotTriviallyCopyableObject)) - .bind("dest"), - this); + Finder->addMatcher( + traverse(TK_AsIs, callExpr(callee(functionDecl(hasAnyName( + "::memset", "::memcpy", "::memmove"))), + hasArgument(0, NotTriviallyCopyableObject)) + .bind("dest")), + this); // Check whether source object is not TriviallyCopyable. // Only applicable to memcpy() and memmove(). Finder->addMatcher( - callExpr(callee(functionDecl(hasAnyName("::memcpy", "::memmove"))), - hasArgument(1, NotTriviallyCopyableObject)) - .bind("src"), + traverse(TK_AsIs, callExpr(callee(functionDecl( + hasAnyName("::memcpy", "::memmove"))), + hasArgument(1, NotTriviallyCopyableObject)) + .bind("src")), this); } diff --git a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp @@ -216,11 +216,13 @@ void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( - cxxMethodDecl( - unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(), - cxxDestructorDecl(), cxxConversionDecl(), isStatic(), - isOverloadedOperator()))) - .bind("method"), + traverse( + TK_AsIs, + cxxMethodDecl( + unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(), + cxxDestructorDecl(), cxxConversionDecl(), isStatic(), + isOverloadedOperator()))) + .bind("method")), this); } diff --git a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp --- a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp @@ -168,10 +168,11 @@ .bind("class"))))) .bind("method"); - Finder->addMatcher(expr(anyOf(callExpr(callee(Methods)).bind("call"), - declRefExpr(to(Methods)).bind("ref")), - LocationFilter), - this); + Finder->addMatcher( + traverse(TK_AsIs, expr(anyOf(callExpr(callee(Methods)).bind("call"), + declRefExpr(to(Methods)).bind("ref")), + LocationFilter)), + this); Finder->addMatcher( usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(Methods)), LocationFilter) diff --git a/clang-tools-extra/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp b/clang-tools-extra/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp --- a/clang-tools-extra/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp +++ b/clang-tools-extra/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp @@ -18,23 +18,25 @@ void ExceptionBaseclassCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( - cxxThrowExpr( - unless(has(expr(anyOf(isTypeDependent(), isValueDependent())))), - // The thrown value is not derived from 'std::exception'. - has(expr(unless( - hasType(qualType(hasCanonicalType(hasDeclaration(cxxRecordDecl( - isSameOrDerivedFrom(hasName("::std::exception")))))))))), - // This condition is always true, but will bind to the - // template value if the thrown type is templated. - anyOf(has(expr( - hasType(substTemplateTypeParmType().bind("templ_type")))), - anything()), - // Bind to the declaration of the type of the value that - // is thrown. 'anything()' is necessary to always succeed - // in the 'eachOf' because builtin types are not - // 'namedDecl'. - eachOf(has(expr(hasType(namedDecl().bind("decl")))), anything())) - .bind("bad_throw"), + traverse( + TK_AsIs, + cxxThrowExpr( + unless(has(expr(anyOf(isTypeDependent(), isValueDependent())))), + // The thrown value is not derived from 'std::exception'. + has(expr(unless(hasType( + qualType(hasCanonicalType(hasDeclaration(cxxRecordDecl( + isSameOrDerivedFrom(hasName("::std::exception")))))))))), + // This condition is always true, but will bind to the + // template value if the thrown type is templated. + anyOf(has(expr(hasType( + substTemplateTypeParmType().bind("templ_type")))), + anything()), + // Bind to the declaration of the type of the value that + // is thrown. 'anything()' is necessary to always succeed + // in the 'eachOf' because builtin types are not + // 'namedDecl'. + eachOf(has(expr(hasType(namedDecl().bind("decl")))), anything())) + .bind("bad_throw")), this); } diff --git a/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp @@ -34,7 +34,8 @@ auto IoStateType = qualType(hasDeclaration(IoStateDecl), unless(elaboratedType())); - Finder->addMatcher(typeLoc(loc(IoStateType)).bind("TypeLoc"), this); + Finder->addMatcher( + traverse(TK_AsIs, typeLoc(loc(IoStateType)).bind("TypeLoc")), this); } void DeprecatedIosBaseAliasesCheck::check( diff --git a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -114,6 +114,8 @@ // pointer, 'make_smart_ptr' refers to 'std::make_shared' or // 'std::make_unique' or other function that creates smart_ptr. + TraversalKindScope RAII(*Result.Context, TK_AsIs); + SourceManager &SM = *Result.SourceManager; const auto *Construct = Result.Nodes.getNodeAs(ConstructorCall); diff --git a/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp @@ -95,19 +95,23 @@ // // std::auto_ptr fn(std::auto_ptr); // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~ - Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType, - // Skip elaboratedType() as the named - // type will match soon thereafter. - unless(elaboratedType())))) - .bind(AutoPtrTokenId), - this); + Finder->addMatcher( + traverse(TK_AsIs, + typeLoc(loc(qualType(AutoPtrType, + // Skip elaboratedType() as the named + // type will match soon thereafter. + unless(elaboratedType())))) + .bind(AutoPtrTokenId)), + this); // using std::auto_ptr; // ^~~~~~~~~~~~~~~~~~~ - Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(namedDecl( - hasName("auto_ptr"), isFromStdNamespace())))) - .bind(AutoPtrTokenId), - this); + Finder->addMatcher( + traverse(TK_AsIs, + usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(namedDecl( + hasName("auto_ptr"), isFromStdNamespace())))) + .bind(AutoPtrTokenId)), + this); // Find ownership transfers via copy construction and assignment. // AutoPtrOwnershipTransferId is bound to the part that has to be wrapped @@ -119,9 +123,10 @@ expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId); Finder->addMatcher( - cxxOperatorCallExpr(hasOverloadedOperatorName("="), - callee(cxxMethodDecl(ofClass(AutoPtrDecl))), - hasArgument(1, MovableArgumentMatcher)), + traverse(TK_AsIs, + cxxOperatorCallExpr(hasOverloadedOperatorName("="), + callee(cxxMethodDecl(ofClass(AutoPtrDecl))), + hasArgument(1, MovableArgumentMatcher))), this); Finder->addMatcher( traverse(ast_type_traits::TK_AsIs, diff --git a/clang-tools-extra/clang-tidy/modernize/UseOverrideCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseOverrideCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/UseOverrideCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseOverrideCheck.cpp @@ -34,10 +34,13 @@ void UseOverrideCheck::registerMatchers(MatchFinder *Finder) { if (IgnoreDestructors) Finder->addMatcher( - cxxMethodDecl(isOverride(), unless(cxxDestructorDecl())).bind("method"), + traverse(TK_AsIs, + cxxMethodDecl(isOverride(), unless(cxxDestructorDecl())) + .bind("method")), this); else - Finder->addMatcher(cxxMethodDecl(isOverride()).bind("method"), this); + Finder->addMatcher( + traverse(TK_AsIs, cxxMethodDecl(isOverride()).bind("method")), this); } // Re-lex the tokens to get precise locations to insert 'override' and remove diff --git a/clang-tools-extra/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp @@ -21,29 +21,32 @@ // Using declaration: warning and fix-it. Finder->addMatcher( - usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(hasName(MatchText)))) - .bind("using_decl"), + traverse(TK_AsIs, usingDecl(hasAnyUsingShadowDecl( + hasTargetDecl(hasName(MatchText)))) + .bind("using_decl")), this); // DeclRefExpr: warning, no fix-it. Finder->addMatcher( - declRefExpr(to(functionDecl(hasName(MatchText))), unless(callExpr())) - .bind("decl_ref_expr"), + traverse(TK_AsIs, declRefExpr(to(functionDecl(hasName(MatchText))), + unless(callExpr())) + .bind("decl_ref_expr")), this); auto DirectCallToUncaughtException = callee(expr(ignoringImpCasts( declRefExpr(hasDeclaration(functionDecl(hasName(MatchText))))))); // CallExpr: warning, fix-it. - Finder->addMatcher(callExpr(DirectCallToUncaughtException, - unless(hasAncestor(initListExpr()))) - .bind("call_expr"), - this); + Finder->addMatcher( + traverse(TK_AsIs, callExpr(DirectCallToUncaughtException, + unless(hasAncestor(initListExpr()))) + .bind("call_expr")), + this); // CallExpr in initialisation list: warning, fix-it with avoiding narrowing // conversions. - Finder->addMatcher(callExpr(DirectCallToUncaughtException, - hasAncestor(initListExpr())) - .bind("init_call_expr"), + Finder->addMatcher(traverse(TK_AsIs, callExpr(DirectCallToUncaughtException, + hasAncestor(initListExpr())) + .bind("init_call_expr")), this); } diff --git a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp --- a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp @@ -69,15 +69,17 @@ "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( - StringLikeClasses.begin(), StringLikeClasses.end()))))))), - unless(hasSubstitutedType())))), + traverse(TK_AsIs, + cxxMemberCallExpr( + callee(functionDecl(StringFindFunctions).bind("func")), + anyOf(argumentCountIs(1), argumentCountIs(2)), + hasArgument(0, SingleChar), + on(expr(hasType(hasUnqualifiedDesugaredType( + recordType(hasDeclaration(recordDecl( + hasAnyName(SmallVector( + StringLikeClasses.begin(), + StringLikeClasses.end()))))))), + unless(hasSubstitutedType()))))), this); } diff --git a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/ContainerSizeEmptyCheck.cpp @@ -55,13 +55,15 @@ hasParent(explicitCastExpr(hasDestinationType(booleanType()))))); Finder->addMatcher( - cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer), - hasType(pointsTo(ValidContainer)), - hasType(references(ValidContainer))))), - callee(cxxMethodDecl(hasName("size"))), WrongUse, - unless(hasAncestor(cxxMethodDecl( - ofClass(equalsBoundNode("container")))))) - .bind("SizeCallExpr"), + traverse(TK_AsIs, + cxxMemberCallExpr( + on(expr(anyOf(hasType(ValidContainer), + hasType(pointsTo(ValidContainer)), + hasType(references(ValidContainer))))), + callee(cxxMethodDecl(hasName("size"))), WrongUse, + unless(hasAncestor( + cxxMethodDecl(ofClass(equalsBoundNode("container")))))) + .bind("SizeCallExpr")), this); // Empty constructor matcher. @@ -86,13 +88,15 @@ expr(hasType(pointsTo(ValidContainer))).bind("Pointee"))), expr(hasType(ValidContainer)).bind("STLObject")); Finder->addMatcher( - cxxOperatorCallExpr( - hasAnyOverloadedOperatorName("==", "!="), - anyOf(allOf(hasArgument(0, WrongComparend), hasArgument(1, STLArg)), - allOf(hasArgument(0, STLArg), hasArgument(1, WrongComparend))), - unless(hasAncestor( - cxxMethodDecl(ofClass(equalsBoundNode("container")))))) - .bind("BinCmp"), + traverse(TK_AsIs, + cxxOperatorCallExpr(hasAnyOverloadedOperatorName("==", "!="), + anyOf(allOf(hasArgument(0, WrongComparend), + hasArgument(1, STLArg)), + allOf(hasArgument(0, STLArg), + hasArgument(1, WrongComparend))), + unless(hasAncestor(cxxMethodDecl( + ofClass(equalsBoundNode("container")))))) + .bind("BinCmp")), this); } diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp @@ -127,30 +127,35 @@ auto IsBoundToType = refersToType(equalsBoundNode("type")); Finder->addMatcher( - ExplicitSingleVarDecl(hasType(autoType(hasDeducedType( + traverse(TK_AsIs, ExplicitSingleVarDecl( + hasType(autoType(hasDeducedType( pointerType(pointee(unless(functionType())))))), - "auto"), + "auto")), this); Finder->addMatcher( - ExplicitSingleVarDeclInTemplate( - allOf(hasType(autoType(hasDeducedType(pointerType( - pointee(hasUnqualifiedType(qualType().bind("type")), - unless(functionType())))))), - anyOf(hasAncestor( - functionDecl(hasAnyTemplateArgument(IsBoundToType))), - hasAncestor(classTemplateSpecializationDecl( - hasAnyTemplateArgument(IsBoundToType))))), - "auto"), + traverse(TK_AsIs, + ExplicitSingleVarDeclInTemplate( + allOf(hasType(autoType(hasDeducedType(pointerType(pointee( + hasUnqualifiedType(qualType().bind("type")), + unless(functionType())))))), + anyOf(hasAncestor(functionDecl( + hasAnyTemplateArgument(IsBoundToType))), + hasAncestor(classTemplateSpecializationDecl( + hasAnyTemplateArgument(IsBoundToType))))), + "auto")), this); if (!AddConstToQualified) return; - Finder->addMatcher(ExplicitSingleVarDecl( - hasType(pointerType(pointee(autoType()))), "auto_ptr"), - this); Finder->addMatcher( - ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))), - "auto_ref"), + traverse(TK_AsIs, + ExplicitSingleVarDecl(hasType(pointerType(pointee(autoType()))), + "auto_ptr")), + this); + Finder->addMatcher( + traverse(TK_AsIs, ExplicitSingleVarDecl( + hasType(lValueReferenceType(pointee(autoType()))), + "auto_ref")), this); } diff --git a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp @@ -39,8 +39,7 @@ void StaticAccessedThroughInstanceCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( memberExpr(hasDeclaration(anyOf(cxxMethodDecl(isStaticStorageClass()), - varDecl(hasStaticStorageDuration()))), - unless(isInTemplateInstantiation())) + varDecl(hasStaticStorageDuration())))) .bind("memberExpression"), this); } diff --git a/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.cpp b/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.cpp --- a/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.cpp +++ b/clang-tools-extra/clang-tidy/zircon/TemporaryObjectsCheck.cpp @@ -28,11 +28,12 @@ void TemporaryObjectsCheck::registerMatchers(MatchFinder *Finder) { // Matcher for default constructors. - Finder->addMatcher( - cxxTemporaryObjectExpr(hasDeclaration(cxxConstructorDecl(hasParent( - cxxRecordDecl(matchesAnyName(Names)))))) - .bind("temps"), - this); + Finder->addMatcher(traverse(ast_type_traits::TK_AsIs, + cxxTemporaryObjectExpr( + hasDeclaration(cxxConstructorDecl(hasParent( + cxxRecordDecl(matchesAnyName(Names)))))) + .bind("temps")), + this); // Matcher for user-defined constructors. Finder->addMatcher( diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -78,6 +78,7 @@ bool getDeserialize() const { return Deserialize; } void SetTraversalKind(TraversalKind TK) { Traversal = TK; } + TraversalKind GetTraversalKind() const { return Traversal; } void Visit(const Decl *D) { getNodeDelegate().AddChild([=] { @@ -475,8 +476,9 @@ Visit(D->getTemplatedDecl()); - for (const auto *Child : D->specializations()) - dumpTemplateDeclSpecialization(Child); + if (Traversal == TK_AsIs) + for (const auto *Child : D->specializations()) + dumpTemplateDeclSpecialization(Child); } void VisitTypeAliasDecl(const TypeAliasDecl *D) { diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1056,6 +1056,8 @@ virtual ASTContext &getASTContext() const = 0; + virtual bool isMatchingInImplicitTemplateInstantiation() const = 0; + protected: virtual bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx, const DynTypedMatcher &Matcher, diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -129,9 +129,10 @@ Visit(D->getTemplatedDecl()); - for (const auto *Child : D->specializations()) - dumpTemplateDeclSpecialization(Child, DumpExplicitInst, - !D->isCanonicalDecl()); + if (GetTraversalKind() == TK_AsIs) + for (const auto *Child : D->specializations()) + dumpTemplateDeclSpecialization(Child, DumpExplicitInst, + !D->isCanonicalDecl()); } void ASTDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -561,6 +561,114 @@ bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return true; } + bool m_traversingImplicitTemplateInstantiation = false; + + bool isMatchingInImplicitTemplateInstantiation() const override { + return m_traversingImplicitTemplateInstantiation; + } + + struct ImplicitTemplateInstantiationScope { + ImplicitTemplateInstantiationScope(MatchASTVisitor *V, bool b) : m_V(V) { + m_B = V->m_traversingImplicitTemplateInstantiation; + V->m_traversingImplicitTemplateInstantiation = b; + } + ~ImplicitTemplateInstantiationScope() { + m_V->m_traversingImplicitTemplateInstantiation = m_B; + } + + private: + MatchASTVisitor *m_V; + bool m_B; + }; + +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!getDerived().CALL_EXPR) \ + return false; \ + } while (false) + + bool TraverseTemplateInstantiations(ClassTemplateDecl *D) { + for (auto *SD : D->specializations()) { + for (auto *RD : SD->redecls()) { + // We don't want to visit injected-class-names in this traversal. + if (cast(RD)->isInjectedClassName()) + continue; + + switch (cast(RD) + ->getSpecializationKind()) { + // Visit the implicit instantiations with the requested pattern. + case TSK_Undeclared: + case TSK_ImplicitInstantiation: { + ImplicitTemplateInstantiationScope RAII(this, true); + TRY_TO(TraverseDecl(RD)); + break; + } + + // We don't need to do anything on an explicit instantiation + // or explicit specialization because there will be an explicit + // node for it elsewhere. + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + case TSK_ExplicitSpecialization: + break; + } + } + } + + return true; + } + + bool TraverseTemplateInstantiations(VarTemplateDecl *D) { + for (auto *SD : D->specializations()) { + for (auto *RD : SD->redecls()) { + switch ( + cast(RD)->getSpecializationKind()) { + case TSK_Undeclared: + case TSK_ImplicitInstantiation: { + ImplicitTemplateInstantiationScope RAII(this, true); + TRY_TO(TraverseDecl(RD)); + break; + } + + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + case TSK_ExplicitSpecialization: + break; + } + } + } + + return true; + } + + bool TraverseTemplateInstantiations(FunctionTemplateDecl *D) { + for (auto *FD : D->specializations()) { + for (auto *RD : FD->redecls()) { + switch (RD->getTemplateSpecializationKind()) { + case TSK_Undeclared: + case TSK_ImplicitInstantiation: { + // We don't know what kind of FunctionDecl this is. + ImplicitTemplateInstantiationScope RAII(this, true); + TRY_TO(TraverseDecl(RD)); + break; + } + + // FIXME: For now traverse explicit instantiations here. Change that + // once they are represented as dedicated nodes in the AST. + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + TRY_TO(TraverseDecl(RD)); + break; + + case TSK_ExplicitSpecialization: + break; + } + } + } + + return true; + } + private: class TimeBucketRegion { public: diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -282,6 +282,13 @@ TraversalKindScope RAII(Finder->getASTContext(), Implementation->TraversalKind()); + if (Finder->getASTContext().getParentMapContext().getTraversalKind() == + TK_IgnoreUnlessSpelledInSource) { + if (Finder->isMatchingInImplicitTemplateInstantiation()) { + return false; + } + } + auto N = Finder->getASTContext().getParentMapContext().traverseIgnored(DynNode); diff --git a/clang/unittests/AST/ASTTraverserTest.cpp b/clang/unittests/AST/ASTTraverserTest.cpp --- a/clang/unittests/AST/ASTTraverserTest.cpp +++ b/clang/unittests/AST/ASTTraverserTest.cpp @@ -68,6 +68,14 @@ void Visit(const TemplateArgument &A, SourceRange R = {}, const Decl *From = nullptr, const char *Label = nullptr) { OS << "TemplateArgument"; + switch (A.getKind()) { + case TemplateArgument::Type: { + OS << " type " << A.getAsType().getAsString(); + break; + } + default: + break; + } } template void Visit(T...) {} @@ -420,8 +428,9 @@ { auto FN = ast_matchers::match( - functionDecl(hasName("template_test"), - hasDescendant(staticAssertDecl().bind("staticAssert"))), + traverse(TK_AsIs, functionDecl(hasName("template_test"), + hasDescendant(staticAssertDecl().bind( + "staticAssert")))), AST->getASTContext()); EXPECT_EQ(FN.size(), 2u); @@ -873,4 +882,141 @@ } } +TEST(Traverse, IgnoreUnlessSpelledInSourceTemplateInstantiations) { + + auto AST = buildASTFromCode(R"cpp( +template +struct TemplStruct { + TemplStruct() {} + ~TemplStruct() {} + +private: + T m_t; +}; + +template +T timesTwo(T input) +{ + return input * 2; +} + +void instantiate() +{ + TemplStruct ti; + TemplStruct td; + (void)timesTwo(2); + (void)timesTwo(2); +} +)cpp"); + { + auto BN = ast_matchers::match( + classTemplateDecl(hasName("TemplStruct")).bind("rec"), + AST->getASTContext()); + EXPECT_EQ(BN.size(), 1u); + + EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, + BN[0].getNodeAs("rec")), + R"cpp( +ClassTemplateDecl 'TemplStruct' +|-TemplateTypeParmDecl 'T' +`-CXXRecordDecl 'TemplStruct' + |-CXXRecordDecl 'TemplStruct' + |-CXXConstructorDecl 'TemplStruct' + | `-CompoundStmt + |-CXXDestructorDecl '~TemplStruct' + | `-CompoundStmt + |-AccessSpecDecl + `-FieldDecl 'm_t' +)cpp"); + + EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs("rec")), + R"cpp( +ClassTemplateDecl 'TemplStruct' +|-TemplateTypeParmDecl 'T' +|-CXXRecordDecl 'TemplStruct' +| |-CXXRecordDecl 'TemplStruct' +| |-CXXConstructorDecl 'TemplStruct' +| | `-CompoundStmt +| |-CXXDestructorDecl '~TemplStruct' +| | `-CompoundStmt +| |-AccessSpecDecl +| `-FieldDecl 'm_t' +|-ClassTemplateSpecializationDecl 'TemplStruct' +| |-TemplateArgument type int +| |-CXXRecordDecl 'TemplStruct' +| |-CXXConstructorDecl 'TemplStruct' +| | `-CompoundStmt +| |-CXXDestructorDecl '~TemplStruct' +| | `-CompoundStmt +| |-AccessSpecDecl +| |-FieldDecl 'm_t' +| `-CXXConstructorDecl 'TemplStruct' +| `-ParmVarDecl '' +`-ClassTemplateSpecializationDecl 'TemplStruct' + |-TemplateArgument type double + |-CXXRecordDecl 'TemplStruct' + |-CXXConstructorDecl 'TemplStruct' + | `-CompoundStmt + |-CXXDestructorDecl '~TemplStruct' + | `-CompoundStmt + |-AccessSpecDecl + |-FieldDecl 'm_t' + `-CXXConstructorDecl 'TemplStruct' + `-ParmVarDecl '' +)cpp"); + } + { + auto BN = ast_matchers::match( + functionTemplateDecl(hasName("timesTwo")).bind("fn"), + AST->getASTContext()); + EXPECT_EQ(BN.size(), 1u); + + EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, + BN[0].getNodeAs("fn")), + R"cpp( +FunctionTemplateDecl 'timesTwo' +|-TemplateTypeParmDecl 'T' +`-FunctionDecl 'timesTwo' + |-ParmVarDecl 'input' + `-CompoundStmt + `-ReturnStmt + `-BinaryOperator + |-DeclRefExpr 'input' + `-IntegerLiteral +)cpp"); + + EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs("fn")), + R"cpp( +FunctionTemplateDecl 'timesTwo' +|-TemplateTypeParmDecl 'T' +|-FunctionDecl 'timesTwo' +| |-ParmVarDecl 'input' +| `-CompoundStmt +| `-ReturnStmt +| `-BinaryOperator +| |-DeclRefExpr 'input' +| `-IntegerLiteral +|-FunctionDecl 'timesTwo' +| |-TemplateArgument type int +| |-ParmVarDecl 'input' +| `-CompoundStmt +| `-ReturnStmt +| `-BinaryOperator +| |-ImplicitCastExpr +| | `-DeclRefExpr 'input' +| `-IntegerLiteral +`-FunctionDecl 'timesTwo' + |-TemplateArgument type double + |-ParmVarDecl 'input' + `-CompoundStmt + `-ReturnStmt + `-BinaryOperator + |-ImplicitCastExpr + | `-DeclRefExpr 'input' + `-ImplicitCastExpr + `-IntegerLiteral +)cpp"); + } +} + } // namespace clang diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1890,7 +1890,66 @@ substNonTypeTemplateParmExpr(has(integerLiteral()))))))))); EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, - staticAssertDecl(has(integerLiteral()))))); + staticAssertDecl(has(declRefExpr()))))); + + Code = R"cpp( + +template +struct TemplStruct { + TemplStruct() {} + ~TemplStruct() {} + +private: + T m_t; +}; + +template +T timesTwo(T input) +{ + return input * 2; +} + +void instantiate() +{ + TemplStruct ti; + TemplStruct td; + (void)timesTwo(2); + (void)timesTwo(2); +} + +)cpp"; + { + auto M = cxxRecordDecl(hasName("TemplStruct"), + has(fieldDecl(hasType(asString("int"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = cxxRecordDecl(hasName("TemplStruct"), + has(fieldDecl(hasType(asString("double"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = cxxRecordDecl(hasName("TemplStruct"), + has(fieldDecl(hasType(asString("double"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = + functionDecl(hasName("timesTwo"), + hasParameter(0, parmVarDecl(hasType(asString("int"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = + functionDecl(hasName("timesTwo"), + hasParameter(0, parmVarDecl(hasType(asString("double"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } } template @@ -3337,24 +3396,24 @@ StatementMatcher IsPlacementNew = cxxNewExpr(hasAnyPlacementArg(anything())); EXPECT_TRUE(matches(R"( - void* operator new(decltype(sizeof(void*)), void*); + void* operator new(decltype(sizeof(void*)), void*); int *foo(void* Storage) { - return new (Storage) int; + return new (Storage) int; })", IsPlacementNew)); EXPECT_TRUE(matches(R"( - void* operator new(decltype(sizeof(void*)), void*, unsigned); + void* operator new(decltype(sizeof(void*)), void*, unsigned); int *foo(void* Storage) { - return new (Storage, 16) int; + return new (Storage, 16) int; })", cxxNewExpr(hasPlacementArg( 1, ignoringImpCasts(integerLiteral(equals(16))))))); EXPECT_TRUE(notMatches(R"( - void* operator new(decltype(sizeof(void*)), void*); + void* operator new(decltype(sizeof(void*)), void*); int *foo(void* Storage) { - return new int; + return new int; })", IsPlacementNew)); } diff --git a/clang/unittests/Tooling/TransformerTest.cpp b/clang/unittests/Tooling/TransformerTest.cpp --- a/clang/unittests/Tooling/TransformerTest.cpp +++ b/clang/unittests/Tooling/TransformerTest.cpp @@ -695,6 +695,70 @@ EXPECT_EQ(ErrorCount, 0); } +TEST_F(TransformerTest, TemplateInstantiation) { + + std::string NonTemplatesInput = R"cpp( +struct S { + int m_i; +}; +)cpp"; + std::string NonTemplatesExpected = R"cpp( +struct S { + safe_int m_i; +}; +)cpp"; + + std::string TemplatesInput = R"cpp( +template +struct TemplStruct { + TemplStruct() {} + ~TemplStruct() {} + +private: + T m_t; +}; + +void instantiate() +{ + TemplStruct ti; +} +)cpp"; + + auto MatchedField = fieldDecl(hasType(asString("int"))).bind("theField"); + + // Changes the 'int' in 'S', but not the 'T' in 'TemplStruct': + testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedField), + changeTo(cat("safe_int ", name("theField")))), + NonTemplatesInput + TemplatesInput, + NonTemplatesExpected + TemplatesInput); + + // In AsIs mode, template instantiations are modified, which is + // often not desired: + + std::string IncorrectTemplatesExpected = R"cpp( +template +struct TemplStruct { + TemplStruct() {} + ~TemplStruct() {} + +private: + safe_int m_t; +}; + +void instantiate() +{ + TemplStruct ti; +} +)cpp"; + + // Changes the 'int' in 'S', and (incorrectly) the 'T' in 'TemplStruct': + testRule(makeRule(traverse(TK_AsIs, MatchedField), + changeTo(cat("safe_int ", name("theField")))), + + NonTemplatesInput + TemplatesInput, + NonTemplatesExpected + IncorrectTemplatesExpected); +} + // Transformation of macro source text when the change encompasses the entirety // of the expanded text. TEST_F(TransformerTest, SimpleMacro) {