Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
retention-days: 1

build-macos:
runs-on: macos-14
runs-on: macos-15

steps:
- uses: actions/checkout@v4
Expand Down
125 changes: 49 additions & 76 deletions src/parser/cxx/preprocessor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <utf8/unchecked.h>

#include <algorithm>
#include <array>
#include <cassert>
#include <format>
#include <forward_list>
Expand Down Expand Up @@ -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<fs::path> firstMatch;
Mode mode;
std::vector<std::string> candidates;
std::array<std::string, 1> 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<std::string> {
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<std::string> {
const auto &headerName = include.fileName;
using SearchPaths = std::vector<std::span<const std::string>>;

[[nodiscard]] auto operator()(const Include &include)
-> std::optional<std::string> {
// 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<QuoteInclude>(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<std::span<const std::string>> paths;
paths.push_back(std::span<const std::string>(currentPaths));
paths.push_back(std::span<const std::string>(d->quoteIncludePaths_));
paths.push_back(std::span<const std::string>(d->systemIncludePaths_));
return paths;
}

[[nodiscard]] auto systemHeaderSearchPaths() -> SearchPaths {
std::vector<std::span<const std::string>> paths;
paths.push_back(std::span<const std::string>(d->systemIncludePaths_));
return paths;
}

[[nodiscard]] auto search(auto view, const std::string &headerName)
-> std::optional<std::string> {
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;
Expand All @@ -1111,7 +1093,7 @@ struct Preprocessor::Private {
-> std::optional<std::string> {
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 {
Expand Down Expand Up @@ -1797,15 +1779,6 @@ auto Preprocessor::Private::expand(
.loc = parsedInclude->loc,
};

nextState.candidates = [=, this]() -> std::vector<std::string> {
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,
Expand Down