[flang] Improve preprocessor error message provenances, pass f90_correct test pre21.
Original-commit: flang-compiler/f18@af5943e3bce4961594bd06c575a24ea51f0881a7
Reviewed-on: https://github.com/flang-compiler/f18/pull/26
Tree-same-pre-rewrite: false
diff --git a/flang/lib/parser/preprocessor.cc b/flang/lib/parser/preprocessor.cc
index cc9e186..a8a057ea 100644
--- a/flang/lib/parser/preprocessor.cc
+++ b/flang/lib/parser/preprocessor.cc
@@ -8,6 +8,7 @@
#include <ctime>
#include <map>
#include <memory>
+#include <optional>
#include <set>
#include <sstream>
#include <utility>
@@ -362,7 +363,7 @@
return;
}
if (dir[j].ToString() != "#") {
- prescanner->Error("missing '#'"_en_US);
+ prescanner->Error("missing '#'"_en_US, dir.GetTokenProvenance(j));
return;
}
j = SkipBlanks(dir, j + 1, tokens);
@@ -372,7 +373,8 @@
if (IsDecimalDigit(dir[j][0]) || dir[j][0] == '"') {
return; // TODO: treat as #line
}
- std::string dirName{ToLowerCaseLetters(dir[j].ToString())};
+ size_t dirOffset{j};
+ std::string dirName{ToLowerCaseLetters(dir[dirOffset].ToString())};
j = SkipBlanks(dir, j + 1, tokens);
ContiguousChars nameToken;
if (j < tokens && IsLegalIdentifierStart(dir[j][0])) {
@@ -382,7 +384,8 @@
// TODO: implement #line
} else if (dirName == "define") {
if (nameToken.empty()) {
- prescanner->Error("#define: missing or invalid name"_en_US);
+ prescanner->Error("#define: missing or invalid name"_en_US,
+ dir.GetTokenProvenance(j < tokens ? j : tokens - 1));
return;
}
nameToken = SaveTokenAsName(nameToken);
@@ -399,14 +402,16 @@
} else {
if (an.empty() || !IsLegalIdentifierStart(an[0])) {
prescanner->Error(
- "#define: missing or invalid argument name"_en_US);
+ "#define: missing or invalid argument name"_en_US,
+ dir.GetTokenProvenance(j));
return;
}
argName.push_back(an);
}
j = SkipBlanks(dir, j + 1, tokens);
if (j == tokens) {
- prescanner->Error("#define: malformed argument list"_en_US);
+ prescanner->Error("#define: malformed argument list"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
return;
}
std::string punc{dir[j].ToString()};
@@ -414,18 +419,21 @@
break;
}
if (isVariadic || punc != ",") {
- prescanner->Error("#define: malformed argument list"_en_US);
+ prescanner->Error("#define: malformed argument list"_en_US,
+ dir.GetTokenProvenance(j));
return;
}
j = SkipBlanks(dir, j + 1, tokens);
if (j == tokens) {
- prescanner->Error("#define: malformed argument list"_en_US);
+ prescanner->Error("#define: malformed argument list"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
return;
}
}
if (std::set<std::string>(argName.begin(), argName.end()).size() !=
argName.size()) {
- prescanner->Error("#define: argument names are not distinct"_en_US);
+ prescanner->Error("#define: argument names are not distinct"_en_US,
+ dir.GetTokenProvenance(dirOffset));
return;
}
}
@@ -439,11 +447,13 @@
}
} else if (dirName == "undef") {
if (nameToken.empty()) {
- prescanner->Error("# missing or invalid name"_en_US);
+ prescanner->Error("# missing or invalid name"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
} else {
j = SkipBlanks(dir, j + 1, tokens);
if (j != tokens) {
- prescanner->Error("#undef: excess tokens at end of directive"_en_US);
+ prescanner->Error("#undef: excess tokens at end of directive"_en_US,
+ dir.GetTokenProvenance(j));
} else {
definitions_.erase(nameToken);
}
@@ -451,71 +461,89 @@
} else if (dirName == "ifdef" || dirName == "ifndef") {
if (nameToken.empty()) {
prescanner->Error(
- MessageFormattedText("#%s: missing name"_en_US, dirName.data()));
+ MessageFormattedText("#%s: missing name"_en_US, dirName.data()),
+ dir.GetTokenProvenance(tokens - 1));
return;
}
j = SkipBlanks(dir, j + 1, tokens);
if (j != tokens) {
- prescanner->Error(MessageFormattedText(
- "#%s: excess tokens at end of directive"_en_US, dirName.data()));
+ prescanner->Error(
+ MessageFormattedText(
+ "#%s: excess tokens at end of directive"_en_US, dirName.data()),
+ dir.GetTokenProvenance(j));
} else if (IsNameDefined(nameToken) == (dirName == "ifdef")) {
ifStack_.push(CanDeadElseAppear::Yes);
} else {
- SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner);
+ SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner,
+ dir.GetTokenProvenance(dirOffset));
}
} else if (dirName == "if") {
if (IsIfPredicateTrue(dir, j, tokens - j, prescanner)) {
ifStack_.push(CanDeadElseAppear::Yes);
} else {
- SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner);
+ SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner,
+ dir.GetTokenProvenance(dirOffset));
}
} else if (dirName == "else") {
if (j != tokens) {
- prescanner->Error("#else: excess tokens at end of directive"_en_US);
+ prescanner->Error("#else: excess tokens at end of directive"_en_US,
+ dir.GetTokenProvenance(j));
} else if (ifStack_.empty()) {
prescanner->Error(
- "#else: not nested within #if, #ifdef, or #ifndef"_en_US);
+ "#else: not nested within #if, #ifdef, or #ifndef"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
} else if (ifStack_.top() != CanDeadElseAppear::Yes) {
prescanner->Error(
- "#else: already appeared within this #if, #ifdef, or #ifndef"_en_US);
+ "#else: already appeared within this #if, #ifdef, or #ifndef"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
} else {
ifStack_.pop();
- SkipDisabledConditionalCode("else", IsElseActive::No, prescanner);
+ SkipDisabledConditionalCode("else", IsElseActive::No, prescanner,
+ dir.GetTokenProvenance(dirOffset));
}
} else if (dirName == "elif") {
if (ifStack_.empty()) {
prescanner->Error(
- "#elif: not nested within #if, #ifdef, or #ifndef"_en_US);
+ "#elif: not nested within #if, #ifdef, or #ifndef"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
} else if (ifStack_.top() != CanDeadElseAppear::Yes) {
prescanner->Error("#elif: #else previously appeared within this "
- "#if, #ifdef, or #ifndef"_en_US);
+ "#if, #ifdef, or #ifndef"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
} else {
ifStack_.pop();
- SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner);
+ SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner,
+ dir.GetTokenProvenance(dirOffset));
}
} else if (dirName == "endif") {
if (j != tokens) {
- prescanner->Error("#endif: excess tokens at end of directive"_en_US);
+ prescanner->Error("#endif: excess tokens at end of directive"_en_US,
+ dir.GetTokenProvenance(j));
} else if (ifStack_.empty()) {
- prescanner->Error("#endif: no #if, #ifdef, or #ifndef"_en_US);
+ prescanner->Error("#endif: no #if, #ifdef, or #ifndef"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
} else {
ifStack_.pop();
}
} else if (dirName == "error") {
prescanner->Error(
- MessageFormattedText("#error: %s"_en_US, dir.ToString().data()));
+ MessageFormattedText("#error: %s"_en_US, dir.ToString().data()),
+ dir.GetTokenProvenance(dirOffset));
} else if (dirName == "warning") {
prescanner->Complain(
- MessageFormattedText("#warning: %s"_en_US, dir.ToString().data()));
+ MessageFormattedText("#warning: %s"_en_US, dir.ToString().data()),
+ dir.GetTokenProvenance(dirOffset));
} else if (dirName == "include") {
if (j == tokens) {
- prescanner->Error("#include: missing name of file to include"_en_US);
+ prescanner->Error("#include: missing name of file to include"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
return;
}
std::string include;
if (dir[j].ToString() == "<") {
if (dir[tokens - 1].ToString() != ">") {
- prescanner->Error("#include: expected '>' at end of directive"_en_US);
+ prescanner->Error("#include: expected '>' at end of directive"_en_US,
+ dir.GetTokenProvenance(tokens - 1));
return;
}
TokenSequence braced{dir, j + 1, tokens - j - 2};
@@ -525,18 +553,21 @@
include.substr(include.size() - 1, 1) == "\"") {
include = include.substr(1, include.size() - 2);
} else {
- prescanner->Error("#include: expected name of file to include"_en_US);
+ prescanner->Error("#include: expected name of file to include"_en_US,
+ dir.GetTokenProvenance(j < tokens ? j : tokens - 1));
return;
}
if (include.empty()) {
- prescanner->Error("#include: empty include file name"_en_US);
+ prescanner->Error("#include: empty include file name"_en_US,
+ dir.GetTokenProvenance(dirOffset));
return;
}
std::stringstream error;
const SourceFile *included{allSources_->Open(include, &error)};
if (included == nullptr) {
prescanner->Error(
- MessageFormattedText("#include: %s"_en_US, error.str().data()));
+ MessageFormattedText("#include: %s"_en_US, error.str().data()),
+ dir.GetTokenProvenance(dirOffset));
return;
}
ProvenanceRange fileRange{
@@ -545,8 +576,10 @@
prescanner->set_anyFatalErrors();
}
} else {
- prescanner->Error(MessageFormattedText(
- "#%s: unknown or unimplemented directive"_en_US, dirName.data()));
+ prescanner->Error(
+ MessageFormattedText(
+ "#%s: unknown or unimplemented directive"_en_US, dirName.data()),
+ dir.GetTokenProvenance(dirOffset));
}
}
@@ -576,7 +609,7 @@
}
void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
- IsElseActive isElseActive, Prescanner *prescanner) {
+ IsElseActive isElseActive, Prescanner *prescanner, Provenance provenance) {
int nesting{0};
while (!prescanner->IsAtEnd()) {
if (!prescanner->IsNextLinePreprocessorDirective()) {
@@ -605,7 +638,8 @@
}
}
prescanner->Error(
- MessageFormattedText("#%s: missing #endif"_en_US, dirName.data()));
+ MessageFormattedText("#%s: missing #endif"_en_US, dirName.data()),
+ provenance);
}
// Precedence level codes used here to accommodate mixed Fortran and C:
@@ -626,7 +660,7 @@
// 1: ? :
// 0: ,
static std::int64_t ExpressionValue(const TokenSequence &token,
- int minimumPrecedence, size_t *atToken, MessageFixedText *error) {
+ int minimumPrecedence, size_t *atToken, std::optional<Message> *error) {
enum Operator {
PARENS,
CONST,
@@ -704,22 +738,27 @@
size_t tokens{token.size()};
if (*atToken >= tokens) {
- *error = "incomplete expression"_en_US;
+ *error = Message{
+ token.GetTokenProvenance(tokens - 1), "incomplete expression"_en_US};
return 0;
}
- std::string t{token[*atToken].ToString()};
- enum Operator op;
// Parse and evaluate a primary or a unary operator and its operand.
+ size_t opAt{*atToken};
+ std::string t{token[opAt].ToString()};
+ enum Operator op;
std::int64_t left{0};
if (t == "(") {
op = PARENS;
} else if (IsDecimalDigit(t[0])) {
op = CONST;
size_t consumed{0};
- left = std::stoll(t, &consumed);
+ left = std::stoll(t, &consumed, 0 /*base to be detected*/);
if (consumed < t.size()) {
- *error = "uninterpretable numeric constant '"_en_US;
+ *error = Message{token.GetTokenProvenance(opAt),
+ MessageFormattedText(
+ "uninterpretable numeric constant '%s'"_en_US, t.data())};
+ return 0;
}
} else if (IsLegalIdentifierStart(t[0])) {
// undefined macro name -> zero
@@ -739,22 +778,30 @@
if (it != opNameMap.end()) {
op = it->second;
} else {
- *error = "operand expected in expression"_en_US;
+ *error = Message{token.GetTokenProvenance(tokens - 1),
+ "operand expected in expression"_en_US};
return 0;
}
}
- if (precedence[op] < minimumPrecedence && error->empty()) {
- *error = "operator precedence error"_en_US;
+ if (precedence[op] < minimumPrecedence) {
+ *error = Message{
+ token.GetTokenProvenance(opAt), "operator precedence error"_en_US};
+ return 0;
}
++*atToken;
- if (op != CONST && error->empty()) {
+ if (op != CONST) {
left = ExpressionValue(token, operandPrecedence[op], atToken, error);
+ if (error->has_value()) {
+ return 0;
+ }
switch (op) {
case PARENS:
if (*atToken < tokens && token[*atToken].ToString() == ")") {
++*atToken;
- } else if (error->empty()) {
- *error = "')' missing from expression"_en_US;
+ } else {
+ *error = Message{token.GetTokenProvenance(tokens - 1),
+ "')' missing from expression"_en_US};
+ return 0;
}
break;
case NOTZERO: left = !left; break;
@@ -765,7 +812,7 @@
default: CRASH_NO_CASE;
}
}
- if (!error->empty() || *atToken >= tokens) {
+ if (*atToken >= tokens) {
return left;
}
@@ -785,13 +832,19 @@
if (precedence[op] < minimumPrecedence) {
return left;
}
+ opAt = *atToken;
*atToken += advance;
std::int64_t right{
ExpressionValue(token, operandPrecedence[op], atToken, error)};
+ if (error->has_value()) {
+ return 0;
+ }
switch (op) {
case POWER:
if (left == 0 && right < 0) {
- *error = "0 ** negative power"_en_US;
+ *error =
+ Message{token.GetTokenProvenance(opAt), "0 ** negative power"_en_US};
+ return 0;
}
if (left == 0 || left == 1 || right == 1) {
return left;
@@ -803,7 +856,8 @@
std::int64_t power{1};
for (; right > 0; --right) {
if ((power * left) / left != power) {
- *error = "overflow in exponentation"_en_US;
+ *error = Message{token.GetTokenProvenance(opAt),
+ "overflow in exponentation"_en_US};
return 0;
}
power *= left;
@@ -815,39 +869,45 @@
return 0;
}
if ((left * right) / left != right) {
- *error = "overflow in multiplication"_en_US;
+ *error = Message{
+ token.GetTokenProvenance(opAt), "overflow in multiplication"_en_US};
}
return left * right;
case DIVIDE:
if (right == 0) {
- *error = "division by zero"_en_US;
+ *error =
+ Message{token.GetTokenProvenance(opAt), "division by zero"_en_US};
return 0;
}
return left / right;
case MODULUS:
if (right == 0) {
- *error = "modulus by zero"_en_US;
+ *error = Message{token.GetTokenProvenance(opAt), "modulus by zero"_en_US};
return 0;
}
return left % right;
case ADD:
if ((left < 0) == (right < 0) && (left < 0) != (left + right < 0)) {
- *error = "overflow in addition"_en_US;
+ *error =
+ Message{token.GetTokenProvenance(opAt), "overflow in addition"_en_US};
}
return left + right;
case SUBTRACT:
if ((left < 0) != (right < 0) && (left < 0) == (left - right < 0)) {
- *error = "overflow in subtraction"_en_US;
+ *error = Message{
+ token.GetTokenProvenance(opAt), "overflow in subtraction"_en_US};
}
return left - right;
case LEFTSHIFT:
if (right < 0 || right > 64) {
- *error = "bad left shift count"_en_US;
+ *error =
+ Message{token.GetTokenProvenance(opAt), "bad left shift count"_en_US};
}
return right >= 64 ? 0 : left << right;
case RIGHTSHIFT:
if (right < 0 || right > 64) {
- *error = "bad right shift count"_en_US;
+ *error = Message{
+ token.GetTokenProvenance(opAt), "bad right shift count"_en_US};
}
return right >= 64 ? 0 : left >> right;
case BITAND:
@@ -865,7 +925,8 @@
case NEQV: return -(!left != !right);
case SELECT:
if (*atToken >= tokens || token[*atToken].ToString() != ":") {
- *error = "':' required in selection expression"_en_US;
+ *error = Message{token.GetTokenProvenance(opAt),
+ "':' required in selection expression"_en_US};
return left;
} else {
++*atToken;
@@ -904,14 +965,14 @@
TokenSequence expr3{ReplaceMacros(expr2, *prescanner)};
TokenSequence expr4{StripBlanks(expr3, 0, expr3.size())};
size_t atToken{0};
- MessageFixedText error;
+ std::optional<Message> error;
bool result{ExpressionValue(expr4, 0, &atToken, &error) != 0};
- if (!error.empty()) {
- prescanner->Error(error);
+ if (error.has_value()) {
+ prescanner->Error(std::move(*error));
} else if (atToken < expr4.size()) {
- prescanner->Error(atToken == 0
- ? "could not parse any expression"_en_US
- : "excess characters after expression"_en_US);
+ prescanner->Error(atToken == 0 ? "could not parse any expression"_en_US
+ : "excess characters after expression"_en_US,
+ expr4.GetTokenProvenance(atToken));
}
return result;
}
diff --git a/flang/lib/parser/preprocessor.h b/flang/lib/parser/preprocessor.h
index bf34a247..7b6d1a5 100644
--- a/flang/lib/parser/preprocessor.h
+++ b/flang/lib/parser/preprocessor.h
@@ -77,7 +77,7 @@
bool IsNameDefined(const ContiguousChars &);
TokenSequence ReplaceMacros(const TokenSequence &, const Prescanner &);
void SkipDisabledConditionalCode(
- const std::string &, IsElseActive, Prescanner *);
+ const std::string &, IsElseActive, Prescanner *, Provenance);
bool IsIfPredicateTrue(
const TokenSequence &expr, size_t first, size_t exprTokens, Prescanner *);
diff --git a/flang/lib/parser/prescan.cc b/flang/lib/parser/prescan.cc
index 13f3d5b..2e71a43 100644
--- a/flang/lib/parser/prescan.cc
+++ b/flang/lib/parser/prescan.cc
@@ -82,22 +82,31 @@
return {std::move(tokens)};
}
-Message &Prescanner::Error(MessageFixedText text) {
+Message &Prescanner::Error(Message &&message) {
anyFatalErrors_ = true;
- return Complain(text);
+ return messages_->Put(std::move(message));
}
-Message &Prescanner::Error(MessageFormattedText &&text) {
+Message &Prescanner::Error(MessageFixedText text, Provenance p) {
anyFatalErrors_ = true;
- return Complain(std::move(text));
+ return messages_->Put({p, text});
}
-Message &Prescanner::Complain(MessageFixedText text) {
- return messages_->Put({GetCurrentProvenance(), text});
+Message &Prescanner::Error(MessageFormattedText &&text, Provenance p) {
+ anyFatalErrors_ = true;
+ return messages_->Put({p, std::move(text)});
}
-Message &Prescanner::Complain(MessageFormattedText &&text) {
- return messages_->Put({GetCurrentProvenance(), std::move(text)});
+Message &Prescanner::Complain(Message &&message) {
+ return messages_->Put(std::move(message));
+}
+
+Message &Prescanner::Complain(MessageFixedText text, Provenance p) {
+ return messages_->Put({p, text});
+}
+
+Message &Prescanner::Complain(MessageFormattedText &&text, Provenance p) {
+ return messages_->Put({p, std::move(text)});
}
void Prescanner::NextLine() {
@@ -196,11 +205,12 @@
if (inFixedForm_) {
SkipSpaces();
} else if (*at_ == ' ' || *at_ == '\t') {
- Provenance here{GetCurrentProvenance()};
+ // Compress white space into a single character.
+ const auto theSpace = at_;
NextChar();
SkipSpaces();
if (*at_ != '\n') {
- tokens->PutNextTokenChar(' ', here);
+ tokens->PutNextTokenChar(' ', GetProvenance(theSpace));
tokens->CloseToken();
return true;
}
@@ -213,14 +223,15 @@
QuotedCharacterLiteral(tokens);
preventHollerith_ = false;
} else if (IsDecimalDigit(*at_)) {
- int n{0};
+ int n{0}, digits{0};
static constexpr int maxHollerith{256 /*lines*/ * (132 - 6 /*columns*/)};
do {
if (n < maxHollerith) {
n = 10 * n + DecimalDigitValue(*at_);
}
EmitCharAndAdvance(tokens, *at_);
- if (inFixedForm_) {
+ ++digits;
+ if (inFixedForm_ && !inPreprocessorDirective_) {
SkipSpaces();
}
} while (IsDecimalDigit(*at_));
@@ -232,6 +243,11 @@
}
ExponentAndKind(tokens);
} else if (ExponentAndKind(tokens)) {
+ } else if (digits == 1 && n == 0 && (*at_ == 'x' || *at_ == 'X') &&
+ inPreprocessorDirective_) {
+ do {
+ EmitCharAndAdvance(tokens, *at_);
+ } while (IsHexadecimalDigit(*at_));
} else if (IsLetter(*at_)) {
// Handles FORMAT(3I9HHOLLERITH) by skipping over the first I so that
// we don't misrecognize I9HOLLERITH as an identifier in the next case.
@@ -318,9 +334,7 @@
}
if (*at_ == '\n') {
if (!inPreprocessorDirective_) {
- messages_->Put(
- {GetProvenance(start), "incomplete character literal"_en_US});
- anyFatalErrors_ = true;
+ Error("incomplete character literal"_en_US, GetProvenance(start));
}
break;
}
@@ -330,7 +344,7 @@
// in the literal (later). There can be spaces between the quotes in
// fixed form source.
EmitCharAndAdvance(tokens, quote);
- if (inFixedForm_) {
+ if (inFixedForm_ && !inPreprocessorDirective_) {
SkipSpaces();
}
if (*at_ != quote) {
@@ -370,9 +384,7 @@
}
if (*at_ == '\n') {
if (!inPreprocessorDirective_) {
- messages_->Put(
- {GetProvenance(start), "incomplete Hollerith literal"_en_US});
- anyFatalErrors_ = true;
+ Error("incomplete Hollerith literal"_en_US, GetProvenance(start));
}
} else {
NextChar();
@@ -474,14 +486,13 @@
path += *p;
}
if (*p != quote) {
- Error("malformed path name string"_en_US);
+ Error("malformed path name string"_en_US, GetProvenance(p));
return true;
}
for (++p; *p == ' ' || *p == '\t'; ++p) {
}
if (*p != '\n' && *p != '!') {
- messages_->Put(
- {GetProvenance(p), "excess characters after path name"_en_US});
+ Complain("excess characters after path name"_en_US, GetProvenance(p));
}
std::stringstream error;
Provenance provenance{GetProvenance(start)};
@@ -495,9 +506,8 @@
allSources->PopSearchPathDirectory();
}
if (included == nullptr) {
- messages_->Put({provenance,
- MessageFormattedText("INCLUDE: %s"_en_US, error.str().data())});
- anyFatalErrors_ = true;
+ Error(MessageFormattedText("INCLUDE: %s"_en_US, error.str().data()),
+ provenance);
return true;
}
ProvenanceRange includeLineRange{provenance, static_cast<size_t>(p - start)};
diff --git a/flang/lib/parser/prescan.h b/flang/lib/parser/prescan.h
index 299782d..5097632 100644
--- a/flang/lib/parser/prescan.h
+++ b/flang/lib/parser/prescan.h
@@ -59,10 +59,13 @@
bool IsNextLinePreprocessorDirective() const;
TokenSequence TokenizePreprocessorDirective();
Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
- Message &Error(MessageFixedText);
- Message &Error(MessageFormattedText &&);
- Message &Complain(MessageFixedText);
- Message &Complain(MessageFormattedText &&);
+
+ Message &Error(Message &&);
+ Message &Error(MessageFixedText, Provenance);
+ Message &Error(MessageFormattedText &&, Provenance);
+ Message &Complain(Message &&);
+ Message &Complain(MessageFixedText, Provenance);
+ Message &Complain(MessageFormattedText &&, Provenance);
private:
void BeginSourceLine(const char *at) {
diff --git a/flang/lib/parser/provenance.h b/flang/lib/parser/provenance.h
index 50d28cd..92a19f1 100644
--- a/flang/lib/parser/provenance.h
+++ b/flang/lib/parser/provenance.h
@@ -42,6 +42,9 @@
Provenance(Provenance &&that) = default;
Provenance &operator=(const Provenance &that) = default;
Provenance &operator=(Provenance &&that) = default;
+
+ size_t offset() const { return offset_; }
+
Provenance operator+(ptrdiff_t n) const {
CHECK(n > -static_cast<ptrdiff_t>(offset_));
return {offset_ + static_cast<size_t>(n)};
@@ -55,7 +58,6 @@
bool operator<=(Provenance that) const { return !(that < *this); }
bool operator==(Provenance that) const { return offset_ == that.offset_; }
bool operator!=(Provenance that) const { return !(*this == that); }
- size_t offset() const { return offset_; }
private:
size_t offset_{0};