[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:
peter klausler 2019-05-03 11:51:14 -07:00
parent bc0732002e
commit 68209d4f87
5 changed files with 76 additions and 26 deletions

View file

@ -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);
}

View file

@ -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();
}
},

View file

@ -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) {

View file

@ -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])) {

View file

@ -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_)};