14 #include "../../clang-tidy/ClangTidyCheck.h"
15 #include "../../clang-tidy/ClangTidyModule.h"
16 #include "../../clang-tidy/ClangTidyModuleRegistry.h"
27 #include "clang/AST/DeclTemplate.h"
28 #include "clang/Basic/SourceLocation.h"
29 #include "clang/Basic/SourceManager.h"
30 #include "clang/Basic/TokenKinds.h"
31 #include "clang/Lex/PPCallbacks.h"
32 #include "clang/Lex/Token.h"
33 #include "clang/Tooling/Syntax/Tokens.h"
34 #include "llvm/ADT/STLExtras.h"
35 #include "llvm/ADT/StringRef.h"
36 #include "llvm/Support/ScopedPrinter.h"
37 #include "gmock/gmock-matchers.h"
38 #include "gmock/gmock.h"
39 #include "gtest/gtest.h"
45 using ::testing::AllOf;
46 using ::testing::ElementsAre;
47 using ::testing::ElementsAreArray;
50 if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
51 if (ND->getName() ==
Name)
53 if (
auto *Stream = result_listener->stream()) {
54 llvm::raw_os_ostream
OS(*Stream);
62 MATCHER_P(WithTemplateArgs, ArgName,
"") {
63 if (
const FunctionDecl *FD = dyn_cast<FunctionDecl>(arg)) {
64 if (
const auto *Args = FD->getTemplateSpecializationArgs()) {
65 std::string SpecializationArgs;
68 PrintingPolicy Policy(LO);
69 Policy.adjustForCPlusPlus();
70 for (
const auto &Arg : Args->asArray()) {
71 if (SpecializationArgs.size() > 0)
72 SpecializationArgs +=
",";
73 SpecializationArgs += Arg.getAsType().getAsString(Policy);
75 if (Args->size() == 0)
76 return ArgName == SpecializationArgs;
77 return ArgName ==
"<" + SpecializationArgs +
">";
80 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
86 return arg.beginOffset() == R.Begin && arg.endOffset() == R.End;
90 Inclusion Actual = testing::get<0>(arg);
91 Inclusion
Expected = testing::get<1>(arg);
92 return std::tie(Actual.HashLine, Actual.Written) ==
96 TEST(ParsedASTTest, TopLevelDecls) {
102 TU.Code = "int main();";
103 auto AST = TU.
build();
107 TEST(ParsedASTTest, DoesNotGetIncludedTopDecls) {
109 TU.HeaderCode = R
"cpp(
110 #define LL void foo(){}
123 auto AST = TU.
build();
127 TEST(ParsedASTTest, DoesNotGetImplicitTemplateTopDecls) {
137 auto AST = TU.
build();
139 ElementsAre(DeclNamed(
"f"), DeclNamed(
"s")));
143 GetsExplicitInstantiationAndSpecializationTemplateTopDecls) {
146 template <typename T>
150 template void f(double);
162 double d = foo<double>;
171 TU.ExtraArgs.push_back(
"-fno-delayed-template-parsing");
173 auto AST = TU.
build();
176 ElementsAreArray({AllOf(DeclNamed(
"f"), WithTemplateArgs(
"")),
177 AllOf(DeclNamed(
"f"), WithTemplateArgs(
"<bool>")),
178 AllOf(DeclNamed(
"f"), WithTemplateArgs(
"<double>")),
179 AllOf(DeclNamed(
"V"), WithTemplateArgs(
"")),
180 AllOf(DeclNamed(
"V"), WithTemplateArgs(
"<T *>")),
181 AllOf(DeclNamed(
"V"), WithTemplateArgs(
"<bool>")),
182 AllOf(DeclNamed(
"foo"), WithTemplateArgs(
"")),
183 AllOf(DeclNamed(
"i"), WithTemplateArgs(
"")),
184 AllOf(DeclNamed(
"d"), WithTemplateArgs(
"")),
185 AllOf(DeclNamed(
"foo"), WithTemplateArgs(
"<T *>")),
186 AllOf(DeclNamed(
"foo"), WithTemplateArgs(
"<bool>"))}));
189 TEST(ParsedASTTest, IgnoresDelayedTemplateParsing) {
191 template <typename T> void xxx() {
195 TU.ExtraArgs.push_back("-fdelayed-template-parsing");
196 auto AST = TU.
build();
200 TEST(ParsedASTTest, TokensAfterPreamble) {
202 TU.AdditionalFiles[
"foo.h"] = R
"(
209 // error-ok: invalid syntax, just examining token stream
213 auto AST = TU.
build();
214 const syntax::TokenBuffer &T = AST.
getTokens();
217 ASSERT_GT(T.expandedTokens().size(), 2u);
219 EXPECT_EQ(T.expandedTokens().front().text(SM),
"first_token");
221 EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
223 EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM),
"last_token");
226 auto Spelled = T.spelledTokens(SM.getMainFileID());
228 EXPECT_EQ(
Spelled.front().kind(), tok::hash);
229 EXPECT_EQ(
Spelled.back().text(SM),
"last_token");
232 TEST(ParsedASTTest, NoCrashOnTokensWithTidyCheck) {
236 TU.ClangTidyChecks =
"modernize-use-trailing-return-type";
237 TU.Code =
"inline int foo() {}";
239 auto AST = TU.
build();
240 const syntax::TokenBuffer &T = AST.
getTokens();
243 ASSERT_GT(T.expandedTokens().size(), 7u);
245 EXPECT_EQ(T.expandedTokens().front().text(SM),
"inline");
247 EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
249 EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM),
"}");
252 TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) {
265 "clang",
"-Xclang",
"-fsome-unknown-flag",
testPath(
"foo.cpp")};
269 TEST(ParsedASTTest, CollectsMainFileMacroExpansions) {
270 Annotations TestCase(R
"cpp(
271 #define ^MACRO_ARGS(X, Y) X Y
274 // Macro arguments included.
275 ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), E), ^ID(= 2));
277 // Macro names inside other macros not included.
278 #define ^MACRO_ARGS2(X, Y) X Y
283 // Macros from token concatenations not included.
284 #define ^CONCAT(X) X##A()
285 #define ^PREPEND(X) MACRO##X()
286 #define ^MACROA() 123
287 int G = ^CONCAT(MACRO);
290 // Macros included not from preamble not included.
293 int printf(const char*, ...);
295 #define ^assert(COND) if (!(COND)) { printf("%s", #COND); exit(0); }
298 // Includes macro expansions in arguments that are expressions
305 #define ^MULTIPLE_DEFINITION 1
306 #undef ^MULTIPLE_DEFINITION
308 #define ^MULTIPLE_DEFINITION 2
309 #undef ^MULTIPLE_DEFINITION
314 #define MACRO_EXP(X) ID(X)
317 TU.AdditionalFiles["foo.inc"] = R
"cpp(
322 ParsedAST AST = TU.build();
323 std::vector<Position> MacroExpansionPositions;
325 for (
const auto &R : SIDToRefs.second)
326 MacroExpansionPositions.push_back(R.start);
329 MacroExpansionPositions.push_back(R.start);
330 EXPECT_THAT(MacroExpansionPositions,
331 testing::UnorderedElementsAreArray(TestCase.points()));
334 MATCHER_P(WithFileName, Inc,
"") {
return arg.FileName == Inc; }
336 TEST(ParsedASTTest, ReplayPreambleForTidyCheckers) {
338 Inclusion(
const SourceManager &SM, SourceLocation HashLoc,
340 CharSourceRange FilenameRange)
341 :
HashOffset(SM.getDecomposedLoc(HashLoc).second), IncTok(IncludeTok),
342 IncDirective(IncludeTok.getIdentifierInfo()->getName()),
343 FileNameOffset(SM.getDecomposedLoc(FilenameRange.getBegin()).second),
346 syntax::Token IncTok;
347 llvm::StringRef IncDirective;
348 size_t FileNameOffset;
352 static std::vector<Inclusion> Includes;
353 static std::vector<syntax::Token> SkippedFiles;
354 struct ReplayPreamblePPCallback :
public PPCallbacks {
355 const SourceManager &SM;
356 explicit ReplayPreamblePPCallback(
const SourceManager &SM) : SM(SM) {}
358 void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
360 CharSourceRange FilenameRange,
const FileEntry *,
361 StringRef, StringRef,
const Module *,
362 SrcMgr::CharacteristicKind)
override {
367 void FileSkipped(
const FileEntryRef &,
const Token &FilenameTok,
368 SrcMgr::CharacteristicKind)
override {
369 SkippedFiles.emplace_back(FilenameTok);
372 struct ReplayPreambleCheck :
public tidy::ClangTidyCheck {
373 ReplayPreambleCheck(StringRef
Name, tidy::ClangTidyContext *Context)
374 : ClangTidyCheck(
Name, Context) {}
375 void registerPPCallbacks(
const SourceManager &SM, Preprocessor *
PP,
376 Preprocessor *ModuleExpanderPP)
override {
377 PP->addPPCallbacks(::std::make_unique<ReplayPreamblePPCallback>(SM));
380 struct ReplayPreambleModule :
public tidy::ClangTidyModule {
382 addCheckFactories(tidy::ClangTidyCheckFactories &CheckFactories)
override {
383 CheckFactories.registerCheck<ReplayPreambleCheck>(
384 "replay-preamble-check");
388 static tidy::ClangTidyModuleRegistry::Add<ReplayPreambleModule>
X(
389 "replay-preamble-module",
"");
392 TU.ClangTidyChecks =
"replay-preamble-check";
393 llvm::Annotations Test(R
"cpp(
394 $hash^#$include[[import]] $filebegin^"$filerange[[bar.h]]"
395 $hash^#$include[[include_next]] $filebegin^"$filerange[[baz.h]]"
396 $hash^#$include[[include]] $filebegin^<$filerange[[a.h]]>)cpp");
397 llvm::StringRef Code = Test.code();
398 TU.Code = Code.str();
399 TU.AdditionalFiles["bar.h"] =
"";
400 TU.AdditionalFiles[
"baz.h"] =
"";
401 TU.AdditionalFiles[
"a.h"] =
"";
405 TU.ExtraArgs = {
"-isystem.",
"-xobjective-c"};
407 const auto &AST = TU.
build();
410 auto HashLocs = Test.points(
"hash");
411 ASSERT_EQ(HashLocs.size(), Includes.size());
412 auto IncludeRanges = Test.ranges(
"include");
413 ASSERT_EQ(IncludeRanges.size(), Includes.size());
414 auto FileBeginLocs = Test.points(
"filebegin");
415 ASSERT_EQ(FileBeginLocs.size(), Includes.size());
416 auto FileRanges = Test.ranges(
"filerange");
417 ASSERT_EQ(FileRanges.size(), Includes.size());
419 ASSERT_EQ(SkippedFiles.size(), Includes.size());
420 for (
size_t I = 0; I < Includes.size(); ++I) {
421 const auto &Inc = Includes[I];
423 EXPECT_EQ(Inc.HashOffset, HashLocs[I]);
425 auto IncRange = IncludeRanges[I];
426 EXPECT_THAT(Inc.IncTok.range(SM), RangeIs(IncRange));
427 EXPECT_EQ(Inc.IncTok.kind(), tok::identifier);
428 EXPECT_EQ(Inc.IncDirective,
429 Code.substr(IncRange.Begin, IncRange.End - IncRange.Begin));
431 EXPECT_EQ(Inc.FileNameOffset, FileBeginLocs[I]);
432 EXPECT_EQ(Inc.IsAngled,
Code[FileBeginLocs[I]] ==
'<');
434 auto FileRange = FileRanges[I];
435 EXPECT_EQ(Inc.FileName,
436 Code.substr(FileRange.Begin, FileRange.End - FileRange.Begin));
438 EXPECT_EQ(SM.getDecomposedLoc(SkippedFiles[I].location()).second,
443 SkippedFiles[I].text(SM),
444 Code.substr(FileRange.Begin - 1, FileRange.End - FileRange.Begin + 2));
445 EXPECT_EQ(SkippedFiles[I].kind(), tok::header_name);
448 TU.AdditionalFiles[
"a.h"] =
"";
449 TU.AdditionalFiles[
"b.h"] =
"";
450 TU.AdditionalFiles[
"c.h"] =
"";
452 llvm::StringLiteral Baseline = R
"cpp(
456 TU.Code = Baseline.str();
458 auto BaselinePreamble = TU.preamble();
459 ASSERT_TRUE(BaselinePreamble);
462 llvm::StringLiteral Cases[] = {
491 for (llvm::StringLiteral Case : Cases) {
492 TU.Code = Case.str();
494 IgnoreDiagnostics
Diags;
497 std::move(
CI), {}, BaselinePreamble);
498 ASSERT_TRUE(PatchedAST);
499 EXPECT_TRUE(PatchedAST->getDiagnostics().empty());
509 IgnoreDiagnostics Diags;
512 std::move(
CI), {}, BaselinePreamble);
513 ASSERT_TRUE(PatchedAST);
514 EXPECT_TRUE(PatchedAST->getDiagnostics().empty());
515 EXPECT_THAT(Includes,
516 ElementsAre(WithFileName(
testPath(
"__preamble_patch__.h")),
517 WithFileName(
"b.h"), WithFileName(
"a.h")));
520 TEST(ParsedASTTest, PatchesAdditionalIncludes) {
521 llvm::StringLiteral ModifiedContents = R
"cpp(
532 TU.Filename =
"foo.cpp";
533 TU.AdditionalFiles[
"foo.h"] =
"void foo();";
534 TU.AdditionalFiles[
"sub/baz.h"] =
"void baz();";
535 TU.AdditionalFiles[
"sub/aux.h"] =
"void aux();";
536 TU.ExtraArgs = {
"-I" +
testPath(
"sub")};
537 TU.Code = ModifiedContents.str();
538 auto ExpectedAST = TU.build();
548 ASSERT_TRUE(EmptyPreamble);
549 EXPECT_THAT(EmptyPreamble->Includes.MainFileIncludes, testing::IsEmpty());
552 TU.Code = ModifiedContents.str();
556 ASSERT_TRUE(PatchedAST);
557 ASSERT_TRUE(PatchedAST->getDiagnostics().empty());
560 EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
562 EqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
563 auto StringMapToVector = [](
const llvm::StringMap<unsigned> SM) {
564 std::vector<std::pair<std::string, unsigned>> Res;
565 for (
const auto &
E : SM)
566 Res.push_back({
E.first().str(),
E.second});
571 EXPECT_EQ(StringMapToVector(PatchedAST->getIncludeStructure().includeDepth(
573 StringMapToVector(ExpectedAST.getIncludeStructure().includeDepth(
577 TEST(ParsedASTTest, PatchesDeletedIncludes) {
579 TU.Filename =
"foo.cpp";
581 auto ExpectedAST = TU.build();
584 TU.Code = R
"cpp(#include <foo.h>)cpp";
589 auto BaselinePreamble =
591 ASSERT_TRUE(BaselinePreamble);
592 EXPECT_THAT(BaselinePreamble->Includes.MainFileIncludes,
600 {}, BaselinePreamble);
601 ASSERT_TRUE(PatchedAST);
604 EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
606 EqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
607 auto StringMapToVector = [](
const llvm::StringMap<unsigned> SM) {
608 std::vector<std::pair<std::string, unsigned>> Res;
609 for (
const auto &
E : SM)
610 Res.push_back({
E.first().str(),
E.second});
615 EXPECT_EQ(StringMapToVector(PatchedAST->getIncludeStructure().includeDepth(
617 StringMapToVector(ExpectedAST.getIncludeStructure().includeDepth(