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/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/StringRef.h"
35 #include "llvm/Support/Registry.h"
36 #include "llvm/Testing/Annotations/Annotations.h"
37 #include "gmock/gmock-matchers.h"
38 #include "gmock/gmock.h"
39 #include "gtest/gtest.h"
51 Inclusion(const SourceManager
&SM
, SourceLocation HashLoc
,
52 const Token
&IncludeTok
, llvm::StringRef FileName
, bool IsAngled
,
53 CharSourceRange FilenameRange
)
54 : HashOffset(SM
.getDecomposedLoc(HashLoc
).second
), IncTok(IncludeTok
),
55 IncDirective(IncludeTok
.getIdentifierInfo()->getName()),
56 FileNameOffset(SM
.getDecomposedLoc(FilenameRange
.getBegin()).second
),
57 FileName(FileName
), IsAngled(IsAngled
) {
59 toSourceCode(SM
, FilenameRange
.getAsRange()).drop_back().drop_front(),
64 llvm::StringRef IncDirective
;
65 size_t FileNameOffset
;
66 llvm::StringRef FileName
;
69 static std::vector
<Inclusion
> Includes
;
70 static std::vector
<syntax::Token
> SkippedFiles
;
71 struct ReplayPreamblePPCallback
: public PPCallbacks
{
72 const SourceManager
&SM
;
73 explicit ReplayPreamblePPCallback(const SourceManager
&SM
) : SM(SM
) {}
75 void InclusionDirective(SourceLocation HashLoc
, const Token
&IncludeTok
,
76 StringRef FileName
, bool IsAngled
,
77 CharSourceRange FilenameRange
, OptionalFileEntryRef
,
78 StringRef
, StringRef
, const clang::Module
*, bool,
79 SrcMgr::CharacteristicKind
) override
{
80 Includes
.emplace_back(SM
, HashLoc
, IncludeTok
, FileName
, IsAngled
,
84 void FileSkipped(const FileEntryRef
&, const Token
&FilenameTok
,
85 SrcMgr::CharacteristicKind
) override
{
86 SkippedFiles
.emplace_back(FilenameTok
);
89 struct ReplayPreambleCheck
: public tidy::ClangTidyCheck
{
90 ReplayPreambleCheck(StringRef Name
, tidy::ClangTidyContext
*Context
)
91 : ClangTidyCheck(Name
, Context
) {}
92 void registerPPCallbacks(const SourceManager
&SM
, Preprocessor
*PP
,
93 Preprocessor
*ModuleExpanderPP
) override
{
94 PP
->addPPCallbacks(::std::make_unique
<ReplayPreamblePPCallback
>(SM
));
97 llvm::StringLiteral CheckName
= "replay-preamble-check";
98 struct ReplayPreambleModule
: public tidy::ClangTidyModule
{
100 addCheckFactories(tidy::ClangTidyCheckFactories
&CheckFactories
) override
{
101 CheckFactories
.registerCheck
<ReplayPreambleCheck
>(CheckName
);
104 static tidy::ClangTidyModuleRegistry::Add
<ReplayPreambleModule
>
105 X("replay-preamble-module", "");
107 MATCHER_P(rangeIs
, R
, "") {
108 return arg
.beginOffset() == R
.Begin
&& arg
.endOffset() == R
.End
;
111 TEST(ReplayPreambleTest
, IncludesAndSkippedFiles
) {
113 // This check records inclusion directives replayed by clangd.
114 TU
.ClangTidyProvider
= addTidyChecks(CheckName
);
115 llvm::Annotations
Test(R
"cpp(
116 $hash^#$include[[import]] $filebegin^"$filerange
[[bar
.h
]]"
117 $hash^#$include[[include_next]] $filebegin^"$filerange
[[baz
.h
]]"
118 $hash^#$include[[include]] $filebegin^<$filerange[[a.h]]>)cpp");
119 llvm::StringRef Code
= Test
.code();
120 TU
.Code
= Code
.str();
121 TU
.AdditionalFiles
["bar.h"] = "";
122 TU
.AdditionalFiles
["baz.h"] = "";
123 TU
.AdditionalFiles
["a.h"] = "";
124 // Since we are also testing #import directives, and they don't make much
125 // sense in c++ (also they actually break on windows), just set language to
127 TU
.ExtraArgs
= {"-isystem.", "-xobjective-c"};
129 // Allow the check to run even though not marked as fast.
131 Cfg
.Diagnostics
.ClangTidy
.FastCheckFilter
= Config::FastCheckPolicy::Loose
;
132 WithContextValue
WithCfg(Config::Key
, std::move(Cfg
));
134 const auto &AST
= TU
.build();
135 const auto &SM
= AST
.getSourceManager();
137 auto HashLocs
= Test
.points("hash");
138 ASSERT_EQ(HashLocs
.size(), Includes
.size());
139 auto IncludeRanges
= Test
.ranges("include");
140 ASSERT_EQ(IncludeRanges
.size(), Includes
.size());
141 auto FileBeginLocs
= Test
.points("filebegin");
142 ASSERT_EQ(FileBeginLocs
.size(), Includes
.size());
143 auto FileRanges
= Test
.ranges("filerange");
144 ASSERT_EQ(FileRanges
.size(), Includes
.size());
146 ASSERT_EQ(SkippedFiles
.size(), Includes
.size());
147 for (size_t I
= 0; I
< Includes
.size(); ++I
) {
148 const auto &Inc
= Includes
[I
];
150 EXPECT_EQ(Inc
.HashOffset
, HashLocs
[I
]);
152 auto IncRange
= IncludeRanges
[I
];
153 EXPECT_THAT(Inc
.IncTok
.range(SM
), rangeIs(IncRange
));
154 EXPECT_EQ(Inc
.IncTok
.kind(), tok::identifier
);
155 EXPECT_EQ(Inc
.IncDirective
,
156 Code
.substr(IncRange
.Begin
, IncRange
.End
- IncRange
.Begin
));
158 EXPECT_EQ(Inc
.FileNameOffset
, FileBeginLocs
[I
]);
159 EXPECT_EQ(Inc
.IsAngled
, Code
[FileBeginLocs
[I
]] == '<');
161 auto FileRange
= FileRanges
[I
];
162 EXPECT_EQ(Inc
.FileName
,
163 Code
.substr(FileRange
.Begin
, FileRange
.End
- FileRange
.Begin
));
165 EXPECT_EQ(SM
.getDecomposedLoc(SkippedFiles
[I
].location()).second
,
167 // This also contains quotes/angles so increment the range by one from both
170 SkippedFiles
[I
].text(SM
),
171 Code
.substr(FileRange
.Begin
- 1, FileRange
.End
- FileRange
.Begin
+ 2));
172 EXPECT_EQ(SkippedFiles
[I
].kind(), tok::header_name
);
176 } // namespace clangd