diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c25a3cd..f0c7760b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,7 +130,7 @@ jobs: retention-days: 1 build-macos: - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 diff --git a/src/parser/cxx/preprocessor.cc b/src/parser/cxx/preprocessor.cc index 80d3c7a4..0f622405 100644 --- a/src/parser/cxx/preprocessor.cc +++ b/src/parser/cxx/preprocessor.cc @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -1010,97 +1011,78 @@ struct Preprocessor::Private { [[nodiscard]] auto checkPragmaOnceProtected(TokList *ts) const -> bool; struct Resolve { - enum struct Mode { - kFirst, - kAll, - }; - const Private *d; bool wantNextInlude; bool didFindCurrentPath = false; std::optional firstMatch; - Mode mode; - std::vector candidates; + std::array currentPaths; Resolve(const Resolve &other) = delete; auto operator=(const Resolve &other) -> Resolve & = delete; - Resolve(const Private *d, bool next, Mode mode = Mode::kFirst) - : d(d), wantNextInlude(next), mode(mode) {} - - [[nodiscard]] auto search(auto view, const std::string &headerName) - -> std::optional { - auto transformToIncludePath = std::views::transform( - [&](const fs::path &path) { return path / headerName; }); - - auto filterExistingFiles = std::views::filter( - [&](const fs::path &path) { return d->fileExists(path); }); - - for (const auto &path : view | transformToIncludePath | - filterExistingFiles | std::views::reverse) { - firstMatch = path; - - if (wantNextInlude && path == d->currentPath_) { - didFindCurrentPath = true; - continue; - } - - if (wantNextInlude && !didFindCurrentPath) { - continue; - } - - if (mode == Mode::kFirst) return path.string(); - - candidates.push_back(path.string()); - } - - return std::nullopt; + Resolve(const Private *d, bool next) : d(d), wantNextInlude(next) { + currentPaths[0] = d->currentPath_.string(); } - [[nodiscard]] auto operator()(const QuoteInclude &include) - -> std::optional { - const auto &headerName = include.fileName; + using SearchPaths = std::vector>; + [[nodiscard]] auto operator()(const Include &include) + -> std::optional { // search in the current path - if (auto p = search(std::views::single(d->currentPath_), headerName); - mode == Mode::kFirst && p) { - return p; - } + auto header = getHeaderName(include); - // search in the quote include paths - if (auto p = search(d->quoteIncludePaths_, headerName); - mode == Mode::kFirst && p) { - return p; + SearchPaths searchPaths; + + if (std::holds_alternative(include)) { + searchPaths = userHeaderSearchPaths(); + } else { + searchPaths = systemHeaderSearchPaths(); } - // fallback to system include paths - if (auto p = search(d->systemIncludePaths_, headerName); - mode == Mode::kFirst && p) { + if (auto p = search(searchPaths | std::views::join, header)) { return p; } - if (wantNextInlude && !didFindCurrentPath) { - if (mode == Mode::kFirst) - return firstMatch->string(); - else { - candidates.push_back(firstMatch->string()); - } + if (wantNextInlude && !didFindCurrentPath && firstMatch.has_value()) { + return firstMatch->string(); } return std::nullopt; } - [[nodiscard]] auto operator()(const SystemInclude &include) + [[nodiscard]] auto userHeaderSearchPaths() -> SearchPaths { + std::vector> paths; + paths.push_back(std::span(currentPaths)); + paths.push_back(std::span(d->quoteIncludePaths_)); + paths.push_back(std::span(d->systemIncludePaths_)); + return paths; + } + + [[nodiscard]] auto systemHeaderSearchPaths() -> SearchPaths { + std::vector> paths; + paths.push_back(std::span(d->systemIncludePaths_)); + return paths; + } + + [[nodiscard]] auto search(auto view, const std::string &headerName) -> std::optional { - if (auto p = search(d->systemIncludePaths_, include.fileName)) { - return p; - } + auto transformToIncludePath = std::views::transform( + [&](const fs::path &path) { return path / headerName; }); - if (wantNextInlude && !didFindCurrentPath) { - if (mode == Mode::kFirst) - return firstMatch->string(); - else - candidates.push_back(firstMatch->string()); + auto filterExistingFiles = std::views::filter( + [&](const fs::path &path) { return d->fileExists(path); }); + + for (const auto &path : view | transformToIncludePath | + filterExistingFiles | std::views::reverse) { + if (!wantNextInlude) return path.string(); + + firstMatch = path; + + if (path == d->currentPath_) { + didFindCurrentPath = true; + } else if (didFindCurrentPath) { + return path.string(); + } } return std::nullopt; @@ -1111,7 +1093,7 @@ struct Preprocessor::Private { -> std::optional { if (!canResolveFiles_) return std::nullopt; - return std::visit(Resolve{this, next}, include); + return Resolve{this, next}(include); } [[nodiscard]] auto isDefined(const std::string_view &id) const -> bool { @@ -1797,15 +1779,6 @@ auto Preprocessor::Private::expand( .loc = parsedInclude->loc, }; - nextState.candidates = [=, this]() -> std::vector { - auto resolve = - Resolve{this, parsedInclude->includeNext, Resolve::Mode::kAll}; - - (void)std::visit(resolve, parsedInclude->header); - - return resolve.candidates; - }; - // suspend the current file and start processing the continuation buffers_.push_back(Buffer{ .source = source,