clang-tools 24.0.0git
FeatureModulesTests.cpp
Go to the documentation of this file.
1//===--- FeatureModulesTests.cpp -------------------------------*- C++ -*-===//
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#include "Annotations.h"
10#include "FeatureModule.h"
11#include "Selection.h"
12#include "TestTU.h"
13#include "refactor/Tweak.h"
14#include "support/Logger.h"
15#include "clang/AST/Decl.h"
16#include "clang/Frontend/FrontendOptions.h"
17#include "clang/Lex/PPCallbacks.h"
18#include "clang/Lex/PreprocessorOptions.h"
19#include "llvm/Support/Error.h"
20#include "gmock/gmock.h"
21#include "gtest/gtest.h"
22#include <functional>
23#include <memory>
24
25namespace clang {
26namespace clangd {
27namespace {
28
29struct TestModule final : FeatureModule {
30 struct Listener final : ASTListener {
31 Listener(TestModule &Module) : Module(Module) {}
32
33 void beforePPCallbacks(CompilerInstance &CI) override {
34 if (Module.BeforePPCallbacks)
35 Module.BeforePPCallbacks(CI);
36 }
37 void beforeExecute(CompilerInstance &CI) override {
38 if (Module.BeforeExecute)
39 Module.BeforeExecute(CI);
40 }
41 void afterExecute(CompilerInstance &CI) override {
42 if (Module.AfterExecute)
43 Module.AfterExecute(CI);
44 }
45 void finalizeDiagnostic(clangd::Diag &Diag) override {
46 if (Module.FinalizeDiagnostic)
47 Module.FinalizeDiagnostic(Diag);
48 }
49
50 private:
51 TestModule &Module;
52 };
53
54 std::unique_ptr<ASTListener> astListeners() override {
55 return std::make_unique<Listener>(*this);
56 }
57
58 std::function<void(CompilerInstance &)> BeforePPCallbacks;
59 std::function<void(CompilerInstance &)> BeforeExecute;
60 std::function<void(CompilerInstance &)> AfterExecute;
61 std::function<void(clangd::Diag &)> FinalizeDiagnostic;
62};
63
64TEST(FeatureModulesTest, ContributesTweak) {
65 static constexpr const char *TweakID = "ModuleTweak";
66 struct TweakContributingModule final : public FeatureModule {
67 struct ModuleTweak final : public Tweak {
68 const char *id() const override { return TweakID; }
69 bool prepare(const Selection &Sel) override { return true; }
70 Expected<Effect> apply(const Selection &Sel) override {
71 return error("not implemented");
72 }
73 std::string title() const override { return id(); }
74 llvm::StringLiteral kind() const override {
75 return llvm::StringLiteral("");
76 };
77 };
78
79 void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) override {
80 Out.emplace_back(new ModuleTweak);
81 }
82 };
83
85 Set.add(std::make_unique<TweakContributingModule>());
86
87 auto AST = TestTU::withCode("").build();
88 auto Tree =
89 SelectionTree::createRight(AST.getASTContext(), AST.getTokens(), 0, 0);
90 auto Actual = prepareTweak(
91 TweakID, Tweak::Selection(nullptr, AST, 0, 0, std::move(Tree), nullptr),
92 &Set);
93 ASSERT_TRUE(bool(Actual));
94 EXPECT_EQ(Actual->get()->id(), TweakID);
95}
96
97TEST(FeatureModulesTest, SuppressDiags) {
98 struct DiagModifierModule final : public FeatureModule {
99 struct Listener : public FeatureModule::ASTListener {
100 void sawDiagnostic(const clang::Diagnostic &Info,
101 clangd::Diag &Diag) override {
102 Diag.Severity = DiagnosticsEngine::Ignored;
103 }
104 };
105 std::unique_ptr<ASTListener> astListeners() override {
106 return std::make_unique<Listener>();
107 };
108 };
110 FMS.add(std::make_unique<DiagModifierModule>());
111
112 Annotations Code("[[test]]; /* error-ok */");
113 TestTU TU;
114 TU.Code = Code.code().str();
115
116 {
117 auto AST = TU.build();
118 EXPECT_THAT(AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
119 }
120
121 TU.FeatureModules = &FMS;
122 {
123 auto AST = TU.build();
124 EXPECT_THAT(AST.getDiagnostics(), testing::IsEmpty());
125 }
126}
127
128TEST(FeatureModulesTest, BeforePPCallbacks) {
129 struct IncludeRecorder : public PPCallbacks {
130 IncludeRecorder(std::vector<std::string> &Includes) : Includes(Includes) {}
131
132 void InclusionDirective(SourceLocation, const Token &, StringRef FileName,
133 bool, CharSourceRange, OptionalFileEntryRef,
134 StringRef, StringRef, const clang::Module *, bool,
135 SrcMgr::CharacteristicKind) override {
136 Includes.push_back(FileName.str());
137 }
138
139 private:
140 std::vector<std::string> &Includes;
141 };
142 std::vector<std::string> Includes;
143 auto Module = std::make_unique<TestModule>();
144 Module->BeforePPCallbacks = [&Includes](CompilerInstance &CI) {
145 // The preamble build processes the main file's initial directives,
146 // including #include "header.h", and the included header's contents. The
147 // main-file build reuses that preamble and skips those directives.
148 // ReplayPreamble synthesizes InclusionDirective callbacks for the saved
149 // direct includes. Register only during the main-file build to observe this
150 // replay, rather than the original include during preamble construction.
151 if (CI.getFrontendOpts().ProgramAction == frontend::ParseSyntaxOnly)
152 CI.getPreprocessor().addPPCallbacks(
153 std::make_unique<IncludeRecorder>(Includes));
154 };
156 FMS.add(std::move(Module));
157
158 TestTU TU = TestTU::withCode(R"cpp(
159 #include "header.h"
160 void mainFileFunc(); // Ends the preamble; parsed during the main-file build.
161 )cpp");
162 TU.AdditionalFiles["header.h"] = "";
163 TU.FeatureModules = &FMS;
164 TU.build();
165 EXPECT_THAT(Includes, testing::ElementsAre("header.h"));
166}
167
168TEST(FeatureModulesTest, BeforeExecute) {
169 auto Module = std::make_unique<TestModule>();
170 Module->BeforeExecute = [](CompilerInstance &CI) {
171 CI.getPreprocessor().SetSuppressIncludeNotFoundError(true);
172 };
174 FMS.add(std::move(Module));
175
176 TestTU TU = TestTU::withCode(R"cpp(
177 /*error-ok*/
178 #include "not_found.h"
179
180 void foo() {
181 #include "not_found_not_preamble.h"
182 }
183 )cpp");
184
185 {
186 auto AST = TU.build();
187 EXPECT_THAT(AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
188 }
189
190 TU.FeatureModules = &FMS;
191 {
192 auto AST = TU.build();
193 EXPECT_THAT(AST.getDiagnostics(), testing::IsEmpty());
194 }
195}
196
197TEST(FeatureModulesTest, AfterExecute) {
198 std::vector<std::string> DeclNames;
199 auto Module = std::make_unique<TestModule>();
200 Module->AfterExecute = [&DeclNames](CompilerInstance &CI) {
201 for (Decl *D : CI.getASTContext().getTraversalScope())
202 if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
203 DeclNames.push_back(ND->getNameAsString());
204 };
206 FMS.add(std::move(Module));
207
208 TestTU TU = TestTU::withCode(R"cpp(
209 #include "header.h"
210 void mainFileFunc();
211 )cpp");
212 TU.AdditionalFiles["header.h"] = "void headerFunc();";
213 TU.FeatureModules = &FMS;
214 TU.build();
215
216 // afterExecute runs once clangd has restricted the traversal scope, so the
217 // declaration from the header is intentionally not visible here.
218 EXPECT_THAT(DeclNames, testing::ElementsAre("mainFileFunc"));
219}
220
221TEST(FeatureModulesTest, FinalizeDiagnostic) {
222 unsigned Notes = 0;
223 unsigned Fixes = 0;
224 auto Module = std::make_unique<TestModule>();
225 Module->FinalizeDiagnostic = [&](clangd::Diag &Diag) {
226 if (Diag.Message.find("undeclared identifier 'fooo'") == std::string::npos)
227 return;
228 Notes = Diag.Notes.size();
229 Fixes = Diag.Fixes.size();
230 };
232 FMS.add(std::move(Module));
233
234 TestTU TU = TestTU::withCode(R"cpp(
235 void foo();
236 void bar() { fooo(); } // error-ok
237 )cpp");
238 TU.FeatureModules = &FMS;
239 EXPECT_THAT(TU.build().getDiagnostics(), testing::SizeIs(1));
240 EXPECT_EQ(Notes, 1u);
241 EXPECT_EQ(Fixes, 1u);
242}
243
244} // namespace
245} // namespace clangd
246} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
A FeatureModuleSet is a collection of feature modules installed in clangd.
void add(std::unique_ptr< FeatureModule > M)
A FeatureModule contributes a vertical feature to clangd.
static SelectionTree createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End)
llvm::Error error(std::error_code, std::string &&)
Definition Logger.cpp:80
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
llvm::Expected< std::unique_ptr< Tweak > > prepareTweak(StringRef ID, const Tweak::Selection &S, const FeatureModuleSet *Modules)
Definition Tweak.cpp:91
TEST(BackgroundQueueTest, Priority)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A top-level diagnostic that may have Notes and Fixes.
Definition Diagnostics.h:98
std::vector< Fix > Fixes
Alternative fixes for this diagnostic, one should be chosen.
std::vector< Note > Notes
Elaborate on the problem, usually pointing to a related piece of code.
std::string Code
Definition TestTU.h:49
ParsedAST build() const
Definition TestTU.cpp:115
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36
Input to prepare and apply tweaks.
Definition Tweak.h:49