10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/PPCallbacks.h"
13#include "clang/Lex/Preprocessor.h"
21 "Google Test APIs named with 'case' are deprecated; use equivalent APIs "
25 static const llvm::StringMap<StringRef> ReplacementMap = {
26 {
"TYPED_TEST_CASE",
"TYPED_TEST_SUITE"},
27 {
"TYPED_TEST_CASE_P",
"TYPED_TEST_SUITE_P"},
28 {
"REGISTER_TYPED_TEST_CASE_P",
"REGISTER_TYPED_TEST_SUITE_P"},
29 {
"INSTANTIATE_TYPED_TEST_CASE_P",
"INSTANTIATE_TYPED_TEST_SUITE_P"},
30 {
"INSTANTIATE_TEST_CASE_P",
"INSTANTIATE_TEST_SUITE_P"},
33 if (
const auto MappingIt = ReplacementMap.find(MacroName);
34 MappingIt != ReplacementMap.end())
35 return MappingIt->second;
41class UpgradeGoogletestCasePPCallback :
public PPCallbacks {
43 UpgradeGoogletestCasePPCallback(UpgradeGoogletestCaseCheck *Check,
45 : Check(Check), PP(PP) {}
47 void MacroExpands(
const Token &MacroNameTok,
const MacroDefinition &MD,
48 SourceRange Range,
const MacroArgs *)
override {
49 macroUsed(MacroNameTok, MD, Range.getBegin(), CheckAction::Rename);
52 void MacroUndefined(
const Token &MacroNameTok,
const MacroDefinition &MD,
53 const MacroDirective *Undef)
override {
55 macroUsed(MacroNameTok, MD, Undef->getLocation(), CheckAction::Warn);
58 void MacroDefined(
const Token &MacroNameTok,
59 const MacroDirective *MD)
override {
60 if (!ReplacementFound && MD !=
nullptr) {
64 const StringRef FileName = PP->getSourceManager().getFilename(
65 MD->getMacroInfo()->getDefinitionLoc());
66 ReplacementFound = FileName.ends_with(
"gtest/gtest-typed-test.h") &&
67 PP->getSpelling(MacroNameTok) ==
"TYPED_TEST_SUITE";
71 void Defined(
const Token &MacroNameTok,
const MacroDefinition &MD,
72 SourceRange Range)
override {
73 macroUsed(MacroNameTok, MD, Range.getBegin(), CheckAction::Warn);
76 void Ifdef(SourceLocation Loc,
const Token &MacroNameTok,
77 const MacroDefinition &MD)
override {
78 macroUsed(MacroNameTok, MD, Loc, CheckAction::Warn);
81 void Ifndef(SourceLocation Loc,
const Token &MacroNameTok,
82 const MacroDefinition &MD)
override {
83 macroUsed(MacroNameTok, MD, Loc, CheckAction::Warn);
87 enum class CheckAction { Warn, Rename };
89 void macroUsed(
const Token &MacroNameTok,
const MacroDefinition &MD,
90 SourceLocation Loc, CheckAction Action) {
91 if (!ReplacementFound)
94 const std::string Name = PP->getSpelling(MacroNameTok);
100 const StringRef FileName = PP->getSourceManager().getFilename(
101 MD.getMacroInfo()->getDefinitionLoc());
102 if (!FileName.ends_with(
"gtest/gtest-typed-test.h"))
107 if (Action == CheckAction::Rename)
108 Diag << FixItHint::CreateReplacement(
109 CharSourceRange::getTokenRange(Loc, Loc), *Replacement);
112 bool ReplacementFound =
false;
113 UpgradeGoogletestCaseCheck *Check;
123 std::make_unique<UpgradeGoogletestCasePPCallback>(
this, PP));
127 auto LocationFilter =
128 unless(isExpansionInFileMatching(
"gtest/gtest(-typed-test)?\\.h$"));
138 hasAnyName(
"SetUpTestCase",
"TearDownTestCase"),
140 cxxRecordDecl(isSameOrDerivedFrom(cxxRecordDecl(
141 hasName(
"::testing::Test"),
142 hasMethod(hasName(
"SetUpTestSuite")))))
145 hasName(
"test_case_name"),
147 cxxRecordDecl(isSameOrDerivedFrom(cxxRecordDecl(
148 hasName(
"::testing::TestInfo"),
149 hasMethod(hasName(
"test_suite_name")))))
152 hasAnyName(
"OnTestCaseStart",
"OnTestCaseEnd"),
153 ofClass(cxxRecordDecl(
154 isSameOrDerivedFrom(cxxRecordDecl(
155 hasName(
"::testing::TestEventListener"),
156 hasMethod(hasName(
"OnTestSuiteStart")))))
159 hasAnyName(
"current_test_case",
"successful_test_case_count",
160 "failed_test_case_count",
"total_test_case_count",
161 "test_case_to_run_count",
"GetTestCase"),
162 ofClass(cxxRecordDecl(
163 isSameOrDerivedFrom(cxxRecordDecl(
164 hasName(
"::testing::UnitTest"),
165 hasMethod(hasName(
"current_test_suite")))))
169 Finder->addMatcher(expr(anyOf(callExpr(callee(Methods)).bind(
"call"),
170 declRefExpr(to(Methods)).bind(
"ref")),
175 usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(Methods)), LocationFilter)
179 Finder->addMatcher(cxxMethodDecl(Methods, LocationFilter),
this);
184 auto TestCaseTypeAlias =
185 typeAliasDecl(hasName(
"::testing::TestCase")).bind(
"test-case");
187 typeLoc(loc(qualType(typedefType(hasDeclaration(TestCaseTypeAlias)))),
188 unless(hasAncestor(decl(isImplicit()))), LocationFilter)
192 usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(TestCaseTypeAlias)))
196 typeLoc(loc(usingType(hasUnderlyingType(
197 typedefType(hasDeclaration(TestCaseTypeAlias))))),
198 unless(hasAncestor(decl(isImplicit()))), LocationFilter)
204 static const llvm::StringMap<StringRef> ReplacementMap = {
205 {
"SetUpTestCase",
"SetUpTestSuite"},
206 {
"TearDownTestCase",
"TearDownTestSuite"},
207 {
"test_case_name",
"test_suite_name"},
208 {
"OnTestCaseStart",
"OnTestSuiteStart"},
209 {
"OnTestCaseEnd",
"OnTestSuiteEnd"},
210 {
"current_test_case",
"current_test_suite"},
211 {
"successful_test_case_count",
"successful_test_suite_count"},
212 {
"failed_test_case_count",
"failed_test_suite_count"},
213 {
"total_test_case_count",
"total_test_suite_count"},
214 {
"test_case_to_run_count",
"test_suite_to_run_count"},
215 {
"GetTestCase",
"GetTestSuite"}};
217 if (
const auto MappingIt = ReplacementMap.find(CurrentName);
218 MappingIt != ReplacementMap.end())
219 return MappingIt->second;
221 llvm_unreachable(
"Unexpected function name");
224template <
typename NodeType>
226 const MatchFinder::MatchResult &Result) {
227 return !match(isInTemplateInstantiation(), Node, *Result.Context).empty();
230template <
typename NodeType>
232 const MatchFinder::MatchResult &Result) {
233 const internal::Matcher<NodeType> IsInsideTemplate =
234 hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl())));
235 return !match(IsInsideTemplate, Node, *Result.Context).empty();
240 StringRef ReplacementMethod) {
241 const auto *Class = Result.Nodes.getNodeAs<CXXRecordDecl>(
"class");
242 return !match(cxxRecordDecl(
243 unless(isExpansionInFileMatching(
244 "gtest/gtest(-typed-test)?\\.h$")),
245 hasMethod(cxxMethodDecl(hasName(ReplacementMethod)))),
246 *Class, *Result.Context)
250static CharSourceRange
252 if (
const auto *Using = Result.Nodes.getNodeAs<UsingDecl>(
"using")) {
253 return CharSourceRange::getTokenRange(
254 Using->getNameInfo().getSourceRange());
256 TypeLoc TL = *Result.Nodes.getNodeAs<TypeLoc>(
"typeloc");
257 if (
auto QTL = TL.getAs<QualifiedTypeLoc>())
258 TL = QTL.getUnqualifiedLoc();
260 if (
auto TTL = TL.getAs<TypedefTypeLoc>())
261 return CharSourceRange::getTokenRange(TTL.getNameLoc());
262 return CharSourceRange::getTokenRange(TL.castAs<UsingTypeLoc>().getNameLoc());
266 StringRef ReplacementText;
267 CharSourceRange ReplacementRange;
268 if (
const auto *Method = Result.Nodes.getNodeAs<CXXMethodDecl>(
"method")) {
271 bool IsInInstantiation =
false;
272 bool IsInTemplate =
false;
274 if (
const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"call")) {
275 const auto *Callee = cast<MemberExpr>(Call->getCallee());
276 ReplacementRange = CharSourceRange::getTokenRange(Callee->getMemberLoc(),
277 Callee->getMemberLoc());
280 }
else if (
const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>(
"ref")) {
282 CharSourceRange::getTokenRange(Ref->getNameInfo().getSourceRange());
285 }
else if (
const auto *Using = Result.Nodes.getNodeAs<UsingDecl>(
"using")) {
287 CharSourceRange::getTokenRange(Using->getNameInfo().getSourceRange());
295 ReplacementRange = CharSourceRange::getTokenRange(
296 Method->getNameInfo().getSourceRange());
306 if (IsInInstantiation) {
307 if (!MatchedTemplateLocations.contains(ReplacementRange.getBegin())) {
321 MatchedTemplateLocations.insert(ReplacementRange.getBegin());
330 assert(Result.Nodes.getNodeAs<TypeAliasDecl>(
"test-case") !=
nullptr);
331 ReplacementText =
"TestSuite";
339 const DiagnosticBuilder Diag =
342 ReplacementRange = Lexer::makeFileCharRange(
343 ReplacementRange, *Result.SourceManager, Result.Context->getLangOpts());
344 if (ReplacementRange.isInvalid())
349 Diag << FixItHint::CreateReplacement(ReplacementRange, ReplacementText);
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
static constexpr StringRef RenameCaseToSuiteMessage
static bool isInInstantiation(const NodeType &Node, const MatchFinder::MatchResult &Result)
static std::optional< StringRef > getNewMacroName(StringRef MacroName)
static StringRef getNewMethodName(StringRef CurrentName)
static CharSourceRange getAliasNameRange(const MatchFinder::MatchResult &Result)
static bool isInTemplate(const NodeType &Node, const MatchFinder::MatchResult &Result)
static bool derivedTypeHasReplacementMethod(const MatchFinder::MatchResult &Result, StringRef ReplacementMethod)