Skip to content

Commit dbf486c

Browse files
committed
[clangd] Config: Index.Background
Summary: We only support Build/Skip for now, but with 'Load' or similar as an option for future (load existing shards but don't build new ones). This requires creating the config for each TU on startup. In LLVM, this is 4000 occurrences for a total of 800ms on my machine. But together with caching from D83755 it is only 25ms. Reviewers: kadircet Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D83768
1 parent bfd6433 commit dbf486c

File tree

7 files changed

+117
-1
lines changed

7 files changed

+117
-1
lines changed

clang-tools-extra/clangd/Config.h

+7
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ struct Config {
5555
std::vector<llvm::unique_function<void(std::vector<std::string> &) const>>
5656
Edits;
5757
} CompileFlags;
58+
59+
enum class BackgroundPolicy { Build, Skip };
60+
/// Controls background-index behavior.
61+
struct {
62+
/// Whether this TU should be indexed.
63+
BackgroundPolicy Background = BackgroundPolicy::Build;
64+
} Index;
5865
};
5966

6067
} // namespace clangd

clang-tools-extra/clangd/ConfigCompile.cpp

+62
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
#include "ConfigFragment.h"
2929
#include "support/Logger.h"
3030
#include "support/Trace.h"
31+
#include "llvm/ADT/STLExtras.h"
3132
#include "llvm/ADT/StringRef.h"
33+
#include "llvm/ADT/StringSwitch.h"
3234
#include "llvm/Support/Regex.h"
3335
#include "llvm/Support/SMLoc.h"
3436
#include "llvm/Support/SourceMgr.h"
@@ -80,9 +82,56 @@ struct FragmentCompiler {
8082
return Result;
8183
}
8284

85+
// Helper with similar API to StringSwitch, for parsing enum values.
86+
template <typename T> class EnumSwitch {
87+
FragmentCompiler &Outer;
88+
llvm::StringRef EnumName;
89+
const Located<std::string> &Input;
90+
llvm::Optional<T> Result;
91+
llvm::SmallVector<llvm::StringLiteral, 8> ValidValues;
92+
93+
public:
94+
EnumSwitch(llvm::StringRef EnumName, const Located<std::string> &In,
95+
FragmentCompiler &Outer)
96+
: Outer(Outer), EnumName(EnumName), Input(In) {}
97+
98+
EnumSwitch &map(llvm::StringLiteral Name, T Value) {
99+
assert(!llvm::is_contained(ValidValues, Name) && "Duplicate value!");
100+
ValidValues.push_back(Name);
101+
if (!Result && *Input == Name)
102+
Result = Value;
103+
return *this;
104+
}
105+
106+
llvm::Optional<T> value() {
107+
if (!Result)
108+
Outer.diag(
109+
Warning,
110+
llvm::formatv("Invalid {0} value '{1}'. Valid values are {2}.",
111+
EnumName, *Input, llvm::join(ValidValues, ", "))
112+
.str(),
113+
Input.Range);
114+
return Result;
115+
};
116+
};
117+
118+
// Attempt to parse a specified string into an enum.
119+
// Yields llvm::None and produces a diagnostic on failure.
120+
//
121+
// Optional<T> Value = compileEnum<En>("Foo", Frag.Foo)
122+
// .map("Foo", Enum::Foo)
123+
// .map("Bar", Enum::Bar)
124+
// .value();
125+
template <typename T>
126+
EnumSwitch<T> compileEnum(llvm::StringRef EnumName,
127+
const Located<std::string> &In) {
128+
return EnumSwitch<T>(EnumName, In, *this);
129+
}
130+
83131
void compile(Fragment &&F) {
84132
compile(std::move(F.If));
85133
compile(std::move(F.CompileFlags));
134+
compile(std::move(F.Index));
86135
}
87136

88137
void compile(Fragment::IfBlock &&F) {
@@ -148,7 +197,20 @@ struct FragmentCompiler {
148197
}
149198
}
150199

200+
void compile(Fragment::IndexBlock &&F) {
201+
if (F.Background) {
202+
if (auto Val = compileEnum<Config::BackgroundPolicy>("Background",
203+
**F.Background)
204+
.map("Build", Config::BackgroundPolicy::Build)
205+
.map("Skip", Config::BackgroundPolicy::Skip)
206+
.value())
207+
Out.Apply.push_back([Val](Config &C) { C.Index.Background = *Val; });
208+
}
209+
}
210+
151211
constexpr static llvm::SourceMgr::DiagKind Error = llvm::SourceMgr::DK_Error;
212+
constexpr static llvm::SourceMgr::DiagKind Warning =
213+
llvm::SourceMgr::DK_Warning;
152214
void diag(llvm::SourceMgr::DiagKind Kind, llvm::StringRef Message,
153215
llvm::SMRange Range) {
154216
if (Range.isValid() && SourceMgr != nullptr)

clang-tools-extra/clangd/ConfigFragment.h

+11
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ struct Fragment {
150150
std::vector<Located<std::string>> Remove;
151151
};
152152
CompileFlagsBlock CompileFlags;
153+
154+
/// Controls how clangd understands code outside the current file.
155+
/// clangd's indexes provide information about symbols that isn't available
156+
/// to clang's parser, such as incoming references.
157+
struct IndexBlock {
158+
/// Whether files are built in the background to produce a project index.
159+
/// This is checked for translation units only, not headers they include.
160+
/// Legal values are "Build" or "Skip".
161+
llvm::Optional<Located<std::string>> Background;
162+
};
163+
IndexBlock Index;
153164
};
154165

