[flang] Recognize compiler directives after expansion in comment (#183626)

The compiler can recognize a compiler directive when one results from a
macro expansion at the beginning of a non-comment source line, as in
"#define FOO !$OMP". But it can't recognize a compiler directive that
initially appears as a comment line, as in "!BAR" after "#define BAR
$OMP". Extend the prescanner to recognize such cases in free form
source. (Fixed form is a much more complicated case for this recognition
and will be addressed later if needed.)

This is the 2nd version of this patch; the first was reverted after
problems with continuation lines were encountered.

Fixes https://github.com/llvm/llvm-project/issues/178481.
This commit is contained in:
Peter Klausler 2026-03-02 14:47:08 -08:00 committed by GitHub
parent 4e3e4f25bc
commit 743428688f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 230 additions and 123 deletions

View file

@ -49,8 +49,8 @@ public:
bool set_isDisabled(bool disable);
TokenSequence Apply(const std::vector<TokenSequence> &args, Prescanner &,
bool inIfExpression = false);
TokenSequence Apply(const std::vector<TokenSequence> &args,
const Prescanner &, bool inIfExpression = false) const;
void Print(llvm::raw_ostream &out, const char *macroName = "") const;
@ -95,7 +95,7 @@ public:
// that result and try again. All other Fortran preprocessors share this
// behavior.
std::optional<TokenSequence> MacroReplacement(const TokenSequence &,
Prescanner &,
const Prescanner &,
std::optional<std::size_t> *partialFunctionLikeMacro = nullptr,
bool inIfExpression = false);
@ -109,7 +109,7 @@ private:
enum class CanDeadElseAppear { No, Yes };
CharBlock SaveTokenAsName(const CharBlock &);
TokenSequence ReplaceMacros(const TokenSequence &, Prescanner &,
TokenSequence ReplaceMacros(const TokenSequence &, const Prescanner &,
std::optional<std::size_t> *partialFunctionLikeMacro = nullptr,
bool inIfExpression = false);
void SkipDisabledConditionalCode(

View file

@ -214,7 +214,7 @@ constexpr bool IsDefinedKeyword(CharBlock token) {
}
TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
Prescanner &prescanner, bool inIfExpression) {
const Prescanner &prescanner, bool inIfExpression) const {
TokenSequence result;
bool skipping{false};
int parenthesesNesting{0};
@ -254,7 +254,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
CHECK(resultSize > 0 &&
result.TokenAt(resultSize - 1) == replacement_.TokenAt(prev - 1));
result.pop_back();
result.CopyAll(Stringify(args[index], prescanner.allSources()));
AllSources &allSources{
*const_cast<AllSources *>(&prescanner.allSources())};
result.CopyAll(Stringify(args[index], allSources));
} else {
const TokenSequence *arg{&args[index]};
std::optional<TokenSequence> replaced;
@ -267,7 +269,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
auto next{replacement_.SkipBlanks(j + 1)};
if (next >= tokens || !IsTokenPasting(replacement_.TokenAt(next))) {
// Apply macro replacement to the actual argument
replaced = prescanner.preprocessor().MacroReplacement(
Preprocessor &preprocessor{
*const_cast<Preprocessor *>(&prescanner.preprocessor())};
replaced = preprocessor.MacroReplacement(
*arg, prescanner, nullptr, inIfExpression);
if (replaced) {
arg = &*replaced;
@ -278,9 +282,9 @@ TokenSequence Definition::Apply(const std::vector<TokenSequence> &args,
}
} else if (bytes == 11 && isVariadic_ &&
token.ToString() == "__VA_ARGS__") {
Provenance commaProvenance{
prescanner.preprocessor().allSources().CompilerInsertionProvenance(
',')};
AllSources &allSources{
*const_cast<AllSources *>(&prescanner.allSources())};
Provenance commaProvenance{allSources.CompilerInsertionProvenance(',')};
for (std::size_t k{argumentCount()}; k < args.size(); ++k) {
if (k > argumentCount()) {
result.Put(","s, commaProvenance);
@ -440,7 +444,7 @@ void Preprocessor::Define(const std::string &macro, const std::string &value) {
void Preprocessor::Undefine(std::string macro) { definitions_.erase(macro); }
std::optional<TokenSequence> Preprocessor::MacroReplacement(
const TokenSequence &input, Prescanner &prescanner,
const TokenSequence &input, const Prescanner &prescanner,
std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {
// Do quick scan for any use of a defined name.
if (!inIfExpression && definitions_.empty()) {
@ -663,7 +667,7 @@ std::optional<TokenSequence> Preprocessor::MacroReplacement(
}
TokenSequence Preprocessor::ReplaceMacros(const TokenSequence &tokens,
Prescanner &prescanner,
const Prescanner &prescanner,
std::optional<std::size_t> *partialFunctionLikeMacro, bool inIfExpression) {
if (std::optional<TokenSequence> repl{MacroReplacement(
tokens, prescanner, partialFunctionLikeMacro, inIfExpression)}) {

View file

@ -46,7 +46,8 @@ Prescanner::Prescanner(const Prescanner &that, Preprocessor &prepro,
prescannerNesting_{that.prescannerNesting_ + 1},
skipLeadingAmpersand_{that.skipLeadingAmpersand_},
compilerDirectiveBloomFilter_{that.compilerDirectiveBloomFilter_},
compilerDirectiveSentinels_{that.compilerDirectiveSentinels_} {}
compilerDirectiveSentinels_{that.compilerDirectiveSentinels_},
maxSentinelLength_{that.maxSentinelLength_} {}
// Returns number of bytes to skip
static inline int IsSpace(const char *p) {
@ -212,6 +213,19 @@ void Prescanner::Statement() {
}
break;
}
case LineClassification::Kind::CompilerDirectiveAfterMacroExpansion:
directiveSentinel_ = line.sentinel;
CHECK(InCompilerDirective());
BeginStatementAndAdvance();
while (*at_ != '!' && *at_ != '\n') {
++at_, ++column_;
}
CHECK(*at_ == '!');
EmitChar(tokens, '!');
tokens.CloseToken();
++at_;
++column_;
break;
case LineClassification::Kind::Source: {
BeginStatementAndAdvance();
bool checkLabelField{false};
@ -238,30 +252,17 @@ void Prescanner::Statement() {
// a comment marker or directive sentinel. If so, disable line
// continuation, so that NextToken() won't consume anything from
// following lines.
if (IsLegalIdentifierStart(*at_)) {
// TODO: Only bother with these cases when any keyword macro has
// been defined with replacement text that could begin a comment
// or directive sentinel.
const char *p{at_};
while (IsLegalInIdentifier(*++p)) {
}
CharBlock id{at_, static_cast<std::size_t>(p - at_)};
if (preprocessor_.IsNameDefined(id) &&
!preprocessor_.IsFunctionLikeDefinition(id)) {
checkLabelField = false;
TokenSequence toks;
toks.Put(id, GetProvenance(at_));
if (auto replaced{preprocessor_.MacroReplacement(toks, *this)}) {
auto newLineClass{ClassifyLine(*replaced, GetCurrentProvenance())};
if (newLineClass.kind ==
LineClassification::Kind::CompilerDirective) {
directiveSentinel_ = newLineClass.sentinel;
disableSourceContinuation_ = false;
} else {
disableSourceContinuation_ = !replaced->empty() &&
newLineClass.kind != LineClassification::Kind::Source;
}
}
if (auto kwName{GetKeywordMacroName(at_)}) {
checkLabelField = false;
Provenance here{GetCurrentProvenance()};
TokenSequence replacement{ExpandKeywordMacro(*kwName, here)};
auto newLineClass{ClassifyLine(replacement, here)};
if (newLineClass.kind == LineClassification::Kind::CompilerDirective) {
directiveSentinel_ = newLineClass.sentinel;
disableSourceContinuation_ = false;
} else {
disableSourceContinuation_ = !replacement.empty() &&
newLineClass.kind != LineClassification::Kind::Source;
}
}
if (checkLabelField) {
@ -272,6 +273,7 @@ void Prescanner::Statement() {
while (NextToken(tokens)) {
}
if (continuationLines_ > 255) {
if (features_.ShouldWarn(common::LanguageFeature::MiscSourceExtensions)) {
Say(common::LanguageFeature::MiscSourceExtensions,
@ -304,6 +306,7 @@ void Prescanner::Statement() {
CheckAndEmitLine(preprocessed->ToLowerCase(), newlineProvenance);
break;
case LineClassification::Kind::CompilerDirective:
case LineClassification::Kind::CompilerDirectiveAfterMacroExpansion:
if (preprocessed->HasRedundantBlanks()) {
preprocessed->RemoveRedundantBlanks();
}
@ -335,40 +338,41 @@ void Prescanner::Statement() {
preprocessed->ToLowerCase().ClipComment(*this), newlineProvenance);
break;
}
} else { // no macro replacement
if (line.kind == LineClassification::Kind::CompilerDirective) {
while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
newlineProvenance = GetCurrentProvenance();
}
if (preprocessingOnly_ && inFixedForm_ && InConditionalLine() &&
nextLine_ < limit_) {
// In -E mode, when the line after !$ conditional compilation is a
// regular fixed form continuation line, append a '&' to the line.
const char *p{nextLine_};
int col{1};
while (int n{IsSpace(p)}) {
if (*p == '\t') {
break;
}
p += n;
++col;
}
if (col == 6 && *p != '0' && *p != '\t' && *p != '\n') {
EmitChar(tokens, '&');
tokens.CloseToken();
} else if (line.kind == LineClassification::Kind::CompilerDirective ||
line.kind ==
LineClassification::Kind::CompilerDirectiveAfterMacroExpansion) {
while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
newlineProvenance = GetCurrentProvenance();
}
if (preprocessingOnly_ && inFixedForm_ && InConditionalLine() &&
nextLine_ < limit_) {
// In -E mode, when the line after !$ conditional compilation is a
// regular fixed form continuation line, append a '&' to the line.
const char *p{nextLine_};
int col{1};
while (int n{IsSpace(p)}) {
if (*p == '\t') {
break;
}
p += n;
++col;
}
tokens.ToLowerCase();
if (!SourceFormChange(tokens.ToString())) {
CheckAndEmitLine(tokens, newlineProvenance);
}
} else { // Kind::Source
tokens.ToLowerCase();
if (inFixedForm_) {
EnforceStupidEndStatementRules(tokens);
if (col == 6 && *p != '0' && *p != '\t' && *p != '\n') {
EmitChar(tokens, '&');
tokens.CloseToken();
}
}
tokens.ToLowerCase();
if (!SourceFormChange(tokens.ToString())) {
CheckAndEmitLine(tokens, newlineProvenance);
}
} else {
CHECK(line.kind == LineClassification::Kind::Source);
tokens.ToLowerCase();
if (inFixedForm_) {
EnforceStupidEndStatementRules(tokens);
}
CheckAndEmitLine(tokens, newlineProvenance);
}
directiveSentinel_ = nullptr;
}
@ -567,7 +571,8 @@ bool Prescanner::MustSkipToEndOfLine() const {
return true; // skip over ignored columns in right margin (73:80)
} else if (*at_ == '!' && !inCharLiteral_ &&
(!inFixedForm_ || tabInCurrentLine_ || column_ != 6)) {
return InCompilerDirective() || !IsCompilerDirectiveSentinel(at_ + 1);
return InCompilerDirective() ||
!IsCompilerDirectiveSentinelAfterKeywordMacro(at_ + 1);
} else {
return false;
}
@ -1463,29 +1468,37 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) {
if (InCompilerDirective()) {
if (InConditionalLine()) {
if (preprocessingOnly_) {
// in -E mode, don't treat !$ as a continuation
// in -E mode, don't treat !$/!@acc/!@cuf as a continuation
return nullptr;
} else if (p[0] == '!' && (p[1] == '$' || p[1] == '@')) {
p += 2;
if (InOpenACCOrCUDAConditionalLine()) {
if (IsDirective("acc", p) || IsDirective("cuf", p)) {
p += 3;
} else {
return nullptr;
} else if (*p == '!') {
if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
if (lClass->sentinel &&
((IsOpenMPConditionalLine(lClass->sentinel) &&
InOpenMPConditionalLine()) ||
(IsOpenACCConditionalLine(lClass->sentinel) &&
InOpenACCConditionalLine()) ||
(IsCUDAConditionalLine(lClass->sentinel) &&
InCUDAConditionalLine()))) {
p += 1 + lClass->payloadOffset;
}
}
if (*p != '&' && !IsSpaceOrTab(p)) {
return nullptr;
}
}
} else if (*p++ == '!') {
for (const char *s{directiveSentinel_}; *s != '\0'; ++p, ++s) {
if (*s != ToLowerCaseLetter(*p)) {
} else if (*p == '!') {
if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
if (lClass->sentinel &&
std::strcmp(directiveSentinel_, lClass->sentinel) == 0) {
p += 1 + lClass->payloadOffset;
} else {
return nullptr; // not the same directive class
}
} else {
return nullptr; // not a compiler directive
}
} else {
return nullptr;
return nullptr; // not a '!'
}
p = SkipWhiteSpace(p);
if (*p == '&') {
@ -1501,14 +1514,16 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) {
}
if (p[0] == '!' && !preprocessingOnly_) {
// Conditional lines can be continuations
if (p[1] == '$' && features_.IsEnabled(LanguageFeature::OpenMP)) {
p = lineStart = SkipWhiteSpace(p + 2);
} else if (IsDirective("@acc", p + 1) &&
features_.IsEnabled(LanguageFeature::OpenACC)) {
p = lineStart = SkipWhiteSpace(p + 5);
} else if (IsDirective("@cuf", p + 1) &&
features_.IsEnabled(LanguageFeature::CUDA)) {
p = lineStart = SkipWhiteSpace(p + 5);
if (auto lClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
if (lClass->sentinel &&
((IsOpenMPConditionalLine(lClass->sentinel) &&
features_.IsEnabled(LanguageFeature::OpenMP)) ||
(IsOpenACCConditionalLine(lClass->sentinel) &&
features_.IsEnabled(LanguageFeature::OpenACC)) ||
(IsCUDAConditionalLine(lClass->sentinel) &&
features_.IsEnabled(LanguageFeature::CUDA)))) {
lineStart = p = SkipWhiteSpace(p + 1 + lClass->payloadOffset);
}
}
}
if (*p == '&') {
@ -1616,6 +1631,9 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
if (!IsFixedFormCommentChar(col1)) {
return std::nullopt;
}
// TODO: Handle keyword macros that expand to directives in fixed form.
// The comment character can't be 'c' or 'C'. Need to figure out whether
// fixed form continuation should apply to the expansions.
char sentinel[5], *sp{sentinel};
int column{2};
for (; column < 6; ++column) {
@ -1678,20 +1696,28 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
std::optional<Prescanner::LineClassification>
Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const {
if (const char *p{SkipWhiteSpaceIncludingEmptyMacros(start)};
p && *p++ == '!') {
if (auto maybePair{IsCompilerDirectiveSentinel(p)}) {
auto offset{static_cast<std::size_t>(p - start - 1)};
const char *sentinel{maybePair->first};
if ((sentinel[0] == '$' && sentinel[1] == '\0') || sentinel[1] == '@') {
if (const char *comment{IsFreeFormComment(maybePair->second)}) {
if (*comment == '!') {
// Conditional line comment - treat as comment
return std::nullopt;
p && *p == '!') {
if (auto lnClass{IsCompilerDirectiveSentinelAfterKeywordMacro(p + 1)}) {
if (lnClass->kind == LineClassification::Kind::CompilerDirective) {
const char *sentinel{lnClass->sentinel};
CHECK(sentinel != nullptr);
const char *payload{nullptr};
if (sentinel[0] == '$' && sentinel[1] == '\0') {
payload = p + 2; // !$
} else if (sentinel[1] == '@') {
payload = p + 5; // !@acc or !@cuf
}
if (payload) {
if (const char *comment{IsFreeFormComment(payload)}) {
if (*comment == '!') { // !$ !blah or !@acc !blah
// Conditional line comment - treat as comment
return std::nullopt;
}
}
}
lnClass->payloadOffset = static_cast<std::size_t>(p - start);
}
return {LineClassification{
LineClassification::Kind::CompilerDirective, offset, sentinel}};
return lnClass;
}
}
return std::nullopt;
@ -1705,9 +1731,38 @@ Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) {
compilerDirectiveBloomFilter_.set(packed % prime1);
compilerDirectiveBloomFilter_.set(packed % prime2);
compilerDirectiveSentinels_.insert(dir);
int len{static_cast<int>(dir.size())};
if (len > maxSentinelLength_) {
maxSentinelLength_ = len;
}
return *this;
}
std::optional<CharBlock> Prescanner::GetKeywordMacroName(
const char *start) const {
if (IsLegalIdentifierStart(*start)) {
// TODO: Only bother with these cases when any keyword macro has
// been defined with replacement text that could begin a comment
// or directive sentinel.
const char *p{start};
while (IsLegalInIdentifier(*++p)) {
}
CharBlock name{start, static_cast<std::size_t>(p - start)};
if (preprocessor_.IsNameDefined(name) &&
!preprocessor_.IsFunctionLikeDefinition(name)) {
return name;
}
}
return std::nullopt;
}
TokenSequence Prescanner::ExpandKeywordMacro(
CharBlock name, Provenance provenance) const {
TokenSequence toks;
toks.Put(name, provenance);
return preprocessor_.MacroReplacement(toks, *this).value();
}
const char *Prescanner::IsCompilerDirectiveSentinel(
const char *sentinel, std::size_t len) const {
std::uint64_t packed{0};
@ -1734,27 +1789,29 @@ const char *Prescanner::IsCompilerDirectiveSentinel(CharBlock token) const {
while (end > p && (end[-1] == ' ' || end[-1] == '\t')) {
--end;
}
if (end > p + maxSentinelLength_) {
end = p + maxSentinelLength_;
}
return end > p && IsCompilerDirectiveSentinel(p, end - p) ? p : nullptr;
}
std::optional<std::pair<const char *, const char *>>
Prescanner::IsCompilerDirectiveSentinel(const char *p) const {
char sentinel[8];
std::size_t maxLen{static_cast<std::size_t>(maxSentinelLength_)};
for (std::size_t j{0}; j + 1 < sizeof sentinel; ++p, ++j) {
if (int n{IsSpaceOrTab(p)};
n || !(IsLetter(*p) || *p == '$' || *p == '@')) {
if (j > 0) {
if (j == 1 && sentinel[0] == '$' && n == 0 && *p != '&' && *p != '\n') {
// Free form OpenMP conditional compilation line sentinels have to
// be immediately followed by a space or &, not a digit
// or anything else. A newline also works for an initial line.
break;
}
n || j >= maxLen || !(IsLetter(*p) || *p == '$' || *p == '@')) {
if (j <= 1 && sentinel[0] == '$' && n == 0 && *p != '&' && *p != '\n') {
// Free form OpenMP conditional compilation line sentinels have to
// be immediately followed by a space or &, not a digit
// or anything else. A newline also works for an initial line.
break;
}
if (*p != '!') {
sentinel[j] = '\0';
if (*p != '!') {
if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
return std::make_pair(sp, p);
}
if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
return std::make_pair(sp, p);
}
}
break;
@ -1765,8 +1822,26 @@ Prescanner::IsCompilerDirectiveSentinel(const char *p) const {
return std::nullopt;
}
Prescanner::LineClassification Prescanner::ClassifyLine(
const char *start) const {
auto Prescanner::IsCompilerDirectiveSentinelAfterKeywordMacro(
const char *p) const -> std::optional<LineClassification> {
if (auto name{GetKeywordMacroName(p)}) {
Provenance provenance{GetProvenance(p)};
TokenSequence expansion{ExpandKeywordMacro(*name, provenance)};
expansion.Put("\n", 1, provenance); // termination
CharBlock block{expansion.ToLowerCase().ToCharBlock()};
if (auto maybePair{IsCompilerDirectiveSentinel(block.begin())}) {
return LineClassification{
LineClassification::Kind::CompilerDirectiveAfterMacroExpansion,
name->size(), maybePair->first};
}
} else if (auto maybePair{IsCompilerDirectiveSentinel(p)}) {
return LineClassification{LineClassification::Kind::CompilerDirective,
static_cast<std::size_t>(maybePair->second - p), maybePair->first};
}
return std::nullopt;
}
auto Prescanner::ClassifyLine(const char *start) const -> LineClassification {
if (inFixedForm_) {
if (std::optional<LineClassification> lc{
IsFixedFormCompilerDirectiveLine(start)}) {

View file

@ -81,6 +81,9 @@ public:
TokenSequence TokenizePreprocessorDirective();
Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
std::optional<CharBlock> GetKeywordMacroName(const char *) const;
TokenSequence ExpandKeywordMacro(CharBlock, Provenance) const;
const char *IsCompilerDirectiveSentinel(const char *, std::size_t) const;
const char *IsCompilerDirectiveSentinel(CharBlock) const;
// 'first' is the sentinel, 'second' is beginning of payload
@ -109,6 +112,7 @@ private:
PreprocessorDirective,
IncludeLine, // Fortran INCLUDE
CompilerDirective,
CompilerDirectiveAfterMacroExpansion, // !MACRO -> !$OMP ...
Source
};
LineClassification(Kind k, std::size_t po = 0, const char *s = nullptr)
@ -156,7 +160,7 @@ private:
}
void EmitInsertedChar(TokenSequence &tokens, char ch) {
Provenance provenance{allSources_.CompilerInsertionProvenance(ch)};
Provenance provenance{allSources().CompilerInsertionProvenance(ch)};
tokens.PutNextTokenChar(ch, provenance);
}
@ -166,18 +170,29 @@ private:
return *at_;
}
bool IsOpenMPConditionalLine(const char *sentinel) const {
return sentinel && sentinel[0] == '$' && !sentinel[1];
}
bool IsOpenACCConditionalLine(const char *sentinel) const {
return sentinel && sentinel[0] == '@' && sentinel[1] == 'a' &&
sentinel[2] == 'c' && sentinel[3] == 'c' && sentinel[4] == '\0';
}
bool IsCUDAConditionalLine(const char *sentinel) const {
return sentinel && sentinel[0] == '@' && sentinel[1] == 'c' &&
sentinel[2] == 'u' && sentinel[3] == 'f' && sentinel[4] == '\0';
}
bool InCompilerDirective() const { return directiveSentinel_ != nullptr; }
bool InOpenMPConditionalLine() const {
return directiveSentinel_ && directiveSentinel_[0] == '$' &&
!directiveSentinel_[1];
return IsOpenMPConditionalLine(directiveSentinel_);
}
bool InOpenACCConditionalLine() const {
return IsOpenACCConditionalLine(directiveSentinel_);
}
bool InCUDAConditionalLine() const {
return IsCUDAConditionalLine(directiveSentinel_);
}
bool InOpenACCOrCUDAConditionalLine() const {
return directiveSentinel_ && directiveSentinel_[0] == '@' &&
((directiveSentinel_[1] == 'a' && directiveSentinel_[2] == 'c' &&
directiveSentinel_[3] == 'c') ||
(directiveSentinel_[1] == 'c' && directiveSentinel_[2] == 'u' &&
directiveSentinel_[3] == 'f')) &&
directiveSentinel_[4] == '\0';
return InOpenACCConditionalLine() || InCUDAConditionalLine();
}
bool InConditionalLine() const {
return InOpenMPConditionalLine() || InOpenACCOrCUDAConditionalLine();
@ -240,6 +255,8 @@ private:
bool SourceFormChange(std::string &&);
bool CompilerDirectiveContinuation(TokenSequence &, const char *sentinel);
bool SourceLineContinuation(TokenSequence &);
std::optional<LineClassification>
IsCompilerDirectiveSentinelAfterKeywordMacro(const char *p) const;
Messages &messages_;
CookedSource &cooked_;
@ -298,15 +315,16 @@ private:
const std::size_t firstCookedCharacterOffset_{cooked_.BufferedBytes()};
const Provenance spaceProvenance_{
allSources_.CompilerInsertionProvenance(' ')};
allSources().CompilerInsertionProvenance(' ')};
const Provenance backslashProvenance_{
allSources_.CompilerInsertionProvenance('\\')};
allSources().CompilerInsertionProvenance('\\')};
// To avoid probing the set of active compiler directive sentinel strings
// on every comment line, they're checked first with a cheap Bloom filter.
static const int prime1{1019}, prime2{1021};
std::bitset<prime2> compilerDirectiveBloomFilter_; // 128 bytes
std::unordered_set<std::string> compilerDirectiveSentinels_;
int maxSentinelLength_{0};
};
} // namespace Fortran::parser
#endif // FORTRAN_PARSER_PRESCAN_H_

View file

@ -0,0 +1,10 @@
!RUN: %flang_fc1 -fdebug-unparse -fopenmp %s 2>&1 | FileCheck %s
#define OMP_DECLARE_TARGET $OMP declare target
#define OMP_BANG $OMP
subroutine s
!CHECK: !$OMP DECLARE TARGET
!OMP_DECLARE_TARGET
!CHECK: !$OMP DECLARE TARGET
!OMP_BANG declare &
!OMP_BANG target
end