[flang] Support std::string and CharBlock in formatted messages
fix off-by-one Original-commit: flang-compiler/f18@58eb11c5c7 Reviewed-on: https://github.com/flang-compiler/f18/pull/441 Tree-same-pre-rewrite: false
This commit is contained in:
parent
bc0732002e
commit
68209d4f87
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -23,8 +23,7 @@ std::optional<Success> DebugParser::Parse(ParseState &state) const {
|
|||
if (auto ustate{state.userState()}) {
|
||||
if (auto out{ustate->debugOutput()}) {
|
||||
std::string note{str_, length_};
|
||||
Message message{
|
||||
state.GetLocation(), "parser debug: %s"_en_US, note.data()};
|
||||
Message message{state.GetLocation(), "parser debug: %S"_en_US, note};
|
||||
message.SetContext(state.context().get());
|
||||
message.Emit(*out, ustate->cooked(), true);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,56 @@ std::ostream &operator<<(std::ostream &o, const MessageFixedText &t) {
|
|||
return o;
|
||||
}
|
||||
|
||||
static bool NeedsSpecialFormatting(const char *p) {
|
||||
while (*p != '\0') {
|
||||
if (*p++ == '%') {
|
||||
if (*p == 'S' || *p == 'B') {
|
||||
return true;
|
||||
} else if (*p != '%' && *p != 's' && *p != 'd' &&
|
||||
!(p[1] == 'd' && (*p == 'z' || *p == 'j'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string SpecialFormatting(const char *p, va_list &ap) {
|
||||
std::string result;
|
||||
while (*p != '\0') {
|
||||
if (*p != '%') {
|
||||
result += *p++;
|
||||
} else {
|
||||
++p;
|
||||
switch (*p++) {
|
||||
case '%': result += '%'; break;
|
||||
case 's': result += va_arg(ap, const char *); break;
|
||||
case 'd': result += std::to_string(va_arg(ap, int)); break;
|
||||
case 'S': result += va_arg(ap, std::string); break;
|
||||
case 'B': result += va_arg(ap, CharBlock).ToString(); break;
|
||||
default:
|
||||
if (*p == 'd') {
|
||||
if (p[-1] == 'z') {
|
||||
result += std::to_string(va_arg(ap, std::size_t));
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
if (p[-1] == 'j') {
|
||||
result += std::to_string(va_arg(ap, std::intmax_t));
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
char buffer[256];
|
||||
vsnprintf(buffer, sizeof buffer, p - 2, ap);
|
||||
result += buffer;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MessageFormattedText::MessageFormattedText(MessageFixedText text, ...)
|
||||
: isFatal_{text.isFatal()} {
|
||||
const char *p{text.text().begin()};
|
||||
|
@ -40,22 +90,25 @@ MessageFormattedText::MessageFormattedText(MessageFixedText text, ...)
|
|||
if (*text.text().end() != '\0') {
|
||||
// not NUL-terminated
|
||||
asString = text.text().NULTerminatedToString();
|
||||
p = asString.data();
|
||||
p = asString.c_str();
|
||||
}
|
||||
char buffer[256];
|
||||
va_list ap;
|
||||
va_start(ap, text);
|
||||
vsnprintf(buffer, sizeof buffer, p, ap);
|
||||
if (NeedsSpecialFormatting(p)) {
|
||||
string_ = SpecialFormatting(p, ap);
|
||||
} else {
|
||||
char buffer[256];
|
||||
vsnprintf(buffer, sizeof buffer, p, ap);
|
||||
string_ = buffer;
|
||||
}
|
||||
va_end(ap);
|
||||
string_ = buffer;
|
||||
}
|
||||
|
||||
std::string MessageExpectedText::ToString() const {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
[](const CharBlock &cb) {
|
||||
return MessageFormattedText(
|
||||
"expected '%s'"_err_en_US, cb.NULTerminatedToString().data())
|
||||
return MessageFormattedText("expected '%B'"_err_en_US, cb)
|
||||
.MoveString();
|
||||
},
|
||||
[](const SetOfChars &set) {
|
||||
|
@ -68,22 +121,21 @@ std::string MessageExpectedText::ToString() const {
|
|||
std::string s{expect.ToString()};
|
||||
if (s.size() == 1) {
|
||||
return MessageFormattedText(
|
||||
"expected end of line or '%s'"_err_en_US, s.data())
|
||||
"expected end of line or '%S'"_err_en_US, s)
|
||||
.MoveString();
|
||||
} else {
|
||||
return MessageFormattedText(
|
||||
"expected end of line or one of '%s'"_err_en_US, s.data())
|
||||
"expected end of line or one of '%S'"_err_en_US, s)
|
||||
.MoveString();
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string s{expect.ToString()};
|
||||
if (s.size() != 1) {
|
||||
return MessageFormattedText(
|
||||
"expected one of '%s'"_err_en_US, s.data())
|
||||
return MessageFormattedText("expected one of '%S'"_err_en_US, s)
|
||||
.MoveString();
|
||||
} else {
|
||||
return MessageFormattedText("expected '%s'"_err_en_US, s.data())
|
||||
return MessageFormattedText("expected '%S'"_err_en_US, s)
|
||||
.MoveString();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
|
||||
// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -43,7 +43,7 @@ void Parsing::Prescan(const std::string &path, Options options) {
|
|||
}
|
||||
if (sourceFile == nullptr) {
|
||||
ProvenanceRange range{allSources.AddCompilerInsertion(path)};
|
||||
messages_.Say(range, "%s"_err_en_US, fileError.str().data());
|
||||
messages_.Say(range, "%S"_err_en_US, fileError.str());
|
||||
return;
|
||||
}
|
||||
if (sourceFile->bytes() == 0) {
|
||||
|
|
|
@ -472,12 +472,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
|||
if (nameToken.empty()) {
|
||||
prescanner->Say(
|
||||
dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
|
||||
"#%s: missing name"_err_en_US, dirName.data());
|
||||
"#%S: missing name"_err_en_US, dirName);
|
||||
} else {
|
||||
j = dir.SkipBlanks(j + 1);
|
||||
if (j != tokens) {
|
||||
prescanner->Say(dir.GetIntervalProvenanceRange(j, tokens - j),
|
||||
"#%s: excess tokens at end of directive"_en_US, dirName.data());
|
||||
"#%S: excess tokens at end of directive"_en_US, dirName);
|
||||
}
|
||||
doThen = IsNameDefined(nameToken) == (dirName == "ifdef");
|
||||
}
|
||||
|
@ -534,12 +534,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
|||
} else if (dirName == "error") {
|
||||
prescanner->Say(
|
||||
dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
|
||||
"%s"_err_en_US, dir.ToString().data());
|
||||
"%S"_err_en_US, dir.ToString());
|
||||
} else if (dirName == "warning" || dirName == "comment" ||
|
||||
dirName == "note") {
|
||||
prescanner->Say(
|
||||
dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset),
|
||||
"%s"_en_US, dir.ToString().data());
|
||||
"%S"_en_US, dir.ToString());
|
||||
} else if (dirName == "include") {
|
||||
if (j == tokens) {
|
||||
prescanner->Say(
|
||||
|
@ -585,7 +585,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
|||
const SourceFile *included{allSources_.Open(include, &error)};
|
||||
if (included == nullptr) {
|
||||
prescanner->Say(dir.GetTokenProvenanceRange(dirOffset),
|
||||
"#include: %s"_err_en_US, error.str().data());
|
||||
"#include: %S"_err_en_US, error.str());
|
||||
} else if (included->bytes() > 0) {
|
||||
ProvenanceRange fileRange{
|
||||
allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
|
||||
|
@ -593,7 +593,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
|||
}
|
||||
} else {
|
||||
prescanner->Say(dir.GetTokenProvenanceRange(dirOffset),
|
||||
"#%s: unknown or unimplemented directive"_err_en_US, dirName.data());
|
||||
"#%S: unknown or unimplemented directive"_err_en_US, dirName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -654,8 +654,7 @@ void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
|
|||
}
|
||||
}
|
||||
}
|
||||
prescanner->Say(
|
||||
provenanceRange, "#%s: missing #endif"_err_en_US, dirName.data());
|
||||
prescanner->Say(provenanceRange, "#%S: missing #endif"_err_en_US, dirName);
|
||||
}
|
||||
|
||||
// Precedence level codes used here to accommodate mixed Fortran and C:
|
||||
|
@ -774,7 +773,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
|||
left = std::stoll(t, &consumed, 0 /*base to be detected*/);
|
||||
if (consumed < t.size()) {
|
||||
*error = Message{token.GetTokenProvenanceRange(opAt),
|
||||
"uninterpretable numeric constant '%s'"_err_en_US, t.data()};
|
||||
"Uninterpretable numeric constant '%S'"_err_en_US, t};
|
||||
return 0;
|
||||
}
|
||||
} else if (IsLegalIdentifierStart(t[0])) {
|
||||
|
|
|
@ -729,7 +729,7 @@ void Prescanner::FortranInclude(const char *firstQuote) {
|
|||
allSources.PopSearchPathDirectory();
|
||||
}
|
||||
if (included == nullptr) {
|
||||
Say(provenance, "INCLUDE: %s"_err_en_US, error.str().data());
|
||||
Say(provenance, "INCLUDE: %S"_err_en_US, error.str());
|
||||
} else if (included->bytes() > 0) {
|
||||
ProvenanceRange includeLineRange{
|
||||
provenance, static_cast<std::size_t>(p - lineStart_)};
|
||||
|
|
Loading…
Reference in a new issue