aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Parser/preprocessor.cpp
diff options
context:
space:
mode:
authorPeter Klausler <pklausler@nvidia.com>2025-07-14 11:11:43 -0700
committerGitHub <noreply@github.com>2025-07-14 11:11:43 -0700
commit4dceb25dd1b96c93a7bd302c5d81bb1aacf75f4d (patch)
tree6f1140dcb8610694c89aa521d9779e96c127a02a /flang/lib/Parser/preprocessor.cpp
parente86bd05bdc110abcf6c6d2b6b48ede4361ce42e4 (diff)
downloadllvm-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.cpp43
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;