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 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 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