aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/FileCheck/FileCheck.cpp
diff options
context:
space:
mode:
authorMészáros Gergely <gergely.meszaros@intel.com>2025-06-27 22:39:07 +0200
committerGitHub <noreply@github.com>2025-06-27 22:39:07 +0200
commit98f7d756e334278e2e34177fa11e5a604d3b01ff (patch)
tree7f54ec79720800e665fca2d9f9e8de5519684963 /llvm/lib/FileCheck/FileCheck.cpp
parent526701f8dc541af71269bf88c3697177a20e8705 (diff)
downloadllvm-98f7d756e334278e2e34177fa11e5a604d3b01ff.zip
llvm-98f7d756e334278e2e34177fa11e5a604d3b01ff.tar.gz
llvm-98f7d756e334278e2e34177fa11e5a604d3b01ff.tar.bz2
[FileCheck] Improve printing variables with escapes (#145865)
Firstly fix FileCheck printing string variables double-escaped (first regex, then C-style). This is confusing because it is not clear if the printed value is the literal value or exactly how it is escaped, without looking at FileCheck's source code. Secondly, only escape when doing so makes it easier to read the value (when the string contains tabs, newlines or non-printable characters). When the variable value is escaped, make a note of it in the output too, in order to avoid confusion. The common case that is motivating this change is variables that contain windows style paths with backslashes. These were printed as `"C:\\\\Program Files\\\\MyApp\\\\file.txt"`. Now prefer to print them as `"C:\Program Files\MyApp\file.txt"`. Printing the value literally also makes it easier to search for variables in the output, since the user can just copy-paste it.
Diffstat (limited to 'llvm/lib/FileCheck/FileCheck.cpp')
-rw-r--r--llvm/lib/FileCheck/FileCheck.cpp54
1 files changed, 48 insertions, 6 deletions
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index bcca499..b79f6ec 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -264,7 +264,7 @@ BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {
: *RightFormat;
}
-Expected<std::string> NumericSubstitution::getResult() const {
+Expected<std::string> NumericSubstitution::getResultRegex() const {
assert(ExpressionPointer->getAST() != nullptr &&
"Substituting empty expression");
Expected<APInt> EvaluatedValue = ExpressionPointer->getAST()->eval();
@@ -274,7 +274,18 @@ Expected<std::string> NumericSubstitution::getResult() const {
return Format.getMatchingString(*EvaluatedValue);
}
-Expected<std::string> StringSubstitution::getResult() const {
+Expected<std::string> NumericSubstitution::getResultForDiagnostics() const {
+ // The "regex" returned by getResultRegex() is just a numeric value
+ // like '42', '0x2A', '-17', 'DEADBEEF' etc. This is already suitable for use
+ // in diagnostics.
+ Expected<std::string> Literal = getResultRegex();
+ if (!Literal)
+ return Literal;
+
+ return "\"" + std::move(*Literal) + "\"";
+}
+
+Expected<std::string> StringSubstitution::getResultRegex() const {
// Look up the value and escape it so that we can put it into the regex.
Expected<StringRef> VarVal = Context->getPatternVarValue(FromStr);
if (!VarVal)
@@ -282,6 +293,36 @@ Expected<std::string> StringSubstitution::getResult() const {
return Regex::escape(*VarVal);
}
+Expected<std::string> StringSubstitution::getResultForDiagnostics() const {
+ Expected<StringRef> VarVal = Context->getPatternVarValue(FromStr);
+ if (!VarVal)
+ return VarVal.takeError();
+
+ std::string Result;
+ Result.reserve(VarVal->size() + 2);
+ raw_string_ostream OS(Result);
+
+ OS << '"';
+ // Escape the string if it contains any characters that
+ // make it hard to read, such as non-printable characters (including all
+ // whitespace except space) and double quotes. These are the characters that
+ // are escaped by write_escaped(), except we do not include backslashes,
+ // because they are common in Windows paths and escaping them would make the
+ // output harder to read. However, when we do escape, backslashes are escaped
+ // as well, otherwise the output would be ambiguous.
+ const bool NeedsEscaping =
+ llvm::any_of(*VarVal, [](char C) { return !isPrint(C) || C == '"'; });
+ if (NeedsEscaping)
+ OS.write_escaped(*VarVal);
+ else
+ OS << *VarVal;
+ OS << '"';
+ if (NeedsEscaping)
+ OS << " (escaped value)";
+
+ return Result;
+}
+
bool Pattern::isValidVarNameStart(char C) { return C == '_' || isAlpha(C); }
Expected<Pattern::VariableProperties>
@@ -1106,7 +1147,7 @@ Pattern::MatchResult Pattern::match(StringRef Buffer,
Error Errs = Error::success();
for (const auto &Substitution : Substitutions) {
// Substitute and check for failure (e.g. use of undefined variable).
- Expected<std::string> Value = Substitution->getResult();
+ Expected<std::string> Value = Substitution->getResultRegex();
if (!Value) {
// Convert to an ErrorDiagnostic to get location information. This is
// done here rather than printMatch/printNoMatch since now we know which
@@ -1210,7 +1251,8 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
SmallString<256> Msg;
raw_svector_ostream OS(Msg);
- Expected<std::string> MatchedValue = Substitution->getResult();
+ Expected<std::string> MatchedValue =
+ Substitution->getResultForDiagnostics();
// Substitution failures are handled in printNoMatch().
if (!MatchedValue) {
consumeError(MatchedValue.takeError());
@@ -1218,8 +1260,8 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
}
OS << "with \"";
- OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
- OS.write_escaped(*MatchedValue) << "\"";
+ OS.write_escaped(Substitution->getFromString()) << "\" equal to ";
+ OS << *MatchedValue;
// We report only the start of the match/search range to suggest we are
// reporting the substitutions as set at the start of the match/search.