[flang-commits] [flang] 7434286 - [flang] Recognize compiler directives after expansion in comment (#183626)

via flang-commits flang-commits at lists.llvm.org
Mon Mar 2 14:47:12 PST 2026


Author: Peter Klausler
Date: 2026-03-02T14:47:08-08:00
New Revision: 743428688fb0b3e6e6d466688a3930520d741aba

URL: https://github.com/llvm/llvm-project/commit/743428688fb0b3e6e6d466688a3930520d741aba
DIFF: https://github.com/llvm/llvm-project/commit/743428688fb0b3e6e6d466688a3930520d741aba.diff

LOG: [flang] Recognize compiler directives after expansion in comment (#183626)

The compiler can recognize a compiler directive when one results from a
macro expansion at the beginning of a non-comment source line, as in
"#define FOO !$OMP". But it can't recognize a compiler directive that
initially appears as a comment line, as in "!BAR" after "#define BAR
$OMP". Extend the prescanner to recognize such cases in free form
source. (Fixed form is a much more complicated case for this recognition
and will be addressed later if needed.)

This is the 2nd version of this patch; the first was reverted after
problems with continuation lines were encountered.

Fixes https://github.com/llvm/llvm-project/issues/178481.

Added: 
    flang/test/Preprocessing/bug178481.F90

Modified: 
    flang/include/flang/Parser/preprocessor.h
    flang/lib/Parser/preprocessor.cpp
    flang/lib/Parser/prescan.cpp
    flang/lib/Parser/prescan.h

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Parser/preprocessor.h b/flang/include/flang/Parser/preprocessor.h
index 0405d42e64f7b..5db2b020f879a 100644
--- a/flang/include/flang/Parser/preprocessor.h
+++ b/flang/include/flang/Parser/preprocessor.h
@@ -49,8 +49,8 @@ class Definition {
 
   bool set_isDisabled(bool disable);
 
-  TokenSequence Apply(const std::vector<TokenSequence> &args, Prescanner &,
-      bool inIfExpression = false);
+  TokenSequence Apply(const std::vector<TokenSequence> &args,
+      const Prescanner &, bool inIfExpression = false) const;
 
   void Print(llvm::raw_ostream &out, const char *macroName = "") const;
 
@@ -95,7 +95,7 @@ class Preprocessor {
   // that result and try again.  All other Fortran preprocessors share this
   // behavior.
   std::optional<TokenSequence> MacroReplacement(const TokenSequence &,
-      Prescanner &,
+      const Prescanner &,
       std::optional<std::size_t> *partialFunctionLikeMacro = nullptr,
       bool inIfExpression = false);
 
@@ -109,7 +109,7 @@ class Preprocessor {
   enum class CanDeadElseAppear { No, Yes };
 
   CharBlock SaveTokenAsName(const CharBlock &);
-  TokenSequence ReplaceMacros(const TokenSequence &, Prescanner &,
+  TokenSequence ReplaceMacros(const TokenSequence &, const Prescanner &,
       std::optional<std::size_t> *partialFunctionLikeMacro = nullptr,
       bool inIfExpression = false);
   void SkipDisabledConditionalCode(

diff  --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp
index 529d2c345c112..3f9abf05d796b 100644
--- a/flang/lib/Parser/preprocessor.cpp
+++ b/flang/lib/Parser/preprocessor.cpp
@@ -214,7 +214,7 @@ constexpr bool IsDefinedKeyword(CharBlock token) {
 }
 
 TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
-    Prescanner &prescanner, bool inIfExpression) {
+    const Prescanner &prescanner, bool inIfExpression) const {
   TokenSequence result;
   bool skipping{false};
   int parenthesesNesting{0};
@@ -254,7 +254,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
         CHECK(resultSize > 0 &&
             result.TokenAt(resultSize - 1) == replacement_.TokenAt(prev - 1));
         result.pop_back();
-        result.CopyAll(Stringify(args[index], prescanner.allSources()));
+        AllSources &allSources{
+            *const_cast<AllSources *>(&prescanner.allSources())};
+        result.CopyAll(Stringify(args[index], allSources));
       } else {
         const TokenSequence *arg{&args[index]};
         std::optional<TokenSequence> replaced;
@@ -267,7 +269,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
           auto next{replacement_.SkipBlanks(j + 1)};
           if (next >= tokens || !IsTokenPasting(replacement_.TokenAt(next))) {
             // Apply macro replacement to the actual argument
-            replaced = prescanner.preprocessor().MacroReplacement(
+            Preprocessor &preprocessor{
+                *const_cast<Preprocessor *>(&prescanner.preprocessor())};
+            replaced = preprocessor.MacroReplacement(
                 *arg, prescanner, nullptr, inIfExpression);
             if (replaced) {
               arg = &*replaced;
@@ -278,9 +282,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
       }
     } else if (bytes == 11 && isVariadic_ &&
         token.ToString() == "__VA_ARGS__") {
-      Provenance commaProvenance{
-          prescanner.preprocessor().allSources().CompilerInsertionProvenance(
-              ',')};
+      AllSources &allSources{
+          *const_cast<AllSources *>(&prescanner.allSources())};
+      Provenance commaProvenance{allSources.CompilerInsertionProvenance(',')};
       for (std::size_t k{argumentCount()}; k < args.size(); ++k) {
         if (k > argumentCount()) {
           result.Put(","s, commaProvenance);
@@ -440,7 +444,7 @@ void Preprocessor::Define(const std::string &macro, const std::string &value) {
 void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); }
 
 std::optional<TokenSequence> Preprocessor::MacroReplacement(
-    const TokenSequence &input, Prescanner &prescanner,
+    const TokenSequence &input, const Prescanner &prescanner,
     std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {
   // Do quick scan for any use of a defined name.
   if (!inIfExpression && definitions_.empty()) {
@@ -663,7 +667,7 @@ std::optional<TokenSequence> Preprocessor::MacroReplacement(
 }
 
 TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens,
-    Prescanner &prescanner,
+    const Prescanner &prescanner,
     std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {
   if (std::optional<TokenSequence> repl{MacroReplacement(
           tokens, prescanner, partialFunctionLikeMacro, inIfExpression)}) {

diff  --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index 036b2f3fca3e1..8d74568ca4cf6 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -46,7 +46,8 @@ Prescanner::Prescanner(const Prescanner &that, Preprocessor &prepro,
       prescannerNesting_{that.prescannerNesting_ + 1},
       skipLeadingAmpersand_{that.skipLeadingAmpersand_},
       compilerDirectiveBloomFilter_{that.compilerDirectiveBloomFilter_},
-      compilerDirectiveSentinels_{that.compilerDirectiveSentinels_} {}
+      compilerDirectiveSentinels_{that.compilerDirectiveSentinels_},
+      maxSentinelLength_{that.maxSentinelLength_} {}
 
 // Returns number of bytes to skip
 static inline int IsSpace(const char *p) {
@@ -212,6 +213,19 @@ void Prescanner::Statement() {
     }
     break;
   }
+  case LineClassification::Kind::CompilerDirectiveAfterMacroExpansion:
+    directiveSentinel_ = line.sentinel;
+    CHECK(InCompilerDirective());
+    BeginStatementAndAdvance();
+    while (*at_ != '!' && *at_ != '\n') {
+      ++at_, ++column_;
+    }
+    CHECK(*at_ == '!');
+    EmitChar(tokens, '!');
+    tokens.CloseToken();
+    ++at_;
+    ++column_;
+    break;
   case LineClassification::Kind::Source: {
     BeginStatementAndAdvance();
     bool checkLabelField{false};
@@ -238,30 +252,17 @@ void Prescanner::Statement() {
     // a comment marker or directive sentinel.  If so, disable line
     // continuation, so that NextToken() won't consume anything from
     // following lines.
-    if (IsLegalIdentifierStart(*at_)) {
-      // TODO: Only bother with these cases when any keyword macro has
-      // been defined with replacement text that could begin a comment
-      // or directive sentinel.
-      const char *p{at_};
-      while (IsLegalInIdentifier(*++p)) {
-      }
-      CharBlock id{at_, static_cast<std::size_t>(p - at_)};
-      if (preprocessor_.IsNameDefined(id) &&
-          !preprocessor_.IsFunctionLikeDefinition(id)) {
-        checkLabelField = false;
-        TokenSequence toks;
-        toks.Put(id, GetProvenance(at_));
-        if (auto replaced{preprocessor_.MacroReplacement(toks, *this)}) {
-          auto newLineClass{ClassifyLine(*replaced, GetCurrentProvenance())};
-          if (newLineClass.kind ==
-              LineClassification::Kind::CompilerDirective) {
-            directiveSentinel_ = newLineClass.sentinel;
-            disableSourceContinuation_ = false;
-          } else {
-            disableSourceContinuation_ = !replaced->empty() &&
-                newLineClass.kind != LineClassification::Kind::Source;
-          }
-        }
+    if (auto kwName{GetKeywordMacroName(at_)}) {
+      checkLabelField = false;
+      Provenance here{GetCurrentProvenance()};
+      TokenSequence replacement{ExpandKeywordMacro(*kwName, here)};
+      auto newLineClass{ClassifyLine(replacement, here)};
+      if (newLineClass.kind == LineClassification::Kind::CompilerDirective) {
+        directiveSentinel_ = newLineClass.sentinel;
+        disableSourceContinuation_ = false;
+      } else {
+        disableSourceContinuation_ = !replacement.empty() &&
+            newLineClass.kind != LineClassification::Kind::Source;
       }
     }
     if (checkLabelField) {
@@ -272,6 +273,7 @@ void Prescanner::Statement() {
 
   while (NextToken(tokens)) {
   }
+
   if (continuationLines_ > 255) {
     if (features_.ShouldWarn(common::LanguageFeature::MiscSourceExtensions)) {
       Say(common::LanguageFeature::MiscSourceExtensions,
@@ -304,6 +306,7 @@ void Prescanner::Statement() {
       CheckAndEmitLine(preprocessed->ToLowerCase(), newlineProvenance);
       break;
     case LineClassification::Kind::CompilerDirective:
+    case LineClassification::Kind::CompilerDirectiveAfterMacroExpansion:
       if (preprocessed->HasRedundantBlanks()) {
         preprocessed->RemoveRedundantBlanks();
       }
@@ -335,40 +338,41 @@ void Prescanner::Statement() {
           preprocessed->ToLowerCase().ClipComment(*this), newlineProvenance);
       break;
     }
-  } else { // no macro replacement
-    if (line.kind == LineClassification::Kind::CompilerDirective) {
-      while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
-        newlineProvenance = GetCurrentProvenance();
-      }
-      if (preprocessingOnly_ && inFixedForm_ && InConditionalLine() &&
-          nextLine_ < limit_) {
-        // In -E mode, when the line after !$ conditional compilation is a
-        // regular fixed form continuation line, append a '&' to the line.
-        const char *p{nextLine_};
-        int col{1};
-        while (int n{IsSpace(p)}) {
-          if (*p == '\t') {
-            break;
-          }
-          p += n;
-          ++col;
-        }
-        if (col == 6 && *p != '0' && *p != '\t' && *p != '\n') {
-          EmitChar(tokens, '&');
-          tokens.CloseToken();
+  } else if (line.kind == LineClassification::Kind::CompilerDirective ||
+      line.kind ==
+          LineClassification::Kind::CompilerDirectiveAfterMacroExpansion) {
+    while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
+      newlineProvenance = GetCurrentProvenance();
+    }
+    if (preprocessingOnly_ && inFixedForm_ && InConditionalLine() &&
+        nextLine_ < limit_) {
+      // In -E mode, when the line after !$ conditional compilation is a
+      // regular fixed form continuation line, append a '&' to the line.
+      const char *p{nextLine_};
+      int col{1};
+      while (int n{IsSpace(p)}) {
+        if (*p == '\t') {
+          break;
         }
+        p += n;
+        ++col;
       }
-      tokens.ToLowerCase();
-      if (!SourceFormChange(tokens.ToString())) {
-        CheckAndEmitLine(tokens, newlineProvenance);
-      }
-    } else { // Kind::Source
-      tokens.ToLowerCase();
-      if (inFixedForm_) {
-        EnforceStupidEndStatementRules(tokens);
+      if (col == 6 && *p != '0' && *p != '\t' && *p != '\n') {
+        EmitChar(tokens, '&');
+        tokens.CloseToken();
       }
+    }
+    tokens.ToLowerCase();
+    if (!SourceFormChange(tokens.ToString())) {
       CheckAndEmitLine(tokens, newlineProvenance);
     }
+  } else {
+    CHECK(line.kind == LineClassification::Kind::Source);
+    tokens.ToLowerCase();
+    if (inFixedForm_) {
+      EnforceStupidEndStatementRules(tokens);
+    }
+    CheckAndEmitLine(tokens, newlineProvenance);
   }
   directiveSentinel_ = nullptr;
 }
@@ -567,7 +571,8 @@ bool Prescanner::MustSkipToEndOfLine() const {
     return true; // skip over ignored columns in right margin (73:80)
   } else if (*at_ == '!' && !inCharLiteral_ &&
       (!inFixedForm_ || tabInCurrentLine_ || column_ != 6)) {
-    return InCompilerDirective() || !IsCompilerDirectiveSentinel(at_ + 1);
+    return InCompilerDirective() ||
+        !IsCompilerDirectiveSentinelAfterKeywordMacro(at_ + 1);
   } else {
     return false;
   }
@@ -1463,29 +1468,37 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) {
   if (InCompilerDirective()) {
     if (InConditionalLine()) {
       if (preprocessingOnly_) {
-        // in -E mode, don't treat !$ as a continuation
+        // in -E mode, don't treat !$/!@acc/!@cuf as a continuation
         return nullptr;
-      } else if (p[0] == '!' && (p[1] == '$' || p[1] == '@')) {
-        p += 2;
-        if (InOpenACCOrCUDAConditionalLine()) {
-          if (IsDirective("acc", p) || IsDirective("cuf", p)) {
-            p += 3;
-          } else {
-            return nullptr;
+      } else if (*p == '!') {
+        if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+          if (lClass->sentinel &&
+              ((IsOpenMPConditionalLine(lClass->sentinel) &&
+                   InOpenMPConditionalLine()) ||
+                  (IsOpenACCConditionalLine(lClass->sentinel) &&
+                      InOpenACCConditionalLine()) ||
+                  (IsCUDAConditionalLine(lClass->sentinel) &&
+                      InCUDAConditionalLine()))) {
+            p += 1 + lClass->payloadOffset;
           }
         }
         if (*p != '&' && !IsSpaceOrTab(p)) {
           return nullptr;
         }
       }
-    } else if (*p++ == '!') {
-      for (const char *s{directiveSentinel_}; *s != '\0'; ++p, ++s) {
-        if (*s != ToLowerCaseLetter(*p)) {
+    } else if (*p == '!') {
+      if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+        if (lClass->sentinel &&
+            std::strcmp(directiveSentinel_, lClass->sentinel) == 0) {
+          p += 1 + lClass->payloadOffset;
+        } else {
           return nullptr; // not the same directive class
         }
+      } else {
+        return nullptr; // not a compiler directive
       }
     } else {
-      return nullptr;
+      return nullptr; // not a '!'
     }
     p = SkipWhiteSpace(p);
     if (*p == '&') {
@@ -1501,14 +1514,16 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) {
   }
   if (p[0] == '!' && !preprocessingOnly_) {
     // Conditional lines can be continuations
-    if (p[1] == '$' && features_.IsEnabled(LanguageFeature::OpenMP)) {
-      p = lineStart = SkipWhiteSpace(p + 2);
-    } else if (IsDirective("@acc", p + 1) &&
-        features_.IsEnabled(LanguageFeature::OpenACC)) {
-      p = lineStart = SkipWhiteSpace(p + 5);
-    } else if (IsDirective("@cuf", p + 1) &&
-        features_.IsEnabled(LanguageFeature::CUDA)) {
-      p = lineStart = SkipWhiteSpace(p + 5);
+    if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+      if (lClass->sentinel &&
+          ((IsOpenMPConditionalLine(lClass->sentinel) &&
+               features_.IsEnabled(LanguageFeature::OpenMP)) ||
+              (IsOpenACCConditionalLine(lClass->sentinel) &&
+                  features_.IsEnabled(LanguageFeature::OpenACC)) ||
+              (IsCUDAConditionalLine(lClass->sentinel) &&
+                  features_.IsEnabled(LanguageFeature::CUDA)))) {
+        lineStart = p = SkipWhiteSpace(p + 1 + lClass->payloadOffset);
+      }
     }
   }
   if (*p == '&') {
@@ -1616,6 +1631,9 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
   if (!IsFixedFormCommentChar(col1)) {
     return std::nullopt;
   }
+  // TODO: Handle keyword macros that expand to directives in fixed form.
+  // The comment character can't be 'c' or 'C'.  Need to figure out whether
+  // fixed form continuation should apply to the expansions.
   char sentinel[5], *sp{sentinel};
   int column{2};
   for (; column < 6; ++column) {
@@ -1678,20 +1696,28 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
 std::optional<Prescanner::LineClassification>
 Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const {
   if (const char *p{SkipWhiteSpaceIncludingEmptyMacros(start)};
-      p && *p++ == '!') {
-    if (auto maybePair{IsCompilerDirectiveSentinel(p)}) {
-      auto offset{static_cast<std::size_t>(p - start - 1)};
-      const char *sentinel{maybePair->first};
-      if ((sentinel[0] == '$' && sentinel[1] == '\0') || sentinel[1] == '@') {
-        if (const char *comment{IsFreeFormComment(maybePair->second)}) {
-          if (*comment == '!') {
-            // Conditional line comment - treat as comment
-            return std::nullopt;
+      p && *p == '!') {
+    if (auto lnClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
+      if (lnClass->kind == LineClassification::Kind::CompilerDirective) {
+        const char *sentinel{lnClass->sentinel};
+        CHECK(sentinel != nullptr);
+        const char *payload{nullptr};
+        if (sentinel[0] == '$' && sentinel[1] == '\0') {
+          payload = p + 2; // !$
+        } else if (sentinel[1] == '@') {
+          payload = p + 5; // !@acc or !@cuf
+        }
+        if (payload) {
+          if (const char *comment{IsFreeFormComment(payload)}) {
+            if (*comment == '!') { // !$ !blah or !@acc !blah
+              // Conditional line comment - treat as comment
+              return std::nullopt;
+            }
           }
         }
+        lnClass->payloadOffset = static_cast<std::size_t>(p - start);
       }
-      return {LineClassification{
-          LineClassification::Kind::CompilerDirective, offset, sentinel}};
+      return lnClass;
     }
   }
   return std::nullopt;
@@ -1705,9 +1731,38 @@ Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) {
   compilerDirectiveBloomFilter_.set(packed % prime1);
   compilerDirectiveBloomFilter_.set(packed % prime2);
   compilerDirectiveSentinels_.insert(dir);
+  int len{static_cast<int>(dir.size())};
+  if (len > maxSentinelLength_) {
+    maxSentinelLength_ = len;
+  }
   return *this;
 }
 
+std::optional<CharBlock> Prescanner::GetKeywordMacroName(
+    const char *start) const {
+  if (IsLegalIdentifierStart(*start)) {
+    // TODO: Only bother with these cases when any keyword macro has
+    // been defined with replacement text that could begin a comment
+    // or directive sentinel.
+    const char *p{start};
+    while (IsLegalInIdentifier(*++p)) {
+    }
+    CharBlock name{start, static_cast<std::size_t>(p - start)};
+    if (preprocessor_.IsNameDefined(name) &&
+        !preprocessor_.IsFunctionLikeDefinition(name)) {
+      return name;
+    }
+  }
+  return std::nullopt;
+}
+
+TokenSequence Prescanner::ExpandKeywordMacro(
+    CharBlock name, Provenance provenance) const {
+  TokenSequence toks;
+  toks.Put(name, provenance);
+  return preprocessor_.MacroReplacement(toks, *this).value();
+}
+
 const char *Prescanner::IsCompilerDirectiveSentinel(
     const char *sentinel, std::size_t len) const {
   std::uint64_t packed{0};
@@ -1734,27 +1789,29 @@ const char *Prescanner::IsCompilerDirectiveSentinel(CharBlock token) const {
   while (end > p && (end[-1] == ' ' || end[-1] == '\t')) {
     --end;
   }
+  if (end > p + maxSentinelLength_) {
+    end = p + maxSentinelLength_;
+  }
   return end > p && IsCompilerDirectiveSentinel(p, end - p) ? p : nullptr;
 }
 
 std::optional<std::pair<const char *, const char *>>
 Prescanner::IsCompilerDirectiveSentinel(const char *p) const {
   char sentinel[8];
+  std::size_t maxLen{static_cast<std::size_t>(maxSentinelLength_)};
   for (std::size_t j{0}; j + 1 < sizeof sentinel; ++p, ++j) {
     if (int n{IsSpaceOrTab(p)};
-        n || !(IsLetter(*p) || *p == '$' || *p == '@')) {
-      if (j > 0) {
-        if (j == 1 && sentinel[0] == '$' && n == 0 && *p != '&' && *p != '\n') {
-          // Free form OpenMP conditional compilation line sentinels have to
-          // be immediately followed by a space or &, not a digit
-          // or anything else.  A newline also works for an initial line.
-          break;
-        }
+        n || j >= maxLen || !(IsLetter(*p) || *p == '$' || *p == '@')) {
+      if (j <= 1 && sentinel[0] == '$' && n == 0 && *p != '&' && *p != '\n') {
+        // Free form OpenMP conditional compilation line sentinels have to
+        // be immediately followed by a space or &, not a digit
+        // or anything else.  A newline also works for an initial line.
+        break;
+      }
+      if (*p != '!') {
         sentinel[j] = '\0';
-        if (*p != '!') {
-          if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
-            return std::make_pair(sp, p);
-          }
+        if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
+          return std::make_pair(sp, p);
         }
       }
       break;
@@ -1765,8 +1822,26 @@ Prescanner::IsCompilerDirectiveSentinel(const char *p) const {
   return std::nullopt;
 }
 
-Prescanner::LineClassification Prescanner::ClassifyLine(
-    const char *start) const {
+auto Prescanner::IsCompilerDirectiveSentinelAfterKeywordMacro(
+    const char *p) const -> std::optional<LineClassification> {
+  if (auto name{GetKeywordMacroName(p)}) {
+    Provenance provenance{GetProvenance(p)};
+    TokenSequence expansion{ExpandKeywordMacro(*name, provenance)};
+    expansion.Put("\n", 1, provenance); // termination
+    CharBlock block{expansion.ToLowerCase().ToCharBlock()};
+    if (auto maybePair{IsCompilerDirectiveSentinel(block.begin())}) {
+      return LineClassification{
+          LineClassification::Kind::CompilerDirectiveAfterMacroExpansion,
+          name->size(), maybePair->first};
+    }
+  } else if (auto maybePair{IsCompilerDirectiveSentinel(p)}) {
+    return LineClassification{LineClassification::Kind::CompilerDirective,
+        static_cast<std::size_t>(maybePair->second - p), maybePair->first};
+  }
+  return std::nullopt;
+}
+
+auto Prescanner::ClassifyLine(const char *start) const -> LineClassification {
   if (inFixedForm_) {
     if (std::optional<LineClassification> lc{
             IsFixedFormCompilerDirectiveLine(start)}) {

diff  --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index efbeb3709a94c..a3726e0f31b33 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -81,6 +81,9 @@ class Prescanner {
   TokenSequence TokenizePreprocessorDirective();
   Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
 
+  std::optional<CharBlock> GetKeywordMacroName(const char *) const;
+  TokenSequence ExpandKeywordMacro(CharBlock, Provenance) const;
+
   const char *IsCompilerDirectiveSentinel(const char *, std::size_t) const;
   const char *IsCompilerDirectiveSentinel(CharBlock) const;
   // 'first' is the sentinel, 'second' is beginning of payload
@@ -109,6 +112,7 @@ class Prescanner {
       PreprocessorDirective,
       IncludeLine, // Fortran INCLUDE
       CompilerDirective,
+      CompilerDirectiveAfterMacroExpansion, // !MACRO -> !$OMP ...
       Source
     };
     LineClassification(Kind k, std::size_t po = 0, const char *s = nullptr)
@@ -156,7 +160,7 @@ class Prescanner {
   }
 
   void EmitInsertedChar(TokenSequence &tokens, char ch) {
-    Provenance provenance{allSources_.CompilerInsertionProvenance(ch)};
+    Provenance provenance{allSources().CompilerInsertionProvenance(ch)};
     tokens.PutNextTokenChar(ch, provenance);
   }
 
@@ -166,18 +170,29 @@ class Prescanner {
     return *at_;
   }
 
+  bool IsOpenMPConditionalLine(const char *sentinel) const {
+    return sentinel && sentinel[0] == '$' && !sentinel[1];
+  }
+  bool IsOpenACCConditionalLine(const char *sentinel) const {
+    return sentinel && sentinel[0] == '@' && sentinel[1] == 'a' &&
+        sentinel[2] == 'c' && sentinel[3] == 'c' && sentinel[4] == '\0';
+  }
+  bool IsCUDAConditionalLine(const char *sentinel) const {
+    return sentinel && sentinel[0] == '@' && sentinel[1] == 'c' &&
+        sentinel[2] == 'u' && sentinel[3] == 'f' && sentinel[4] == '\0';
+  }
   bool InCompilerDirective() const { return directiveSentinel_ != nullptr; }
   bool InOpenMPConditionalLine() const {
-    return directiveSentinel_ && directiveSentinel_[0] == '$' &&
-        !directiveSentinel_[1];
+    return IsOpenMPConditionalLine(directiveSentinel_);
+  }
+  bool InOpenACCConditionalLine() const {
+    return IsOpenACCConditionalLine(directiveSentinel_);
+  }
+  bool InCUDAConditionalLine() const {
+    return IsCUDAConditionalLine(directiveSentinel_);
   }
   bool InOpenACCOrCUDAConditionalLine() const {
-    return directiveSentinel_ && directiveSentinel_[0] == '@' &&
-        ((directiveSentinel_[1] == 'a' && directiveSentinel_[2] == 'c' &&
-             directiveSentinel_[3] == 'c') ||
-            (directiveSentinel_[1] == 'c' && directiveSentinel_[2] == 'u' &&
-                directiveSentinel_[3] == 'f')) &&
-        directiveSentinel_[4] == '\0';
+    return InOpenACCConditionalLine() || InCUDAConditionalLine();
   }
   bool InConditionalLine() const {
     return InOpenMPConditionalLine() || InOpenACCOrCUDAConditionalLine();
@@ -240,6 +255,8 @@ class Prescanner {
   bool SourceFormChange(std::string &&);
   bool CompilerDirectiveContinuation(TokenSequence &, const char *sentinel);
   bool SourceLineContinuation(TokenSequence &);
+  std::optional<LineClassification>
+  IsCompilerDirectiveSentinelAfterKeywordMacro(const char *p) const;
 
   Messages &messages_;
   CookedSource &cooked_;
@@ -298,15 +315,16 @@ class Prescanner {
   const std::size_t firstCookedCharacterOffset_{cooked_.BufferedBytes()};
 
   const Provenance spaceProvenance_{
-      allSources_.CompilerInsertionProvenance(' ')};
+      allSources().CompilerInsertionProvenance(' ')};
   const Provenance backslashProvenance_{
-      allSources_.CompilerInsertionProvenance('\\')};
+      allSources().CompilerInsertionProvenance('\\')};
 
   // To avoid probing the set of active compiler directive sentinel strings
   // on every comment line, they're checked first with a cheap Bloom filter.
   static const int prime1{1019}, prime2{1021};
   std::bitset<prime2> compilerDirectiveBloomFilter_; // 128 bytes
   std::unordered_set<std::string> compilerDirectiveSentinels_;
+  int maxSentinelLength_{0};
 };
 } // namespace Fortran::parser
 #endif // FORTRAN_PARSER_PRESCAN_H_

diff  --git a/flang/test/Preprocessing/bug178481.F90 b/flang/test/Preprocessing/bug178481.F90
new file mode 100644
index 0000000000000..09a621858814c
--- /dev/null
+++ b/flang/test/Preprocessing/bug178481.F90
@@ -0,0 +1,10 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp %s 2>&1 | FileCheck %s
+#define OMP_DECLARE_TARGET $OMP declare target
+#define OMP_BANG $OMP
+subroutine s
+  !CHECK: !$OMP DECLARE TARGET
+  !OMP_DECLARE_TARGET
+  !CHECK: !$OMP DECLARE TARGET
+  !OMP_BANG declare &
+  !OMP_BANG target
+end


        


More information about the flang-commits mailing list