From 3eb0ff8aa0005b93441a64227ce0e8e6740398f8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 16 Nov 2019 15:18:57 -0500 Subject: [PATCH] Detect wrong number of args on method calls. --- src/V3Width.cpp | 37 +++++++++++++++++--- test_regress/t/t_string_type_methods_bad.out | 13 +++++++ test_regress/t/t_string_type_methods_bad.pl | 18 ++++++++++ test_regress/t/t_string_type_methods_bad.v | 19 ++++++++++ 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 test_regress/t/t_string_type_methods_bad.out create mode 100755 test_regress/t/t_string_type_methods_bad.pl create mode 100644 test_regress/t/t_string_type_methods_bad.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index d55511229..9e6bd34f8 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1646,6 +1646,28 @@ private: <<"' which is a '"<fromp()->dtypep()->prettyTypeName()<<"'"); } } + void methodOkArguments(AstMethodSel* nodep, int minArg, int maxArg) { + int narg = 0; + for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) ++narg; + bool ok = (narg >= minArg) && (narg <= maxArg); + if (!ok) { + nodep->v3error("The "<prettyName() + <<" method does not match its requiring "<addPinsp(new AstArg(nodep->fileline(), "", + new AstConst(nodep->fileline(), 0))); + } + for (; narg > maxArg; --narg) { + AstNode* argp = nodep->pinsp(); + while (argp->nextp()) argp = argp->nextp(); + argp->unlinkFrBack(); argp->deleteTree(); VL_DANGLING(argp); + } + } + } + void methodCallEnum(AstMethodSel* nodep, AstEnumDType* adtypep) { // Method call on enum without following parenthesis, e.g. "ENUM.next" // Convert this into a method call, and let that visitor figure out what to do next @@ -1655,7 +1677,7 @@ private: || nodep->name() == "last") { // Constant value AstConst* newp = NULL; - if (nodep->pinsp()) nodep->v3error("Arguments passed to enum.num method, but it does not take arguments"); + methodOkArguments(nodep, 0, 0); if (nodep->name() == "num") { int items = 0; for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items; @@ -1685,8 +1707,8 @@ private: else if (nodep->name() == "prev") attrType = AstAttrType::ENUM_PREV; else nodep->v3fatalSrc("Bad case"); - if (nodep->pinsp() && nodep->name() == "name") { - nodep->v3error("Arguments passed to enum.name method, but it does not take arguments"); + if (nodep->name() == "name") { + methodOkArguments(nodep, 0, 0); } else if (nodep->pinsp() && !(VN_IS(VN_CAST(nodep->pinsp(), Arg)->exprp(), Const) && VN_CAST(VN_CAST(nodep->pinsp(), Arg)->exprp(), Const)->toUInt() == 1 @@ -1735,7 +1757,7 @@ private: else if (nodep->name() == "xor") methodId = ARRAY_XOR; if (methodId) { - if (nodep->pinsp()) nodep->v3error("Arguments passed to array method, but it does not take arguments"); + methodOkArguments(nodep, 0, 0); FileLine* fl = nodep->fileline(); AstNode* newp = NULL; for (int i = 0; i < adtypep->elementsConst(); ++i) { @@ -1762,19 +1784,24 @@ private: // Method call on string if (nodep->name() == "len") { // Constant value - if (nodep->pinsp()) nodep->v3error("Arguments passed to string.len method, but it does not take arguments"); + methodOkArguments(nodep, 0, 0); AstNode* newp = new AstLenN(nodep->fileline(), nodep->fromp()->unlinkFrBack()); nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); } else if (nodep->name() == "itoa") { + methodOkArguments(nodep, 1, 1); replaceWithSFormat(nodep, "%0d"); VL_DANGLING(nodep); } else if (nodep->name() == "hextoa") { + methodOkArguments(nodep, 1, 1); replaceWithSFormat(nodep, "%0x"); VL_DANGLING(nodep); } else if (nodep->name() == "octtoa") { + methodOkArguments(nodep, 1, 1); replaceWithSFormat(nodep, "%0o"); VL_DANGLING(nodep); } else if (nodep->name() == "bintoa") { + methodOkArguments(nodep, 1, 1); replaceWithSFormat(nodep, "%0b"); VL_DANGLING(nodep); } else if (nodep->name() == "realtoa") { + methodOkArguments(nodep, 1, 1); replaceWithSFormat(nodep, "%g"); VL_DANGLING(nodep); } else { nodep->v3error("Unsupported: built-in string method "<prettyNameQ()); diff --git a/test_regress/t/t_string_type_methods_bad.out b/test_regress/t/t_string_type_methods_bad.out new file mode 100644 index 000000000..92116ca77 --- /dev/null +++ b/test_regress/t/t_string_type_methods_bad.out @@ -0,0 +1,13 @@ +%Error: t/t_string_type_methods_bad.v:14: The 1 arguments passed to .len method does not match its requiring 0 arguments + : ... In instance t + i = s.len(0); + ^~~ +%Error: t/t_string_type_methods_bad.v:15: The 0 arguments passed to .itoa method does not match its requiring 1 arguments + : ... In instance t + s.itoa; + ^~~~ +%Error: t/t_string_type_methods_bad.v:16: The 3 arguments passed to .itoa method does not match its requiring 1 arguments + : ... In instance t + s.itoa(1,2,3); + ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_string_type_methods_bad.pl b/test_regress/t/t_string_type_methods_bad.pl new file mode 100755 index 000000000..3361369bf --- /dev/null +++ b/test_regress/t/t_string_type_methods_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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. + +scenarios(simulator => 1); + +compile( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_string_type_methods_bad.v b/test_regress/t/t_string_type_methods_bad.v new file mode 100644 index 000000000..851cc0338 --- /dev/null +++ b/test_regress/t/t_string_type_methods_bad.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2014 by Wilson Snyder. + +module t (/*AUTOARG*/); + + string s; + integer i; + + // Check constification + initial begin + s="1234"; + i = s.len(0); // BAD + s.itoa; // BAD + s.itoa(1,2,3); // BAD + end + +endmodule