| 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 | |