aboutsummaryrefslogtreecommitdiff
path: root/flang
diff options
context:
space:
mode:
authorPeter Klausler <35819229+klausler@users.noreply.github.com>2024-06-13 11:22:27 -0700
committerGitHub <noreply@github.com>2024-06-13 11:22:27 -0700
commit86bee819120b5ba4b7262c7800a88fbf904d4932 (patch)
tree0bc663b453c94068cc502bc5bda032db90f82111 /flang
parentf8fc883da951064a310e365680b4b567fad58ebc (diff)
downloadllvm-86bee819120b5ba4b7262c7800a88fbf904d4932.zip
llvm-86bee819120b5ba4b7262c7800a88fbf904d4932.tar.gz
llvm-86bee819120b5ba4b7262c7800a88fbf904d4932.tar.bz2
[flang][preprocessor] Fixed-form continuation across preprocessing di… (#95332)
…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.
Diffstat (limited to 'flang')
-rw-r--r--flang/include/flang/Parser/provenance.h5
-rw-r--r--flang/include/flang/Parser/token-sequence.h1
-rw-r--r--flang/lib/Parser/prescan.cpp20
-rw-r--r--flang/lib/Parser/prescan.h2
-rw-r--r--flang/lib/Parser/provenance.cpp10
-rw-r--r--flang/lib/Parser/token-sequence.cpp12
-rw-r--r--flang/test/Preprocessing/ff-args.h1
-rw-r--r--flang/test/Preprocessing/ff-include-args.F14
8 files changed, 58 insertions, 7 deletions
diff --git a/flang/include/flang/Parser/provenance.h b/flang/include/flang/Parser/provenance.h
index 73d500f..42c5b3d 100644
--- a/flang/include/flang/Parser/provenance.h
+++ b/flang/include/flang/Parser/provenance.h
@@ -257,6 +257,10 @@ public:
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 @@ private:
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 ee5f71e..1f82a3c 100644
--- a/flang/include/flang/Parser/token-sequence.h
+++ b/flang/include/flang/Parser/token-sequence.h
@@ -125,6 +125,7 @@ public:
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 e4801c3..8efcd61 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 cf64bdb..b6f6d2c 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -247,6 +247,8 @@ private:
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 55ef67f..6e2e732 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 40560bb..133e60b 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 0000000..9956278
--- /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 0000000..81e4102
--- /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