10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 20 "Google Test APIs named with 'case' are deprecated; use equivalent APIs " 23 static llvm::Optional<llvm::StringRef>
25 std::pair<llvm::StringRef, llvm::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 for (
auto &Mapping : ReplacementMap) {
34 if (MacroName == Mapping.first)
35 return Mapping.second;
43 class UpgradeGoogletestCasePPCallback :
public PPCallbacks {
47 : ReplacementFound(
false), Check(Check), PP(PP) {}
49 void MacroExpands(
const Token &MacroNameTok,
const MacroDefinition &
MD,
50 SourceRange
Range,
const MacroArgs *)
override {
51 macroUsed(MacroNameTok, MD, Range.getBegin(), CheckAction::Rename);
54 void MacroUndefined(
const Token &MacroNameTok,
const MacroDefinition &MD,
55 const MacroDirective *Undef)
override {
57 macroUsed(MacroNameTok, MD, Undef->getLocation(), CheckAction::Warn);
60 void MacroDefined(
const Token &MacroNameTok,
61 const MacroDirective *MD)
override {
62 if (!ReplacementFound && MD !=
nullptr) {
66 llvm::StringRef
FileName = PP->getSourceManager().getFilename(
67 MD->getMacroInfo()->getDefinitionLoc());
68 ReplacementFound = FileName.endswith(
"gtest/gtest-typed-test.h") &&
69 PP->getSpelling(MacroNameTok) ==
"TYPED_TEST_SUITE";
73 void Defined(
const Token &MacroNameTok,
const MacroDefinition &MD,
74 SourceRange Range)
override {
75 macroUsed(MacroNameTok, MD, Range.getBegin(), CheckAction::Warn);
78 void Ifdef(SourceLocation
Loc,
const Token &MacroNameTok,
79 const MacroDefinition &MD)
override {
80 macroUsed(MacroNameTok, MD, Loc, CheckAction::Warn);
83 void Ifndef(SourceLocation Loc,
const Token &MacroNameTok,
84 const MacroDefinition &MD)
override {
85 macroUsed(MacroNameTok, MD, Loc, CheckAction::Warn);
89 enum class CheckAction { Warn, Rename };
91 void macroUsed(
const clang::Token &MacroNameTok,
const MacroDefinition &MD,
92 SourceLocation Loc, CheckAction
Action) {
93 if (!ReplacementFound)
96 std::string
Name = PP->getSpelling(MacroNameTok);
102 llvm::StringRef
FileName = PP->getSourceManager().getFilename(
103 MD.getMacroInfo()->getDefinitionLoc());
104 if (!FileName.endswith(
"gtest/gtest-typed-test.h"))
107 DiagnosticBuilder Diag = Check->
diag(Loc, RenameCaseToSuiteMessage);
109 if (Action == CheckAction::Rename)
110 Diag << FixItHint::CreateReplacement(
114 bool ReplacementFound;
121 void UpgradeGoogletestCaseCheck::registerPPCallbacks(
const SourceManager &,
124 if (!getLangOpts().CPlusPlus)
128 std::make_unique<UpgradeGoogletestCasePPCallback>(
this, PP));
131 void UpgradeGoogletestCaseCheck::registerMatchers(MatchFinder *Finder) {
132 if (!getLangOpts().CPlusPlus)
135 auto LocationFilter =
136 unless(isExpansionInFileMatching(
"gtest/gtest(-typed-test)?\\.h$"));
146 hasAnyName(
"SetUpTestCase",
"TearDownTestCase"),
148 cxxRecordDecl(isSameOrDerivedFrom(cxxRecordDecl(
149 hasName(
"::testing::Test"),
150 hasMethod(hasName(
"SetUpTestSuite")))))
153 hasName(
"test_case_name"),
155 cxxRecordDecl(isSameOrDerivedFrom(cxxRecordDecl(
156 hasName(
"::testing::TestInfo"),
157 hasMethod(hasName(
"test_suite_name")))))
160 hasAnyName(
"OnTestCaseStart",
"OnTestCaseEnd"),
161 ofClass(cxxRecordDecl(
162 isSameOrDerivedFrom(cxxRecordDecl(
163 hasName(
"::testing::TestEventListener"),
164 hasMethod(hasName(
"OnTestSuiteStart")))))
167 hasAnyName(
"current_test_case",
"successful_test_case_count",
168 "failed_test_case_count",
"total_test_case_count",
169 "test_case_to_run_count",
"GetTestCase"),
170 ofClass(cxxRecordDecl(
171 isSameOrDerivedFrom(cxxRecordDecl(
172 hasName(
"::testing::UnitTest"),
173 hasMethod(hasName(
"current_test_suite")))))
177 Finder->addMatcher(expr(anyOf(callExpr(callee(Methods)).bind(
"call"),
178 declRefExpr(to(Methods)).bind(
"ref")),
183 usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(Methods)), LocationFilter)
187 Finder->addMatcher(cxxMethodDecl(Methods, LocationFilter),
this);
192 auto TestCaseTypeAlias =
193 typeAliasDecl(hasName(
"::testing::TestCase")).bind(
"test-case");
195 typeLoc(loc(qualType(typedefType(hasDeclaration(TestCaseTypeAlias)))),
196 unless(hasAncestor(decl(isImplicit()))), LocationFilter)
200 usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(TestCaseTypeAlias)))
206 std::pair<llvm::StringRef, llvm::StringRef> ReplacementMap[] = {
207 {
"SetUpTestCase",
"SetUpTestSuite"},
208 {
"TearDownTestCase",
"TearDownTestSuite"},
209 {
"test_case_name",
"test_suite_name"},
210 {
"OnTestCaseStart",
"OnTestSuiteStart"},
211 {
"OnTestCaseEnd",
"OnTestSuiteEnd"},
212 {
"current_test_case",
"current_test_suite"},
213 {
"successful_test_case_count",
"successful_test_suite_count"},
214 {
"failed_test_case_count",
"failed_test_suite_count"},
215 {
"total_test_case_count",
"total_test_suite_count"},
216 {
"test_case_to_run_count",
"test_suite_to_run_count"},
217 {
"GetTestCase",
"GetTestSuite"}};
219 for (
auto &Mapping : ReplacementMap) {
220 if (CurrentName == Mapping.first)
221 return Mapping.second;
224 llvm_unreachable(
"Unexpected function name");
227 template <
typename NodeType>
229 const MatchFinder::MatchResult &Result) {
230 return !
match(isInTemplateInstantiation(), Node, *Result.Context).empty();
233 template <
typename NodeType>
235 const MatchFinder::MatchResult &Result) {
236 internal::Matcher<NodeType> IsInsideTemplate =
237 hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl())));
238 return !
match(IsInsideTemplate, Node, *Result.Context).empty();
243 llvm::StringRef ReplacementMethod) {
244 const auto *Class = Result.Nodes.getNodeAs<CXXRecordDecl>(
"class");
245 return !
match(cxxRecordDecl(
246 unless(isExpansionInFileMatching(
247 "gtest/gtest(-typed-test)?\\.h$")),
248 hasMethod(cxxMethodDecl(hasName(ReplacementMethod)))),
249 *Class, *Result.Context)
253 static CharSourceRange
255 if (
const auto *Using = Result.Nodes.getNodeAs<UsingDecl>(
"using")) {
257 Using->getNameInfo().getSourceRange());
260 Result.Nodes.getNodeAs<TypeLoc>(
"typeloc")->getSourceRange());
263 void UpgradeGoogletestCaseCheck::check(
const MatchFinder::MatchResult &Result) {
264 llvm::StringRef ReplacementText;
265 CharSourceRange ReplacementRange;
266 if (
const auto *Method = Result.Nodes.getNodeAs<CXXMethodDecl>(
"method")) {
269 bool IsInInstantiation;
272 if (
const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"call")) {
273 const auto *Callee = llvm::cast<MemberExpr>(Call->getCallee());
275 Callee->getMemberLoc());
277 IsInTemplate = isInTemplate<Stmt>(*Call, Result);
278 }
else if (
const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>(
"ref")) {
282 IsInTemplate = isInTemplate<Stmt>(*Ref, Result);
283 }
else if (
const auto *Using = Result.Nodes.getNodeAs<UsingDecl>(
"using")) {
287 IsInTemplate = isInTemplate<Decl>(*Using, Result);
294 Method->getNameInfo().getSourceRange());
296 IsInTemplate = isInTemplate<Decl>(*Method, Result);
304 if (IsInInstantiation) {
305 if (MatchedTemplateLocations.count(
306 ReplacementRange.getBegin().getRawEncoding()) == 0) {
320 MatchedTemplateLocations.insert(
321 ReplacementRange.getBegin().getRawEncoding());
330 assert(Result.Nodes.getNodeAs<TypeAliasDecl>(
"test-case") !=
nullptr);
331 ReplacementText =
"TestSuite";
339 DiagnosticBuilder Diag =
342 ReplacementRange = Lexer::makeFileCharRange(
343 ReplacementRange, *Result.SourceManager, Result.Context->getLangOpts());
344 if (ReplacementRange.isInvalid())
349 Diag << FixItHint::CreateReplacement(ReplacementRange, ReplacementText);
SourceLocation Loc
'#' location in the include directive
static llvm::StringRef getNewMethodName(llvm::StringRef CurrentName)
static bool isInTemplate(const NodeType &Node, const MatchFinder::MatchResult &Result)
static bool isInInstantiation(const NodeType &Node, const MatchFinder::MatchResult &Result)
static const llvm::StringRef RenameCaseToSuiteMessage
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
static llvm::Optional< llvm::StringRef > getNewMacroName(llvm::StringRef MacroName)
llvm::unique_function< void()> Action
static constexpr llvm::StringLiteral Name
Finds uses of deprecated Googletest APIs with names containing "case" and replaces them with equivale...
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
static CharSourceRange getAliasNameRange(const MatchFinder::MatchResult &Result)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
CharSourceRange Range
SourceRange for the file name.
static bool derivedTypeHasReplacementMethod(const MatchFinder::MatchResult &Result, llvm::StringRef ReplacementMethod)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.