155166
} // namespace config

clang-tools-extra/clangd/ConfigYAML.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "ConfigFragment.h"
10+
#include "llvm/ADT/Optional.h"
1011
#include "llvm/ADT/SmallSet.h"
1112
#include "llvm/ADT/StringRef.h"
1213
#include "llvm/Support/MemoryBuffer.h"
@@ -70,6 +71,13 @@ class Parser {
7071
Dict.parse(N);
7172
}
7273

74+
void parse(Fragment::IndexBlock &F, Node &N) {
75+
DictParser Dict("Index", this);
76+
Dict.handle("Background",
77+
[&](Node &N) { F.Background = scalarValue(N, "Background"); });
78+
Dict.parse(N);
79+
}
80+
7381
// Helper for parsing mapping nodes (dictionaries).
7482
// We don't use YamlIO as we want to control over unknown keys.
7583
class DictParser {

clang-tools-extra/clangd/index/Background.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "index/Background.h"
1010
#include "Compiler.h"
11+
#include "Config.h"
1112
#include "Headers.h"
1213
#include "ParsedAST.h"
1314
#include "SourceCode.h"
@@ -354,6 +355,14 @@ llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
354355
// staleness.
355356
std::vector<std::string>
356357
BackgroundIndex::loadProject(std::vector<std::string> MainFiles) {
358+
// Drop files where background indexing is disabled in config.
359+
if (ContextProvider)
360+
llvm::erase_if(MainFiles, [&](const std::string &TU) {
361+
// Load the config for each TU, as indexing may be selectively enabled.
362+
WithContext WithProvidedContext(ContextProvider(TU));
363+
return Config::current().Index.Background ==
364+
Config::BackgroundPolicy::Skip;
365+
});
357366
Rebuilder.startLoading();
358367
// Load shards for all of the mainfiles.
359368
const std::vector<LoadedShard> Result =

clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ TEST_F(BackgroundIndexTest, Config) {
113113
// Set up two identical TUs, foo and bar.
114114
// They define foo::one and bar::one.
115115
std::vector<tooling::CompileCommand> Cmds;
116-
for (std::string Name : {"foo", "bar"}) {
116+
for (std::string Name : {"foo", "bar", "baz"}) {
117117
std::string Filename = Name + ".cpp";
118118
std::string Header = Name + ".h";
119119
FS.Files[Filename] = "#include \"" + Header + "\"";
@@ -126,11 +126,14 @@ TEST_F(BackgroundIndexTest, Config) {
126126
}
127127
// Context provider that installs a configuration mutating foo's command.
128128
// This causes it to define foo::two instead of foo::one.
129+
// It also disables indexing of baz entirely.
129130
auto ContextProvider = [](PathRef P) {
130131
Config C;
131132
if (P.endswith("foo.cpp"))
132133
C.CompileFlags.Edits.push_back(
133134
[](std::vector<std::string> &Argv) { Argv.push_back("-Done=two"); });
135+
if (P.endswith("baz.cpp"))
136+
C.Index.Background = Config::BackgroundPolicy::Skip;
134137
return Context::current().derive(Config::Key, std::move(C));
135138
};
136139
// Create the background index.

clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ TEST_F(ConfigCompileTests, CompileCommands) {
100100
EXPECT_THAT(Argv, ElementsAre("clang", "a.cc", "-foo"));
101101
}
102102

103+
TEST_F(ConfigCompileTests, Index) {
104+
Frag.Index.Background.emplace("Skip");
105+
EXPECT_TRUE(compileAndApply());
106+
EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
107+
108+
Frag = {};
109+
Frag.Index.Background.emplace("Foo");
110+
EXPECT_TRUE(compileAndApply());
111+
EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Build)
112+
<< "by default";
113+
EXPECT_THAT(
114+
Diags.Diagnostics,
115+
ElementsAre(DiagMessage(
116+
"Invalid Background value 'Foo'. Valid values are Build, Skip.")));
117+
}
118+
103119
} // namespace
104120
} // namespace config
105121
} // namespace clangd

0 commit comments

Comments
 (0)