brintos

brintos / llvm-project-archived public Read only

0
0
Text · 31.3 KiB · 18e3180 Raw
856 lines · cpp
1//===--- ConfigCompile.cpp - Translating Fragments into Config ------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// Fragments are applied to Configs in two steps:10//11// 1. (When the fragment is first loaded)12//    FragmentCompiler::compile() traverses the Fragment and creates13//    function objects that know how to apply the configuration.14// 2. (Every time a config is required)15//    CompiledFragment() executes these functions to populate the Config.16//17// Work could be split between these steps in different ways. We try to18// do as much work as possible in the first step. For example, regexes are19// compiled in stage 1 and captured by the apply function. This is because:20//21//  - it's more efficient, as the work done in stage 1 must only be done once22//  - problems can be reported in stage 1, in stage 2 we must silently recover23//24//===----------------------------------------------------------------------===//25 26#include "CompileCommands.h"27#include "Config.h"28#include "ConfigFragment.h"29#include "ConfigProvider.h"30#include "Diagnostics.h"31#include "Feature.h"32#include "TidyProvider.h"33#include "support/Logger.h"34#include "support/Path.h"35#include "support/Trace.h"36#include "llvm/ADT/STLExtras.h"37#include "llvm/ADT/SmallString.h"38#include "llvm/ADT/StringExtras.h"39#include "llvm/ADT/StringRef.h"40#include "llvm/Support/FileSystem.h"41#include "llvm/Support/FormatVariadic.h"42#include "llvm/Support/Path.h"43#include "llvm/Support/Regex.h"44#include "llvm/Support/SMLoc.h"45#include "llvm/Support/SourceMgr.h"46#include <memory>47#include <optional>48#include <string>49#include <vector>50 51namespace clang {52namespace clangd {53namespace config {54namespace {55 56// Returns an empty stringref if Path is not under FragmentDir. Returns Path57// as-is when FragmentDir is empty.58llvm::StringRef configRelative(llvm::StringRef Path,59                               llvm::StringRef FragmentDir) {60  if (FragmentDir.empty())61    return Path;62  if (!Path.consume_front(FragmentDir))63    return llvm::StringRef();64  return Path.empty() ? "." : Path;65}66 67struct CompiledFragmentImpl {68  // The independent conditions to check before using settings from this config.69  // The following fragment has *two* conditions:70  //   If: { Platform: [mac, linux], PathMatch: foo/.* }71  // All of them must be satisfied: the platform and path conditions are ANDed.72  // The OR logic for the platform condition is implemented inside the function.73  std::vector<llvm::unique_function<bool(const Params &) const>> Conditions;74  // Mutations that this fragment will apply to the configuration.75  // These are invoked only if the conditions are satisfied.76  std::vector<llvm::unique_function<void(const Params &, Config &) const>>77      Apply;78 79  bool operator()(const Params &P, Config &C) const {80    for (const auto &C : Conditions) {81      if (!C(P)) {82        dlog("Config fragment {0}: condition not met", this);83        return false;84      }85    }86    dlog("Config fragment {0}: applying {1} rules", this, Apply.size());87    for (const auto &A : Apply)88      A(P, C);89    return true;90  }91};92 93// Wrapper around condition compile() functions to reduce arg-passing.94struct FragmentCompiler {95  FragmentCompiler(CompiledFragmentImpl &Out, DiagnosticCallback D,96                   llvm::SourceMgr *SM)97      : Out(Out), Diagnostic(D), SourceMgr(SM) {}98  CompiledFragmentImpl &Out;99  DiagnosticCallback Diagnostic;100  llvm::SourceMgr *SourceMgr;101  // Normalized Fragment::SourceInfo::Directory.102  std::string FragmentDirectory;103  bool Trusted = false;104 105  std::optional<llvm::Regex>106  compileRegex(const Located<std::string> &Text,107               llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags) {108    std::string Anchored = "^(" + *Text + ")$";109    llvm::Regex Result(Anchored, Flags);110    std::string RegexError;111    if (!Result.isValid(RegexError)) {112      diag(Error, "Invalid regex " + Anchored + ": " + RegexError, Text.Range);113      return std::nullopt;114    }115    return std::move(Result);116  }117 118  std::optional<std::string> makeAbsolute(Located<std::string> Path,119                                          llvm::StringLiteral Description,120                                          llvm::sys::path::Style Style) {121    if (llvm::sys::path::is_absolute(*Path))122      return *Path;123    if (FragmentDirectory.empty()) {124      diag(Error,125           llvm::formatv(126               "{0} must be an absolute path, because this fragment is not "127               "associated with any directory.",128               Description)129               .str(),130           Path.Range);131      return std::nullopt;132    }133    llvm::SmallString<256> AbsPath = llvm::StringRef(*Path);134    llvm::sys::path::make_absolute(FragmentDirectory, AbsPath);135    llvm::sys::path::native(AbsPath, Style);136    return AbsPath.str().str();137  }138 139  // Helper with similar API to StringSwitch, for parsing enum values.140  template <typename T> class EnumSwitch {141    FragmentCompiler &Outer;142    llvm::StringRef EnumName;143    const Located<std::string> &Input;144    std::optional<T> Result;145    llvm::SmallVector<llvm::StringLiteral> ValidValues;146 147  public:148    EnumSwitch(llvm::StringRef EnumName, const Located<std::string> &In,149               FragmentCompiler &Outer)150        : Outer(Outer), EnumName(EnumName), Input(In) {}151 152    EnumSwitch &map(llvm::StringLiteral Name, T Value) {153      assert(!llvm::is_contained(ValidValues, Name) && "Duplicate value!");154      ValidValues.push_back(Name);155      if (!Result && *Input == Name)156        Result = Value;157      return *this;158    }159 160    std::optional<T> value() {161      if (!Result)162        Outer.diag(163            Warning,164            llvm::formatv("Invalid {0} value '{1}'. Valid values are {2}.",165                          EnumName, *Input, llvm::join(ValidValues, ", "))166                .str(),167            Input.Range);168      return Result;169    };170  };171 172  // Attempt to parse a specified string into an enum.173  // Yields std::nullopt and produces a diagnostic on failure.174  //175  // std::optional<T> Value = compileEnum<En>("Foo", Frag.Foo)176  //    .map("Foo", Enum::Foo)177  //    .map("Bar", Enum::Bar)178  //    .value();179  template <typename T>180  EnumSwitch<T> compileEnum(llvm::StringRef EnumName,181                            const Located<std::string> &In) {182    return EnumSwitch<T>(EnumName, In, *this);183  }184 185  void compile(Fragment &&F) {186    Trusted = F.Source.Trusted;187    if (!F.Source.Directory.empty()) {188      FragmentDirectory = llvm::sys::path::convert_to_slash(F.Source.Directory);189      if (FragmentDirectory.back() != '/')190        FragmentDirectory += '/';191    }192    compile(std::move(F.If));193    compile(std::move(F.CompileFlags));194    compile(std::move(F.Index));195    compile(std::move(F.Diagnostics));196    compile(std::move(F.Completion));197    compile(std::move(F.Hover));198    compile(std::move(F.InlayHints));199    compile(std::move(F.SemanticTokens));200    compile(std::move(F.Style));201    compile(std::move(F.Documentation));202  }203 204  void compile(Fragment::IfBlock &&F) {205    if (F.HasUnrecognizedCondition)206      Out.Conditions.push_back([&](const Params &) { return false; });207 208#ifdef CLANGD_PATH_CASE_INSENSITIVE209    llvm::Regex::RegexFlags Flags = llvm::Regex::IgnoreCase;210#else211    llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags;212#endif213 214    auto PathMatch = std::make_unique<std::vector<llvm::Regex>>();215    for (auto &Entry : F.PathMatch) {216      if (auto RE = compileRegex(Entry, Flags))217        PathMatch->push_back(std::move(*RE));218    }219    if (!PathMatch->empty()) {220      Out.Conditions.push_back(221          [PathMatch(std::move(PathMatch)),222           FragmentDir(FragmentDirectory)](const Params &P) {223            if (P.Path.empty())224              return false;225            llvm::StringRef Path = configRelative(P.Path, FragmentDir);226            // Ignore the file if it is not nested under Fragment.227            if (Path.empty())228              return false;229            return llvm::any_of(*PathMatch, [&](const llvm::Regex &RE) {230              return RE.match(Path);231            });232          });233    }234 235    auto PathExclude = std::make_unique<std::vector<llvm::Regex>>();236    for (auto &Entry : F.PathExclude) {237      if (auto RE = compileRegex(Entry, Flags))238        PathExclude->push_back(std::move(*RE));239    }240    if (!PathExclude->empty()) {241      Out.Conditions.push_back(242          [PathExclude(std::move(PathExclude)),243           FragmentDir(FragmentDirectory)](const Params &P) {244            if (P.Path.empty())245              return false;246            llvm::StringRef Path = configRelative(P.Path, FragmentDir);247            // Ignore the file if it is not nested under Fragment.248            if (Path.empty())249              return true;250            return llvm::none_of(*PathExclude, [&](const llvm::Regex &RE) {251              return RE.match(Path);252            });253          });254    }255  }256 257  void compile(Fragment::CompileFlagsBlock &&F) {258    if (F.Compiler)259      Out.Apply.push_back(260          [Compiler(std::move(**F.Compiler))](const Params &, Config &C) {261            C.CompileFlags.Edits.push_back(262                [Compiler](std::vector<std::string> &Args) {263                  if (!Args.empty())264                    Args.front() = Compiler;265                });266          });267 268    if (!F.Remove.empty()) {269      auto Remove = std::make_shared<ArgStripper>();270      for (auto &A : F.Remove)271        Remove->strip(*A);272      Out.Apply.push_back([Remove(std::shared_ptr<const ArgStripper>(273                              std::move(Remove)))](const Params &, Config &C) {274        C.CompileFlags.Edits.push_back(275            [Remove](std::vector<std::string> &Args) {276              Remove->process(Args);277            });278      });279    }280 281    if (!F.Add.empty()) {282      std::vector<std::string> Add;283      for (auto &A : F.Add)284        Add.push_back(std::move(*A));285      Out.Apply.push_back([Add(std::move(Add))](const Params &, Config &C) {286        C.CompileFlags.Edits.push_back([Add](std::vector<std::string> &Args) {287          // The point to insert at. Just append when `--` isn't present.288          auto It = llvm::find(Args, "--");289          Args.insert(It, Add.begin(), Add.end());290        });291      });292    }293 294    if (F.BuiltinHeaders) {295      if (auto Val =296              compileEnum<Config::BuiltinHeaderPolicy>("BuiltinHeaders",297                                                       *F.BuiltinHeaders)298                  .map("Clangd", Config::BuiltinHeaderPolicy::Clangd)299                  .map("QueryDriver", Config::BuiltinHeaderPolicy::QueryDriver)300                  .value())301        Out.Apply.push_back([Val](const Params &, Config &C) {302          C.CompileFlags.BuiltinHeaders = *Val;303        });304    }305 306    if (F.CompilationDatabase) {307      std::optional<Config::CDBSearchSpec> Spec;308      if (**F.CompilationDatabase == "Ancestors") {309        Spec.emplace();310        Spec->Policy = Config::CDBSearchSpec::Ancestors;311      } else if (**F.CompilationDatabase == "None") {312        Spec.emplace();313        Spec->Policy = Config::CDBSearchSpec::NoCDBSearch;314      } else {315        if (auto Path =316                makeAbsolute(*F.CompilationDatabase, "CompilationDatabase",317                             llvm::sys::path::Style::native)) {318          // Drop trailing slash to put the path in canonical form.319          // Should makeAbsolute do this?320          llvm::StringRef Rel = llvm::sys::path::relative_path(*Path);321          if (!Rel.empty() && llvm::sys::path::is_separator(Rel.back()))322            Path->pop_back();323 324          Spec.emplace();325          Spec->Policy = Config::CDBSearchSpec::FixedDir;326          Spec->FixedCDBPath = std::move(Path);327        }328      }329      if (Spec)330        Out.Apply.push_back(331            [Spec(std::move(*Spec))](const Params &, Config &C) {332              C.CompileFlags.CDBSearch = Spec;333            });334    }335  }336 337  void compile(Fragment::IndexBlock &&F) {338    if (F.Background) {339      if (auto Val =340              compileEnum<Config::BackgroundPolicy>("Background", *F.Background)341                  .map("Build", Config::BackgroundPolicy::Build)342                  .map("Skip", Config::BackgroundPolicy::Skip)343                  .value())344        Out.Apply.push_back(345            [Val](const Params &, Config &C) { C.Index.Background = *Val; });346    }347    if (F.External)348      compile(std::move(**F.External), F.External->Range);349    if (F.StandardLibrary)350      Out.Apply.push_back(351          [Val(**F.StandardLibrary)](const Params &, Config &C) {352            C.Index.StandardLibrary = Val;353          });354  }355 356  void compile(Fragment::IndexBlock::ExternalBlock &&External,357               llvm::SMRange BlockRange) {358    if (External.Server && !Trusted) {359      diag(Error,360           "Remote index may not be specified by untrusted configuration. "361           "Copy this into user config to use it.",362           External.Server->Range);363      return;364    }365#ifndef CLANGD_ENABLE_REMOTE366    if (External.Server) {367      elog("Clangd isn't compiled with remote index support, ignoring Server: "368           "{0}",369           *External.Server);370      External.Server.reset();371    }372#endif373    // Make sure exactly one of the Sources is set.374    unsigned SourceCount = External.File.has_value() +375                           External.Server.has_value() + *External.IsNone;376    if (SourceCount != 1) {377      diag(Error, "Exactly one of File, Server or None must be set.",378           BlockRange);379      return;380    }381    Config::ExternalIndexSpec Spec;382    if (External.Server) {383      Spec.Kind = Config::ExternalIndexSpec::Server;384      Spec.Location = std::move(**External.Server);385    } else if (External.File) {386      Spec.Kind = Config::ExternalIndexSpec::File;387      auto AbsPath = makeAbsolute(std::move(*External.File), "File",388                                  llvm::sys::path::Style::native);389      if (!AbsPath)390        return;391      Spec.Location = std::move(*AbsPath);392    } else {393      assert(*External.IsNone);394      Spec.Kind = Config::ExternalIndexSpec::None;395    }396    if (Spec.Kind != Config::ExternalIndexSpec::None) {397      // Make sure MountPoint is an absolute path with forward slashes.398      if (!External.MountPoint)399        External.MountPoint.emplace(FragmentDirectory);400      if ((**External.MountPoint).empty()) {401        diag(Error, "A mountpoint is required.", BlockRange);402        return;403      }404      auto AbsPath = makeAbsolute(std::move(*External.MountPoint), "MountPoint",405                                  llvm::sys::path::Style::posix);406      if (!AbsPath)407        return;408      Spec.MountPoint = std::move(*AbsPath);409    }410    Out.Apply.push_back([Spec(std::move(Spec))](const Params &P, Config &C) {411      if (Spec.Kind == Config::ExternalIndexSpec::None) {412        C.Index.External = Spec;413        return;414      }415      if (P.Path.empty() || !pathStartsWith(Spec.MountPoint, P.Path,416                                            llvm::sys::path::Style::posix))417        return;418      C.Index.External = Spec;419      // Disable background indexing for the files under the mountpoint.420      // Note that this will overwrite statements in any previous fragments421      // (including the current one).422      C.Index.Background = Config::BackgroundPolicy::Skip;423    });424  }425 426  void compile(Fragment::DiagnosticsBlock &&F) {427    std::vector<std::string> Normalized;428    for (const auto &Suppressed : F.Suppress) {429      if (*Suppressed == "*") {430        Out.Apply.push_back([&](const Params &, Config &C) {431          C.Diagnostics.SuppressAll = true;432          C.Diagnostics.Suppress.clear();433        });434        return;435      }436      Normalized.push_back(normalizeSuppressedCode(*Suppressed).str());437    }438    if (!Normalized.empty())439      Out.Apply.push_back(440          [Normalized(std::move(Normalized))](const Params &, Config &C) {441            if (C.Diagnostics.SuppressAll)442              return;443            C.Diagnostics.Suppress.insert_range(Normalized);444          });445 446    if (F.UnusedIncludes) {447      auto Val = compileEnum<Config::IncludesPolicy>("UnusedIncludes",448                                                     **F.UnusedIncludes)449                     .map("Strict", Config::IncludesPolicy::Strict)450                     .map("None", Config::IncludesPolicy::None)451                     .value();452      if (!Val && **F.UnusedIncludes == "Experiment") {453        diag(Warning,454             "Experiment is deprecated for UnusedIncludes, use Strict instead.",455             F.UnusedIncludes->Range);456        Val = Config::IncludesPolicy::Strict;457      }458      if (Val) {459        Out.Apply.push_back([Val](const Params &, Config &C) {460          C.Diagnostics.UnusedIncludes = *Val;461        });462      }463    }464 465    if (F.MissingIncludes)466      if (auto Val = compileEnum<Config::IncludesPolicy>("MissingIncludes",467                                                         **F.MissingIncludes)468                         .map("Strict", Config::IncludesPolicy::Strict)469                         .map("None", Config::IncludesPolicy::None)470                         .value())471        Out.Apply.push_back([Val](const Params &, Config &C) {472          C.Diagnostics.MissingIncludes = *Val;473        });474 475    compile(std::move(F.Includes));476    compile(std::move(F.ClangTidy));477  }478 479  void compile(Fragment::StyleBlock &&F) {480    if (!F.FullyQualifiedNamespaces.empty()) {481      std::vector<std::string> FullyQualifiedNamespaces;482      for (auto &N : F.FullyQualifiedNamespaces) {483        // Normalize the data by dropping both leading and trailing ::484        StringRef Namespace(*N);485        Namespace.consume_front("::");486        Namespace.consume_back("::");487        FullyQualifiedNamespaces.push_back(Namespace.str());488      }489      Out.Apply.push_back([FullyQualifiedNamespaces(490                              std::move(FullyQualifiedNamespaces))](491                              const Params &, Config &C) {492        C.Style.FullyQualifiedNamespaces.insert(493            C.Style.FullyQualifiedNamespaces.begin(),494            FullyQualifiedNamespaces.begin(), FullyQualifiedNamespaces.end());495      });496    }497    auto QuotedFilter = compileHeaderRegexes(F.QuotedHeaders);498    if (QuotedFilter.has_value()) {499      Out.Apply.push_back(500          [QuotedFilter = *QuotedFilter](const Params &, Config &C) {501            C.Style.QuotedHeaders.emplace_back(QuotedFilter);502          });503    }504    auto AngledFilter = compileHeaderRegexes(F.AngledHeaders);505    if (AngledFilter.has_value()) {506      Out.Apply.push_back(507          [AngledFilter = *AngledFilter](const Params &, Config &C) {508            C.Style.AngledHeaders.emplace_back(AngledFilter);509          });510    }511  }512 513  auto compileHeaderRegexes(llvm::ArrayRef<Located<std::string>> HeaderPatterns)514      -> std::optional<std::function<bool(llvm::StringRef)>> {515    // TODO: Share this code with Diagnostics.Includes.IgnoreHeader516#ifdef CLANGD_PATH_CASE_INSENSITIVE517    static llvm::Regex::RegexFlags Flags = llvm::Regex::IgnoreCase;518#else519    static llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags;520#endif521    auto Filters = std::make_shared<std::vector<llvm::Regex>>();522    for (auto &HeaderPattern : HeaderPatterns) {523      // Anchor on the right.524      std::string AnchoredPattern = "(" + *HeaderPattern + ")$";525      llvm::Regex CompiledRegex(AnchoredPattern, Flags);526      std::string RegexError;527      if (!CompiledRegex.isValid(RegexError)) {528        diag(Warning,529             llvm::formatv("Invalid regular expression '{0}': {1}",530                           *HeaderPattern, RegexError)531                 .str(),532             HeaderPattern.Range);533        continue;534      }535      Filters->push_back(std::move(CompiledRegex));536    }537    if (Filters->empty())538      return std::nullopt;539    auto Filter = [Filters = std::move(Filters)](llvm::StringRef Path) {540      for (auto &Regex : *Filters)541        if (Regex.match(Path))542          return true;543      return false;544    };545    return Filter;546  }547 548  void appendTidyCheckSpec(std::string &CurSpec,549                           const Located<std::string> &Arg, bool IsPositive) {550    StringRef Str = StringRef(*Arg).trim();551    // Don't support negating here, its handled if the item is in the Add or552    // Remove list.553    if (Str.starts_with("-") || Str.contains(',')) {554      diag(Error, "Invalid clang-tidy check name", Arg.Range);555      return;556    }557    if (!Str.contains('*')) {558      if (!isRegisteredTidyCheck(Str)) {559        diag(Warning,560             llvm::formatv("clang-tidy check '{0}' was not found", Str).str(),561             Arg.Range);562        return;563      }564      auto Fast = isFastTidyCheck(Str);565      if (!Fast.has_value()) {566        diag(Warning,567             llvm::formatv(568                 "Latency of clang-tidy check '{0}' is not known. "569                 "It will only run if ClangTidy.FastCheckFilter is Loose or None",570                 Str)571                 .str(),572             Arg.Range);573      } else if (!*Fast) {574        diag(Warning,575             llvm::formatv(576                 "clang-tidy check '{0}' is slow. "577                 "It will only run if ClangTidy.FastCheckFilter is None",578                 Str)579                 .str(),580             Arg.Range);581      }582    }583    CurSpec += ',';584    if (!IsPositive)585      CurSpec += '-';586    CurSpec += Str;587  }588 589  void compile(Fragment::DiagnosticsBlock::ClangTidyBlock &&F) {590    std::string Checks;591    for (auto &CheckGlob : F.Add)592      appendTidyCheckSpec(Checks, CheckGlob, true);593 594    for (auto &CheckGlob : F.Remove)595      appendTidyCheckSpec(Checks, CheckGlob, false);596 597    if (!Checks.empty())598      Out.Apply.push_back(599          [Checks = std::move(Checks)](const Params &, Config &C) {600            C.Diagnostics.ClangTidy.Checks.append(601                Checks,602                C.Diagnostics.ClangTidy.Checks.empty() ? /*skip comma*/ 1 : 0,603                std::string::npos);604          });605    if (!F.CheckOptions.empty()) {606      std::vector<std::pair<std::string, std::string>> CheckOptions;607      for (auto &Opt : F.CheckOptions)608        CheckOptions.emplace_back(std::move(*Opt.first),609                                  std::move(*Opt.second));610      Out.Apply.push_back(611          [CheckOptions = std::move(CheckOptions)](const Params &, Config &C) {612            for (auto &StringPair : CheckOptions)613              C.Diagnostics.ClangTidy.CheckOptions.insert_or_assign(614                  StringPair.first, StringPair.second);615          });616    }617    if (F.FastCheckFilter.has_value())618      if (auto Val = compileEnum<Config::FastCheckPolicy>("FastCheckFilter",619                                                          *F.FastCheckFilter)620                         .map("Strict", Config::FastCheckPolicy::Strict)621                         .map("Loose", Config::FastCheckPolicy::Loose)622                         .map("None", Config::FastCheckPolicy::None)623                         .value())624        Out.Apply.push_back([Val](const Params &, Config &C) {625          C.Diagnostics.ClangTidy.FastCheckFilter = *Val;626        });627  }628 629  void compile(Fragment::DiagnosticsBlock::IncludesBlock &&F) {630#ifdef CLANGD_PATH_CASE_INSENSITIVE631    static llvm::Regex::RegexFlags Flags = llvm::Regex::IgnoreCase;632#else633    static llvm::Regex::RegexFlags Flags = llvm::Regex::NoFlags;634#endif635    std::shared_ptr<std::vector<llvm::Regex>> Filters;636    if (!F.IgnoreHeader.empty()) {637      Filters = std::make_shared<std::vector<llvm::Regex>>();638      for (auto &HeaderPattern : F.IgnoreHeader) {639        // Anchor on the right.640        std::string AnchoredPattern = "(" + *HeaderPattern + ")$";641        llvm::Regex CompiledRegex(AnchoredPattern, Flags);642        std::string RegexError;643        if (!CompiledRegex.isValid(RegexError)) {644          diag(Warning,645               llvm::formatv("Invalid regular expression '{0}': {1}",646                             *HeaderPattern, RegexError)647                   .str(),648               HeaderPattern.Range);649          continue;650        }651        Filters->push_back(std::move(CompiledRegex));652      }653    }654    // Optional to override the resulting AnalyzeAngledIncludes655    // only if it's explicitly set in the current fragment.656    // Otherwise it's inherited from parent fragment.657    std::optional<bool> AnalyzeAngledIncludes;658    if (F.AnalyzeAngledIncludes.has_value())659      AnalyzeAngledIncludes = **F.AnalyzeAngledIncludes;660    if (!Filters && !AnalyzeAngledIncludes.has_value())661      return;662    Out.Apply.push_back([Filters = std::move(Filters),663                         AnalyzeAngledIncludes](const Params &, Config &C) {664      if (Filters) {665        auto Filter = [Filters](llvm::StringRef Path) {666          for (auto &Regex : *Filters)667            if (Regex.match(Path))668              return true;669          return false;670        };671        C.Diagnostics.Includes.IgnoreHeader.emplace_back(std::move(Filter));672      }673      if (AnalyzeAngledIncludes.has_value())674        C.Diagnostics.Includes.AnalyzeAngledIncludes = *AnalyzeAngledIncludes;675    });676  }677 678  void compile(Fragment::CompletionBlock &&F) {679    if (F.AllScopes) {680      Out.Apply.push_back(681          [AllScopes(**F.AllScopes)](const Params &, Config &C) {682            C.Completion.AllScopes = AllScopes;683          });684    }685    if (F.ArgumentLists) {686      if (auto Val =687              compileEnum<Config::ArgumentListsPolicy>("ArgumentLists",688                                                       *F.ArgumentLists)689                  .map("None", Config::ArgumentListsPolicy::None)690                  .map("OpenDelimiter",691                       Config::ArgumentListsPolicy::OpenDelimiter)692                  .map("Delimiters", Config::ArgumentListsPolicy::Delimiters)693                  .map("FullPlaceholders",694                       Config::ArgumentListsPolicy::FullPlaceholders)695                  .value())696        Out.Apply.push_back([Val](const Params &, Config &C) {697          C.Completion.ArgumentLists = *Val;698        });699    }700    if (F.HeaderInsertion) {701      if (auto Val =702              compileEnum<Config::HeaderInsertionPolicy>("HeaderInsertion",703                                                         *F.HeaderInsertion)704                  .map("IWYU", Config::HeaderInsertionPolicy::IWYU)705                  .map("Never", Config::HeaderInsertionPolicy::NeverInsert)706                  .value())707        Out.Apply.push_back([Val](const Params &, Config &C) {708          C.Completion.HeaderInsertion = *Val;709        });710    }711 712    if (F.CodePatterns) {713      if (auto Val = compileEnum<Config::CodePatternsPolicy>("CodePatterns",714                                                             *F.CodePatterns)715                         .map("All", Config::CodePatternsPolicy::All)716                         .map("None", Config::CodePatternsPolicy::None)717                         .value())718        Out.Apply.push_back([Val](const Params &, Config &C) {719          C.Completion.CodePatterns = *Val;720        });721    }722  }723 724  void compile(Fragment::HoverBlock &&F) {725    if (F.ShowAKA) {726      Out.Apply.push_back([ShowAKA(**F.ShowAKA)](const Params &, Config &C) {727        C.Hover.ShowAKA = ShowAKA;728      });729    }730    if (F.MacroContentsLimit) {731      Out.Apply.push_back(732          [Limit(**F.MacroContentsLimit)](const Params &, Config &C) {733            C.Hover.MacroContentsLimit = Limit;734          });735    }736  }737 738  void compile(Fragment::InlayHintsBlock &&F) {739    if (F.Enabled)740      Out.Apply.push_back([Value(**F.Enabled)](const Params &, Config &C) {741        C.InlayHints.Enabled = Value;742      });743    if (F.ParameterNames)744      Out.Apply.push_back(745          [Value(**F.ParameterNames)](const Params &, Config &C) {746            C.InlayHints.Parameters = Value;747          });748    if (F.DeducedTypes)749      Out.Apply.push_back([Value(**F.DeducedTypes)](const Params &, Config &C) {750        C.InlayHints.DeducedTypes = Value;751      });752    if (F.Designators)753      Out.Apply.push_back([Value(**F.Designators)](const Params &, Config &C) {754        C.InlayHints.Designators = Value;755      });756    if (F.BlockEnd)757      Out.Apply.push_back([Value(**F.BlockEnd)](const Params &, Config &C) {758        C.InlayHints.BlockEnd = Value;759      });760    if (F.DefaultArguments)761      Out.Apply.push_back(762          [Value(**F.DefaultArguments)](const Params &, Config &C) {763            C.InlayHints.DefaultArguments = Value;764          });765    if (F.TypeNameLimit)766      Out.Apply.push_back(767          [Value(**F.TypeNameLimit)](const Params &, Config &C) {768            C.InlayHints.TypeNameLimit = Value;769          });770  }771 772  void compile(Fragment::SemanticTokensBlock &&F) {773    if (!F.DisabledKinds.empty()) {774      std::vector<std::string> DisabledKinds;775      for (auto &Kind : F.DisabledKinds)776        DisabledKinds.push_back(std::move(*Kind));777 778      Out.Apply.push_back(779          [DisabledKinds(std::move(DisabledKinds))](const Params &, Config &C) {780            for (auto &Kind : DisabledKinds) {781              auto It = llvm::find(C.SemanticTokens.DisabledKinds, Kind);782              if (It == C.SemanticTokens.DisabledKinds.end())783                C.SemanticTokens.DisabledKinds.push_back(std::move(Kind));784            }785          });786    }787    if (!F.DisabledModifiers.empty()) {788      std::vector<std::string> DisabledModifiers;789      for (auto &Kind : F.DisabledModifiers)790        DisabledModifiers.push_back(std::move(*Kind));791 792      Out.Apply.push_back([DisabledModifiers(std::move(DisabledModifiers))](793                              const Params &, Config &C) {794        for (auto &Kind : DisabledModifiers) {795          auto It = llvm::find(C.SemanticTokens.DisabledModifiers, Kind);796          if (It == C.SemanticTokens.DisabledModifiers.end())797            C.SemanticTokens.DisabledModifiers.push_back(std::move(Kind));798        }799      });800    }801  }802 803  void compile(Fragment::DocumentationBlock &&F) {804    if (F.CommentFormat) {805      if (auto Val =806              compileEnum<Config::CommentFormatPolicy>("CommentFormat",807                                                       *F.CommentFormat)808                  .map("Plaintext", Config::CommentFormatPolicy::PlainText)809                  .map("Markdown", Config::CommentFormatPolicy::Markdown)810                  .map("Doxygen", Config::CommentFormatPolicy::Doxygen)811                  .value())812        Out.Apply.push_back([Val](const Params &, Config &C) {813          C.Documentation.CommentFormat = *Val;814        });815    }816  }817 818  constexpr static llvm::SourceMgr::DiagKind Error = llvm::SourceMgr::DK_Error;819  constexpr static llvm::SourceMgr::DiagKind Warning =820      llvm::SourceMgr::DK_Warning;821  void diag(llvm::SourceMgr::DiagKind Kind, llvm::StringRef Message,822            llvm::SMRange Range) {823    if (Range.isValid() && SourceMgr != nullptr)824      Diagnostic(SourceMgr->GetMessage(Range.Start, Kind, Message, Range));825    else826      Diagnostic(llvm::SMDiagnostic("", Kind, Message));827  }828};829 830} // namespace831 832CompiledFragment Fragment::compile(DiagnosticCallback D) && {833  llvm::StringRef ConfigFile = "<unknown>";834  std::pair<unsigned, unsigned> LineCol = {0, 0};835  if (auto *SM = Source.Manager.get()) {836    unsigned BufID = SM->getMainFileID();837    LineCol = SM->getLineAndColumn(Source.Location, BufID);838    ConfigFile = SM->getBufferInfo(BufID).Buffer->getBufferIdentifier();839  }840  trace::Span Tracer("ConfigCompile");841  SPAN_ATTACH(Tracer, "ConfigFile", ConfigFile);842  auto Result = std::make_shared<CompiledFragmentImpl>();843  vlog("Config fragment: compiling {0}:{1} -> {2} (trusted={3})", ConfigFile,844       LineCol.first, Result.get(), Source.Trusted);845 846  FragmentCompiler{*Result, D, Source.Manager.get()}.compile(std::move(*this));847  // Return as cheaply-copyable wrapper.848  return [Result(std::move(Result))](const Params &P, Config &C) {849    return (*Result)(P, C);850  };851}852 853} // namespace config854} // namespace clangd855} // namespace clang856