[flang-commits] [flang] [flang][preprocessor] Fixed-form continuation across preprocessing di… (PR #95332)

via flang-commits flang-commits at lists.llvm.org
Wed Jun 12 16:45:46 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-parser

Author: Peter Klausler (klausler)

<details>
<summary>Changes</summary>

…rective

Implement fixed-form line continuation when the continuation line is the result of text produced by an #include or other preprocessing directive.  This accommodates the somewhat common practice of putting dummy or actual arguments into a header file and #including it into several code sites.

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

---
Full diff: https://github.com/llvm/llvm-project/pull/95332.diff


8 Files Affected:

- (modified) flang/include/flang/Parser/provenance.h (+5) 
- (modified) flang/include/flang/Parser/token-sequence.h (+1) 
- (modified) flang/lib/Parser/prescan.cpp (+17-3) 
- (modified) flang/lib/Parser/prescan.h (+2) 
- (modified) flang/lib/Parser/provenance.cpp (+10) 
- (modified) flang/lib/Parser/token-sequence.cpp (+8-4) 
- (added) flang/test/Preprocessing/ff-args.h (+1) 
- (added) flang/test/Preprocessing/ff-include-args.F (+14) 


``````````diff
diff --git a/flang/include/flang/Parser/provenance.h b/flang/include/flang/Parser/provenance.h
index 73d500f32831b..42c5b3de2cbe2 100644
--- a/flang/include/flang/Parser/provenance.h
+++ b/flang/include/flang/Parser/provenance.h
@@ -257,6 +257,10 @@ class CookedSource {
     provenanceMap_.Put(pm);
   }
 
+  void MarkPossibleFixedFormContinuation() {
+    possibleFixedFormContinuations_.push_back(BufferedBytes());
+  }
+
   std::size_t BufferedBytes() const;
   void Marshal(AllCookedSources &); // marshals text into one contiguous block
   void CompileProvenanceRangeToOffsetMappings(AllSources &);
@@ -269,6 +273,7 @@ class CookedSource {
   std::string data_; // all of it, prescanned and preprocessed
   OffsetToProvenanceMappings provenanceMap_;
   ProvenanceRangeToOffsetMappings invertedMap_;
+  std::list<std::size_t> possibleFixedFormContinuations_;
 };
 
 class AllCookedSources {
diff --git a/flang/include/flang/Parser/token-sequence.h b/flang/include/flang/Parser/token-sequence.h
index ee5f71edd03c8..1f82a3c1a203a 100644
--- a/flang/include/flang/Parser/token-sequence.h
+++ b/flang/include/flang/Parser/token-sequence.h
@@ -125,6 +125,7 @@ class TokenSequence {
   TokenSequence &ClipComment(const Prescanner &, bool skipFirst = false);
   const TokenSequence &CheckBadFortranCharacters(
       Messages &, const Prescanner &, bool allowAmpersand) const;
+  bool BadlyNestedParentheses() const;
   const TokenSequence &CheckBadParentheses(Messages &) const;
   void Emit(CookedSource &) const;
   llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index e4801c36505b4..8efcd617cf0f9 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -295,8 +295,13 @@ void Prescanner::CheckAndEmitLine(
   // Applications play shenanigans with line continuation before and
   // after #include'd subprogram argument lists.
   if (!isNestedInIncludeDirective_ && !omitNewline_ &&
-      !afterIncludeDirective_) {
-    tokens.CheckBadParentheses(messages_);
+      !afterIncludeDirective_ && tokens.BadlyNestedParentheses()) {
+    if (inFixedForm_ && nextLine_ < limit_ &&
+        IsPreprocessorDirectiveLine(nextLine_)) {
+      // don't complain
+    } else {
+      tokens.CheckBadParentheses(messages_);
+    }
   }
   tokens.Emit(cooked_);
   if (omitNewline_) {
@@ -350,7 +355,16 @@ void Prescanner::LabelField(TokenSequence &token) {
     ++column_;
   }
   if (badColumn && !preprocessor_.IsNameDefined(token.CurrentOpenToken())) {
-    if (features_.ShouldWarn(common::UsageWarning::Scanning)) {
+    if (prescannerNesting_ > 0 && *badColumn == 6 &&
+        cooked_.BufferedBytes() == firstCookedCharacterOffset_) {
+      // This is the first source line in #included text or conditional
+      // code under #if.
+      // If it turns out that the preprocessed text begins with a
+      // fixed form continuation line, the newline at the end
+      // of the latest source line beforehand will be deleted in
+      // CookedSource::Marshal().
+      cooked_.MarkPossibleFixedFormContinuation();
+    } else if (features_.ShouldWarn(common::UsageWarning::Scanning)) {
       Say(GetProvenance(start + *badColumn - 1),
           *badColumn == 6
               ? "Statement should not begin with a continuation line"_warn_en_US
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index cf64bdb02a9b7..b6f6d2ca439ee 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -247,6 +247,8 @@ class Prescanner {
   bool omitNewline_{false};
   bool skipLeadingAmpersand_{false};
 
+  const std::size_t firstCookedCharacterOffset_{cooked_.BufferedBytes()};
+
   const Provenance spaceProvenance_{
       allSources_.CompilerInsertionProvenance(' ')};
   const Provenance backslashProvenance_{
diff --git a/flang/lib/Parser/provenance.cpp b/flang/lib/Parser/provenance.cpp
index 55ef67fd6288d..6e2e7326e2167 100644
--- a/flang/lib/Parser/provenance.cpp
+++ b/flang/lib/Parser/provenance.cpp
@@ -513,6 +513,16 @@ void CookedSource::Marshal(AllCookedSources &allCookedSources) {
       "(after end of source)"));
   data_ = buffer_.Marshal();
   buffer_.clear();
+  for (std::size_t ffStart : possibleFixedFormContinuations_) {
+    if (ffStart > 0 && ffStart + 1 < data_.size() &&
+        data_[ffStart - 1] == '\n' && data_[ffStart] == ' ') {
+      // This fixed form include line is the first source line in an
+      // #include file (or after an empty one).  Connect it with the previous
+      // source line by deleting its terminal newline.
+      data_[ffStart - 1] = ' ';
+    }
+  }
+  possibleFixedFormContinuations_.clear();
   allCookedSources.Register(*this);
 }
 
diff --git a/flang/lib/Parser/token-sequence.cpp b/flang/lib/Parser/token-sequence.cpp
index 40560bbacb54f..133e60ba4f009 100644
--- a/flang/lib/Parser/token-sequence.cpp
+++ b/flang/lib/Parser/token-sequence.cpp
@@ -378,9 +378,7 @@ const TokenSequence &TokenSequence::CheckBadFortranCharacters(
   return *this;
 }
 
-const TokenSequence &TokenSequence::CheckBadParentheses(
-    Messages &messages) const {
-  // First, a quick pass with no allocation for the common case
+bool TokenSequence::BadlyNestedParentheses() const {
   int nesting{0};
   std::size_t tokens{SizeInTokens()};
   for (std::size_t j{0}; j < tokens; ++j) {
@@ -394,8 +392,14 @@ const TokenSequence &TokenSequence::CheckBadParentheses(
       }
     }
   }
-  if (nesting != 0) {
+  return nesting != 0;
+}
+
+const TokenSequence &TokenSequence::CheckBadParentheses(
+    Messages &messages) const {
+  if (BadlyNestedParentheses()) {
     // There's an error; diagnose it
+    std::size_t tokens{SizeInTokens()};
     std::vector<std::size_t> stack;
     for (std::size_t j{0}; j < tokens; ++j) {
       CharBlock token{TokenAt(j)};
diff --git a/flang/test/Preprocessing/ff-args.h b/flang/test/Preprocessing/ff-args.h
new file mode 100644
index 0000000000000..99562784006c9
--- /dev/null
+++ b/flang/test/Preprocessing/ff-args.h
@@ -0,0 +1 @@
+     +3.14159)
\ No newline at end of file
diff --git a/flang/test/Preprocessing/ff-include-args.F b/flang/test/Preprocessing/ff-include-args.F
new file mode 100644
index 0000000000000..81e4102598c2f
--- /dev/null
+++ b/flang/test/Preprocessing/ff-include-args.F
@@ -0,0 +1,14 @@
+! RUN: %flang -E %s 2>&1 | FileCheck %s
+! CHECK: call foo (  3.14159)
+! CHECK: subroutine foo(test)
+      call foo (
+#include "ff-args.h"
+      end
+#define TEST
+      subroutine foo(
+#ifdef TEST
+     +test)
+#else
+     +)
+#endif
+      end

``````````

</details>


https://github.com/llvm/llvm-project/pull/95332


More information about the flang-commits mailing list