From 4698f11af4865cf38444e4d9c7331f6574a6b334 Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Fri, 7 Mar 2025 11:32:17 -0800 Subject: [PATCH] [flang] Ignore empty keyword macros before directives Ignore any keyword macros with empty directives that might appear before a compiler directive. Fixes https://github.com/llvm/llvm-project/issues/126459. --- flang/include/flang/Parser/preprocessor.h | 1 + flang/lib/Parser/preprocessor.cpp | 9 +++++ flang/lib/Parser/prescan.cpp | 41 ++++++++++++++++++----- flang/lib/Parser/prescan.h | 1 + flang/test/Preprocessing/bug126459.F90 | 5 +++ 5 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 flang/test/Preprocessing/bug126459.F90 diff --git a/flang/include/flang/Parser/preprocessor.h b/flang/include/flang/Parser/preprocessor.h index f8d3346065981..86528a7e68def 100644 --- a/flang/include/flang/Parser/preprocessor.h +++ b/flang/include/flang/Parser/preprocessor.h @@ -81,6 +81,7 @@ class Preprocessor { void Define(const std::string ¯o, const std::string &value); void Undefine(std::string macro); bool IsNameDefined(const CharBlock &); + bool IsNameDefinedEmpty(const CharBlock &); bool IsFunctionLikeDefinition(const CharBlock &); bool AnyDefinitions() const { return !definitions_.empty(); } bool InConditional() const { return !ifStack_.empty(); } diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp index 6c257f57c2d57..7e6a1c2ca6977 100644 --- a/flang/lib/Parser/preprocessor.cpp +++ b/flang/lib/Parser/preprocessor.cpp @@ -842,6 +842,15 @@ bool Preprocessor::IsNameDefined(const CharBlock &token) { return definitions_.find(token) != definitions_.end(); } +bool Preprocessor::IsNameDefinedEmpty(const CharBlock &token) { + if (auto it{definitions_.find(token)}; it != definitions_.end()) { + const Definition &def{it->second}; + return !def.isFunctionLike() && def.replacement().SizeInChars() == 0; + } else { + return false; + } +} + bool Preprocessor::IsFunctionLikeDefinition(const CharBlock &token) { auto it{definitions_.find(token)}; return it != definitions_.end() && it->second.isFunctionLike(); diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp index f479cc51871ef..b4ac82839b73f 100644 --- a/flang/lib/Parser/prescan.cpp +++ b/flang/lib/Parser/prescan.cpp @@ -145,9 +145,8 @@ void Prescanner::Statement() { if (inFixedForm_) { CHECK(IsFixedFormCommentChar(*at_)); } else { - while (int n{IsSpaceOrTab(at_)}) { - at_ += n, ++column_; - } + at_ += line.payloadOffset; + column_ += line.payloadOffset; CHECK(*at_ == '!'); } std::optional condOffset; @@ -597,6 +596,30 @@ const char *Prescanner::SkipWhiteSpace(const char *p) { return p; } +const char *Prescanner::SkipWhiteSpaceIncludingEmptyMacros( + const char *p) const { + while (true) { + if (int n{IsSpaceOrTab(p)}) { + p += n; + } else if (preprocessor_.AnyDefinitions() && IsLegalIdentifierStart(*p)) { + // Skip keyword macros with empty definitions + const char *q{p + 1}; + while (IsLegalInIdentifier(*q)) { + ++q; + } + if (preprocessor_.IsNameDefinedEmpty( + CharBlock{p, static_cast(q - p)})) { + p = q; + } else { + break; + } + } else { + break; + } + } + return p; +} + const char *Prescanner::SkipWhiteSpaceAndCComments(const char *p) const { while (true) { if (int n{IsSpaceOrTab(p)}) { @@ -1463,18 +1486,18 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const { *sp = '\0'; if (const char *ss{IsCompilerDirectiveSentinel( sentinel, static_cast(sp - sentinel))}) { - std::size_t payloadOffset = p - start; - return {LineClassification{ - LineClassification::Kind::CompilerDirective, payloadOffset, ss}}; + return { + LineClassification{LineClassification::Kind::CompilerDirective, 0, ss}}; } return std::nullopt; } std::optional Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const { - if (const char *p{SkipWhiteSpace(start)}; p && *p++ == '!') { + if (const char *p{SkipWhiteSpaceIncludingEmptyMacros(start)}; + p && *p++ == '!') { if (auto maybePair{IsCompilerDirectiveSentinel(p)}) { - auto offset{static_cast(maybePair->second - start)}; + auto offset{static_cast(p - start - 1)}; return {LineClassification{LineClassification::Kind::CompilerDirective, offset, maybePair->first}}; } @@ -1529,7 +1552,7 @@ Prescanner::IsCompilerDirectiveSentinel(const char *p) const { if (int n{*p == '&' ? 1 : IsSpaceOrTab(p)}) { if (j > 0) { sentinel[j] = '\0'; - p = SkipWhiteSpace(p + n); + p = SkipWhiteSpaceIncludingEmptyMacros(p + n); if (*p != '!') { if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) { return std::make_pair(sp, p); diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h index e2440ad6fbc42..9cf1b389f5b19 100644 --- a/flang/lib/Parser/prescan.h +++ b/flang/lib/Parser/prescan.h @@ -182,6 +182,7 @@ class Prescanner { void SkipCComments(); void SkipSpaces(); static const char *SkipWhiteSpace(const char *); + const char *SkipWhiteSpaceIncludingEmptyMacros(const char *) const; const char *SkipWhiteSpaceAndCComments(const char *) const; const char *SkipCComment(const char *) const; bool NextToken(TokenSequence &); diff --git a/flang/test/Preprocessing/bug126459.F90 b/flang/test/Preprocessing/bug126459.F90 new file mode 100644 index 0000000000000..fae8a07659f72 --- /dev/null +++ b/flang/test/Preprocessing/bug126459.F90 @@ -0,0 +1,5 @@ +! RUN: %flang -E -fopenmp %s 2>&1 | FileCheck %s +!CHECK: NDIR=0 +#define BLANKMACRO +BLANKMACRO !$ NDIR=0 +end