From e6b1f1f45e880d821ad234ba68987c61efe94d96 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Mon, 23 Apr 2018 12:17:11 -0700 Subject: [PATCH] [flang] Use ranges of provenances in messages. Original-commit: flang-compiler/f18@67d4f878a4392689ba1f768a69198de93af871c2 Reviewed-on: https://github.com/flang-compiler/f18/pull/66 Tree-same-pre-rewrite: false --- flang/lib/parser/char-block.h | 4 +- flang/lib/parser/parse-state.h | 2 +- flang/lib/parser/parsing.cc | 4 +- flang/lib/parser/preprocessor.cc | 120 ++++++++++++++++++++------------------- flang/lib/parser/preprocessor.h | 2 +- flang/lib/parser/prescan.cc | 25 +++++--- flang/lib/parser/prescan.h | 10 +++- flang/lib/parser/provenance.cc | 16 +++++- flang/lib/parser/token-parsers.h | 6 +- 9 files changed, 111 insertions(+), 78 deletions(-) diff --git a/flang/lib/parser/char-block.h b/flang/lib/parser/char-block.h index 85db0f9..4ceaf37 100644 --- a/flang/lib/parser/char-block.h +++ b/flang/lib/parser/char-block.h @@ -17,8 +17,8 @@ class CharBlock { public: CharBlock() {} CharBlock(const char *x, std::size_t n = 1) : interval_{x, n} {} - CharBlock(const char *b, const char *e) - : interval_{b, static_cast(e - b)} {} + CharBlock(const char *b, const char *ep1) + : interval_{b, static_cast(ep1 - b)} {} CharBlock(const std::string &s) : interval_{s.data(), s.size()} {} CharBlock(const CharBlock &) = default; CharBlock(CharBlock &&) = default; diff --git a/flang/lib/parser/parse-state.h b/flang/lib/parser/parse-state.h index 8a39fd1..8e3017f 100644 --- a/flang/lib/parser/parse-state.h +++ b/flang/lib/parser/parse-state.h @@ -134,7 +134,7 @@ public: const char *GetLocation() const { return p_; } void PushContext(MessageFixedText text) { - auto m = new Message{p_, text}; + auto m = new Message{p_, text}; // reference-counted, it's ok m->set_context(context_.get()); context_ = Message::Context{m}; } diff --git a/flang/lib/parser/parsing.cc b/flang/lib/parser/parsing.cc index 02a9ed5..b8176fb 100644 --- a/flang/lib/parser/parsing.cc +++ b/flang/lib/parser/parsing.cc @@ -24,12 +24,12 @@ void Parsing::Prescan(const std::string &path, Options options) { if (sourceFile == nullptr) { ProvenanceRange range{allSources_.AddCompilerInsertion(path)}; MessageFormattedText msg("%s"_err_en_US, fileError.str().data()); - messages_.Put(Message(range.start(), std::move(msg))); + messages_.Put(Message{range, std::move(msg)}); return; } if (sourceFile->bytes() == 0) { ProvenanceRange range{allSources_.AddCompilerInsertion(path)}; - messages_.Put(Message{range.start(), "file is empty"_err_en_US}); + messages_.Put(Message{range, "file is empty"_err_en_US}); return; } diff --git a/flang/lib/parser/preprocessor.cc b/flang/lib/parser/preprocessor.cc index 91c7282..7eaebfc 100644 --- a/flang/lib/parser/preprocessor.cc +++ b/flang/lib/parser/preprocessor.cc @@ -369,7 +369,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { return; } if (dir.TokenAt(j).ToString() != "#") { - prescanner->Say("missing '#'"_err_en_US, dir.GetTokenProvenance(j)); + prescanner->Say("missing '#'"_err_en_US, dir.GetTokenProvenanceRange(j)); return; } j = SkipBlanks(dir, j + 1, tokens); @@ -391,7 +391,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } else if (dirName == "define") { if (nameToken.empty()) { prescanner->Say("#define: missing or invalid name"_err_en_US, - dir.GetTokenProvenance(j < tokens ? j : tokens - 1)); + dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1)); return; } nameToken = SaveTokenAsName(nameToken); @@ -410,7 +410,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { if (an.empty() || !IsLegalIdentifierStart(an[0])) { prescanner->Say( "#define: missing or invalid argument name"_err_en_US, - dir.GetTokenProvenance(j)); + dir.GetTokenProvenanceRange(j)); return; } argName.push_back(an); @@ -418,7 +418,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { j = SkipBlanks(dir, j + 1, tokens); if (j == tokens) { prescanner->Say("#define: malformed argument list"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetTokenProvenanceRange(tokens - 1)); return; } std::string punc{dir.TokenAt(j).ToString()}; @@ -427,13 +427,13 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } if (isVariadic || punc != ",") { prescanner->Say("#define: malformed argument list"_err_en_US, - dir.GetTokenProvenance(j)); + dir.GetTokenProvenanceRange(j)); return; } j = SkipBlanks(dir, j + 1, tokens); if (j == tokens) { prescanner->Say("#define: malformed argument list"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetTokenProvenanceRange(tokens - 1)); return; } } @@ -455,12 +455,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } else if (dirName == "undef") { if (nameToken.empty()) { prescanner->Say("# missing or invalid name"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); } else { j = SkipBlanks(dir, j + 1, tokens); if (j != tokens) { prescanner->Say("#undef: excess tokens at end of directive"_err_en_US, - dir.GetTokenProvenance(j)); + dir.GetIntervalProvenanceRange(j, tokens - j)); } else { definitions_.erase(nameToken); } @@ -469,7 +469,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { if (nameToken.empty()) { prescanner->Say( MessageFormattedText("#%s: missing name"_err_en_US, dirName.data()), - dir.GetTokenProvenance(tokens - 1)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); return; } j = SkipBlanks(dir, j + 1, tokens); @@ -477,7 +477,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { prescanner->Say(MessageFormattedText( "#%s: excess tokens at end of directive"_err_en_US, dirName.data()), - dir.GetTokenProvenance(j)); + dir.GetIntervalProvenanceRange(j, tokens - j)); } else if (IsNameDefined(nameToken) == (dirName == "ifdef")) { ifStack_.push(CanDeadElseAppear::Yes); } else { @@ -489,68 +489,68 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { ifStack_.push(CanDeadElseAppear::Yes); } else { SkipDisabledConditionalCode(dirName, IsElseActive::Yes, prescanner, - dir.GetTokenProvenance(dirOffset)); + dir.GetTokenProvenanceRange(dirOffset)); } } else if (dirName == "else") { if (j != tokens) { prescanner->Say("#else: excess tokens at end of directive"_err_en_US, - dir.GetTokenProvenance(j)); + dir.GetIntervalProvenanceRange(j, tokens - j)); } else if (ifStack_.empty()) { prescanner->Say( "#else: not nested within #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetTokenProvenanceRange(dirOffset)); } else if (ifStack_.top() != CanDeadElseAppear::Yes) { prescanner->Say( "#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetTokenProvenanceRange(dirOffset)); } else { ifStack_.pop(); SkipDisabledConditionalCode("else", IsElseActive::No, prescanner, - dir.GetTokenProvenance(dirOffset)); + dir.GetTokenProvenanceRange(dirOffset)); } } else if (dirName == "elif") { if (ifStack_.empty()) { prescanner->Say( "#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetTokenProvenanceRange(dirOffset)); } else if (ifStack_.top() != CanDeadElseAppear::Yes) { prescanner->Say("#elif: #else previously appeared within this " "#if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetTokenProvenanceRange(dirOffset)); } else { ifStack_.pop(); SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner, - dir.GetTokenProvenance(dirOffset)); + dir.GetTokenProvenanceRange(dirOffset)); } } else if (dirName == "endif") { if (j != tokens) { prescanner->Say("#endif: excess tokens at end of directive"_err_en_US, - dir.GetTokenProvenance(j)); + dir.GetIntervalProvenanceRange(j, tokens - j)); } else if (ifStack_.empty()) { prescanner->Say("#endif: no #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetTokenProvenanceRange(dirOffset)); } else { ifStack_.pop(); } } else if (dirName == "error") { prescanner->Say( MessageFormattedText("#error: %s"_err_en_US, dir.ToString().data()), - dir.GetTokenProvenance(dirOffset)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); } else if (dirName == "warning") { prescanner->Say( MessageFormattedText("#warning: %s"_en_US, dir.ToString().data()), - dir.GetTokenProvenance(dirOffset)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); } else if (dirName == "include") { if (j == tokens) { prescanner->Say("#include: missing name of file to include"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); return; } std::string include; if (dir.TokenAt(j).ToString() == "<") { if (dir.TokenAt(tokens - 1).ToString() != ">") { prescanner->Say("#include: expected '>' at end of directive"_err_en_US, - dir.GetTokenProvenance(tokens - 1)); + dir.GetIntervalProvenanceRange(j, tokens - j)); return; } TokenSequence braced{dir, j + 1, tokens - j - 2}; @@ -561,12 +561,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { include = include.substr(1, include.size() - 2); } else { prescanner->Say("#include: expected name of file to include"_err_en_US, - dir.GetTokenProvenance(j < tokens ? j : tokens - 1)); + dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1)); return; } if (include.empty()) { prescanner->Say("#include: empty include file name"_err_en_US, - dir.GetTokenProvenance(dirOffset)); + dir.GetTokenProvenanceRange(dirOffset)); return; } std::stringstream error; @@ -574,7 +574,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { if (included == nullptr) { prescanner->Say( MessageFormattedText("#include: %s"_err_en_US, error.str().data()), - dir.GetTokenProvenance(dirOffset)); + dir.GetTokenProvenanceRange(dirOffset)); } else if (included->bytes() > 0) { ProvenanceRange fileRange{ allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())}; @@ -584,7 +584,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { prescanner->Say(MessageFormattedText( "#%s: unknown or unimplemented directive"_err_en_US, dirName.data()), - dir.GetTokenProvenance(dirOffset)); + dir.GetTokenProvenanceRange(dirOffset)); } } @@ -615,7 +615,8 @@ static std::string GetDirectiveName( } void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName, - IsElseActive isElseActive, Prescanner *prescanner, Provenance provenance) { + IsElseActive isElseActive, Prescanner *prescanner, + ProvenanceRange provenanceRange) { int nesting{0}; while (!prescanner->IsAtEnd()) { if (!prescanner->IsNextLinePreprocessorDirective()) { @@ -646,7 +647,7 @@ void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName, } prescanner->Say( MessageFormattedText("#%s: missing #endif"_err_en_US, dirName.data()), - provenance); + provenanceRange); } // Precedence level codes used here to accommodate mixed Fortran and C: @@ -746,8 +747,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token, std::size_t tokens{token.SizeInTokens()}; if (*atToken >= tokens) { - *error = Message{token.GetTokenProvenance(tokens - 1), - "incomplete expression"_err_en_US}; + *error = + Message{token.GetProvenanceRange(), "incomplete expression"_err_en_US}; return 0; } @@ -763,7 +764,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token, std::size_t consumed{0}; left = std::stoll(t, &consumed, 0 /*base to be detected*/); if (consumed < t.size()) { - *error = Message{token.GetTokenProvenance(opAt), + *error = Message{token.GetTokenProvenanceRange(opAt), MessageFormattedText( "uninterpretable numeric constant '%s'"_err_en_US, t.data())}; return 0; @@ -786,14 +787,14 @@ static std::int64_t ExpressionValue(const TokenSequence &token, if (it != opNameMap.end()) { op = it->second; } else { - *error = Message{token.GetTokenProvenance(tokens - 1), + *error = Message{token.GetTokenProvenanceRange(opAt), "operand expected in expression"_err_en_US}; return 0; } } if (precedence[op] < minimumPrecedence) { - *error = Message{ - token.GetTokenProvenance(opAt), "operator precedence error"_err_en_US}; + *error = Message{token.GetTokenProvenanceRange(opAt), + "operator precedence error"_err_en_US}; return 0; } ++*atToken; @@ -806,12 +807,16 @@ static std::int64_t ExpressionValue(const TokenSequence &token, case PARENS: if (*atToken < tokens && token.TokenAt(*atToken).ToString() == ")") { ++*atToken; - } else { - *error = Message{token.GetTokenProvenance(tokens - 1), + break; + } + if (*atToken >= tokens) { + *error = Message{token.GetProvenanceRange(), "')' missing from expression"_err_en_US}; - return 0; + } else { + *error = Message{ + token.GetTokenProvenanceRange(*atToken), "expected ')'"_err_en_US}; } - break; + return 0; case NOTZERO: left = !left; break; case COMPLEMENT: left = ~left; break; case UPLUS: break; @@ -851,7 +856,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token, case POWER: if (left == 0 && right < 0) { *error = Message{ - token.GetTokenProvenance(opAt), "0 ** negative power"_err_en_US}; + token.GetTokenProvenanceRange(opAt), "0 ** negative power"_err_en_US}; return 0; } if (left == 0 || left == 1 || right == 1) { @@ -864,7 +869,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token, std::int64_t power{1}; for (; right > 0; --right) { if ((power * left) / left != power) { - *error = Message{token.GetTokenProvenance(opAt), + *error = Message{token.GetTokenProvenanceRange(opAt), "overflow in exponentation"_err_en_US}; return 0; } @@ -877,46 +882,46 @@ static std::int64_t ExpressionValue(const TokenSequence &token, return 0; } if ((left * right) / left != right) { - *error = Message{token.GetTokenProvenance(opAt), + *error = Message{token.GetTokenProvenanceRange(opAt), "overflow in multiplication"_err_en_US}; } return left * right; case DIVIDE: if (right == 0) { - *error = - Message{token.GetTokenProvenance(opAt), "division by zero"_err_en_US}; + *error = Message{ + token.GetTokenProvenanceRange(opAt), "division by zero"_err_en_US}; return 0; } return left / right; case MODULUS: if (right == 0) { - *error = - Message{token.GetTokenProvenance(opAt), "modulus by zero"_err_en_US}; + *error = Message{ + token.GetTokenProvenanceRange(opAt), "modulus by zero"_err_en_US}; return 0; } return left % right; case ADD: if ((left < 0) == (right < 0) && (left < 0) != (left + right < 0)) { - *error = Message{ - token.GetTokenProvenance(opAt), "overflow in addition"_err_en_US}; + *error = Message{token.GetTokenProvenanceRange(opAt), + "overflow in addition"_err_en_US}; } return left + right; case SUBTRACT: if ((left < 0) != (right < 0) && (left < 0) == (left - right < 0)) { - *error = Message{ - token.GetTokenProvenance(opAt), "overflow in subtraction"_err_en_US}; + *error = Message{token.GetTokenProvenanceRange(opAt), + "overflow in subtraction"_err_en_US}; } return left - right; case LEFTSHIFT: if (right < 0 || right > 64) { - *error = Message{ - token.GetTokenProvenance(opAt), "bad left shift count"_err_en_US}; + *error = Message{token.GetTokenProvenanceRange(opAt), + "bad left shift count"_err_en_US}; } return right >= 64 ? 0 : left << right; case RIGHTSHIFT: if (right < 0 || right > 64) { - *error = Message{ - token.GetTokenProvenance(opAt), "bad right shift count"_err_en_US}; + *error = Message{token.GetTokenProvenanceRange(opAt), + "bad right shift count"_err_en_US}; } return right >= 64 ? 0 : left >> right; case BITAND: @@ -934,7 +939,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token, case NEQV: return -(!left != !right); case SELECT: if (*atToken >= tokens || token.TokenAt(*atToken).ToString() != ":") { - *error = Message{token.GetTokenProvenance(opAt), + *error = Message{token.GetTokenProvenanceRange(opAt), "':' required in selection expression"_err_en_US}; return left; } else { @@ -984,7 +989,8 @@ bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, prescanner->Say(atToken == 0 ? "could not parse any expression"_err_en_US : "excess characters after expression"_err_en_US, - expr4.GetTokenProvenance(atToken)); + expr4.GetIntervalProvenanceRange( + atToken, expr4.SizeInTokens() - atToken)); } return result; } diff --git a/flang/lib/parser/preprocessor.h b/flang/lib/parser/preprocessor.h index e0a50c7..b8a1680 100644 --- a/flang/lib/parser/preprocessor.h +++ b/flang/lib/parser/preprocessor.h @@ -75,7 +75,7 @@ private: bool IsNameDefined(const CharBlock &); TokenSequence ReplaceMacros(const TokenSequence &, const Prescanner &); void SkipDisabledConditionalCode( - const std::string &, IsElseActive, Prescanner *, Provenance); + const std::string &, IsElseActive, Prescanner *, ProvenanceRange); bool IsIfPredicateTrue(const TokenSequence &expr, std::size_t first, std::size_t exprTokens, Prescanner *); diff --git a/flang/lib/parser/prescan.cc b/flang/lib/parser/prescan.cc index 1f42903..5d95e99 100644 --- a/flang/lib/parser/prescan.cc +++ b/flang/lib/parser/prescan.cc @@ -137,7 +137,7 @@ void Prescanner::Statement() { break; case LineClassification::Kind::PreprocessorDirective: Say("preprocessed line looks like a preprocessor directive"_en_US, - preprocessed->GetProvenanceRange().start()); + preprocessed->GetProvenanceRange()); preprocessed->ToLowerCase().Emit(&cooked_); break; case LineClassification::Kind::CompilerDirective: @@ -176,12 +176,12 @@ TokenSequence Prescanner::TokenizePreprocessorDirective() { void Prescanner::Say(Message &&message) { messages_.Put(std::move(message)); } -void Prescanner::Say(MessageFixedText text, Provenance p) { - messages_.Put({p, text}); +void Prescanner::Say(MessageFixedText text, ProvenanceRange r) { + messages_.Put({r, text}); } -void Prescanner::Say(MessageFormattedText &&text, Provenance p) { - messages_.Put({p, std::move(text)}); +void Prescanner::Say(MessageFormattedText &&text, ProvenanceRange r) { + messages_.Put({r, std::move(text)}); } void Prescanner::NextLine() { @@ -416,7 +416,8 @@ void Prescanner::QuotedCharacterLiteral(TokenSequence *tokens) { } if (*at_ == '\n') { if (!inPreprocessorDirective_) { - Say("incomplete character literal"_err_en_US, GetProvenance(start)); + Say("incomplete character literal"_err_en_US, + GetProvenanceRange(start, at_)); } break; } @@ -466,7 +467,8 @@ void Prescanner::Hollerith(TokenSequence *tokens, int count) { } if (*at_ == '\n') { if (!inPreprocessorDirective_) { - Say("incomplete Hollerith literal"_err_en_US, GetProvenance(start)); + Say("incomplete Hollerith literal"_err_en_US, + GetProvenanceRange(start, at_)); } } else { NextChar(); @@ -567,13 +569,18 @@ void Prescanner::FortranInclude(const char *firstQuote) { path += *p; } if (*p != quote) { - Say("malformed path name string"_err_en_US, GetProvenance(p)); + Say("malformed path name string"_err_en_US, + GetProvenanceRange(firstQuote, p)); return; } for (++p; *p == ' ' || *p == '\t'; ++p) { } if (*p != '\n' && *p != '!') { - Say("excess characters after path name"_en_US, GetProvenance(p)); + const char *garbage{p}; + for (; *p != '\n' && *p != '!'; ++p) { + } + Say("excess characters after path name"_en_US, + GetProvenanceRange(garbage, p)); } std::stringstream error; Provenance provenance{GetProvenance(lineStart_)}; diff --git a/flang/lib/parser/prescan.h b/flang/lib/parser/prescan.h index 63850e7..689429f 100644 --- a/flang/lib/parser/prescan.h +++ b/flang/lib/parser/prescan.h @@ -68,8 +68,8 @@ public: Provenance GetCurrentProvenance() const { return GetProvenance(at_); } void Say(Message &&); - void Say(MessageFixedText, Provenance); - void Say(MessageFormattedText &&, Provenance); + void Say(MessageFixedText, ProvenanceRange); + void Say(MessageFormattedText &&, ProvenanceRange); private: struct LineClassification { @@ -106,6 +106,12 @@ private: return startProvenance_ + (sourceChar - start_); } + ProvenanceRange GetProvenanceRange( + const char *first, const char *afterLast) const { + std::size_t bytes = afterLast - first; + return {startProvenance_ + (first - start_), bytes}; + } + void EmitChar(TokenSequence *tokens, char ch) { tokens->PutNextTokenChar(ch, GetCurrentProvenance()); } diff --git a/flang/lib/parser/provenance.cc b/flang/lib/parser/provenance.cc index 6da3d88..4e398ef 100644 --- a/flang/lib/parser/provenance.cc +++ b/flang/lib/parser/provenance.cc @@ -151,8 +151,20 @@ void AllSources::Identify(std::ostream &o, ProvenanceRange range, char ch{text[j - 1]}; o << (ch == '\t' ? '\t' : ' '); } - o << "^\n" << prefix; - // TODO mark a wider range + o << '^'; + if (range.size() > 1) { + auto last = range.start() + range.size() - 1; + if (&MapToOrigin(last) == &origin) { + auto endOffset = origin.covers.MemberOffset(last); + auto endPos = inc.source.FindOffsetLineAndColumn(endOffset); + if (pos.first == endPos.first) { + for (int j{pos.second}; j < endPos.second; ++j) { + o << '^'; + } + } + } + } + o << '\n' << prefix; } else { o << ' '; } diff --git a/flang/lib/parser/token-parsers.h b/flang/lib/parser/token-parsers.h index 3042762..6cee1a1 100644 --- a/flang/lib/parser/token-parsers.h +++ b/flang/lib/parser/token-parsers.h @@ -215,7 +215,8 @@ struct CharLiteralChar { } char ch{**och}; if (ch == '\n') { - state.Say(at, "unclosed character constant"_err_en_US); + state.Say(CharBlock{at, state.GetLocation()}, + "unclosed character constant"_err_en_US); return {}; } if (ch != '\\') { @@ -226,7 +227,8 @@ struct CharLiteralChar { } ch = **och; if (ch == '\n') { - state.Say(at, "unclosed character constant"_err_en_US); + state.Say(CharBlock{at, state.GetLocation()}, + "unclosed character constant"_err_en_US); return {}; } if (std::optional escChar{BackslashEscapeValue(ch)}) { -- 2.7.4