diff --git a/clang/include/clang/Tooling/Transformer/RangeSelector.h b/clang/include/clang/Tooling/Transformer/RangeSelector.h --- a/clang/include/clang/Tooling/Transformer/RangeSelector.h +++ b/clang/include/clang/Tooling/Transformer/RangeSelector.h @@ -73,9 +73,9 @@ /// binding in the match result. RangeSelector member(std::string ID); -/// Given a node with a "name", (like \c NamedDecl, \c DeclRefExpr or \c -/// CxxCtorInitializer) selects the name's token. Only selects the final -/// identifier of a qualified name, but not any qualifiers or template +/// Given a node with a "name", (like \c NamedDecl, \c DeclRefExpr, \c +/// CxxCtorInitializer, and \c TypeLoc) selects the name's token. Only selects\ +/// the final identifier of a qualified name, but not any qualifiers or template /// arguments. For example, for `::foo::bar::baz` and `::foo::bar::baz`, /// it selects only `baz`. /// diff --git a/clang/lib/Tooling/Transformer/RangeSelector.cpp b/clang/lib/Tooling/Transformer/RangeSelector.cpp --- a/clang/lib/Tooling/Transformer/RangeSelector.cpp +++ b/clang/lib/Tooling/Transformer/RangeSelector.cpp @@ -8,6 +8,7 @@ #include "clang/Tooling/Transformer/RangeSelector.h" #include "clang/AST/Expr.h" +#include "clang/AST/TypeLoc.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceLocation.h" #include "clang/Lex/Lexer.h" @@ -228,6 +229,14 @@ SourceLocation L = I->getMemberLocation(); return CharSourceRange::getTokenRange(L, L); } + if (const auto *T = Node.get()) { + TypeLoc Loc = *T; + auto ET = T->getAs(); + if (!ET.isNull()) { + Loc = ET.getNamedTypeLoc(); + } + return CharSourceRange::getTokenRange(Loc.getSourceRange()); + } return typeError(ID, Node.getNodeKind(), "DeclRefExpr, NamedDecl, CXXCtorInitializer"); }; diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp b/clang/unittests/Tooling/RangeSelectorTest.cpp --- a/clang/unittests/Tooling/RangeSelectorTest.cpp +++ b/clang/unittests/Tooling/RangeSelectorTest.cpp @@ -457,6 +457,35 @@ EXPECT_THAT_EXPECTED(select(name(Init), Match), HasValue("field")); } +TEST(RangeSelectorTest, NameOpTypeLoc) { + StringRef Code = R"cc( + namespace ns { + struct Foo { + Foo(); + Foo(int); + Foo(int, int); + }; + } // namespace ns + + ns::Foo a; + auto b = ns::Foo(3); + auto c = ns::Foo(1, 2); + )cc"; + const char *CtorTy = "ctor_ty"; + // Matches declaration of `a` + TestMatch MatchA = matchCode( + Code, varDecl(hasName("a"), hasTypeLoc(typeLoc().bind(CtorTy)))); + EXPECT_THAT_EXPECTED(select(name(CtorTy), MatchA), HasValue("Foo")); + // Matches call of Foo(int) + TestMatch MatchB = matchCode( + Code, cxxFunctionalCastExpr(hasTypeLoc(typeLoc().bind(CtorTy)))); + EXPECT_THAT_EXPECTED(select(name(CtorTy), MatchB), HasValue("Foo")); + // Matches call of Foo(int, int) + TestMatch MatchC = matchCode( + Code, cxxTemporaryObjectExpr(hasTypeLoc(typeLoc().bind(CtorTy)))); + EXPECT_THAT_EXPECTED(select(name(CtorTy), MatchC), HasValue("Foo")); +} + TEST(RangeSelectorTest, NameOpErrors) { EXPECT_THAT_EXPECTED(selectFromTrivial(name("unbound_id")), Failed(withUnboundNodeMessage()));