From 5ca62de1673935e367d476ab8eea39eb3606bdd3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 29 Apr 2025 19:27:38 -0400 Subject: [PATCH] Fix filename backslash escapes in C code (#5947). --- Changes | 1 + src/V3EmitXml.cpp | 4 ++-- src/V3FileLine.h | 2 ++ src/V3PreLex.l | 2 +- src/V3String.cpp | 2 +- src/V3String.h | 6 +++--- src/V3Task.cpp | 4 ++-- src/V3Timing.cpp | 2 +- src/V3Waiver.cpp | 4 ++-- test_regress/t/t_pp_line.out | 20 ++++++++++++-------- test_regress/t/t_pp_line.v | 10 ++++++---- test_regress/t/t_stop_winos_bad.out | 4 ++++ test_regress/t/t_stop_winos_bad.py | 18 ++++++++++++++++++ test_regress/t/t_stop_winos_bad.v | 17 +++++++++++++++++ 14 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 test_regress/t/t_stop_winos_bad.out create mode 100755 test_regress/t/t_stop_winos_bad.py create mode 100644 test_regress/t/t_stop_winos_bad.v 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