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; }
105 Inclusion Expected = testing::get<1>(arg);
106 return std::tie(Actual.HashLine, Actual.Written) ==
107 std::tie(Expected.HashLine, Expected.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) {
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) {
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) {
254 TU.Code =
"inline int foo() { return 0; }";
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) {
275 Inputs.CompileCommand.CommandLine = {
"clang",
"-fsome-unknown-flag",
281 Inputs.CompileCommand.CommandLine = {
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(
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(
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();
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);
406 EXPECT_THAT(Includes.IncludeChildren[*MainID], Contains(*AuxID));
409TEST(ParsedASTTest, PatchesDeletedIncludes) {
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();
444 auto MainFE = FM.getFileRef(
testPath(
"foo.cpp"));
445 ASSERT_THAT_EXPECTED(MainFE, llvm::Succeeded());
446 auto MainID = Includes.getOrCreateID(*MainFE);
447 auto &PatchedFM = PatchedAST->getSourceManager().getFileManager();
449 auto PatchedMainFE = PatchedFM.getFileRef(
testPath(
"foo.cpp"));
450 ASSERT_THAT_EXPECTED(PatchedMainFE, llvm::Succeeded());
451 auto PatchedMainID = PatchedIncludes.getOrCreateID(*PatchedMainFE);
452 EXPECT_EQ(Includes.includeDepth(MainID)[MainID],
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);
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) {
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) {
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 {
668 std::string Implementation = R"cpp(
669 // error-ok: we assert on diagnostics explicitly
671 template <class T> unsigned Traits<T>::size() {
678 TU.ExtraArgs.push_back(
"-xc++-header");
682 TU.Filename =
"iface.h";
684 TU.AdditionalFiles = {{
"impl.h", Implementation}};
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";
697 TU.Code = Implementation;
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) {
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) {
737 std::string Filename =
"TestTU.S";
738 std::string Code = R
"S(
748 std::string FullFilename =
testPath(Filename);
749 FS.Files[FullFilename] = Code;
753 Argv.push_back(FullFilename);
754 Inputs.CompileCommand.Filename = FullFilename;
755 Inputs.CompileCommand.Directory =
testRoot();
756 Inputs.Contents = Code;
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");
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::StringMap< std::string > Files
Stores and provides access to parsed AST.
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.
StoreDiags collects the diagnostics that can later be reported by clangd.
WithContextValue extends Context::current() with a single value.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
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)
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Settings that express user/project preferences and control clangd behavior.
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
IncludesPolicy MissingIncludes
struct clang::clangd::Config::@224206046260313204212274150166346126315121140114 Diagnostics
Controls warnings and errors when parsing code.
TidyProvider ClangTidyProvider
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
static TestTU withCode(llvm::StringRef Code)
llvm::StringMap< std::string > AdditionalFiles
static constexpr const char ArgName[]