diff --git a/Changes b/Changes
index acda7d5ef..eba22c063 100644
--- a/Changes
+++ b/Changes
@@ -14,6 +14,7 @@ Verilator 5.037 devel
**Other:**
* Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu]
+* Fix filename backslash escapes in C code (#5947).
* Fix sign extension of signed compared with unsigned case items (#5968).
diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp
index 52cf00368..332ca6f06 100644
--- a/src/V3EmitXml.cpp
+++ b/src/V3EmitXml.cpp
@@ -355,8 +355,8 @@ public:
// Xml output
m_os << "\n";
for (const FileLine* ifp : m_nodeModules) {
- m_os << "filenameLetters() << "\" filename=\"" << ifp->filename()
- << "\" language=\"" << ifp->language().ascii() << "\"/>\n";
+ m_os << "filenameLetters() << "\" filename=\""
+ << ifp->filenameEsc() << "\" language=\"" << ifp->language().ascii() << "\"/>\n";
}
m_os << "\n";
}
diff --git a/src/V3FileLine.h b/src/V3FileLine.h
index 13bbb8472..fe3a6d986 100644
--- a/src/V3FileLine.h
+++ b/src/V3FileLine.h
@@ -268,6 +268,8 @@ public:
string asciiLineCol() const;
int filenameno() const VL_MT_SAFE { return m_filenameno; }
string filename() const VL_MT_SAFE { return singleton().numberToName(filenameno()); }
+ // Filename with C string escapes
+ string filenameEsc() const VL_MT_SAFE { return VString::quoteBackslash(filename()); }
bool filenameIsGlobal() const VL_MT_SAFE {
return (filename() == commandLineFilename() || filename() == builtInFilename());
}
diff --git a/src/V3PreLex.l b/src/V3PreLex.l
index 7f71af112..eaa899ea3 100644
--- a/src/V3PreLex.l
+++ b/src/V3PreLex.l
@@ -237,7 +237,7 @@ bom [\357\273\277]
return VP_TEXT; }
"`__FILE__" { FL_FWDC;
static string rtnfile;
- rtnfile = '"'; rtnfile += LEXP->curFilelinep()->filename();
+ rtnfile = '"'; rtnfile += LEXP->curFilelinep()->filenameEsc();
rtnfile += '"'; yytext = (char*)rtnfile.c_str(); yyleng = rtnfile.length();
return VP_STRING; }
"`__LINE__" { FL_FWDC;
diff --git a/src/V3String.cpp b/src/V3String.cpp
index fc8c35e2c..05405037b 100644
--- a/src/V3String.cpp
+++ b/src/V3String.cpp
@@ -91,7 +91,7 @@ string VString::upcase(const string& str) VL_PURE {
return result;
}
-string VString::quoteAny(const string& str, char tgt, char esc) {
+string VString::quoteAny(const string& str, char tgt, char esc) VL_PURE {
string result;
for (const char c : str) {
if (c == tgt) result += esc;
diff --git a/src/V3String.h b/src/V3String.h
index 8e1e97b3e..1c70ec96e 100644
--- a/src/V3String.h
+++ b/src/V3String.h
@@ -94,11 +94,11 @@ public:
// Convert string to upper case (toupper)
static string upcase(const string& str) VL_PURE;
// Insert esc just before tgt
- static string quoteAny(const string& str, char tgt, char esc);
+ static string quoteAny(const string& str, char tgt, char esc) VL_PURE;
// Replace any \'s with \\ (two consecutive backslashes)
- static string quoteBackslash(const string& str) { return quoteAny(str, '\\', '\\'); }
+ static string quoteBackslash(const string& str) VL_PURE { return quoteAny(str, '\\', '\\'); }
// Replace any %'s with %%
- static string quotePercent(const string& str) { return quoteAny(str, '%', '%'); }
+ static string quotePercent(const string& str) VL_PURE { return quoteAny(str, '%', '%'); }
// Replace any %%'s with %
static string dequotePercent(const string& str);
// Surround a raw string by double quote and escape if necessary
diff --git a/src/V3Task.cpp b/src/V3Task.cpp
index 14cf382bc..1cfe49025 100644
--- a/src/V3Task.cpp
+++ b/src/V3Task.cpp
@@ -685,8 +685,8 @@ class TaskVisitor final : public VNVisitor {
UASSERT_OBJ(snp, refp, "Missing scoping context");
ccallp->addArgsp(snp);
// __Vfilenamep
- ccallp->addArgsp(new AstCExpr{refp->fileline(),
- "\"" + refp->fileline()->filename() + "\"", 64, true});
+ ccallp->addArgsp(new AstCExpr{
+ refp->fileline(), "\"" + refp->fileline()->filenameEsc() + "\"", 64, true});
// __Vlineno
ccallp->addArgsp(new AstConst(refp->fileline(), refp->fileline()->lineno()));
}
diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp
index 09092095b..0d6be45b7 100644
--- a/src/V3Timing.cpp
+++ b/src/V3Timing.cpp
@@ -654,7 +654,7 @@ class TimingControlVisitor final : public VNVisitor {
void addDebugInfo(AstCMethodHard* const methodp) const {
if (v3Global.opt.protectIds()) return;
FileLine* const flp = methodp->fileline();
- AstCExpr* const ap = new AstCExpr{flp, '"' + flp->filename() + '"', 0};
+ AstCExpr* const ap = new AstCExpr{flp, '"' + flp->filenameEsc() + '"', 0};
ap->dtypeSetString();
methodp->addPinsp(ap);
AstCExpr* const bp = new AstCExpr{flp, cvtToStr(flp->lineno()), 0};
diff --git a/src/V3Waiver.cpp b/src/V3Waiver.cpp
index 8578133ad..41ef8c44b 100644
--- a/src/V3Waiver.cpp
+++ b/src/V3Waiver.cpp
@@ -70,8 +70,8 @@ void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, cons
}
std::stringstream entry;
- entry << "lint_off -rule " << errorCode.ascii() << " -file \"*" << filename << "\" -match \""
- << trimmsg << "\"";
+ entry << "lint_off -rule " << errorCode.ascii() << " -file \"*"
+ << VString::quoteBackslash(filename) << "\" -match \"" << trimmsg << "\"";
s_waiverList.push_back(entry.str());
}
diff --git a/test_regress/t/t_pp_line.out b/test_regress/t/t_pp_line.out
index a3e32422f..5dd46780a 100644
--- a/test_regress/t/t_pp_line.out
+++ b/test_regress/t/t_pp_line.out
@@ -1,16 +1,20 @@
--Info: some file:100:1: aaaaaaaa
+-Info: some file:100:1: aaaaaaaa file='some file'
: ... note: In instance 't'
- 100 | $info("aaaaaaaa");
+ 100 | $info("aaaaaaaa file='%s'", "some file");
| ^~~~~
--Info: some file:101:1: bbbbbbbb
+-Info: some file:101:1: bbbbbbbb file='some file'
: ... note: In instance 't'
- 101 | $info("bbbbbbbb");
+ 101 | $info("bbbbbbbb file='%s'", "some file");
| ^~~~~
--Info: somefile.v:200:1: cccccccc
+-Info: somefile.v:200:1: cccccccc file='somefile.v'
: ... note: In instance 't'
- 200 | $info("cccccccc");
+ 200 | $info("cccccccc file='%s'", "somefile.v");
| ^~~~~
--Info: /a/somefile.v:300:1: dddddddd
+-Info: /a/somefile.v:300:1: dddddddd file='/a/somefile.v'
: ... note: In instance 't'
- 300 | $info("dddddddd");
+ 300 | $info("dddddddd file='%s'", "/a/somefile.v");
+ | ^~~~~
+-Info: C:\a\somefile.v:400:1: eeeeeeee file='C:\a\somefile.v'
+ : ... note: In instance 't'
+ 400 | $info("eeeeeeee file='%s'", "C:\\a\\somefile.v");
| ^~~~~
diff --git a/test_regress/t/t_pp_line.v b/test_regress/t/t_pp_line.v
index ac991e550..efc22d470 100644
--- a/test_regress/t/t_pp_line.v
+++ b/test_regress/t/t_pp_line.v
@@ -6,10 +6,12 @@
module t;
`line 100 "some file" 0
-$info("aaaaaaaa");
-$info("bbbbbbbb");
+$info("aaaaaaaa file='%s'", `__FILE__);
+$info("bbbbbbbb file='%s'", `__FILE__);
`line 200 "somefile.v" 0
-$info("cccccccc");
+$info("cccccccc file='%s'", `__FILE__);
`line 300 "/a/somefile.v" 0
-$info("dddddddd");
+$info("dddddddd file='%s'", `__FILE__);
+`line 400 "C:\\a\\somefile.v" 0
+$info("eeeeeeee file='%s'", `__FILE__);
endmodule
diff --git a/test_regress/t/t_stop_winos_bad.out b/test_regress/t/t_stop_winos_bad.out
new file mode 100644
index 000000000..8f72dc0d5
--- /dev/null
+++ b/test_regress/t/t_stop_winos_bad.out
@@ -0,0 +1,4 @@
+Intentional stop
+Filename 'C:\some\windows\path\t_stop_winos_bad.v' Length = 39
+%Error: C:\some\windows\path\t_stop_winos_bad.v:14: Verilog $stop
+Aborting...
diff --git a/test_regress/t/t_stop_winos_bad.py b/test_regress/t/t_stop_winos_bad.py
new file mode 100755
index 000000000..12c7421fd
--- /dev/null
+++ b/test_regress/t/t_stop_winos_bad.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+# DESCRIPTION: Verilator: Verilog Test driver/expect definition
+#
+# Copyright 2024 by Wilson Snyder. This program is free software; you
+# can redistribute it and/or modify it under the terms of either the GNU
+# Lesser General Public License Version 3 or the Perl Artistic License
+# Version 2.0.
+# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+
+import vltest_bootstrap
+
+test.scenarios('simulator')
+
+test.compile(verilator_flags2=['-no-MMD'])
+
+test.execute(fails=True, expect_filename=test.golden_filename)
+
+test.passes()
diff --git a/test_regress/t/t_stop_winos_bad.v b/test_regress/t/t_stop_winos_bad.v
new file mode 100644
index 000000000..1b9cddd79
--- /dev/null
+++ b/test_regress/t/t_stop_winos_bad.v
@@ -0,0 +1,17 @@
+// DESCRIPTION: Verilator: Verilog Test module
+//
+// This file ONLY is placed under the Creative Commons Public Domain, for
+// any use, without warranty, 2019 by Wilson Snyder.
+// SPDX-License-Identifier: CC0-1.0
+
+`line 7 "C:\\some\\windows\\path\\t_stop_winos_bad.v" 0
+
+module t;
+ localparam string FILENAME = `__FILE__;
+ initial begin
+ $write("Intentional stop\n");
+ // Print length to make sure \\ counts as 1 character
+ $write("Filename '%s' Length = %0d\n", FILENAME, FILENAME.len());
+ $stop;
+ end
+endmodule