1 //===-- ReplayPreambleTests.cpp -------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // These tests cover clangd's logic to replay PP events from preamble to
12 //===----------------------------------------------------------------------===//
14 #include "../../clang-tidy/ClangTidyCheck.h"
15 #include "../../clang-tidy/ClangTidyModule.h"
16 #include "../../clang-tidy/ClangTidyModuleRegistry.h"
19 #include "Diagnostics.h"
20 #include "ParsedAST.h"
21 #include "SourceCode.h"
23 #include "TidyProvider.h"
24 #include "support/Context.h"
25 #include "clang/AST/DeclTemplate.h"
26 #include "clang/Basic/FileEntry.h"
27 #include "clang/Basic/LLVM.h"
28 #include "clang/Basic/Module.h"
29 #include "clang/Basic/SourceLocation.h"
30 #include "clang/Basic/SourceManager.h"
31 #include "clang/Basic/TokenKinds.h"
32 #include "clang/Lex/PPCallbacks.h"
33 #include "clang/Lex/Token.h"
34 #include "clang/Tooling/Syntax/Tokens.h"
35 #include "llvm/ADT/StringRef.h"
36 #include "llvm/Support/Registry.h"
37 #include "llvm/Testing/Annotations/Annotations.h"
38 #include "gmock/gmock-matchers.h"
39 #include "gmock/gmock.h"
40 #include "gtest/gtest.h"
45 namespace clang::clangd
{
48 Inclusion(const SourceManager
&SM
, SourceLocation HashLoc
,
49 const Token
&IncludeTok
, llvm::StringRef FileName
, bool IsAngled
,
50 CharSourceRange FilenameRange
)
51 : HashOffset(SM
.getDecomposedLoc(HashLoc
).second
), IncTok(IncludeTok
),
52 IncDirective(IncludeTok
.getIdentifierInfo()->getName()),
53 FileNameOffset(SM
.getDecomposedLoc(FilenameRange
.getBegin()).second
),
54 FileName(FileName
), IsAngled(IsAngled
) {
56 toSourceCode(SM
, FilenameRange
.getAsRange()).drop_back().drop_front(),
61 llvm::StringRef IncDirective
;
62 size_t FileNameOffset
;
63 llvm::StringRef FileName
;
66 static std::vector
<Inclusion
> Includes
;
67 static std::vector
<syntax::Token
> SkippedFiles
;
68 struct ReplayPreamblePPCallback
: public PPCallbacks
{
69 const SourceManager
&SM
;
70 explicit ReplayPreamblePPCallback(const SourceManager
&SM
) : SM(SM
) {}
72 void InclusionDirective(SourceLocation HashLoc
, const Token
&IncludeTok
,
73 StringRef FileName
, bool IsAngled
,
74 CharSourceRange FilenameRange
, OptionalFileEntryRef
,
75 StringRef
, StringRef
, const clang::Module
*,
76 SrcMgr::CharacteristicKind
) override
{
77 Includes
.emplace_back(SM
, HashLoc
, IncludeTok
, FileName
, IsAngled
,
81 void FileSkipped(const FileEntryRef
&, const Token
&FilenameTok
,
82 SrcMgr::CharacteristicKind
) override
{
83 SkippedFiles
.emplace_back(FilenameTok
);
86 struct ReplayPreambleCheck
: public tidy::ClangTidyCheck
{
87 ReplayPreambleCheck(StringRef Name
, tidy::ClangTidyContext
*Context
)
88 : ClangTidyCheck(Name
, Context
) {}
89 void registerPPCallbacks(const SourceManager
&SM
, Preprocessor
*PP
,
90 Preprocessor
*ModuleExpanderPP
) override
{
91 PP
->addPPCallbacks(::std::make_unique
<ReplayPreamblePPCallback
>(SM
));
94 llvm::StringLiteral CheckName
= "replay-preamble-check";
95 struct ReplayPreambleModule
: public tidy::ClangTidyModule
{
97 addCheckFactories(tidy::ClangTidyCheckFactories
&CheckFactories
) override
{
98 CheckFactories
.registerCheck
<ReplayPreambleCheck
>(CheckName
);
101 static tidy::ClangTidyModuleRegistry::Add
<ReplayPreambleModule
>
102 X("replay-preamble-module", "");
104 MATCHER_P(rangeIs
, R
, "") {
105 return arg
.beginOffset() == R
.Begin
&& arg
.endOffset() == R
.End
;
108 TEST(ReplayPreambleTest
, IncludesAndSkippedFiles
) {
110 // This check records inclusion directives replayed by clangd.
111 TU
.ClangTidyProvider
= addTidyChecks(CheckName
);
112 llvm::Annotations
Test(R
"cpp(
113 $hash^#$include[[import]] $filebegin^"$filerange
[[bar
.h
]]"
114 $hash^#$include[[include_next]] $filebegin^"$filerange
[[baz
.h
]]"
115 $hash^#$include[[include]] $filebegin^<$filerange[[a.h]]>)cpp");
116 llvm::StringRef Code
= Test
.code();
117 TU
.Code
= Code
.str();
118 TU
.AdditionalFiles
["bar.h"] = "";
119 TU
.AdditionalFiles
["baz.h"] = "";
120 TU
.AdditionalFiles
["a.h"] = "";
121 // Since we are also testing #import directives, and they don't make much
122 // sense in c++ (also they actually break on windows), just set language to
124 TU
.ExtraArgs
= {"-isystem.", "-xobjective-c"};
126 // Allow the check to run even though not marked as fast.
128 Cfg
.Diagnostics
.ClangTidy
.FastCheckFilter
= Config::FastCheckPolicy::Loose
;
129 WithContextValue
WithCfg(Config::Key
, std::move(Cfg
));
131 const auto &AST
= TU
.build();
132 const auto &SM
= AST
.getSourceManager();
134 auto HashLocs
= Test
.points("hash");
135 ASSERT_EQ(HashLocs
.size(), Includes
.size());
136 auto IncludeRanges
= Test
.ranges("include");
137 ASSERT_EQ(IncludeRanges
.size(), Includes
.size());
138 auto FileBeginLocs
= Test
.points("filebegin");
139 ASSERT_EQ(FileBeginLocs
.size(), Includes
.size());
140 auto FileRanges
= Test
.ranges("filerange");
141 ASSERT_EQ(FileRanges
.size(), Includes
.size());
143 ASSERT_EQ(SkippedFiles
.size(), Includes
.size());
144 for (size_t I
= 0; I
< Includes
.size(); ++I
) {
145 const auto &Inc
= Includes
[I
];
147 EXPECT_EQ(Inc
.HashOffset
, HashLocs
[I
]);
149 auto IncRange
= IncludeRanges
[I
];
150 EXPECT_THAT(Inc
.IncTok
.range(SM
), rangeIs(IncRange
));
151 EXPECT_EQ(Inc
.IncTok
.kind(), tok::identifier
);
152 EXPECT_EQ(Inc
.IncDirective
,
153 Code
.substr(IncRange
.Begin
, IncRange
.End
- IncRange
.Begin
));
155 EXPECT_EQ(Inc
.FileNameOffset
, FileBeginLocs
[I
]);
156 EXPECT_EQ(Inc
.IsAngled
, Code
[FileBeginLocs
[I
]] == '<');
158 auto FileRange
= FileRanges
[I
];
159 EXPECT_EQ(Inc
.FileName
,
160 Code
.substr(FileRange
.Begin
, FileRange
.End
- FileRange
.Begin
));
162 EXPECT_EQ(SM
.getDecomposedLoc(SkippedFiles
[I
].location()).second
,
164 // This also contains quotes/angles so increment the range by one from both
167 SkippedFiles
[I
].text(SM
),
168 Code
.substr(FileRange
.Begin
- 1, FileRange
.End
- FileRange
.Begin
+ 2));
169 EXPECT_EQ(SkippedFiles
[I
].kind(), tok::header_name
);
173 } // namespace clang::clangd