diff --git a/scripts/diagnostics.txt b/scripts/diagnostics.txt index b292e6e60..5a78a9132 100644 --- a/scripts/diagnostics.txt +++ b/scripts/diagnostics.txt @@ -697,6 +697,7 @@ warning negative-timing-limit NegativeTimingLimit "timing check limit value '{}' warning sign-conversion SignConversion "implicit conversion changes signedness from {} to {}" warning float-bool-conv FloatBoolConv "implicit conversion from floating point type {} to boolean value" warning int-bool-conv IntBoolConv "implicit conversion from {} to boolean value" +warning useless-cast UselessCast "useless cast from {} to the same type" subsystem Statements error ReturnNotInSubroutine "return statement is only valid inside task and function blocks" @@ -1054,7 +1055,8 @@ group default = { real-underflow real-overflow vector-overflow int-overflow unco group extra = { empty-member empty-stmt dup-import pointless-void-cast case-gen-none case-gen-dup unused-result format-real ignored-slice task-ignored width-trunc dup-attr event-const - ineffective-sign port-width-trunc constant-conversion float-bool-conv int-bool-conv } + ineffective-sign port-width-trunc constant-conversion float-bool-conv int-bool-conv + useless-cast } group pedantic = { empty-pattern implicit-net-port nonstandard-escape-code nonstandard-generate format-multibit-strength nonstandard-sys-func nonstandard-foreach nonstandard-dist } diff --git a/scripts/warning_docs.txt b/scripts/warning_docs.txt index d049c67ce..752e040e7 100644 --- a/scripts/warning_docs.txt +++ b/scripts/warning_docs.txt @@ -1117,3 +1117,12 @@ module m; initial if (i + 2) begin end // Did you mean i + 2 != 0 or something else? endmodule ``` + +-Wuseless-cast +An explicit cast converts an expression to the same type, so the cast does nothing. +``` +module m; + int i, j; + assign i = int'(j); +endmodule +``` diff --git a/source/ast/expressions/AssignmentExpressions.cpp b/source/ast/expressions/AssignmentExpressions.cpp index aa383b886..3b9e4d719 100644 --- a/source/ast/expressions/AssignmentExpressions.cpp +++ b/source/ast/expressions/AssignmentExpressions.cpp @@ -805,6 +805,7 @@ Expression& ConversionExpression::fromSyntax(Compilation& compilation, if (!Bitstream::checkClassAccess(*type, context, targetExpr.sourceRange)) { return badExpr(compilation, result()); } + if (operand.kind == ExpressionKind::Streaming) { if (!Bitstream::isBitstreamCast(*type, operand.as())) { @@ -827,6 +828,11 @@ Expression& ConversionExpression::fromSyntax(Compilation& compilation, return *result(ConversionKind::BitstreamCast); } + if (type->isMatching(*operand.type)) { + context.addDiag(diag::UselessCast, syntax.apostrophe.location()) + << *operand.type << targetExpr.sourceRange << operand.sourceRange; + } + return *result(); } diff --git a/tests/unittests/ast/EvalTests.cpp b/tests/unittests/ast/EvalTests.cpp index c84796e9a..44dea009a 100644 --- a/tests/unittests/ast/EvalTests.cpp +++ b/tests/unittests/ast/EvalTests.cpp @@ -931,7 +931,7 @@ TEST_CASE("Dynamic string ops") { CHECK(session.eval("{\"Hi\",str2}").str() == "Hiaaaaa"); CHECK(session.eval("str2 = {\"Hi\", \"Bye\"}").str() == "HiBye"); - CHECK(session.eval("string'(str1)").str() == "a"); + CHECK(session.eval("str1").str() == "a"); session.eval("byte ba[] = \"asdf\";"); CHECK(session.eval("string'(ba)").str() == "asdf"); diff --git a/tests/unittests/ast/MemberTests.cpp b/tests/unittests/ast/MemberTests.cpp index c734de2ab..3fdc82815 100644 --- a/tests/unittests/ast/MemberTests.cpp +++ b/tests/unittests/ast/MemberTests.cpp @@ -2065,7 +2065,7 @@ module m(input [4:0] a, output [4:0] b, z[6], output [5:0] l, I.m foo, I bar); if (k < 2) (a => z[1]) = 1; if (1 < 2) (a => z[2]) = 1; - if (int'(g) == 1) (a => z[3]) = 1; + if (byte'(g) == 1) (a => z[3]) = 1; if (+g == 1) (a => z[4]) = 1; if (g inside { 1, 2 }) (a => z[5]) = 1; diff --git a/tests/unittests/ast/WarningTests.cpp b/tests/unittests/ast/WarningTests.cpp index 0f623f47b..bda1b2079 100644 --- a/tests/unittests/ast/WarningTests.cpp +++ b/tests/unittests/ast/WarningTests.cpp @@ -769,3 +769,23 @@ endmodule CHECK(diags[0].code == diag::FloatBoolConv); CHECK(diags[1].code == diag::IntBoolConv); } + +TEST_CASE("Useless cast warnings") { + auto tree = SyntaxTree::fromText(R"( +module m; + int i, j; + assign i = int'(j); + + logic [3:0] k, l; + assign k = 4'(l); +endmodule +)"); + + Compilation compilation; + compilation.addSyntaxTree(tree); + + auto& diags = compilation.getAllDiagnostics(); + REQUIRE(diags.size() == 2); + CHECK(diags[0].code == diag::UselessCast); + CHECK(diags[1].code == diag::UselessCast); +}