Skip to content

Commit 86bee81

Browse files
authored
[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 #78928.
1 parent f8fc883 commit 86bee81

File tree

8 files changed

+58
-7
lines changed

8 files changed

+58
-7
lines changed

flang/include/flang/Parser/provenance.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ class CookedSource {
257257
provenanceMap_.Put(pm);
258258
}
259259

260+
void MarkPossibleFixedFormContinuation() {
261+
possibleFixedFormContinuations_.push_back(BufferedBytes());
262+
}
263+
260264
std::size_t BufferedBytes() const;
261265
void Marshal(AllCookedSources &); // marshals text into one contiguous block
262266
void CompileProvenanceRangeToOffsetMappings(AllSources &);
@@ -269,6 +273,7 @@ class CookedSource {
269273
std::string data_; // all of it, prescanned and preprocessed
270274
OffsetToProvenanceMappings provenanceMap_;
271275
ProvenanceRangeToOffsetMappings invertedMap_;
276+
std::list<std::size_t> possibleFixedFormContinuations_;
272277
};
273278

274279
class AllCookedSources {

flang/include/flang/Parser/token-sequence.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class TokenSequence {
125125
TokenSequence &ClipComment(const Prescanner &, bool skipFirst = false);
126126
const TokenSequence &CheckBadFortranCharacters(
127127
Messages &, const Prescanner &, bool allowAmpersand) const;
128+
bool BadlyNestedParentheses() const;
128129
const TokenSequence &CheckBadParentheses(Messages &) const;
129130
void Emit(CookedSource &) const;
130131
llvm::raw_ostream &Dump(llvm::raw_ostream &) const;

flang/lib/Parser/prescan.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,13 @@ void Prescanner::CheckAndEmitLine(
295295
// Applications play shenanigans with line continuation before and
296296
// after #include'd subprogram argument lists.
297297
if (!isNestedInIncludeDirective_ && !omitNewline_ &&
298-
!afterIncludeDirective_) {
299-
tokens.CheckBadParentheses(messages_);
298+
!afterIncludeDirective_ && tokens.BadlyNestedParentheses()) {
299+
if (inFixedForm_ && nextLine_ < limit_ &&
300+
IsPreprocessorDirectiveLine(nextLine_)) {
301+
// don't complain
302+
} else {
303+
tokens.CheckBadParentheses(messages_);
304+
}
300305
}
301306
tokens.Emit(cooked_);
302307
if (omitNewline_) {
@@ -350,7 +355,16 @@ void Prescanner::LabelField(TokenSequence &token) {
350355
++column_;
351356
}
352357
if (badColumn && !preprocessor_.IsNameDefined(token.CurrentOpenToken())) {
353-
if (features_.ShouldWarn(common::UsageWarning::Scanning)) {
358+
if (prescannerNesting_ > 0 && *badColumn == 6 &&
359+
cooked_.BufferedBytes() == firstCookedCharacterOffset_) {
360+
// This is the first source line in #included text or conditional
361+
// code under #if.
362+
// If it turns out that the preprocessed text begins with a
363+
// fixed form continuation line, the newline at the end
364+
// of the latest source line beforehand will be deleted in
365+
// CookedSource::Marshal().
366+
cooked_.MarkPossibleFixedFormContinuation();
367+
} else if (features_.ShouldWarn(common::UsageWarning::Scanning)) {
354368
Say(GetProvenance(start + *badColumn - 1),
355369
*badColumn == 6
356370
? "Statement should not begin with a continuation line"_warn_en_US

flang/lib/Parser/prescan.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ class Prescanner {
247247
bool omitNewline_{false};
248248
bool skipLeadingAmpersand_{false};
249249

250+
const std::size_t firstCookedCharacterOffset_{cooked_.BufferedBytes()};
251+
250252
const Provenance spaceProvenance_{
251253
allSources_.CompilerInsertionProvenance(' ')};
252254
const Provenance backslashProvenance_{

flang/lib/Parser/provenance.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,16 @@ void CookedSource::Marshal(AllCookedSources &allCookedSources) {
513513
"(after end of source)"));
514514
data_ = buffer_.Marshal();
515515
buffer_.clear();
516+
for (std::size_t ffStart : possibleFixedFormContinuations_) {
517+
if (ffStart > 0 && ffStart + 1 < data_.size() &&
518+
data_[ffStart - 1] == '\n' && data_[ffStart] == ' ') {
519+
// This fixed form include line is the first source line in an
520+
// #include file (or after an empty one). Connect it with the previous
521+
// source line by deleting its terminal newline.
522+
data_[ffStart - 1] = ' ';
523+
}
524+
}
525+
possibleFixedFormContinuations_.clear();
516526
allCookedSources.Register(*this);
517527
}
518528

flang/lib/Parser/token-sequence.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,9 +378,7 @@ const TokenSequence &TokenSequence::CheckBadFortranCharacters(
378378
return *this;
379379
}
380380

381-
const TokenSequence &TokenSequence::CheckBadParentheses(
382-
Messages &messages) const {
383-
// First, a quick pass with no allocation for the common case
381+
bool TokenSequence::BadlyNestedParentheses() const {
384382
int nesting{0};
385383
std::size_t tokens{SizeInTokens()};
386384
for (std::size_t j{0}; j < tokens; ++j) {
@@ -394,8 +392,14 @@ const TokenSequence &TokenSequence::CheckBadParentheses(
394392
}
395393
}
396394
}
397-
if (nesting != 0) {
395+
return nesting != 0;
396+
}
397+
398+
const TokenSequence &TokenSequence::CheckBadParentheses(
399+
Messages &messages) const {
400+
if (BadlyNestedParentheses()) {
398401
// There's an error; diagnose it
402+
std::size_t tokens{SizeInTokens()};
399403
std::vector<std::size_t> stack;
400404
for (std::size_t j{0}; j < tokens; ++j) {
401405
CharBlock token{TokenAt(j)};

flang/test/Preprocessing/ff-args.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
+3.14159)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
! RUN: %flang -E %s 2>&1 | FileCheck %s
2+
! CHECK: call foo ( 3.14159)
3+
! CHECK: subroutine foo(test)
4+
call foo (
5+
#include "ff-args.h"
6+
end
7+
#define TEST
8+
subroutine foo(
9+
#ifdef TEST
10+
+test)
11+
#else
12+
+)
13+
#endif
14+
end

0 commit comments

Comments
 (0)