clang-tools 23.0.0git
ReplayPeambleTests.cpp
Go to the documentation of this file.
1//===-- ReplayPreambleTests.cpp -------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// These tests cover clangd's logic to replay PP events from preamble to
10// clang-tidy checks.
11//
12//===----------------------------------------------------------------------===//
13
16#include "AST.h"
17#include "Config.h"
18#include "Diagnostics.h"
19#include "ParsedAST.h"
20#include "SourceCode.h"
21#include "TestTU.h"
22#include "TidyProvider.h"
23#include "support/Context.h"
24#include "clang/AST/DeclTemplate.h"
25#include "clang/Basic/FileEntry.h"
26#include "clang/Basic/LLVM.h"
27#include "clang/Basic/SourceLocation.h"
28#include "clang/Basic/SourceManager.h"
29#include "clang/Basic/TokenKinds.h"
30#include "clang/Lex/PPCallbacks.h"
31#include "clang/Lex/Token.h"
32#include "clang/Tooling/Syntax/Tokens.h"
33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/Registry.h"
35#include "llvm/Testing/Annotations/Annotations.h"
36#include "gmock/gmock-matchers.h"
37#include "gmock/gmock.h"
38#include "gtest/gtest.h"
39#include <cstddef>
40#include <memory>
41#include <vector>
42
43namespace clang {
44
45class Module;
46
47namespace clangd {
48namespace {
49struct Inclusion {
50 Inclusion(const SourceManager &SM, SourceLocation HashLoc,
51 const Token &IncludeTok, llvm::StringRef FileName, bool IsAngled,
52 CharSourceRange FilenameRange)
53 : HashOffset(SM.getDecomposedLoc(HashLoc).second), IncTok(IncludeTok),
54 IncDirective(IncludeTok.getIdentifierInfo()->getName()),
55 FileNameOffset(SM.getDecomposedLoc(FilenameRange.getBegin()).second),
56 FileName(FileName), IsAngled(IsAngled) {
57 EXPECT_EQ(
58 toSourceCode(SM, FilenameRange.getAsRange()).drop_back().drop_front(),
59 FileName);
60 }
61 size_t HashOffset;
62 syntax::Token IncTok;
63 llvm::StringRef IncDirective;
64 size_t FileNameOffset;
65 llvm::StringRef FileName;
66 bool IsAngled;
67};
68static std::vector<Inclusion> Includes;
69static std::vector<syntax::Token> SkippedFiles;
70static std::vector<std::string> DefinedMacros;
71struct ReplayPreamblePPCallback : public PPCallbacks {
72 const SourceManager &SM;
73 explicit ReplayPreamblePPCallback(const SourceManager &SM) : SM(SM) {}
74
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,
81 FilenameRange);
82 }
83
84 void FileSkipped(const FileEntryRef &, const Token &FilenameTok,
85 SrcMgr::CharacteristicKind) override {
86 SkippedFiles.emplace_back(FilenameTok);
87 }
88
89 void MacroDefined(const Token &MacroNameTok,
90 const MacroDirective *MD) override {
91 DefinedMacros.push_back(MacroNameTok.getIdentifierInfo()->getName().str());
92 }
93};
94struct ReplayPreambleCheck : public tidy::ClangTidyCheck {
95 ReplayPreambleCheck(StringRef Name, tidy::ClangTidyContext *Context)
96 : ClangTidyCheck(Name, Context) {}
97 void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
98 Preprocessor *ModuleExpanderPP) override {
99 PP->addPPCallbacks(::std::make_unique<ReplayPreamblePPCallback>(SM));
100 }
101};
102llvm::StringLiteral CheckName = "replay-preamble-check";
103struct ReplayPreambleModule : public tidy::ClangTidyModule {
104 void
105 addCheckFactories(tidy::ClangTidyCheckFactories &CheckFactories) override {
106 CheckFactories.registerCheck<ReplayPreambleCheck>(CheckName);
107 }
108};
109static tidy::ClangTidyModuleRegistry::Add<ReplayPreambleModule>
110 X("replay-preamble-module", "");
111
112MATCHER_P(rangeIs, R, "") {
113 return arg.beginOffset() == R.Begin && arg.endOffset() == R.End;
114}
115
116TEST(ReplayPreambleTest, MacroDefinitions) {
117 DefinedMacros.clear();
118
119 TestTU TU;
120 TU.ClangTidyProvider = addTidyChecks(CheckName);
121 TU.Code = R"cpp(
122 #ifndef _TEST_H
123 #define _TEST_H
124 #define _TEST_MACRO
125 #endif
126 )cpp";
127
128 Config Cfg;
130 WithContextValue WithCfg(Config::Key, std::move(Cfg));
131
132 TU.build();
133
134 EXPECT_THAT(DefinedMacros, testing::Contains(std::string("_TEST_H")));
135 EXPECT_THAT(DefinedMacros, testing::Contains(std::string("_TEST_MACRO")));
136}
137
138TEST(ReplayPreambleTest, MacroDefinitionsPartialPreamble) {
139 DefinedMacros.clear();
140
141 TestTU TU;
142 TU.ClangTidyProvider = addTidyChecks(CheckName);
143 TU.Code = R"cpp(
144 #ifndef _TEST_H
145 #define _TEST_H
146 void unused(void);
147 #define _TEST_MACRO
148 #endif
149 )cpp";
150
151 Config Cfg;
153 WithContextValue WithCfg(Config::Key, std::move(Cfg));
154
155 TU.build();
156
157 // Both macros should be seen by clang-tidy
158 EXPECT_THAT(DefinedMacros, testing::Contains(std::string("_TEST_H")));
159 EXPECT_THAT(DefinedMacros, testing::Contains(std::string("_TEST_MACRO")));
160}
161
162TEST(ReplayPreambleTest, IncludesAndSkippedFiles) {
163 TestTU TU;
164 // This check records inclusion directives replayed by clangd.
165 TU.ClangTidyProvider = addTidyChecks(CheckName);
166 llvm::Annotations Test(R"cpp(
167 $hash^#$include[[import]] $filebegin^"$filerange[[bar.h]]"
168 $hash^#$include[[include_next]] $filebegin^"$filerange[[baz.h]]"
169 $hash^#$include[[include]] $filebegin^<$filerange[[a.h]]>)cpp");
170 llvm::StringRef Code = Test.code();
171 TU.Code = Code.str();
172 TU.AdditionalFiles["bar.h"] = "";
173 TU.AdditionalFiles["baz.h"] = "";
174 TU.AdditionalFiles["a.h"] = "";
175 // Since we are also testing #import directives, and they don't make much
176 // sense in c++ (also they actually break on windows), just set language to
177 // obj-c.
178 TU.ExtraArgs = {"-isystem.", "-xobjective-c"};
179
180 // Allow the check to run even though not marked as fast.
181 Config Cfg;
183 WithContextValue WithCfg(Config::Key, std::move(Cfg));
184
185 const auto &AST = TU.build();
186 const auto &SM = AST.getSourceManager();
187
188 auto HashLocs = Test.points("hash");
189 ASSERT_EQ(HashLocs.size(), Includes.size());
190 auto IncludeRanges = Test.ranges("include");
191 ASSERT_EQ(IncludeRanges.size(), Includes.size());
192 auto FileBeginLocs = Test.points("filebegin");
193 ASSERT_EQ(FileBeginLocs.size(), Includes.size());
194 auto FileRanges = Test.ranges("filerange");
195 ASSERT_EQ(FileRanges.size(), Includes.size());
196
197 ASSERT_EQ(SkippedFiles.size(), Includes.size());
198 for (size_t I = 0; I < Includes.size(); ++I) {
199 const auto &Inc = Includes[I];
200
201 EXPECT_EQ(Inc.HashOffset, HashLocs[I]);
202
203 auto IncRange = IncludeRanges[I];
204 EXPECT_THAT(Inc.IncTok.range(SM), rangeIs(IncRange));
205 EXPECT_EQ(Inc.IncTok.kind(), tok::identifier);
206 EXPECT_EQ(Inc.IncDirective,
207 Code.substr(IncRange.Begin, IncRange.End - IncRange.Begin));
208
209 EXPECT_EQ(Inc.FileNameOffset, FileBeginLocs[I]);
210 EXPECT_EQ(Inc.IsAngled, Code[FileBeginLocs[I]] == '<');
211
212 auto FileRange = FileRanges[I];
213 EXPECT_EQ(Inc.FileName,
214 Code.substr(FileRange.Begin, FileRange.End - FileRange.Begin));
215
216 EXPECT_EQ(SM.getDecomposedLoc(SkippedFiles[I].location()).second,
217 Inc.FileNameOffset);
218 // This also contains quotes/angles so increment the range by one from both
219 // sides.
220 EXPECT_EQ(
221 SkippedFiles[I].text(SM),
222 Code.substr(FileRange.Begin - 1, FileRange.End - FileRange.Begin + 2));
223 EXPECT_EQ(SkippedFiles[I].kind(), tok::header_name);
224 }
225}
226} // namespace
227} // namespace clangd
228} // namespace clang
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
WithContextValue extends Context::current() with a single value.
Definition Context.h:200
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
MATCHER_P(named, N, "")
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
TidyProvider addTidyChecks(llvm::StringRef Checks, llvm::StringRef WarningsAsErrors)
Provider the enables a specific set of checks and warnings as errors.
TEST(BackgroundQueueTest, Priority)
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Settings that express user/project preferences and control clangd behavior.
Definition Config.h:45
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition Config.h:49
FastCheckPolicy FastCheckFilter
Definition Config.h:113
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252 Diagnostics
Controls warnings and errors when parsing code.
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252::@107156241027253143221327255130274177352007274355 ClangTidy
Configures what clang-tidy checks to run and options to use with them.
TidyProvider ClangTidyProvider
Definition TestTU.h:65