diff options
author | Peter Klausler <pklausler@nvidia.com> | 2025-07-14 11:11:43 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-14 11:11:43 -0700 |
commit | 4dceb25dd1b96c93a7bd302c5d81bb1aacf75f4d (patch) | |
tree | 6f1140dcb8610694c89aa521d9779e96c127a02a /flang/lib/Parser/preprocessor.cpp | |
parent | e86bd05bdc110abcf6c6d2b6b48ede4361ce42e4 (diff) | |
download | llvm-4dceb25dd1b96c93a7bd302c5d81bb1aacf75f4d.zip llvm-4dceb25dd1b96c93a7bd302c5d81bb1aacf75f4d.tar.gz llvm-4dceb25dd1b96c93a7bd302c5d81bb1aacf75f4d.tar.bz2 |
[flang] Don't create bogus tokens from token pasting (##) (#147596)
When blank tokens arise from macro replacement in token sequences with
token pasting (##), the preprocessor is producing some bogus tokens
(e.g., "name(") that can lead to subtle bugs later when macro names are
not recognized as such.
The fix is to not paste tokens together when the result would not be a
valid Fortran or C token in the preprocessing context.
Diffstat (limited to 'flang/lib/Parser/preprocessor.cpp')
-rw-r--r-- | flang/lib/Parser/preprocessor.cpp | 43 |
1 files changed, 35 insertions, 8 deletions
diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp index 8ef9810..0aadc41 100644 --- a/flang/lib/Parser/preprocessor.cpp +++ b/flang/lib/Parser/preprocessor.cpp @@ -156,23 +156,50 @@ static TokenSequence TokenPasting(TokenSequence &&text) { } TokenSequence result; std::size_t tokens{text.SizeInTokens()}; - bool pasting{false}; + std::optional<CharBlock> before; // last non-blank token before ## for (std::size_t j{0}; j < tokens; ++j) { - if (IsTokenPasting(text.TokenAt(j))) { - if (!pasting) { + CharBlock after{text.TokenAt(j)}; + if (!before) { + if (IsTokenPasting(after)) { while (!result.empty() && result.TokenAt(result.SizeInTokens() - 1).IsBlank()) { result.pop_back(); } if (!result.empty()) { - result.ReopenLastToken(); - pasting = true; + before = result.TokenAt(result.SizeInTokens() - 1); } + } else { + result.AppendRange(text, j, 1); + } + } else if (after.IsBlank() || IsTokenPasting(after)) { + // drop it + } else { // pasting before ## after + bool doPaste{false}; + char last{before->back()}; + char first{after.front()}; + // Apply basic sanity checking to pasting so avoid constructing a bogus + // token that might cause macro replacement to fail, like "macro(". + if (IsLegalInIdentifier(last) && IsLegalInIdentifier(first)) { + doPaste = true; + } else if (IsDecimalDigit(first) && + (last == '.' || last == '+' || last == '-')) { + doPaste = true; // 1. ## 0, - ## 1 + } else if (before->size() == 1 && after.size() == 1) { + if (first == last && + (last == '<' || last == '>' || last == '*' || last == '/' || + last == '=' || last == '&' || last == '|' || last == ':')) { + // Fortran **, //, ==, :: + // C <<, >>, &&, || for use in #if expressions + doPaste = true; + } else if (first == '=' && (last == '!' || last == '/')) { + doPaste = true; // != and /= + } + } + if (doPaste) { + result.ReopenLastToken(); } - } else if (pasting && text.TokenAt(j).IsBlank()) { - } else { result.AppendRange(text, j, 1); - pasting = false; + before.reset(); } } return result; |