[flang][preprocessing] Handle #include after & line continuation (#93382)
Some applications like to use a CPP-style #include directive to pull in a common list of arguments, dummy arguments, or COMMON block variables after a free-form & line continuation marker. This works naturally with compilers that run an actual cpp pass over the input before doing anything specific to Fortran, but it's a case that I missed with this integrated preprocessor.
This commit is contained in:
parent
68f4e46c43
commit
0525c201b6
@ -749,17 +749,18 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner &prescanner) {
|
||||
}
|
||||
std::string buf;
|
||||
llvm::raw_string_ostream error{buf};
|
||||
const SourceFile *included{
|
||||
allSources_.Open(include, error, std::move(prependPath))};
|
||||
if (!included) {
|
||||
if (const SourceFile *
|
||||
included{allSources_.Open(include, error, std::move(prependPath))}) {
|
||||
if (included->bytes() > 0) {
|
||||
ProvenanceRange fileRange{
|
||||
allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
|
||||
Prescanner{prescanner, /*isNestedInIncludeDirective=*/true}
|
||||
.set_encoding(included->encoding())
|
||||
.Prescan(fileRange);
|
||||
}
|
||||
} else {
|
||||
prescanner.Say(dir.GetTokenProvenanceRange(j), "#include: %s"_err_en_US,
|
||||
error.str());
|
||||
} else if (included->bytes() > 0) {
|
||||
ProvenanceRange fileRange{
|
||||
allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
|
||||
Prescanner{prescanner}
|
||||
.set_encoding(included->encoding())
|
||||
.Prescan(fileRange);
|
||||
}
|
||||
} else {
|
||||
prescanner.Say(dir.GetTokenProvenanceRange(dirOffset),
|
||||
|
@ -32,10 +32,11 @@ Prescanner::Prescanner(Messages &messages, CookedSource &cooked,
|
||||
backslashFreeFormContinuation_{preprocessor.AnyDefinitions()},
|
||||
encoding_{allSources_.encoding()} {}
|
||||
|
||||
Prescanner::Prescanner(const Prescanner &that)
|
||||
Prescanner::Prescanner(const Prescanner &that, bool isNestedInIncludeDirective)
|
||||
: messages_{that.messages_}, cooked_{that.cooked_},
|
||||
preprocessor_{that.preprocessor_}, allSources_{that.allSources_},
|
||||
features_{that.features_},
|
||||
isNestedInIncludeDirective_{isNestedInIncludeDirective},
|
||||
backslashFreeFormContinuation_{that.backslashFreeFormContinuation_},
|
||||
inFixedForm_{that.inFixedForm_},
|
||||
fixedFormColumnLimit_{that.fixedFormColumnLimit_},
|
||||
@ -104,11 +105,14 @@ void Prescanner::Statement() {
|
||||
NextLine();
|
||||
return;
|
||||
case LineClassification::Kind::ConditionalCompilationDirective:
|
||||
case LineClassification::Kind::IncludeDirective:
|
||||
case LineClassification::Kind::DefinitionDirective:
|
||||
case LineClassification::Kind::PreprocessorDirective:
|
||||
preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
|
||||
return;
|
||||
case LineClassification::Kind::IncludeDirective:
|
||||
preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
|
||||
afterIncludeDirective_ = true;
|
||||
return;
|
||||
case LineClassification::Kind::CompilerDirective: {
|
||||
directiveSentinel_ = line.sentinel;
|
||||
CHECK(InCompilerDirective());
|
||||
@ -213,10 +217,7 @@ void Prescanner::Statement() {
|
||||
Say(preprocessed->GetProvenanceRange(),
|
||||
"Preprocessed line resembles a preprocessor directive"_warn_en_US);
|
||||
}
|
||||
preprocessed->ToLowerCase()
|
||||
.CheckBadFortranCharacters(messages_, *this)
|
||||
.CheckBadParentheses(messages_)
|
||||
.Emit(cooked_);
|
||||
CheckAndEmitLine(preprocessed->ToLowerCase(), newlineProvenance);
|
||||
break;
|
||||
case LineClassification::Kind::CompilerDirective:
|
||||
if (preprocessed->HasRedundantBlanks()) {
|
||||
@ -228,10 +229,9 @@ void Prescanner::Statement() {
|
||||
NormalizeCompilerDirectiveCommentMarker(*preprocessed);
|
||||
preprocessed->ToLowerCase();
|
||||
SourceFormChange(preprocessed->ToString());
|
||||
preprocessed->ClipComment(*this, true /* skip first ! */)
|
||||
.CheckBadFortranCharacters(messages_, *this)
|
||||
.CheckBadParentheses(messages_)
|
||||
.Emit(cooked_);
|
||||
CheckAndEmitLine(preprocessed->ToLowerCase().ClipComment(
|
||||
*this, true /* skip first ! */),
|
||||
newlineProvenance);
|
||||
break;
|
||||
case LineClassification::Kind::Source:
|
||||
if (inFixedForm_) {
|
||||
@ -246,14 +246,11 @@ void Prescanner::Statement() {
|
||||
preprocessed->RemoveRedundantBlanks();
|
||||
}
|
||||
}
|
||||
preprocessed->ToLowerCase()
|
||||
.ClipComment(*this)
|
||||
.CheckBadFortranCharacters(messages_, *this)
|
||||
.CheckBadParentheses(messages_)
|
||||
.Emit(cooked_);
|
||||
CheckAndEmitLine(
|
||||
preprocessed->ToLowerCase().ClipComment(*this), newlineProvenance);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
} else { // no macro replacement
|
||||
if (line.kind == LineClassification::Kind::CompilerDirective) {
|
||||
while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
|
||||
newlineProvenance = GetCurrentProvenance();
|
||||
@ -266,16 +263,29 @@ void Prescanner::Statement() {
|
||||
EnforceStupidEndStatementRules(tokens);
|
||||
}
|
||||
}
|
||||
tokens.CheckBadFortranCharacters(messages_, *this)
|
||||
.CheckBadParentheses(messages_)
|
||||
.Emit(cooked_);
|
||||
CheckAndEmitLine(tokens, newlineProvenance);
|
||||
}
|
||||
directiveSentinel_ = nullptr;
|
||||
}
|
||||
|
||||
void Prescanner::CheckAndEmitLine(
|
||||
TokenSequence &tokens, Provenance newlineProvenance) {
|
||||
tokens.CheckBadFortranCharacters(messages_, *this);
|
||||
// Parenthesis nesting check does not apply while any #include is
|
||||
// active, nor on the lines before and after a top-level #include.
|
||||
// Applications play shenanigans with line continuation before and
|
||||
// after #include'd subprogram argument lists.
|
||||
if (!isNestedInIncludeDirective_ && !omitNewline_ &&
|
||||
!afterIncludeDirective_) {
|
||||
tokens.CheckBadParentheses(messages_);
|
||||
}
|
||||
tokens.Emit(cooked_);
|
||||
if (omitNewline_) {
|
||||
omitNewline_ = false;
|
||||
} else {
|
||||
cooked_.Put('\n', newlineProvenance);
|
||||
afterIncludeDirective_ = false;
|
||||
}
|
||||
directiveSentinel_ = nullptr;
|
||||
}
|
||||
|
||||
TokenSequence Prescanner::TokenizePreprocessorDirective() {
|
||||
@ -985,7 +995,9 @@ void Prescanner::FortranInclude(const char *firstQuote) {
|
||||
provenance, static_cast<std::size_t>(p - nextLine_)};
|
||||
ProvenanceRange fileRange{
|
||||
allSources_.AddIncludedFile(*included, includeLineRange)};
|
||||
Prescanner{*this}.set_encoding(included->encoding()).Prescan(fileRange);
|
||||
Prescanner{*this, /*isNestedInIncludeDirective=*/false}
|
||||
.set_encoding(included->encoding())
|
||||
.Prescan(fileRange);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,9 @@ class Prescanner {
|
||||
public:
|
||||
Prescanner(Messages &, CookedSource &, Preprocessor &,
|
||||
common::LanguageFeatureControl);
|
||||
Prescanner(const Prescanner &);
|
||||
Prescanner(const Prescanner &, bool isNestedInIncludeDirective);
|
||||
Prescanner(const Prescanner &) = delete;
|
||||
Prescanner(Prescanner &&) = delete;
|
||||
|
||||
const AllSources &allSources() const { return allSources_; }
|
||||
AllSources &allSources() { return allSources_; }
|
||||
@ -155,6 +157,7 @@ private:
|
||||
common::LanguageFeature::ClassicCComments)));
|
||||
}
|
||||
|
||||
void CheckAndEmitLine(TokenSequence &, Provenance newlineProvenance);
|
||||
void LabelField(TokenSequence &);
|
||||
void EnforceStupidEndStatementRules(const TokenSequence &);
|
||||
void SkipToEndOfLine();
|
||||
@ -198,6 +201,7 @@ private:
|
||||
Preprocessor &preprocessor_;
|
||||
AllSources &allSources_;
|
||||
common::LanguageFeatureControl features_;
|
||||
bool isNestedInIncludeDirective_{false};
|
||||
bool backslashFreeFormContinuation_{false};
|
||||
bool inFixedForm_{false};
|
||||
int fixedFormColumnLimit_{72};
|
||||
@ -206,6 +210,7 @@ private:
|
||||
int prescannerNesting_{0};
|
||||
int continuationLines_{0};
|
||||
bool isPossibleMacroCall_{false};
|
||||
bool afterIncludeDirective_{false};
|
||||
|
||||
Provenance startProvenance_;
|
||||
const char *start_{nullptr}; // beginning of current source file content
|
||||
|
1
flang/test/Preprocessing/args.h
Normal file
1
flang/test/Preprocessing/args.h
Normal file
@ -0,0 +1 @@
|
||||
3.14159 &
|
6
flang/test/Preprocessing/include-args.F90
Normal file
6
flang/test/Preprocessing/include-args.F90
Normal file
@ -0,0 +1,6 @@
|
||||
! RUN: %flang -E %s 2>&1 | FileCheck %s
|
||||
! CHECK: call foo(3.14159)
|
||||
call foo (&
|
||||
#include "args.h"
|
||||
)
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user