1 | //===--- ConfigYAML.cpp - Loading configuration fragments from YAML files -===// |
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 | #include "ConfigFragment.h" |
9 | #include "support/Logger.h" |
10 | #include "llvm/ADT/SmallSet.h" |
11 | #include "llvm/ADT/SmallString.h" |
12 | #include "llvm/ADT/StringRef.h" |
13 | #include "llvm/Support/MemoryBuffer.h" |
14 | #include "llvm/Support/SourceMgr.h" |
15 | #include "llvm/Support/YAMLParser.h" |
16 | #include <optional> |
17 | #include <string> |
18 | |
19 | namespace clang { |
20 | namespace clangd { |
21 | namespace config { |
22 | namespace { |
23 | using llvm::yaml::BlockScalarNode; |
24 | using llvm::yaml::MappingNode; |
25 | using llvm::yaml::Node; |
26 | using llvm::yaml::ScalarNode; |
27 | using llvm::yaml::SequenceNode; |
28 | |
29 | std::optional<llvm::StringRef> |
30 | bestGuess(llvm::StringRef Search, |
31 | llvm::ArrayRef<llvm::StringRef> AllowedValues) { |
32 | unsigned MaxEdit = (Search.size() + 1) / 3; |
33 | if (!MaxEdit) |
34 | return std::nullopt; |
35 | std::optional<llvm::StringRef> Result; |
36 | for (const auto &AllowedValue : AllowedValues) { |
37 | unsigned EditDistance = Search.edit_distance(Other: AllowedValue, AllowReplacements: true, MaxEditDistance: MaxEdit); |
38 | // We can't do better than an edit distance of 1, so just return this and |
39 | // save computing other values. |
40 | if (EditDistance == 1U) |
41 | return AllowedValue; |
42 | if (EditDistance == MaxEdit && !Result) { |
43 | Result = AllowedValue; |
44 | } else if (EditDistance < MaxEdit) { |
45 | Result = AllowedValue; |
46 | MaxEdit = EditDistance; |
47 | } |
48 | } |
49 | return Result; |
50 | } |
51 | |
52 | class Parser { |
53 | llvm::SourceMgr &SM; |
54 | bool HadError = false; |
55 | |
56 | public: |
57 | Parser(llvm::SourceMgr &SM) : SM(SM) {} |
58 | |
59 | // Tries to parse N into F, returning false if it failed and we couldn't |
60 | // meaningfully recover (YAML syntax error, or hard semantic error). |
61 | bool parse(Fragment &F, Node &N) { |
62 | DictParser Dict("Config" , this); |
63 | Dict.handle(Key: "If" , Parse: [&](Node &N) { parse(F&: F.If, N); }); |
64 | Dict.handle(Key: "CompileFlags" , Parse: [&](Node &N) { parse(F&: F.CompileFlags, N); }); |
65 | Dict.handle(Key: "Index" , Parse: [&](Node &N) { parse(F&: F.Index, N); }); |
66 | Dict.handle(Key: "Style" , Parse: [&](Node &N) { parse(F&: F.Style, N); }); |
67 | Dict.handle(Key: "Diagnostics" , Parse: [&](Node &N) { parse(F&: F.Diagnostics, N); }); |
68 | Dict.handle(Key: "Completion" , Parse: [&](Node &N) { parse(F&: F.Completion, N); }); |
69 | Dict.handle(Key: "Hover" , Parse: [&](Node &N) { parse(F&: F.Hover, N); }); |
70 | Dict.handle(Key: "InlayHints" , Parse: [&](Node &N) { parse(F&: F.InlayHints, N); }); |
71 | Dict.handle(Key: "SemanticTokens" , Parse: [&](Node &N) { parse(F&: F.SemanticTokens, N); }); |
72 | Dict.parse(N); |
73 | return !(N.failed() || HadError); |
74 | } |
75 | |
76 | private: |
77 | void parse(Fragment::IfBlock &F, Node &N) { |
78 | DictParser Dict("If" , this); |
79 | Dict.unrecognized(Handler: [&](Located<std::string>, Node &) { |
80 | F.HasUnrecognizedCondition = true; |
81 | return true; // Emit a warning for the unrecognized key. |
82 | }); |
83 | Dict.handle(Key: "PathMatch" , Parse: [&](Node &N) { |
84 | if (auto Values = scalarValues(N)) |
85 | F.PathMatch = std::move(*Values); |
86 | }); |
87 | Dict.handle(Key: "PathExclude" , Parse: [&](Node &N) { |
88 | if (auto Values = scalarValues(N)) |
89 | F.PathExclude = std::move(*Values); |
90 | }); |
91 | Dict.parse(N); |
92 | } |
93 | |
94 | void parse(Fragment::CompileFlagsBlock &F, Node &N) { |
95 | DictParser Dict("CompileFlags" , this); |
96 | Dict.handle(Key: "Compiler" , Parse: [&](Node &N) { |
97 | if (auto Value = scalarValue(N, Desc: "Compiler" )) |
98 | F.Compiler = std::move(*Value); |
99 | }); |
100 | Dict.handle(Key: "Add" , Parse: [&](Node &N) { |
101 | if (auto Values = scalarValues(N)) |
102 | F.Add = std::move(*Values); |
103 | }); |
104 | Dict.handle(Key: "Remove" , Parse: [&](Node &N) { |
105 | if (auto Values = scalarValues(N)) |
106 | F.Remove = std::move(*Values); |
107 | }); |
108 | Dict.handle(Key: "BuiltinHeaders" , Parse: [&](Node &N) { |
109 | if (auto = scalarValue(N, Desc: "BuiltinHeaders" )) |
110 | F.BuiltinHeaders = *BuiltinHeaders; |
111 | }); |
112 | Dict.handle(Key: "CompilationDatabase" , Parse: [&](Node &N) { |
113 | F.CompilationDatabase = scalarValue(N, Desc: "CompilationDatabase" ); |
114 | }); |
115 | Dict.parse(N); |
116 | } |
117 | |
118 | void parse(Fragment::StyleBlock &F, Node &N) { |
119 | DictParser Dict("Style" , this); |
120 | Dict.handle(Key: "FullyQualifiedNamespaces" , Parse: [&](Node &N) { |
121 | if (auto Values = scalarValues(N)) |
122 | F.FullyQualifiedNamespaces = std::move(*Values); |
123 | }); |
124 | Dict.handle(Key: "QuotedHeaders" , Parse: [&](Node &N) { |
125 | if (auto Values = scalarValues(N)) |
126 | F.QuotedHeaders = std::move(*Values); |
127 | }); |
128 | Dict.handle(Key: "AngledHeaders" , Parse: [&](Node &N) { |
129 | if (auto Values = scalarValues(N)) |
130 | F.AngledHeaders = std::move(*Values); |
131 | }); |
132 | Dict.parse(N); |
133 | } |
134 | |
135 | void parse(Fragment::DiagnosticsBlock &F, Node &N) { |
136 | DictParser Dict("Diagnostics" , this); |
137 | Dict.handle(Key: "Suppress" , Parse: [&](Node &N) { |
138 | if (auto Values = scalarValues(N)) |
139 | F.Suppress = std::move(*Values); |
140 | }); |
141 | Dict.handle(Key: "UnusedIncludes" , Parse: [&](Node &N) { |
142 | F.UnusedIncludes = scalarValue(N, Desc: "UnusedIncludes" ); |
143 | }); |
144 | Dict.handle(Key: "MissingIncludes" , Parse: [&](Node &N) { |
145 | F.MissingIncludes = scalarValue(N, Desc: "MissingIncludes" ); |
146 | }); |
147 | Dict.handle(Key: "Includes" , Parse: [&](Node &N) { parse(F&: F.Includes, N); }); |
148 | Dict.handle(Key: "ClangTidy" , Parse: [&](Node &N) { parse(F&: F.ClangTidy, N); }); |
149 | Dict.parse(N); |
150 | } |
151 | |
152 | void parse(Fragment::DiagnosticsBlock::ClangTidyBlock &F, Node &N) { |
153 | DictParser Dict("ClangTidy" , this); |
154 | Dict.handle(Key: "Add" , Parse: [&](Node &N) { |
155 | if (auto Values = scalarValues(N)) |
156 | F.Add = std::move(*Values); |
157 | }); |
158 | Dict.handle(Key: "Remove" , Parse: [&](Node &N) { |
159 | if (auto Values = scalarValues(N)) |
160 | F.Remove = std::move(*Values); |
161 | }); |
162 | Dict.handle(Key: "CheckOptions" , Parse: [&](Node &N) { |
163 | DictParser CheckOptDict("CheckOptions" , this); |
164 | CheckOptDict.unrecognized(Handler: [&](Located<std::string> &&Key, Node &Val) { |
165 | if (auto Value = scalarValue(N&: Val, Desc: *Key)) |
166 | F.CheckOptions.emplace_back(args: std::move(Key), args: std::move(*Value)); |
167 | return false; // Don't emit a warning |
168 | }); |
169 | CheckOptDict.parse(N); |
170 | }); |
171 | Dict.handle(Key: "FastCheckFilter" , Parse: [&](Node &N) { |
172 | if (auto FastCheckFilter = scalarValue(N, Desc: "FastCheckFilter" )) |
173 | F.FastCheckFilter = *FastCheckFilter; |
174 | }); |
175 | Dict.parse(N); |
176 | } |
177 | |
178 | void parse(Fragment::DiagnosticsBlock::IncludesBlock &F, Node &N) { |
179 | DictParser Dict("Includes" , this); |
180 | Dict.handle(Key: "IgnoreHeader" , Parse: [&](Node &N) { |
181 | if (auto Values = scalarValues(N)) |
182 | F.IgnoreHeader = std::move(*Values); |
183 | }); |
184 | Dict.handle(Key: "AnalyzeAngledIncludes" , Parse: [&](Node &N) { |
185 | if (auto Value = boolValue(N, Desc: "AnalyzeAngledIncludes" )) |
186 | F.AnalyzeAngledIncludes = *Value; |
187 | }); |
188 | Dict.parse(N); |
189 | } |
190 | |
191 | void parse(Fragment::IndexBlock &F, Node &N) { |
192 | DictParser Dict("Index" , this); |
193 | Dict.handle(Key: "Background" , |
194 | Parse: [&](Node &N) { F.Background = scalarValue(N, Desc: "Background" ); }); |
195 | Dict.handle(Key: "External" , Parse: [&](Node &N) { |
196 | Fragment::IndexBlock::ExternalBlock External; |
197 | // External block can either be a mapping or a scalar value. Dispatch |
198 | // accordingly. |
199 | if (N.getType() == Node::NK_Mapping) { |
200 | parse(F&: External, N); |
201 | } else if (N.getType() == Node::NK_Scalar || |
202 | N.getType() == Node::NK_BlockScalar) { |
203 | parse(F&: External, ExternalVal: *scalarValue(N, Desc: "External" )); |
204 | } else { |
205 | error(Msg: "External must be either a scalar or a mapping." , N); |
206 | return; |
207 | } |
208 | F.External.emplace(args: std::move(External)); |
209 | F.External->Range = N.getSourceRange(); |
210 | }); |
211 | Dict.handle(Key: "StandardLibrary" , Parse: [&](Node &N) { |
212 | if (auto StandardLibrary = boolValue(N, Desc: "StandardLibrary" )) |
213 | F.StandardLibrary = *StandardLibrary; |
214 | }); |
215 | Dict.parse(N); |
216 | } |
217 | |
218 | void parse(Fragment::IndexBlock::ExternalBlock &F, |
219 | Located<std::string> ExternalVal) { |
220 | if (!llvm::StringRef(*ExternalVal).equals_insensitive(RHS: "none" )) { |
221 | error(Msg: "Only scalar value supported for External is 'None'" , |
222 | Range: ExternalVal.Range); |
223 | return; |
224 | } |
225 | F.IsNone = true; |
226 | F.IsNone.Range = ExternalVal.Range; |
227 | } |
228 | |
229 | void parse(Fragment::IndexBlock::ExternalBlock &F, Node &N) { |
230 | DictParser Dict("External" , this); |
231 | Dict.handle(Key: "File" , Parse: [&](Node &N) { F.File = scalarValue(N, Desc: "File" ); }); |
232 | Dict.handle(Key: "Server" , |
233 | Parse: [&](Node &N) { F.Server = scalarValue(N, Desc: "Server" ); }); |
234 | Dict.handle(Key: "MountPoint" , |
235 | Parse: [&](Node &N) { F.MountPoint = scalarValue(N, Desc: "MountPoint" ); }); |
236 | Dict.parse(N); |
237 | } |
238 | |
239 | void parse(Fragment::CompletionBlock &F, Node &N) { |
240 | DictParser Dict("Completion" , this); |
241 | Dict.handle(Key: "AllScopes" , Parse: [&](Node &N) { |
242 | if (auto AllScopes = boolValue(N, Desc: "AllScopes" )) |
243 | F.AllScopes = *AllScopes; |
244 | }); |
245 | Dict.handle(Key: "ArgumentLists" , Parse: [&](Node &N) { |
246 | if (auto ArgumentLists = scalarValue(N, Desc: "ArgumentLists" )) |
247 | F.ArgumentLists = *ArgumentLists; |
248 | }); |
249 | Dict.handle(Key: "HeaderInsertion" , Parse: [&](Node &N) { |
250 | if (auto = scalarValue(N, Desc: "HeaderInsertion" )) |
251 | F.HeaderInsertion = *HeaderInsertion; |
252 | }); |
253 | Dict.handle(Key: "CodePatterns" , Parse: [&](Node &N) { |
254 | if (auto CodePatterns = scalarValue(N, Desc: "CodePatterns" )) |
255 | F.CodePatterns = *CodePatterns; |
256 | }); |
257 | Dict.parse(N); |
258 | } |
259 | |
260 | void parse(Fragment::HoverBlock &F, Node &N) { |
261 | DictParser Dict("Hover" , this); |
262 | Dict.handle(Key: "ShowAKA" , Parse: [&](Node &N) { |
263 | if (auto ShowAKA = boolValue(N, Desc: "ShowAKA" )) |
264 | F.ShowAKA = *ShowAKA; |
265 | }); |
266 | Dict.parse(N); |
267 | } |
268 | |
269 | void parse(Fragment::InlayHintsBlock &F, Node &N) { |
270 | DictParser Dict("InlayHints" , this); |
271 | Dict.handle(Key: "Enabled" , Parse: [&](Node &N) { |
272 | if (auto Value = boolValue(N, Desc: "Enabled" )) |
273 | F.Enabled = *Value; |
274 | }); |
275 | Dict.handle(Key: "ParameterNames" , Parse: [&](Node &N) { |
276 | if (auto Value = boolValue(N, Desc: "ParameterNames" )) |
277 | F.ParameterNames = *Value; |
278 | }); |
279 | Dict.handle(Key: "DeducedTypes" , Parse: [&](Node &N) { |
280 | if (auto Value = boolValue(N, Desc: "DeducedTypes" )) |
281 | F.DeducedTypes = *Value; |
282 | }); |
283 | Dict.handle(Key: "Designators" , Parse: [&](Node &N) { |
284 | if (auto Value = boolValue(N, Desc: "Designators" )) |
285 | F.Designators = *Value; |
286 | }); |
287 | Dict.handle(Key: "BlockEnd" , Parse: [&](Node &N) { |
288 | if (auto Value = boolValue(N, Desc: "BlockEnd" )) |
289 | F.BlockEnd = *Value; |
290 | }); |
291 | Dict.handle(Key: "DefaultArguments" , Parse: [&](Node &N) { |
292 | if (auto Value = boolValue(N, Desc: "DefaultArguments" )) |
293 | F.DefaultArguments = *Value; |
294 | }); |
295 | Dict.handle(Key: "TypeNameLimit" , Parse: [&](Node &N) { |
296 | if (auto Value = uint32Value(N, Desc: "TypeNameLimit" )) |
297 | F.TypeNameLimit = *Value; |
298 | }); |
299 | Dict.parse(N); |
300 | } |
301 | |
302 | void parse(Fragment::SemanticTokensBlock &F, Node &N) { |
303 | DictParser Dict("SemanticTokens" , this); |
304 | Dict.handle(Key: "DisabledKinds" , Parse: [&](Node &N) { |
305 | if (auto Values = scalarValues(N)) |
306 | F.DisabledKinds = std::move(*Values); |
307 | }); |
308 | Dict.handle(Key: "DisabledModifiers" , Parse: [&](Node &N) { |
309 | if (auto Values = scalarValues(N)) |
310 | F.DisabledModifiers = std::move(*Values); |
311 | }); |
312 | Dict.parse(N); |
313 | } |
314 | |
315 | // Helper for parsing mapping nodes (dictionaries). |
316 | // We don't use YamlIO as we want to control over unknown keys. |
317 | class DictParser { |
318 | llvm::StringRef Description; |
319 | std::vector<std::pair<llvm::StringRef, std::function<void(Node &)>>> Keys; |
320 | std::function<bool(Located<std::string>, Node &)> UnknownHandler; |
321 | Parser *Outer; |
322 | |
323 | public: |
324 | DictParser(llvm::StringRef Description, Parser *Outer) |
325 | : Description(Description), Outer(Outer) {} |
326 | |
327 | // Parse is called when Key is encountered, and passed the associated value. |
328 | // It should emit diagnostics if the value is invalid (e.g. wrong type). |
329 | // If Key is seen twice, Parse runs only once and an error is reported. |
330 | void handle(llvm::StringLiteral Key, std::function<void(Node &)> Parse) { |
331 | for (const auto &Entry : Keys) { |
332 | (void)Entry; |
333 | assert(Entry.first != Key && "duplicate key handler" ); |
334 | } |
335 | Keys.emplace_back(args&: Key, args: std::move(Parse)); |
336 | } |
337 | |
338 | // Handler is called when a Key is not matched by any handle(). |
339 | // If this is unset or the Handler returns true, a warning is emitted for |
340 | // the unknown key. |
341 | void |
342 | unrecognized(std::function<bool(Located<std::string>, Node &)> Handler) { |
343 | UnknownHandler = std::move(Handler); |
344 | } |
345 | |
346 | // Process a mapping node and call handlers for each key/value pair. |
347 | void parse(Node &N) const { |
348 | if (N.getType() != Node::NK_Mapping) { |
349 | Outer->error(Msg: Description + " should be a dictionary" , N); |
350 | return; |
351 | } |
352 | llvm::SmallSet<std::string, 8> Seen; |
353 | llvm::SmallVector<Located<std::string>, 0> UnknownKeys; |
354 | // We *must* consume all items, even on error, or the parser will assert. |
355 | for (auto &KV : llvm::cast<MappingNode>(Val&: N)) { |
356 | auto *K = KV.getKey(); |
357 | if (!K) // YAMLParser emitted an error. |
358 | continue; |
359 | auto Key = Outer->scalarValue(N&: *K, Desc: "Dictionary key" ); |
360 | if (!Key) |
361 | continue; |
362 | if (!Seen.insert(V: **Key).second) { |
363 | Outer->warning(Msg: "Duplicate key " + **Key + " is ignored" , N: *K); |
364 | if (auto *Value = KV.getValue()) |
365 | Value->skip(); |
366 | continue; |
367 | } |
368 | auto *Value = KV.getValue(); |
369 | if (!Value) // YAMLParser emitted an error. |
370 | continue; |
371 | bool Matched = false; |
372 | for (const auto &Handler : Keys) { |
373 | if (Handler.first == **Key) { |
374 | Matched = true; |
375 | Handler.second(*Value); |
376 | break; |
377 | } |
378 | } |
379 | if (!Matched) { |
380 | bool Warn = !UnknownHandler; |
381 | if (UnknownHandler) |
382 | Warn = UnknownHandler( |
383 | Located<std::string>(**Key, K->getSourceRange()), *Value); |
384 | if (Warn) |
385 | UnknownKeys.push_back(Elt: std::move(*Key)); |
386 | } |
387 | } |
388 | if (!UnknownKeys.empty()) |
389 | warnUnknownKeys(UnknownKeys, SeenKeys: Seen); |
390 | } |
391 | |
392 | private: |
393 | void warnUnknownKeys(llvm::ArrayRef<Located<std::string>> UnknownKeys, |
394 | const llvm::SmallSet<std::string, 8> &SeenKeys) const { |
395 | llvm::SmallVector<llvm::StringRef> UnseenKeys; |
396 | for (const auto &KeyAndHandler : Keys) |
397 | if (!SeenKeys.count(V: KeyAndHandler.first.str())) |
398 | UnseenKeys.push_back(Elt: KeyAndHandler.first); |
399 | |
400 | for (const Located<std::string> &UnknownKey : UnknownKeys) |
401 | if (auto BestGuess = bestGuess(Search: *UnknownKey, AllowedValues: UnseenKeys)) |
402 | Outer->warning(Msg: "Unknown " + Description + " key '" + *UnknownKey + |
403 | "'; did you mean '" + *BestGuess + "'?" , |
404 | Range: UnknownKey.Range); |
405 | else |
406 | Outer->warning(Msg: "Unknown " + Description + " key '" + *UnknownKey + |
407 | "'" , |
408 | Range: UnknownKey.Range); |
409 | } |
410 | }; |
411 | |
412 | // Try to parse a single scalar value from the node, warn on failure. |
413 | std::optional<Located<std::string>> scalarValue(Node &N, |
414 | llvm::StringRef Desc) { |
415 | llvm::SmallString<256> Buf; |
416 | if (auto *S = llvm::dyn_cast<ScalarNode>(Val: &N)) |
417 | return Located<std::string>(S->getValue(Storage&: Buf).str(), N.getSourceRange()); |
418 | if (auto *BS = llvm::dyn_cast<BlockScalarNode>(Val: &N)) |
419 | return Located<std::string>(BS->getValue().str(), N.getSourceRange()); |
420 | warning(Msg: Desc + " should be scalar" , N); |
421 | return std::nullopt; |
422 | } |
423 | |
424 | std::optional<Located<bool>> boolValue(Node &N, llvm::StringRef Desc) { |
425 | if (auto Scalar = scalarValue(N, Desc)) { |
426 | if (auto Bool = llvm::yaml::parseBool(S: **Scalar)) |
427 | return Located<bool>(*Bool, Scalar->Range); |
428 | warning(Msg: Desc + " should be a boolean" , N); |
429 | } |
430 | return std::nullopt; |
431 | } |
432 | |
433 | std::optional<Located<uint32_t>> uint32Value(Node &N, llvm::StringRef Desc) { |
434 | if (auto Scalar = scalarValue(N, Desc)) { |
435 | unsigned long long Num; |
436 | if (!llvm::getAsUnsignedInteger(Str: **Scalar, Radix: 0, Result&: Num)) { |
437 | return Located<uint32_t>(Num, Scalar->Range); |
438 | } |
439 | } |
440 | warning(Msg: Desc + " invalid number" , N); |
441 | return std::nullopt; |
442 | } |
443 | |
444 | // Try to parse a list of single scalar values, or just a single value. |
445 | std::optional<std::vector<Located<std::string>>> scalarValues(Node &N) { |
446 | std::vector<Located<std::string>> Result; |
447 | if (auto *S = llvm::dyn_cast<ScalarNode>(Val: &N)) { |
448 | llvm::SmallString<256> Buf; |
449 | Result.emplace_back(args: S->getValue(Storage&: Buf).str(), args: N.getSourceRange()); |
450 | } else if (auto *S = llvm::dyn_cast<BlockScalarNode>(Val: &N)) { |
451 | Result.emplace_back(args: S->getValue().str(), args: N.getSourceRange()); |
452 | } else if (auto *S = llvm::dyn_cast<SequenceNode>(Val: &N)) { |
453 | // We *must* consume all items, even on error, or the parser will assert. |
454 | for (auto &Child : *S) { |
455 | if (auto Value = scalarValue(N&: Child, Desc: "List item" )) |
456 | Result.push_back(x: std::move(*Value)); |
457 | } |
458 | } else { |
459 | warning(Msg: "Expected scalar or list of scalars" , N); |
460 | return std::nullopt; |
461 | } |
462 | return Result; |
463 | } |
464 | |
465 | // Report a "hard" error, reflecting a config file that can never be valid. |
466 | void error(const llvm::Twine &Msg, llvm::SMRange Range) { |
467 | HadError = true; |
468 | SM.PrintMessage(Loc: Range.Start, Kind: llvm::SourceMgr::DK_Error, Msg, Ranges: Range); |
469 | } |
470 | void error(const llvm::Twine &Msg, const Node &N) { |
471 | return error(Msg, Range: N.getSourceRange()); |
472 | } |
473 | |
474 | // Report a "soft" error that could be caused by e.g. version skew. |
475 | void warning(const llvm::Twine &Msg, llvm::SMRange Range) { |
476 | SM.PrintMessage(Loc: Range.Start, Kind: llvm::SourceMgr::DK_Warning, Msg, Ranges: Range); |
477 | } |
478 | void warning(const llvm::Twine &Msg, const Node &N) { |
479 | return warning(Msg, Range: N.getSourceRange()); |
480 | } |
481 | }; |
482 | |
483 | } // namespace |
484 | |
485 | std::vector<Fragment> Fragment::parseYAML(llvm::StringRef YAML, |
486 | llvm::StringRef BufferName, |
487 | DiagnosticCallback Diags) { |
488 | // The YAML document may contain multiple conditional fragments. |
489 | // The SourceManager is shared for all of them. |
490 | log(Fmt: "Loading config file at {0}" , Vals&: BufferName); |
491 | auto SM = std::make_shared<llvm::SourceMgr>(); |
492 | auto Buf = llvm::MemoryBuffer::getMemBufferCopy(InputData: YAML, BufferName); |
493 | // Adapt DiagnosticCallback to function-pointer interface. |
494 | // Callback receives both errors we emit and those from the YAML parser. |
495 | SM->setDiagHandler( |
496 | DH: [](const llvm::SMDiagnostic &Diag, void *Ctx) { |
497 | (*reinterpret_cast<DiagnosticCallback *>(Ctx))(Diag); |
498 | }, |
499 | Ctx: &Diags); |
500 | std::vector<Fragment> Result; |
501 | for (auto &Doc : llvm::yaml::Stream(*Buf, *SM)) { |
502 | if (Node *N = Doc.getRoot()) { |
503 | Fragment Fragment; |
504 | Fragment.Source.Manager = SM; |
505 | Fragment.Source.Location = N->getSourceRange().Start; |
506 | SM->PrintMessage(Loc: Fragment.Source.Location, Kind: llvm::SourceMgr::DK_Note, |
507 | Msg: "Parsing config fragment" ); |
508 | if (Parser(*SM).parse(F&: Fragment, N&: *N)) |
509 | Result.push_back(x: std::move(Fragment)); |
510 | } |
511 | } |
512 | SM->PrintMessage(Loc: SM->FindLocForLineAndColumn(BufferID: SM->getMainFileID(), LineNo: 0, ColNo: 0), |
513 | Kind: llvm::SourceMgr::DK_Note, |
514 | Msg: "Parsed " + llvm::Twine(Result.size()) + |
515 | " fragments from file" ); |
516 | // Hack: stash the buffer in the SourceMgr to keep it alive. |
517 | // SM has two entries: "main" non-owning buffer, and ignored owning buffer. |
518 | SM->AddNewSourceBuffer(F: std::move(Buf), IncludeLoc: llvm::SMLoc()); |
519 | return Result; |
520 | } |
521 | |
522 | } // namespace config |
523 | } // namespace clangd |
524 | } // namespace clang |
525 | |