14#include "../../clang-tidy/ClangTidyCheck.h"
27#include "clang/AST/DeclTemplate.h"
28#include "clang/Basic/FileEntry.h"
29#include "clang/Basic/SourceLocation.h"
30#include "clang/Basic/SourceManager.h"
31#include "clang/Basic/TokenKinds.h"
32#include "clang/Tooling/Syntax/Tokens.h"
33#include "llvm/ADT/StringRef.h"
34#include "llvm/Testing/Annotations/Annotations.h"
35#include "llvm/Testing/Support/Error.h"
36#include "gmock/gmock-matchers.h"
37#include "gmock/gmock.h"
38#include "gtest/gtest.h"
48using ::testing::AllOf;
49using ::testing::Contains;
50using ::testing::ElementsAre;
51using ::testing::ElementsAreArray;
52using ::testing::IsEmpty;
55 if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
56 if (ND->getName() ==
Name)
58 if (
auto *Stream = result_listener->stream()) {
59 llvm::raw_os_ostream
OS(*Stream);
66 if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
67 if (ND->getDeclKindName() == llvm::StringRef(
Kind))
69 if (
auto *Stream = result_listener->stream()) {
70 llvm::raw_os_ostream
OS(*Stream);
79 if (
const FunctionDecl *FD = dyn_cast<FunctionDecl>(arg)) {
80 if (
const auto *
Args = FD->getTemplateSpecializationArgs()) {
81 std::string SpecializationArgs;
84 PrintingPolicy Policy(LO);
85 Policy.adjustForCPlusPlus();
86 for (
const auto &Arg :
Args->asArray()) {
87 if (SpecializationArgs.size() > 0)
88 SpecializationArgs +=
",";
89 SpecializationArgs += Arg.getAsType().getAsString(Policy);
91 if (
Args->size() == 0)
92 return ArgName == SpecializationArgs;
93 return ArgName ==
"<" + SpecializationArgs +
">";
96 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
101MATCHER_P(pragmaTrivia, P,
"") {
return arg.Trivia == P; }
104 Inclusion Actual = testing::get<0>(arg);
105 Inclusion
Expected = testing::get<1>(arg);
106 return std::tie(Actual.HashLine, Actual.Written) ==
110TEST(ParsedASTTest, TopLevelDecls) {
118 template <typename> bool X = true;
120 auto AST = TU.build();
121 EXPECT_THAT(
AST.getLocalTopLevelDecls(),
122 testing::UnorderedElementsAreArray(
123 {AllOf(declNamed(
"main"), declKind(
"Function")),
124 AllOf(declNamed(
"X"), declKind(
"VarTemplate"))}));
127TEST(ParsedASTTest, DoesNotGetIncludedTopDecls) {
129 TU.HeaderCode = R
"cpp(
130 #define LL void foo(){}
143 auto AST = TU.build();
144 EXPECT_THAT(
AST.getLocalTopLevelDecls(), ElementsAre(declNamed(
"main")));
147TEST(ParsedASTTest, DoesNotGetImplicitTemplateTopDecls) {
157 auto AST = TU.build();
158 EXPECT_THAT(
AST.getLocalTopLevelDecls(),
159 ElementsAre(declNamed(
"f"), declNamed(
"s")));
163 GetsExplicitInstantiationAndSpecializationTemplateTopDecls) {
166 template <typename T>
170 template void f(double);
182 double d = foo<double>;
190 auto AST = TU.build();
192 AST.getLocalTopLevelDecls(),
193 ElementsAreArray({AllOf(declNamed(
"f"), withTemplateArgs(
"")),
194 AllOf(declNamed(
"f"), withTemplateArgs(
"<bool>")),
195 AllOf(declNamed(
"f"), withTemplateArgs(
"<double>")),
196 AllOf(declNamed(
"V"), withTemplateArgs(
"")),
197 AllOf(declNamed(
"V"), withTemplateArgs(
"<T *>")),
198 AllOf(declNamed(
"V"), withTemplateArgs(
"<bool>")),
199 AllOf(declNamed(
"foo"), withTemplateArgs(
"")),
200 AllOf(declNamed(
"i"), withTemplateArgs(
"")),
201 AllOf(declNamed(
"d"), withTemplateArgs(
"")),
202 AllOf(declNamed(
"foo"), withTemplateArgs(
"<T *>")),
203 AllOf(declNamed(
"foo"), withTemplateArgs(
"<bool>"))}));
206TEST(ParsedASTTest, IgnoresDelayedTemplateParsing) {
208 template <typename T> void xxx() {
212 TU.ExtraArgs.push_back("-fdelayed-template-parsing");
213 auto AST = TU.build();
217TEST(ParsedASTTest, TokensAfterPreamble) {
219 TU.AdditionalFiles[
"foo.h"] = R
"(
226 // error-ok: invalid syntax, just examining token stream
230 auto AST = TU.build();
231 const syntax::TokenBuffer &
T =
AST.getTokens();
232 const auto &SM =
AST.getSourceManager();
234 ASSERT_GT(
T.expandedTokens().size(), 2u);
236 EXPECT_EQ(
T.expandedTokens().front().text(SM),
"first_token");
238 EXPECT_EQ(
T.expandedTokens().back().kind(), tok::eof);
240 EXPECT_EQ(
T.expandedTokens().drop_back().back().text(SM),
"last_token");
243 auto Spelled =
T.spelledTokens(SM.getMainFileID());
245 EXPECT_EQ(
Spelled.front().kind(), tok::hash);
246 EXPECT_EQ(
Spelled.back().text(SM),
"last_token");
249TEST(ParsedASTTest, NoCrashOnTokensWithTidyCheck) {
253 TU.ClangTidyProvider =
addTidyChecks(
"modernize-use-trailing-return-type");
254 TU.Code =
"inline int foo() {}";
256 auto AST = TU.build();
257 const syntax::TokenBuffer &
T =
AST.getTokens();
258 const auto &SM =
AST.getSourceManager();
260 ASSERT_GT(
T.expandedTokens().size(), 7u);
262 EXPECT_EQ(
T.expandedTokens().front().text(SM),
"inline");
264 EXPECT_EQ(
T.expandedTokens().back().kind(), tok::eof);
266 EXPECT_EQ(
T.expandedTokens().drop_back().back().text(SM),
"}");
269TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) {
271 FS.Files = {{
testPath(
"foo.cpp"),
"void test() {}"}};
275 Inputs.
CompileCommand.CommandLine = {
"clang",
"-fsome-unknown-flag",
282 "clang",
"-Xclang",
"-fsome-unknown-flag",
testPath(
"foo.cpp")};
286TEST(ParsedASTTest, CollectsMainFileMacroExpansions) {
287 llvm::Annotations TestCase(R
"cpp(
288 #define ^MACRO_ARGS(X, Y) X Y
291 // Macro arguments included.
292 ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), E), ^ID(= 2));
294 // Macro names inside other macros not included.
295 #define ^MACRO_ARGS2(X, Y) X Y
300 // Macros from token concatenations not included.
301 #define ^CONCAT(X) X##A()
302 #define ^PREPEND(X) MACRO##X()
303 #define ^MACROA() 123
304 int G = ^CONCAT(MACRO);
307 // Macros included not from preamble not included.
310 int printf(const char*, ...);
312 #define ^assert(COND) if (!(COND)) { printf("%s", #COND); exit(0); }
315 // Includes macro expansions in arguments that are expressions
322 #define ^MULTIPLE_DEFINITION 1
323 #undef ^MULTIPLE_DEFINITION
325 #define ^MULTIPLE_DEFINITION 2
326 #undef ^MULTIPLE_DEFINITION
329 TU.HeaderCode = R
"cpp(
331 #define MACRO_EXP(X) ID(X)
334 TU.AdditionalFiles["foo.inc"] = R
"cpp(
339 ParsedAST AST = TU.build();
340 std::vector<size_t> MacroExpansionPositions;
341 for (
const auto &SIDToRefs :
AST.getMacros().MacroRefs) {
342 for (
const auto &R : SIDToRefs.second)
343 MacroExpansionPositions.push_back(R.StartOffset);
345 for (
const auto &R :
AST.getMacros().UnknownMacros)
346 MacroExpansionPositions.push_back(R.StartOffset);
347 EXPECT_THAT(MacroExpansionPositions,
348 testing::UnorderedElementsAreArray(TestCase.points()));
351MATCHER_P(withFileName, Inc,
"") {
return arg.FileName == Inc; }
353TEST(ParsedASTTest, PatchesAdditionalIncludes) {
354 llvm::StringLiteral ModifiedContents = R
"cpp(
365 TU.Filename =
"foo.cpp";
366 TU.AdditionalFiles[
"foo.h"] =
"void foo();";
367 TU.AdditionalFiles[
"sub/baz.h"] =
"void baz();";
368 TU.AdditionalFiles[
"sub/aux.h"] =
"void aux();";
369 TU.ExtraArgs = {
"-I" +
testPath(
"sub")};
370 TU.Code = ModifiedContents.str();
371 auto ExpectedAST = TU.build();
377 auto Inputs = TU.inputs(FS);
381 ASSERT_TRUE(EmptyPreamble);
382 EXPECT_THAT(EmptyPreamble->Includes.MainFileIncludes, IsEmpty());
385 TU.Code = ModifiedContents.str();
386 Inputs = TU.inputs(FS);
389 ASSERT_TRUE(PatchedAST);
392 EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
394 eqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
396 auto &SM = PatchedAST->getSourceManager();
397 auto &FM = SM.getFileManager();
399 IncludeStructure Includes = PatchedAST->getIncludeStructure();
400 auto MainFE = FM.getOptionalFileRef(
testPath(
"foo.cpp"));
402 auto MainID = Includes.
getID(*MainFE);
403 auto AuxFE = FM.getOptionalFileRef(
testPath(
"sub/aux.h"));
405 auto AuxID = Includes.
getID(*AuxFE);
409TEST(ParsedASTTest, PatchesDeletedIncludes) {
411 TU.Filename =
"foo.cpp";
413 auto ExpectedAST = TU.build();
416 TU.Code = R
"cpp(#include <foo.h>)cpp";
419 auto Inputs = TU.inputs(FS);
421 auto BaselinePreamble =
423 ASSERT_TRUE(BaselinePreamble);
424 EXPECT_THAT(BaselinePreamble->Includes.MainFileIncludes,
430 Inputs = TU.inputs(FS);
432 {}, BaselinePreamble);
433 ASSERT_TRUE(PatchedAST);
436 EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
438 eqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
440 auto &SM = ExpectedAST.getSourceManager();
441 auto &FM = SM.getFileManager();
443 IncludeStructure Includes = ExpectedAST.getIncludeStructure();
444 auto MainFE = FM.getFileRef(
testPath(
"foo.cpp"));
445 ASSERT_THAT_EXPECTED(MainFE, llvm::Succeeded());
447 auto &PatchedFM = PatchedAST->getSourceManager().getFileManager();
448 IncludeStructure PatchedIncludes = PatchedAST->getIncludeStructure();
449 auto PatchedMainFE = PatchedFM.getFileRef(
testPath(
"foo.cpp"));
450 ASSERT_THAT_EXPECTED(PatchedMainFE, llvm::Succeeded());
451 auto PatchedMainID = PatchedIncludes.getOrCreateID(*PatchedMainFE);
453 PatchedIncludes.includeDepth(PatchedMainID)[PatchedMainID]);
457std::string guard(llvm::StringRef Code) {
458 static int GuardID = 0;
459 std::string GuardName = (
"GUARD_" + llvm::Twine(++GuardID)).str();
460 return llvm::formatv(
"#ifndef {0}\n#define {0}\n{1}\n#endif\n", GuardName,
464std::string once(llvm::StringRef Code) {
465 return llvm::formatv(
"#pragma once\n{0}\n", Code);
468bool mainIsGuarded(
const ParsedAST &
AST) {
469 const auto &SM =
AST.getSourceManager();
470 OptionalFileEntryRef MainFE = SM.getFileEntryRefForID(SM.getMainFileID());
471 return AST.getPreprocessor()
472 .getHeaderSearchInfo()
473 .isFileMultipleIncludeGuarded(*MainFE);
477 return llvm::StringRef(arg.Message).contains(Desc);
481TEST(ParsedASTTest, HeaderGuards) {
483 TU.ImplicitHeaderGuard =
false;
486 EXPECT_FALSE(mainIsGuarded(TU.build()));
488 TU.Code = guard(
";");
489 EXPECT_TRUE(mainIsGuarded(TU.build()));
492 EXPECT_TRUE(mainIsGuarded(TU.build()));
498 EXPECT_FALSE(mainIsGuarded(TU.build()));
507 EXPECT_FALSE(mainIsGuarded(TU.build()));
520TEST(ParsedASTTest, HeaderGuardsSelfInclude) {
529 TU.ImplicitHeaderGuard =
false;
530 TU.Filename =
"self.h";
533 #include "self.h" // error-ok
536 auto AST = TU.build();
537 EXPECT_THAT(
AST.getDiagnostics(),
538 ElementsAre(diag(
"recursively when building a preamble")));
539 EXPECT_FALSE(mainIsGuarded(
AST));
543 #include "self.h" // error-ok
546 EXPECT_THAT(AST.getDiagnostics(), ElementsAre(diag("nested too deeply")));
547 EXPECT_FALSE(mainIsGuarded(
AST));
555 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
556 EXPECT_TRUE(mainIsGuarded(AST));
564 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
565 EXPECT_TRUE(mainIsGuarded(AST));
573 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
574 EXPECT_TRUE(mainIsGuarded(AST));
579 #include "self.h" // error-ok: FIXME, this would be nice to support
584 EXPECT_THAT(AST.getDiagnostics(),
585 ElementsAre(diag("recursively when building a preamble")));
586 EXPECT_TRUE(mainIsGuarded(
AST));
596 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
597 EXPECT_TRUE(mainIsGuarded(AST));
601 #include "self.h" // error-ok
608 EXPECT_THAT(AST.getDiagnostics(),
609 ElementsAre(diag("recursively when building a preamble")));
610 EXPECT_FALSE(mainIsGuarded(
AST));
613 #include "self.h" // error-ok
620 EXPECT_THAT(AST.getDiagnostics(),
621 ElementsAre(diag("recursively when building a preamble")));
622 EXPECT_FALSE(mainIsGuarded(
AST));
632 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
633 EXPECT_FALSE(mainIsGuarded(AST));
636 #include "self.h" // error-ok
641 EXPECT_THAT(AST.getDiagnostics(),
642 ElementsAre(diag("recursively when building a preamble")));
643 EXPECT_TRUE(mainIsGuarded(
AST));
646 #include "self.h" // error-ok
651 EXPECT_THAT(AST.getDiagnostics(),
652 ElementsAre(diag("recursively when building a preamble")));
653 EXPECT_TRUE(mainIsGuarded(
AST));
660TEST(ParsedASTTest, HeaderGuardsImplIface) {
662 // error-ok: we assert on diagnostics explicitly
663 template <class T> struct Traits {
669 // error-ok: we assert on diagnostics explicitly
671 template <class T> unsigned Traits<T>::size() {
677 TU.ImplicitHeaderGuard = false;
678 TU.ExtraArgs.push_back(
"-xc++-header");
682 TU.Filename =
"iface.h";
685 auto AST = TU.build();
686 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
687 EXPECT_TRUE(mainIsGuarded(
AST));
692 EXPECT_THAT(
AST.getDiagnostics(), IsEmpty());
693 EXPECT_TRUE(mainIsGuarded(
AST));
696 TU.Filename =
"impl.h";
698 TU.AdditionalFiles = {{
"iface.h", guard(
Interface)}};
702 EXPECT_THAT(
AST.getDiagnostics(),
703 ElementsAre(diag(
"in included file: main file cannot be included "
704 "recursively when building a preamble")));
705 EXPECT_FALSE(mainIsGuarded(
AST));
707 TU.AdditionalFiles = {{
"iface.h", once(
Interface)}};
709 EXPECT_THAT(
AST.getDiagnostics(),
710 ElementsAre(diag(
"in included file: main file cannot be included "
711 "recursively when building a preamble")));
712 EXPECT_FALSE(mainIsGuarded(
AST));
715TEST(ParsedASTTest, DiscoversPragmaMarks) {
717 TU.AdditionalFiles[
"Header.h"] = R
"(
718 #pragma mark - Something API
720 #pragma mark Something else
724 #pragma mark In Preamble
725 #pragma mark - Something Impl
726 int something() { return 1; }
729 auto AST = TU.build();
731 EXPECT_THAT(
AST.getMarks(), ElementsAre(pragmaTrivia(
" In Preamble"),
732 pragmaTrivia(
" - Something Impl"),
733 pragmaTrivia(
" End")));
736TEST(ParsedASTTest, GracefulFailureOnAssemblyFile) {
738 std::string Code = R
"S(
749 FS.Files[FullFilename] = Code;
753 Argv.push_back(FullFilename);
760 assert(
CI &&
"Failed to build compilation invocation.");
763 EXPECT_FALSE(
AST.has_value())
764 <<
"Should not try to build AST for assembly source file";
767TEST(ParsedASTTest, PreambleWithDifferentTarget) {
768 constexpr std::string_view kPreambleTarget =
"x86_64";
773 TU.Code =
"void bar() { foo(2); }";
774 TU.ExtraArgs.emplace_back(
"-target");
775 TU.ExtraArgs.emplace_back(kPreambleTarget);
776 const auto Preamble = TU.preamble();
779 TU.ExtraArgs.pop_back();
780 TU.ExtraArgs.emplace_back(
"wasm32");
782 IgnoreDiagnostics Diags;
784 auto Inputs = TU.inputs(FS);
786 ASSERT_TRUE(
CI) <<
"Failed to build compiler invocation";
793 EXPECT_EQ(
AST->getASTContext().getTargetInfo().getTriple().getArchName(),
794 llvm::StringRef(kPreambleTarget));
llvm::SmallString< 256U > Name
std::function< std::unique_ptr< Command >()> Implementation
std::string Filename
Filename as a string.
std::vector< const char * > Expected
std::unique_ptr< CompilerInvocation > CI
llvm::DenseMap< HeaderID, unsigned > includeDepth(HeaderID Root=MainFileID) const
HeaderID getOrCreateID(FileEntryRef Entry)
std::optional< HeaderID > getID(const FileEntry *Entry) const
llvm::DenseMap< HeaderID, SmallVector< HeaderID > > IncludeChildren
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
std::string printTemplateSpecializationArgs(const NamedDecl &ND)
Prints template arguments of a decl as written in the source code, including enclosing '<' and '>',...
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
std::string testPath(PathRef File, llvm::sys::path::Style Style)
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
TidyProvider addTidyChecks(llvm::StringRef Checks, llvm::StringRef WarningsAsErrors)
Provider the enables a specific set of checks and warnings as errors.
TEST(BackgroundQueueTest, Priority)
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
IncludesPolicy UnusedIncludes
IncludesPolicy MissingIncludes
struct clang::clangd::Config::@4 Diagnostics
Controls warnings and errors when parsing code.
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
static TestTU withCode(llvm::StringRef Code)
static constexpr const char ArgName[]