From 33e9c020799a99f117a3dd722de53fea6ab58a27 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 17 Apr 2026 09:33:13 +0200 Subject: [PATCH 1/4] C#: Add more tests for `RedundantToStringCall.ql` --- .../RedundantToStringCall/RedundantToStringCall.cs | 14 +++++++++++--- .../RedundantToStringCall.expected | 9 ++++++--- .../RedundantToStringCall.qlref | 4 +++- .../RedundantToStringCallBad.cs | 2 +- 4 files changed, 21 insertions(+), 8 deletions(-) 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..d055307d5398 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()); // $ SPURIOUS: Alert + + Console.WriteLine($"Hello: {base.ToString()}"); // $ SPURIOUS: Alert } } 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..aade48834869 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,7 @@ -| 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. | +| RedundantToStringCall.cs:8:27:8:38 | call to method ToString | Redundant call to 'ToString' on a String object. | +| RedundantToStringCall.cs:11:37:11:48 | call to method ToString | Redundant call to 'ToString' on a String object. | +| RedundantToStringCall.cs:14:39:14:50 | call to method ToString | Redundant call to 'ToString' on a String object. | +| RedundantToStringCall.cs:18:19:18:30 | call to method ToString | Redundant call to 'ToString' on a String object. | +| RedundantToStringCall.cs:20:23:20:34 | call to method ToString | Redundant call to 'ToString' on a String object. | +| RedundantToStringCall.cs:22:37:22:51 | 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. | 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 } } From 426962e348976d4d90afa0561cb56b63296e9f32 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 17 Apr 2026 09:37:19 +0200 Subject: [PATCH 2/4] C#: Fix FPs in `RedundantToStringCall.ql` --- csharp/ql/lib/semmle/code/csharp/commons/Strings.qll | 2 +- csharp/ql/src/Useless code/RedundantToStringCall.ql | 3 ++- .../RedundantToStringCall/RedundantToStringCall.cs | 4 ++-- .../RedundantToStringCall/RedundantToStringCall.expected | 2 -- 4 files changed, 5 insertions(+), 6 deletions(-) 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..3b531f4d097d 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 + mc.getTarget() instanceof ToStringMethod and + not mc.getQualifier() instanceof BaseAccess select mc, "Redundant call to 'ToString' on a String object." 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 d055307d5398..01c770d105b4 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs @@ -17,8 +17,8 @@ public void M(object o) var sb = new StringBuilder(); sb.Append(o.ToString()); // $ Alert sb.Append(o); // GOOD - sb.AppendLine(o.ToString()); // $ SPURIOUS: Alert + sb.AppendLine(o.ToString()); // GOOD - Console.WriteLine($"Hello: {base.ToString()}"); // $ SPURIOUS: Alert + 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 aade48834869..ecfc616335a0 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected @@ -2,6 +2,4 @@ | RedundantToStringCall.cs:11:37:11:48 | call to method ToString | Redundant call to 'ToString' on a String object. | | RedundantToStringCall.cs:14:39:14:50 | call to method ToString | Redundant call to 'ToString' on a String object. | | RedundantToStringCall.cs:18:19:18:30 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCall.cs:20:23:20:34 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCall.cs:22:37:22:51 | 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. | From 0235df87582d56ff8b7f22824f89704a77340423 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 17 Apr 2026 13:15:10 +0200 Subject: [PATCH 3/4] C#: Improve alert message for `RedundantToStringCall.ql` --- csharp/ql/src/Useless code/RedundantToStringCall.ql | 2 +- .../RedundantToStringCall.expected | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/csharp/ql/src/Useless code/RedundantToStringCall.ql b/csharp/ql/src/Useless code/RedundantToStringCall.ql index 3b531f4d097d..e29e071b4d9b 100644 --- a/csharp/ql/src/Useless code/RedundantToStringCall.ql +++ b/csharp/ql/src/Useless code/RedundantToStringCall.ql @@ -20,4 +20,4 @@ where mc instanceof ImplicitToStringExpr and mc.getTarget() instanceof ToStringMethod and not mc.getQualifier() instanceof BaseAccess -select mc, "Redundant call to 'ToString' on a String object." +select mc, "Redundant call to 'ToString'." 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 ecfc616335a0..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,5 +1,5 @@ -| RedundantToStringCall.cs:8:27:8:38 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCall.cs:11:37:11:48 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCall.cs:14:39:14:50 | call to method ToString | Redundant call to 'ToString' on a String object. | -| RedundantToStringCall.cs:18:19:18:30 | 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'. | From 7bfdfbefa9438bfd5180c84444e54517e7271c6c Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 17 Apr 2026 13:57:08 +0200 Subject: [PATCH 4/4] Add change note --- csharp/ql/src/change-notes/2026-04-17-useless-to-string.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 csharp/ql/src/change-notes/2026-04-17-useless-to-string.md 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.