diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll index 678a1f928163..3fde913358b3 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll @@ -29,7 +29,7 @@ class ImplicitToStringExpr extends Expr { m = p.getCallable() | m = any(SystemTextStringBuilderClass c).getAMethod() and - m.getName().regexpMatch("Append(Line)?") and + m.getName() = "Append" and not p.getType() instanceof ArrayType or p instanceof StringFormatItemParameter and diff --git a/csharp/ql/src/Useless code/RedundantToStringCall.ql b/csharp/ql/src/Useless code/RedundantToStringCall.ql index 55e7056e9a08..e29e071b4d9b 100644 --- a/csharp/ql/src/Useless code/RedundantToStringCall.ql +++ b/csharp/ql/src/Useless code/RedundantToStringCall.ql @@ -18,5 +18,6 @@ import semmle.code.csharp.frameworks.System from MethodCall mc where mc instanceof ImplicitToStringExpr and - mc.getTarget() instanceof ToStringMethod -select mc, "Redundant call to 'ToString' on a String object." + mc.getTarget() instanceof ToStringMethod and + not mc.getQualifier() instanceof BaseAccess +select mc, "Redundant call to 'ToString'." diff --git a/csharp/ql/src/change-notes/2026-04-17-useless-to-string.md b/csharp/ql/src/change-notes/2026-04-17-useless-to-string.md new file mode 100644 index 000000000000..9b4c81378c91 --- /dev/null +++ b/csharp/ql/src/change-notes/2026-04-17-useless-to-string.md @@ -0,0 +1,7 @@ +--- +category: minorAnalysis +--- +* The query `cs/useless-tostring-call` has been updated to avoid false + positive results in calls to `StringBuilder.AppendLine` and calls of + the form `base.ToString()`. Moreover, the alert message has been + made more precise. diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs index 981b36002663..01c770d105b4 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs @@ -1,16 +1,24 @@ using System; +using System.Text; class RedundantToString { public void M(object o) { - Console.WriteLine(o.ToString()); // BAD + Console.WriteLine(o.ToString()); // $ Alert Console.WriteLine(o); // GOOD - Console.WriteLine($"Hello: {o.ToString()}"); // BAD + Console.WriteLine($"Hello: {o.ToString()}"); // $ Alert Console.WriteLine($"Hello: {o}"); // GOOD - Console.WriteLine("Hello: " + o.ToString()); // BAD + Console.WriteLine("Hello: " + o.ToString()); // $ Alert Console.WriteLine("Hello: " + o); // GOOD + + var sb = new StringBuilder(); + sb.Append(o.ToString()); // $ Alert + sb.Append(o); // GOOD + sb.AppendLine(o.ToString()); // GOOD + + Console.WriteLine($"Hello: {base.ToString()}"); // GOOD } } diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected index 28775378f049..b81421da571f 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected @@ -1,4 +1,5 @@ -| RedundantToStringCall.cs:7:27:7:38 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCall.cs:10:37:10:48 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCall.cs:13:39:13:50 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCallBad.cs:7:45:7:56 | call to method ToString | Redundant call to 'ToString' on a String object. | +| RedundantToStringCall.cs:8:27:8:38 | call to method ToString | Redundant call to 'ToString'. | +| RedundantToStringCall.cs:11:37:11:48 | call to method ToString | Redundant call to 'ToString'. | +| RedundantToStringCall.cs:14:39:14:50 | call to method ToString | Redundant call to 'ToString'. | +| RedundantToStringCall.cs:18:19:18:30 | call to method ToString | Redundant call to 'ToString'. | +| RedundantToStringCallBad.cs:7:45:7:56 | call to method ToString | Redundant call to 'ToString'. | diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref index c0ee8dd0ec7d..86bf1476007d 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref @@ -1 +1,3 @@ -Useless code/RedundantToStringCall.ql \ No newline at end of file +query: Useless code/RedundantToStringCall.ql +postprocess: + - utils/test/InlineExpectationsTestQuery.ql diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs index d6d043f23762..ed5cd137a855 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs @@ -4,6 +4,6 @@ class Bad { static string Hello(object o) { - return string.Format("Hello, {0}!", o.ToString()); + return string.Format("Hello, {0}!", o.ToString()); // $ Alert } }