summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clangd/ConfigFragment.h
blob: 31c4636efa0b22d72480ec50f4b718e947f0e699 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
//===--- ConfigFragment.h - Unit of user-specified configuration -*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Various clangd features have configurable behaviour (or can be disabled).
// The configuration system allows users to control this:
//  - in a user config file, a project config file, via LSP, or via flags
//  - specifying different settings for different files
//
// This file defines the config::Fragment structure which models one piece of
// configuration as obtained from a source like a file.
//
// This is distinct from how the config is interpreted (CompiledFragment),
// combined (Provider) and exposed to the rest of clangd (Config).
//
//===----------------------------------------------------------------------===//
//
// To add a new configuration option, you must:
//  - add its syntactic form to Fragment
//  - update ConfigYAML.cpp to parse it
//  - add its semantic form to Config (in Config.h)
//  - update ConfigCompile.cpp to map Fragment -> Config
//  - make use of the option inside clangd
//  - document the new option (config.md in the llvm/clangd-www repository)
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONFIGFRAGMENT_H

#include "ConfigProvider.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
#include <string>
#include <vector>

namespace clang {
namespace clangd {
namespace config {

/// An entity written in config along, with its optional location in the file.
template <typename T> struct Located {
  Located(T Value, llvm::SMRange Range = {})
      : Range(Range), Value(std::move(Value)) {}

  llvm::SMRange Range;
  T *operator->() { return &Value; }
  const T *operator->() const { return &Value; }
  T &operator*() { return Value; }
  const T &operator*() const { return Value; }

private:
  T Value;
};

/// A chunk of configuration obtained from a config file, LSP, or elsewhere.
struct Fragment {
  /// Parses fragments from a YAML file (one from each --- delimited document).
  /// Documents that contained fatal errors are omitted from the results.
  /// BufferName is used for the SourceMgr and diagnostics.
  static std::vector<Fragment> parseYAML(llvm::StringRef YAML,
                                         llvm::StringRef BufferName,
                                         DiagnosticCallback);

  /// Analyzes and consumes this fragment, possibly yielding more diagnostics.
  /// This always produces a usable result (errors are recovered).
  ///
  /// Typically, providers will compile a Fragment once when it's first loaded,
  /// caching the result for reuse.
  /// Like a compiled program, this is good for performance and also encourages
  /// errors to be reported early and only once.
  ///
  /// The returned function is a cheap-copyable wrapper of refcounted internals.
  CompiledFragment compile(DiagnosticCallback) &&;

  /// These fields are not part of the user-specified configuration, but
  /// instead are populated by the parser to describe the configuration source.
  struct SourceInfo {
    /// Retains a buffer of the original source this fragment was parsed from.
    /// Locations within Located<T> objects point into this SourceMgr.
    /// Shared because multiple fragments are often parsed from one (YAML) file.
    /// May be null, then all locations should be ignored.
    std::shared_ptr<llvm::SourceMgr> Manager;
    /// The start of the original source for this fragment.
    /// Only valid if SourceManager is set.
    llvm::SMLoc Location;
    /// Absolute path to directory the fragment is associated with. Relative
    /// paths mentioned in the fragment are resolved against this.
    std::string Directory;
    /// Whether this fragment is allowed to make critical security/privacy
    /// decisions.
    bool Trusted = false;
  };
  SourceInfo Source;

  /// Conditions in the If block restrict when a Fragment applies.
  ///
  /// Each separate condition must match (combined with AND).
  /// When one condition has multiple values, any may match (combined with OR).
  /// e.g. `PathMatch: [foo/.*, bar/.*]` matches files in either directory.
  ///
  /// Conditions based on a file's path use the following form:
  /// - if the fragment came from a project directory, the path is relative
  /// - if the fragment is global (e.g. user config), the path is absolute
  /// - paths always use forward-slashes (UNIX-style)
  /// If no file is being processed, these conditions will not match.
  struct IfBlock {
    /// The file being processed must fully match a regular expression.
    std::vector<Located<std::string>> PathMatch;
    /// The file being processed must *not* fully match a regular expression.
    std::vector<Located<std::string>> PathExclude;

    /// An unrecognized key was found while parsing the condition.
    /// The condition will evaluate to false.
    bool HasUnrecognizedCondition = false;
  };
  IfBlock If;

  /// Conditions in the CompileFlags block affect how a file is parsed.
  ///
  /// clangd emulates how clang would interpret a file.
  /// By default, it behaves roughly like `clang $FILENAME`, but real projects
  /// usually require setting the include path (with the `-I` flag), defining
  /// preprocessor symbols, configuring warnings etc.
  /// Often, a compilation database specifies these compile commands. clangd
  /// searches for compile_commands.json in parents of the source file.
  ///
  /// This section modifies how the compile command is constructed.
  struct CompileFlagsBlock {
    /// Override the compiler executable name to simulate.
    ///
    /// The name can affect how flags are parsed (clang++ vs clang).
    /// If the executable name is in the --query-driver allowlist, then it will
    /// be invoked to extract include paths.
    ///
    /// (That this simply replaces argv[0], and may mangle commands that use
    /// more complicated drivers like ccache).
    llvm::Optional<Located<std::string>> Compiler;

