diff options
author | Peter Klausler <35819229+klausler@users.noreply.github.com> | 2024-06-13 11:22:27 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-13 11:22:27 -0700 |
commit | 86bee819120b5ba4b7262c7800a88fbf904d4932 (patch) | |
tree | 0bc663b453c94068cc502bc5bda032db90f82111 /flang | |
parent | f8fc883da951064a310e365680b4b567fad58ebc (diff) | |
download | llvm-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.h | 5 | ||||
-rw-r--r-- | flang/include/flang/Parser/token-sequence.h | 1 | ||||
-rw-r--r-- | flang/lib/Parser/prescan.cpp | 20 | ||||
-rw-r--r-- | flang/lib/Parser/prescan.h | 2 | ||||
-rw-r--r-- | flang/lib/Parser/provenance.cpp | 10 | ||||
-rw-r--r-- | flang/lib/Parser/token-sequence.cpp | 12 | ||||
-rw-r--r-- | flang/test/Preprocessing/ff-args.h | 1 | ||||
-rw-r--r-- | flang/test/Preprocessing/ff-include-args.F | 14 |
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 |