    /// List of flags to append to the compile command.
    std::vector<Located<std::string>> Add;
    /// List of flags to remove from the compile command.
    ///
    /// - If the value is a recognized clang flag (like "-I") then it will be
    ///   removed along with any arguments. Synonyms like --include-dir= will
    ///   also be removed.
    /// - Otherwise, if the value ends in * (like "-DFOO=*") then any argument
    ///   with the prefix will be removed.
    /// - Otherwise any argument exactly matching the value is removed.
    ///
    /// In all cases, -Xclang is also removed where needed.
    ///
    /// Example:
    ///   Command: clang++ --include-directory=/usr/include -DFOO=42 foo.cc
    ///   Remove: [-I, -DFOO=*]
    ///   Result: clang++ foo.cc
    ///
    /// Flags added by the same CompileFlags entry will not be removed.
    std::vector<Located<std::string>> Remove;

    /// Directory to search for compilation database (compile_comands.json etc).
    /// Valid values are:
    /// - A single path to a directory (absolute, or relative to the fragment)
    /// - Ancestors: search all parent directories (the default)
    /// - None: do not use a compilation database, just default flags.
    llvm::Optional<Located<std::string>> CompilationDatabase;
  };
  CompileFlagsBlock CompileFlags;

  /// Controls how clangd understands code outside the current file.
  /// clangd's indexes provide information about symbols that isn't available
  /// to clang's parser, such as incoming references.
  struct IndexBlock {
    /// Whether files are built in the background to produce a project index.
    /// This is checked for translation units only, not headers they include.
    /// Legal values are "Build" or "Skip".
    llvm::Optional<Located<std::string>> Background;
    /// An external index uses data source outside of clangd itself. This is
    /// usually prepared using clangd-indexer.
    /// Exactly one source (File/Server) should be configured.
    struct ExternalBlock {
      /// Whether the block is explicitly set to `None`. Can be used to clear
      /// any external index specified before.
      Located<bool> IsNone = false;
      /// Path to an index file generated by clangd-indexer. Relative paths may
      /// be used, if config fragment is associated with a directory.
      llvm::Optional<Located<std::string>> File;
      /// Address and port number for a clangd-index-server. e.g.
      /// `123.1.1.1:13337`.
      llvm::Optional<Located<std::string>> Server;
      /// Source root governed by this index. Default is the directory
      /// associated with the config fragment. Absolute in case of user config
      /// and relative otherwise. Should always use forward-slashes.
      llvm::Optional<Located<std::string>> MountPoint;
    };
    llvm::Optional<Located<ExternalBlock>> External;
  };
  IndexBlock Index;

  /// Controls behavior of diagnostics (errors and warnings).
  struct DiagnosticsBlock {
    /// Diagnostic codes that should be suppressed.
    ///
    /// Valid values are:
    /// - *, to disable all diagnostics
    /// - diagnostic codes exposed by clangd (e.g unknown_type, -Wunused-result)
    /// - clang internal diagnostic codes (e.g. err_unknown_type)
    /// - warning categories (e.g. unused-result)
    /// - clang-tidy check names (e.g. bugprone-narrowing-conversions)
    ///
    /// This is a simple filter. Diagnostics can be controlled in other ways
    /// (e.g. by disabling a clang-tidy check, or the -Wunused compile flag).
    /// This often has other advantages, such as skipping some analysis.
    std::vector<Located<std::string>> Suppress;

    /// Controls how clangd will correct "unnecessary #include directives.
    /// clangd can warn if a header is `#include`d but not used, and suggest
    /// removing it.
    //
    /// Strict means a header is unused if it does not *directly* provide any
    /// symbol used in the file. Removing it may still break compilation if it
    /// transitively includes headers that are used. This should be fixed by
    /// including those headers directly.
    ///
    /// Valid values are:
    /// - Strict
    /// - None
    llvm::Optional<Located<std::string>> UnusedIncludes;

    /// Controls how clang-tidy will run over the code base.
    ///
    /// The settings are merged with any settings found in .clang-tidy
    /// configiration files with these ones taking precedence.
    struct ClangTidyBlock {
      std::vector<Located<std::string>> Add;
      /// List of checks to disable.
      /// Takes precedence over Add. To enable all llvm checks except include
      /// order:
      ///   Add: llvm-*
      ///   Remove: llvm-include-onder
      std::vector<Located<std::string>> Remove;

      /// A Key-Value pair list of options to pass to clang-tidy checks
      /// These take precedence over options specified in clang-tidy
      /// configuration files. Example:
      ///   CheckOptions:
      ///     readability-braces-around-statements.ShortStatementLines: 2
      std::vector<std::pair<Located<std::string>, Located<std::string>>>
          CheckOptions;
    };
    ClangTidyBlock ClangTidy;
  };
  DiagnosticsBlock Diagnostics;

  // Describes the style of the codebase, beyond formatting.
  struct StyleBlock {
    // Namespaces that should always be fully qualified, meaning no "using"
    // declarations, always spell out the whole name (with or without leading
    // ::). All nested namespaces are affected as well.
    // Affects availability of the AddUsing tweak.
    std::vector<Located<std::string>> FullyQualifiedNamespaces;
  };
  StyleBlock Style;

  /// Describes code completion preferences.
  struct CompletionBlock {
    /// Whether code completion should include suggestions from scopes that are
    /// not visible. The required scope prefix will be inserted.
    llvm::Optional<Located<bool>> AllScopes;
  };
  CompletionBlock Completion;

  /// Describes hover preferences.
  struct HoverBlock {
    /// Whether hover show a.k.a type.
    llvm::Optional<Located<bool>> ShowAKA;
  };
  HoverBlock Hover;
};

} // namespace config
} // namespace clangd
} // namespace clang

#endif