diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index 9241528eb752..a5cb6e316f41 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -129,7 +129,7 @@ public static void CreateDeferred(Context cx, ExpressionSyntax node, IExpression cx.PopulateLater(() => Create(cx, node, parent, child)); } - private static bool ContainsPattern(SyntaxNode node) => + protected static bool ContainsPattern(SyntaxNode node) => node is PatternSyntax || node is VariableDesignationSyntax || node.ChildNodes().Any(ContainsPattern); /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs index 924382a55507..5a2cad8a520a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs @@ -129,7 +129,13 @@ public Location Location public ExprKind Kind { get; set; } = ExprKind.UNKNOWN; - public bool IsCompilerGenerated { get; set; } + public bool IsCompilerGenerated { get; init; } + + /// + /// Whether the expression should have a compiler generated `ToString` call added, + /// if there is no suitable implicit cast. + /// + public bool ImplicitToString { get; private set; } public ExpressionNodeInfo SetParent(IExpressionParentEntity parent, int child) { @@ -157,6 +163,12 @@ public ExpressionNodeInfo SetNode(ExpressionSyntax node) return this; } + public ExpressionNodeInfo SetImplicitToString(bool value) + { + ImplicitToString = value; + return this; + } + private SymbolInfo cachedSymbolInfo; public SymbolInfo SymbolInfo diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs index 3cdfb32277b2..eeb1b9ba63b2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs @@ -14,11 +14,35 @@ private Binary(ExpressionNodeInfo info) public static Expression Create(ExpressionNodeInfo info) => new Binary(info).TryPopulate(); + private Expression CreateChild(Context cx, ExpressionSyntax node, int child) + { + // If this is a "+" expression we might need to wrap the child expressions + // in ToString calls + return Kind == ExprKind.ADD + ? ImplicitToString.Create(cx, node, this, child) + : Create(cx, node, this, child); + } + + /// + /// Creates an expression from a syntax node. + /// Inserts type conversion as required. + /// Population is deferred to avoid overflowing the stack. + /// + private void CreateDeferred(Context cx, ExpressionSyntax node, int child) + { + if (ContainsPattern(node)) + // Expressions with patterns should be created right away, as they may introduce + // local variables referenced in `LocalVariable::GetAlreadyCreated()` + CreateChild(cx, node, child); + else + cx.PopulateLater(() => CreateChild(cx, node, child)); + } + protected override void PopulateExpression(TextWriter trapFile) { OperatorCall(trapFile, Syntax); - CreateDeferred(Context, Syntax.Left, this, 0); - CreateDeferred(Context, Syntax.Right, this, 1); + CreateDeferred(Context, Syntax.Left, 0); + CreateDeferred(Context, Syntax.Right, 1); } private static ExprKind GetKind(Context cx, BinaryExpressionSyntax node) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs index b0508bf83b7e..c1ce5dcbfe99 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs @@ -156,6 +156,12 @@ convertedType.Symbol is IPointerTypeSymbol && return new ImplicitCast(info); } + if (info.ImplicitToString) + { + // x -> x.ToString() in "abc" + x + return ImplicitToString.Wrap(info); + } + // Default: Just create the expression without a conversion. return Factory.Create(info); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs new file mode 100644 index 000000000000..f424e98a7a51 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs @@ -0,0 +1,59 @@ +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Semmle.Extraction.CSharp.Util; +using Semmle.Extraction.Kinds; + + +namespace Semmle.Extraction.CSharp.Entities.Expressions +{ + internal sealed class ImplicitToString : Expression + { + /// + /// Gets the `ToString` method for the given type. + /// + private static IMethodSymbol? GetToStringMethod(ITypeSymbol? type) + { + return type? + .GetMembers() + .OfType() + .Where(method => + method.GetName() == "ToString" && + method.Parameters.Length == 0 + ) + .FirstOrDefault(); + } + + private ImplicitToString(ExpressionNodeInfo info, IMethodSymbol toString) : base(new ExpressionInfo(info.Context, AnnotatedTypeSymbol.CreateNotAnnotated(toString.ReturnType), info.Location, ExprKind.METHOD_INVOCATION, info.Parent, info.Child, isCompilerGenerated: true, info.ExprValue)) + { + Factory.Create(info.SetParent(this, -1)); + + var target = Method.Create(Context, toString); + Context.TrapWriter.Writer.expr_call(this, target); + } + + private static bool IsStringType(AnnotatedTypeSymbol? type) => + type.HasValue && type.Value.Symbol?.SpecialType == SpecialType.System_String; + + /// + /// Creates a new expression, adding a compiler generated `ToString` call if required. + /// + public static Expression Create(Context cx, ExpressionSyntax node, Expression parent, int child) + { + var info = new ExpressionNodeInfo(cx, node, parent, child); + return CreateFromNode(info.SetImplicitToString(IsStringType(parent.Type) && !IsStringType(info.Type))); + } + + /// + /// Wraps the resulting expression in a `ToString` call, if a suitable `ToString` method is available. + /// + public static Expression Wrap(ExpressionNodeInfo info) + { + if (GetToStringMethod(info.Type?.Symbol) is IMethodSymbol toString) + { + return new ImplicitToString(info, toString); + } + return Factory.Create(info); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs index 2dfe4976391d..bdf30bf6618f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs @@ -20,7 +20,7 @@ protected override void PopulateExpression(TextWriter trapFile) { case SyntaxKind.Interpolation: var interpolation = (InterpolationSyntax)c; - Create(Context, interpolation.Expression, this, child++); + ImplicitToString.Create(Context, interpolation.Expression, this, child++); break; case SyntaxKind.InterpolatedStringText: // Create a string literal diff --git a/csharp/ql/examples/snippets/ternary_conditional.ql b/csharp/ql/examples/snippets/ternary_conditional.ql index 22e26e71ea8f..009c56fb2afe 100644 --- a/csharp/ql/examples/snippets/ternary_conditional.ql +++ b/csharp/ql/examples/snippets/ternary_conditional.ql @@ -11,7 +11,7 @@ import csharp from ConditionalExpr e where - e.getThen().stripImplicitCasts() != e.getElse().stripImplicitCasts() and + e.getThen().stripImplicit() != e.getElse().stripImplicit() and not e.getThen().getType() instanceof NullType and not e.getElse().getType() instanceof NullType select e diff --git a/csharp/ql/lib/change-notes/2025-01-09-implicit-to-string.md b/csharp/ql/lib/change-notes/2025-01-09-implicit-to-string.md new file mode 100644 index 000000000000..2956898841ee --- /dev/null +++ b/csharp/ql/lib/change-notes/2025-01-09-implicit-to-string.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added extractor support for extracting implicit `ToString` calls in binary `+` expressions and string interpolation expressions. diff --git a/csharp/ql/lib/semmle/code/csharp/PrintAst.qll b/csharp/ql/lib/semmle/code/csharp/PrintAst.qll index 281e157975ab..fd4bf1cb86b0 100644 --- a/csharp/ql/lib/semmle/code/csharp/PrintAst.qll +++ b/csharp/ql/lib/semmle/code/csharp/PrintAst.qll @@ -32,7 +32,9 @@ private predicate shouldPrint(Element e, Location l) { } private predicate isImplicitExpression(ControlFlowElement element) { - element.(Expr).isImplicit() and + // Include compiler generated cast expressions and `ToString` calls if + // they wrap actual source expressions. + element.(Expr).stripImplicit().isImplicit() and not element instanceof CastExpr and not element.(OperatorCall).getTarget() instanceof ImplicitConversionOperator and not element instanceof ElementInitializer diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll index 1d6e67aa488d..508ba0e5e87d 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll @@ -43,7 +43,7 @@ predicate isConstantComparison(ComparisonOperation co, boolean b) { private module ConstantComparisonOperation { private import semmle.code.csharp.commons.ComparisonTest - private SimpleType convertedType(Expr expr) { result = expr.stripImplicitCasts().getType() } + private SimpleType convertedType(Expr expr) { result = expr.stripImplicit().getType() } private int maxValue(Expr expr) { if convertedType(expr) instanceof IntegralType and exists(expr.getValue()) diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll index 4e007d61737e..908d1c2fb5ab 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll @@ -44,11 +44,11 @@ class ImplicitToStringExpr extends Expr { ) or exists(AddExpr add, Expr o | o = add.getAnOperand() | - o.stripImplicitCasts().getType() instanceof StringType and - this = add.getOtherOperand(o) + o.stripImplicit().getType() instanceof StringType and + this = add.getOtherOperand(o).stripImplicit() ) or - this = any(InterpolatedStringExpr ise).getAnInsert() + this = any(InterpolatedStringExpr ise).getAnInsert().stripImplicit() } } diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll index 080b7d5c7325..7de6c30eb13c 100644 --- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll @@ -857,7 +857,7 @@ private module Internal { private predicate hasDynamicArg(int i, Type argumentType) { exists(Expr argument | argument = this.getArgument(i) and - argument.stripImplicitCasts().getType() instanceof DynamicType and + argument.stripImplicit().getType() instanceof DynamicType and argumentType = getAPossibleType(argument, _) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll index be4577d760eb..eecbc35900aa 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll @@ -281,6 +281,10 @@ class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invoca result = this.getArgument(i - 1) else result = this.getArgument(i) } + + override Expr stripImplicit() { + if this.isImplicit() then result = this.getQualifier().stripImplicit() else result = this + } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll index 935162523a16..85676bbd2701 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll @@ -82,10 +82,18 @@ class Expr extends ControlFlowElement, @expr { Expr stripCasts() { result = this } /** + * DEPRECATED: Use `stripImplicit` instead. + * * Gets an expression that is the result of stripping (recursively) all * implicit casts from this expression, if any. */ - Expr stripImplicitCasts() { result = this } + deprecated Expr stripImplicitCasts() { result = this.stripImplicit() } + + /** + * Gets an expression that is the result of stripping (recursively) all + * implicit casts and implicit ToString calls from this expression, if any. + */ + Expr stripImplicit() { result = this } /** * Gets the explicit parameter name used to pass this expression as an @@ -714,8 +722,8 @@ class Cast extends Expr { override Expr stripCasts() { result = this.getExpr().stripCasts() } - override Expr stripImplicitCasts() { - if this.isImplicit() then result = this.getExpr().stripImplicitCasts() else result = this + override Expr stripImplicit() { + if this.isImplicit() then result = this.getExpr().stripImplicit() else result = this } } diff --git a/csharp/ql/src/Likely Bugs/Dynamic/BadDynamicCall.ql b/csharp/ql/src/Likely Bugs/Dynamic/BadDynamicCall.ql index 6044ebbbb5eb..2efac6773f72 100644 --- a/csharp/ql/src/Likely Bugs/Dynamic/BadDynamicCall.ql +++ b/csharp/ql/src/Likely Bugs/Dynamic/BadDynamicCall.ql @@ -44,11 +44,11 @@ abstract class BadDynamicCall extends DynamicExpr { ultimateSsaDef = ssaDef.getAnUltimateDefinition() | ultimateSsaDef.getADefinition() = - any(AssignableDefinition def | source = def.getSource().stripImplicitCasts()) + any(AssignableDefinition def | source = def.getSource().stripImplicit()) or ultimateSsaDef.getADefinition() = any(AssignableDefinitions::ImplicitParameterDefinition p | - source = p.getParameter().getAnAssignedValue().stripImplicitCasts() + source = p.getParameter().getAnAssignedValue().stripImplicit() ) ) } diff --git a/csharp/ql/src/Likely Bugs/ObjectComparison.ql b/csharp/ql/src/Likely Bugs/ObjectComparison.ql index 53b525b6072c..eec1961fbf50 100644 --- a/csharp/ql/src/Likely Bugs/ObjectComparison.ql +++ b/csharp/ql/src/Likely Bugs/ObjectComparison.ql @@ -28,7 +28,7 @@ class ReferenceEqualityTestOnObject extends EqualityOperation { exists(getObjectOperand(this)) and // Neither operand is 'null'. not this.getAnOperand() instanceof NullLiteral and - not exists(Type t | t = this.getAnOperand().stripImplicitCasts().getType() | + not exists(Type t | t = this.getAnOperand().stripImplicit().getType() | t instanceof NullType or t instanceof ValueType ) and diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index 527816c645be..af76384a3072 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -859,7 +859,7 @@ | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:18:8:23 | Int32 i1 | 8 | | Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:8:13:8:23 | [false] ... is ... | 1 | | Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:8:13:8:23 | [true] ... is ... | 1 | -| Patterns.cs:9:9:11:9 | {...} | Patterns.cs:10:13:10:42 | call to method WriteLine | 6 | +| Patterns.cs:9:9:11:9 | {...} | Patterns.cs:10:13:10:42 | call to method WriteLine | 7 | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:23:12:31 | String s1 | 3 | | Patterns.cs:12:18:12:31 | [false] ... is ... | Patterns.cs:12:18:12:31 | [false] ... is ... | 1 | | Patterns.cs:12:18:12:31 | [true] ... is ... | Patterns.cs:12:18:12:31 | [true] ... is ... | 1 | @@ -872,9 +872,9 @@ | Patterns.cs:23:17:23:22 | break; | Patterns.cs:23:17:23:22 | break; | 1 | | Patterns.cs:24:13:24:36 | case ...: | Patterns.cs:24:18:24:23 | Int32 i2 | 2 | | Patterns.cs:24:30:24:31 | access to local variable i2 | Patterns.cs:24:30:24:35 | ... > ... | 3 | -| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:26:17:26:22 | break; | 6 | +| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:26:17:26:22 | break; | 7 | | Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 | 2 | -| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:29:17:29:22 | break; | 6 | +| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:29:17:29:22 | break; | 7 | | Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 | 2 | | Patterns.cs:31:17:31:50 | ...; | Patterns.cs:32:17:32:22 | break; | 6 | | Patterns.cs:33:13:33:24 | case ...: | Patterns.cs:33:18:33:23 | Object v2 | 2 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 9a1a38b7935a..23a62d593d76 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -3147,7 +3147,8 @@ dominance | Patterns.cs:10:13:10:43 | ...; | Patterns.cs:10:33:10:36 | "int " | | Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:13:10:42 | call to method WriteLine | | Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:38:10:39 | access to local variable i1 | -| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:31:10:41 | $"..." | +| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:38:10:39 | call to method ToString | +| Patterns.cs:10:38:10:39 | call to method ToString | Patterns.cs:10:31:10:41 | $"..." | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:18:12:18 | access to local variable o | | Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:23:12:31 | String s1 | | Patterns.cs:12:18:12:31 | [false] ... is ... | Patterns.cs:16:14:18:9 | if (...) ... | @@ -3179,7 +3180,8 @@ dominance | Patterns.cs:25:17:25:52 | ...; | Patterns.cs:25:37:25:45 | "positive " | | Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:17:25:51 | call to method WriteLine | | Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:47:25:48 | access to local variable i2 | -| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:35:25:50 | $"..." | +| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:47:25:48 | call to method ToString | +| Patterns.cs:25:47:25:48 | call to method ToString | Patterns.cs:25:35:25:50 | $"..." | | Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 | | Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:28:17:28:47 | ...; | | Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:30:13:30:27 | case ...: | @@ -3187,7 +3189,8 @@ dominance | Patterns.cs:28:17:28:47 | ...; | Patterns.cs:28:37:28:40 | "int " | | Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:17:28:46 | call to method WriteLine | | Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:42:28:43 | access to local variable i3 | -| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:35:28:45 | $"..." | +| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:42:28:43 | call to method ToString | +| Patterns.cs:28:42:28:43 | call to method ToString | Patterns.cs:28:35:28:45 | $"..." | | Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 | | Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:31:17:31:50 | ...; | | Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:33:13:33:24 | case ...: | @@ -7341,9 +7344,10 @@ postDominance | Patterns.cs:9:9:11:9 | {...} | Patterns.cs:8:13:8:23 | [true] ... is ... | | Patterns.cs:10:13:10:42 | call to method WriteLine | Patterns.cs:10:31:10:41 | $"..." | | Patterns.cs:10:13:10:43 | ...; | Patterns.cs:9:9:11:9 | {...} | -| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:38:10:39 | access to local variable i1 | +| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:38:10:39 | call to method ToString | | Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:13:10:43 | ...; | | Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:33:10:36 | "int " | +| Patterns.cs:10:38:10:39 | call to method ToString | Patterns.cs:10:38:10:39 | access to local variable i1 | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:8:13:8:23 | [false] ... is ... | | Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:14:18:9 | if (...) ... | | Patterns.cs:12:23:12:31 | String s1 | Patterns.cs:12:18:12:18 | access to local variable o | @@ -7368,15 +7372,17 @@ postDominance | Patterns.cs:24:30:24:35 | ... > ... | Patterns.cs:24:35:24:35 | 0 | | Patterns.cs:24:35:24:35 | 0 | Patterns.cs:24:30:24:31 | access to local variable i2 | | Patterns.cs:25:17:25:51 | call to method WriteLine | Patterns.cs:25:35:25:50 | $"..." | -| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:47:25:48 | access to local variable i2 | +| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:47:25:48 | call to method ToString | | Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:17:25:52 | ...; | | Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:37:25:45 | "positive " | +| Patterns.cs:25:47:25:48 | call to method ToString | Patterns.cs:25:47:25:48 | access to local variable i2 | | Patterns.cs:26:17:26:22 | break; | Patterns.cs:25:17:25:51 | call to method WriteLine | | Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:27:13:27:24 | case ...: | | Patterns.cs:28:17:28:46 | call to method WriteLine | Patterns.cs:28:35:28:45 | $"..." | -| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:42:28:43 | access to local variable i3 | +| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:42:28:43 | call to method ToString | | Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:17:28:47 | ...; | | Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:37:28:40 | "int " | +| Patterns.cs:28:42:28:43 | call to method ToString | Patterns.cs:28:42:28:43 | access to local variable i3 | | Patterns.cs:29:17:29:22 | break; | Patterns.cs:28:17:28:46 | call to method WriteLine | | Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:30:13:30:27 | case ...: | | Patterns.cs:31:17:31:49 | call to method WriteLine | Patterns.cs:31:35:31:48 | $"..." | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index 9edc62925d86..e5d4e3c733d4 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -3437,6 +3437,7 @@ nodeEnclosing | Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:10:33:10:36 | "int " | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:5:10:5:11 | M1 | +| Patterns.cs:10:38:10:39 | call to method ToString | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:12:18:12:31 | [false] ... is ... | Patterns.cs:5:10:5:11 | M1 | @@ -3469,6 +3470,7 @@ nodeEnclosing | Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:5:10:5:11 | M1 | +| Patterns.cs:25:47:25:48 | call to method ToString | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:26:17:26:22 | break; | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:5:10:5:11 | M1 | @@ -3477,6 +3479,7 @@ nodeEnclosing | Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:28:37:28:40 | "int " | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:5:10:5:11 | M1 | +| Patterns.cs:28:42:28:43 | call to method ToString | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:29:17:29:22 | break; | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:5:10:5:11 | M1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected index fddab21f2b62..04e93332380c 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected @@ -2239,6 +2239,7 @@ | Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:33:10:36 | "int " | | Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:33:10:36 | "int " | | Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:38:10:39 | access to local variable i1 | +| Patterns.cs:10:38:10:39 | call to method ToString | Patterns.cs:10:38:10:39 | access to local variable i1 | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:14:18:9 | if (...) ... | | Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:18:12:18 | access to local variable o | | Patterns.cs:12:18:12:31 | ... is ... | Patterns.cs:12:18:12:18 | access to local variable o | @@ -2269,6 +2270,7 @@ | Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:37:25:45 | "positive " | | Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:37:25:45 | "positive " | | Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:47:25:48 | access to local variable i2 | +| Patterns.cs:25:47:25:48 | call to method ToString | Patterns.cs:25:47:25:48 | access to local variable i2 | | Patterns.cs:26:17:26:22 | break; | Patterns.cs:26:17:26:22 | break; | | Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:13:27:24 | case ...: | | Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:27:18:27:23 | Int32 i3 | @@ -2277,6 +2279,7 @@ | Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:37:28:40 | "int " | | Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:37:28:40 | "int " | | Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:42:28:43 | access to local variable i3 | +| Patterns.cs:28:42:28:43 | call to method ToString | Patterns.cs:28:42:28:43 | access to local variable i3 | | Patterns.cs:29:17:29:22 | break; | Patterns.cs:29:17:29:22 | break; | | Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:13:30:27 | case ...: | | Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:30:18:30:26 | String s2 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected index 81ee139f6a68..e8c8a1f75a7f 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected @@ -2912,6 +2912,7 @@ | Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:31:10:41 | $"..." | normal | | Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:33:10:36 | "int " | normal | | Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:38:10:39 | access to local variable i1 | normal | +| Patterns.cs:10:38:10:39 | call to method ToString | Patterns.cs:10:38:10:39 | call to method ToString | normal | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:14:13:14:45 | call to method WriteLine | normal | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:16:18:16:28 | ... is ... | false | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:17:9:18:9 | {...} | normal | @@ -2960,6 +2961,7 @@ | Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:35:25:50 | $"..." | normal | | Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:37:25:45 | "positive " | normal | | Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:47:25:48 | access to local variable i2 | normal | +| Patterns.cs:25:47:25:48 | call to method ToString | Patterns.cs:25:47:25:48 | call to method ToString | normal | | Patterns.cs:26:17:26:22 | break; | Patterns.cs:26:17:26:22 | break; | break | | Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 | no-match | | Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:28:17:28:46 | call to method WriteLine | normal | @@ -2970,6 +2972,7 @@ | Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:35:28:45 | $"..." | normal | | Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:37:28:40 | "int " | normal | | Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:42:28:43 | access to local variable i3 | normal | +| Patterns.cs:28:42:28:43 | call to method ToString | Patterns.cs:28:42:28:43 | call to method ToString | normal | | Patterns.cs:29:17:29:22 | break; | Patterns.cs:29:17:29:22 | break; | break | | Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 | no-match | | Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:31:17:31:49 | call to method WriteLine | normal | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index 0078ba76213d..53fc61f9bc4f 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -3528,7 +3528,8 @@ | Patterns.cs:10:13:10:43 | ...; | Patterns.cs:10:33:10:36 | "int " | | | Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:13:10:42 | call to method WriteLine | | | Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:38:10:39 | access to local variable i1 | | -| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:31:10:41 | $"..." | | +| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:38:10:39 | call to method ToString | | +| Patterns.cs:10:38:10:39 | call to method ToString | Patterns.cs:10:31:10:41 | $"..." | | | Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:18:12:18 | access to local variable o | | | Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:23:12:31 | String s1 | | | Patterns.cs:12:18:12:31 | [false] ... is ... | Patterns.cs:16:14:18:9 | if (...) ... | false | @@ -3565,7 +3566,8 @@ | Patterns.cs:25:17:25:52 | ...; | Patterns.cs:25:37:25:45 | "positive " | | | Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:17:25:51 | call to method WriteLine | | | Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:47:25:48 | access to local variable i2 | | -| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:35:25:50 | $"..." | | +| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:47:25:48 | call to method ToString | | +| Patterns.cs:25:47:25:48 | call to method ToString | Patterns.cs:25:35:25:50 | $"..." | | | Patterns.cs:26:17:26:22 | break; | Patterns.cs:40:9:42:9 | switch (...) {...} | break | | Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 | | | Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:28:17:28:47 | ...; | match | @@ -3574,7 +3576,8 @@ | Patterns.cs:28:17:28:47 | ...; | Patterns.cs:28:37:28:40 | "int " | | | Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:17:28:46 | call to method WriteLine | | | Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:42:28:43 | access to local variable i3 | | -| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:35:28:45 | $"..." | | +| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:42:28:43 | call to method ToString | | +| Patterns.cs:28:42:28:43 | call to method ToString | Patterns.cs:28:35:28:45 | $"..." | | | Patterns.cs:29:17:29:22 | break; | Patterns.cs:40:9:42:9 | switch (...) {...} | break | | Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 | | | Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:31:17:31:50 | ...; | match | diff --git a/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected b/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected index d10365546bd9..ce0b425c1137 100644 --- a/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected +++ b/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected @@ -4,11 +4,11 @@ | csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:47:25:52 | ", and " | | csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:54:25:64 | nameof(...) | | csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:66:25:77 | " has length " | -| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:79:25:94 | ... ?? ... | +| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:79:25:94 | call to method ToString | | csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:20:27:30 | nameof(...) | | csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:32:27:35 | " is " | | csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:37:27:39 | access to local variable foo | | csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:41:27:46 | ", and " | | csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:48:27:58 | nameof(...) | | csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:60:27:71 | " has length " | -| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:73:27:88 | ... ?? ... | +| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:73:27:88 | call to method ToString | diff --git a/csharp/ql/test/library-tests/csharp6/MemberAccess.expected b/csharp/ql/test/library-tests/csharp6/MemberAccess.expected index 1eda117fadd2..c37ea475cbaf 100644 --- a/csharp/ql/test/library-tests/csharp6/MemberAccess.expected +++ b/csharp/ql/test/library-tests/csharp6/MemberAccess.expected @@ -4,6 +4,8 @@ memberAccess | csharp6.cs:32:38:32:70 | access to indexer | csharp6.cs:32:38:32:66 | object creation of type Dictionary | Conditional | | csharp6.cs:32:38:32:73 | access to indexer | csharp6.cs:32:38:32:70 | access to indexer | Unconditional | methodCall +| csharp6.cs:25:79:25:94 | call to method ToString | csharp6.cs:25:79:25:94 | ... ?? ... | Unconditional | +| csharp6.cs:27:73:27:88 | call to method ToString | csharp6.cs:27:73:27:88 | ... ?? ... | Unconditional | | csharp6.cs:30:31:30:44 | call to method ToUpper | csharp6.cs:30:31:30:33 | access to local variable foo | Conditional | extensionMethodCall | csharp6.cs:29:35:29:44 | call to method Any | csharp6.cs:29:35:29:37 | access to local variable bar | Conditional | diff --git a/csharp/ql/test/library-tests/csharp6/PrintAst.expected b/csharp/ql/test/library-tests/csharp6/PrintAst.expected index 892ad6dd4b15..2daa487d8a43 100644 --- a/csharp/ql/test/library-tests/csharp6/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp6/PrintAst.expected @@ -38,10 +38,11 @@ csharp6.cs: # 25| 4: [NameOfExpr] nameof(...) # 25| 0: [LocalVariableAccess] access to local variable bar # 25| 5: [StringLiteralUtf16] " has length " -# 25| 6: [NullCoalescingExpr] ... ?? ... -# 25| 0: [PropertyCall] access to property Length -# 25| -1: [LocalVariableAccess] access to local variable bar -# 25| 1: [IntLiteral] 0 +# 25| 6: [MethodCall] call to method ToString +# 25| -1: [NullCoalescingExpr] ... ?? ... +# 25| 0: [PropertyCall] access to property Length +# 25| -1: [LocalVariableAccess] access to local variable bar +# 25| 1: [IntLiteral] 0 # 27| 2: [ExprStmt] ...; # 27| 0: [MethodCall] call to method Fn # 27| 0: [InterpolatedStringExpr] $"..." @@ -53,10 +54,11 @@ csharp6.cs: # 27| 4: [NameOfExpr] nameof(...) # 27| 0: [LocalVariableAccess] access to local variable bar # 27| 5: [StringLiteralUtf16] " has length " -# 27| 6: [NullCoalescingExpr] ... ?? ... -# 27| 0: [PropertyCall] access to property Length -# 27| -1: [LocalVariableAccess] access to local variable bar -# 27| 1: [IntLiteral] 0 +# 27| 6: [MethodCall] call to method ToString +# 27| -1: [NullCoalescingExpr] ... ?? ... +# 27| 0: [PropertyCall] access to property Length +# 27| -1: [LocalVariableAccess] access to local variable bar +# 27| 1: [IntLiteral] 0 # 29| 3: [LocalVariableDeclStmt] ... ...; # 29| 0: [LocalVariableDeclAndInitExpr] Nullable anythingInBar = ... # 29| -1: [TypeMention] bool? diff --git a/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected b/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected index d55d5d279ada..d693af41f714 100644 --- a/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected @@ -110,4 +110,5 @@ csharp73.cs: # 51| 0: [TypeMention] Console # 51| 0: [InterpolatedStringExpr] $"..." # 51| 0: [StringLiteralUtf16] "x is " -# 51| 1: [LocalVariableAccess] access to local variable x +# 51| 1: [MethodCall] call to method ToString +# 51| -1: [LocalVariableAccess] access to local variable x diff --git a/csharp/ql/test/library-tests/csharp7/IsFlow.expected b/csharp/ql/test/library-tests/csharp7/IsFlow.expected index b3e283528457..1640c8ee678e 100644 --- a/csharp/ql/test/library-tests/csharp7/IsFlow.expected +++ b/csharp/ql/test/library-tests/csharp7/IsFlow.expected @@ -37,7 +37,8 @@ | CSharp7.cs:258:17:258:52 | ...; | CSharp7.cs:258:37:258:45 | "positive " | semmle.label | successor | | CSharp7.cs:258:35:258:50 | $"..." | CSharp7.cs:258:17:258:51 | call to method WriteLine | semmle.label | successor | | CSharp7.cs:258:37:258:45 | "positive " | CSharp7.cs:258:47:258:48 | access to local variable i2 | semmle.label | successor | -| CSharp7.cs:258:47:258:48 | access to local variable i2 | CSharp7.cs:258:35:258:50 | $"..." | semmle.label | successor | +| CSharp7.cs:258:47:258:48 | access to local variable i2 | CSharp7.cs:258:47:258:48 | call to method ToString | semmle.label | successor | +| CSharp7.cs:258:47:258:48 | call to method ToString | CSharp7.cs:258:35:258:50 | $"..." | semmle.label | successor | | CSharp7.cs:259:17:259:22 | break; | CSharp7.cs:230:10:230:13 | exit Test (normal) | semmle.label | break | | CSharp7.cs:260:13:260:24 | case ...: | CSharp7.cs:260:18:260:23 | Int32 i3 | semmle.label | successor | | CSharp7.cs:260:18:260:23 | Int32 i3 | CSharp7.cs:261:17:261:47 | ...; | semmle.label | match | @@ -46,7 +47,8 @@ | CSharp7.cs:261:17:261:47 | ...; | CSharp7.cs:261:37:261:40 | "int " | semmle.label | successor | | CSharp7.cs:261:35:261:45 | $"..." | CSharp7.cs:261:17:261:46 | call to method WriteLine | semmle.label | successor | | CSharp7.cs:261:37:261:40 | "int " | CSharp7.cs:261:42:261:43 | access to local variable i3 | semmle.label | successor | -| CSharp7.cs:261:42:261:43 | access to local variable i3 | CSharp7.cs:261:35:261:45 | $"..." | semmle.label | successor | +| CSharp7.cs:261:42:261:43 | access to local variable i3 | CSharp7.cs:261:42:261:43 | call to method ToString | semmle.label | successor | +| CSharp7.cs:261:42:261:43 | call to method ToString | CSharp7.cs:261:35:261:45 | $"..." | semmle.label | successor | | CSharp7.cs:262:17:262:22 | break; | CSharp7.cs:230:10:230:13 | exit Test (normal) | semmle.label | break | | CSharp7.cs:263:13:263:27 | case ...: | CSharp7.cs:263:18:263:26 | String s2 | semmle.label | successor | | CSharp7.cs:263:18:263:26 | String s2 | CSharp7.cs:264:17:264:50 | ...; | semmle.label | match | diff --git a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected index 4a16e2491dfe..d860099ca203 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected +++ b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected @@ -254,7 +254,7 @@ | CSharp7.cs:233:28:233:33 | ... > ... | CSharp7.cs:233:13:233:33 | [true] ... && ... | | CSharp7.cs:235:13:235:42 | [input] SSA phi read(o) | CSharp7.cs:248:9:274:9 | SSA phi read(o) | | CSharp7.cs:235:33:235:36 | "int " | CSharp7.cs:235:31:235:41 | $"..." | -| CSharp7.cs:235:38:235:39 | access to local variable i1 | CSharp7.cs:235:31:235:41 | $"..." | +| CSharp7.cs:235:38:235:39 | call to method ToString | CSharp7.cs:235:31:235:41 | $"..." | | CSharp7.cs:237:18:237:18 | access to local variable o | CSharp7.cs:237:23:237:31 | String s1 | | CSharp7.cs:237:18:237:18 | access to local variable o | CSharp7.cs:239:13:239:45 | [input] SSA phi read(o) | | CSharp7.cs:237:18:237:18 | access to local variable o | CSharp7.cs:241:18:241:18 | access to local variable o | @@ -289,11 +289,11 @@ | CSharp7.cs:257:30:257:31 | access to local variable i2 | CSharp7.cs:257:30:257:35 | ... > ... | | CSharp7.cs:257:30:257:31 | access to local variable i2 | CSharp7.cs:258:47:258:48 | access to local variable i2 | | CSharp7.cs:258:37:258:45 | "positive " | CSharp7.cs:258:35:258:50 | $"..." | -| CSharp7.cs:258:47:258:48 | access to local variable i2 | CSharp7.cs:258:35:258:50 | $"..." | +| CSharp7.cs:258:47:258:48 | call to method ToString | CSharp7.cs:258:35:258:50 | $"..." | | CSharp7.cs:260:18:260:23 | Int32 i3 | CSharp7.cs:260:18:260:23 | SSA def(i3) | | CSharp7.cs:260:18:260:23 | SSA def(i3) | CSharp7.cs:261:42:261:43 | access to local variable i3 | | CSharp7.cs:261:37:261:40 | "int " | CSharp7.cs:261:35:261:45 | $"..." | -| CSharp7.cs:261:42:261:43 | access to local variable i3 | CSharp7.cs:261:35:261:45 | $"..." | +| CSharp7.cs:261:42:261:43 | call to method ToString | CSharp7.cs:261:35:261:45 | $"..." | | CSharp7.cs:263:18:263:26 | SSA def(s2) | CSharp7.cs:264:45:264:46 | access to local variable s2 | | CSharp7.cs:263:18:263:26 | String s2 | CSharp7.cs:263:18:263:26 | SSA def(s2) | | CSharp7.cs:264:37:264:43 | "string " | CSharp7.cs:264:35:264:48 | $"..." | diff --git a/csharp/ql/test/library-tests/csharp7/PrintAst.expected b/csharp/ql/test/library-tests/csharp7/PrintAst.expected index e5d009e0df63..dd00d798f44d 100644 --- a/csharp/ql/test/library-tests/csharp7/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp7/PrintAst.expected @@ -727,7 +727,8 @@ CSharp7.cs: # 235| 0: [TypeMention] Console # 235| 0: [InterpolatedStringExpr] $"..." # 235| 0: [StringLiteralUtf16] "int " -# 235| 1: [LocalVariableAccess] access to local variable i1 +# 235| 1: [MethodCall] call to method ToString +# 235| -1: [LocalVariableAccess] access to local variable i1 # 237| 2: [IfStmt] if (...) ... # 237| 0: [IsExpr] ... is ... # 237| 0: [LocalVariableAccess] access to local variable o @@ -789,7 +790,8 @@ CSharp7.cs: # 258| 0: [TypeMention] Console # 258| 0: [InterpolatedStringExpr] $"..." # 258| 0: [StringLiteralUtf16] "positive " -# 258| 1: [LocalVariableAccess] access to local variable i2 +# 258| 1: [MethodCall] call to method ToString +# 258| -1: [LocalVariableAccess] access to local variable i2 # 259| 9: [BreakStmt] break; # 260| 10: [CaseStmt] case ...: # 260| 0: [VariablePatternExpr] Int32 i3 @@ -800,7 +802,8 @@ CSharp7.cs: # 261| 0: [TypeMention] Console # 261| 0: [InterpolatedStringExpr] $"..." # 261| 0: [StringLiteralUtf16] "int " -# 261| 1: [LocalVariableAccess] access to local variable i3 +# 261| 1: [MethodCall] call to method ToString +# 261| -1: [LocalVariableAccess] access to local variable i3 # 262| 12: [BreakStmt] break; # 263| 13: [CaseStmt] case ...: # 263| 0: [VariablePatternExpr] String s2 diff --git a/csharp/ql/test/library-tests/csharp8/AlternateInterpolatedStrings.expected b/csharp/ql/test/library-tests/csharp8/AlternateInterpolatedStrings.expected index 06e2f93cbe36..5e065aa35819 100644 --- a/csharp/ql/test/library-tests/csharp8/AlternateInterpolatedStrings.expected +++ b/csharp/ql/test/library-tests/csharp8/AlternateInterpolatedStrings.expected @@ -1,6 +1,6 @@ inserts -| AlternateInterpolatedStrings.cs:5:17:5:26 | $"..." | 1 | AlternateInterpolatedStrings.cs:5:23:5:24 | 12 | -| AlternateInterpolatedStrings.cs:6:17:6:26 | $"..." | 1 | AlternateInterpolatedStrings.cs:6:23:6:24 | 12 | +| AlternateInterpolatedStrings.cs:5:17:5:26 | $"..." | 1 | AlternateInterpolatedStrings.cs:5:23:5:24 | call to method ToString | +| AlternateInterpolatedStrings.cs:6:17:6:26 | $"..." | 1 | AlternateInterpolatedStrings.cs:6:23:6:24 | call to method ToString | text | AlternateInterpolatedStrings.cs:5:17:5:26 | $"..." | 0 | AlternateInterpolatedStrings.cs:5:20:5:21 | "C:" | | AlternateInterpolatedStrings.cs:6:17:6:26 | $"..." | 0 | AlternateInterpolatedStrings.cs:6:20:6:21 | "C:" | diff --git a/csharp/ql/test/library-tests/csharp8/PrintAst.expected b/csharp/ql/test/library-tests/csharp8/PrintAst.expected index c33374e4761d..bdf063f624ec 100644 --- a/csharp/ql/test/library-tests/csharp8/PrintAst.expected +++ b/csharp/ql/test/library-tests/csharp8/PrintAst.expected @@ -4,12 +4,14 @@ AlternateInterpolatedStrings.cs: # 5| -1: [TypeMention] string # 5| 1: [InterpolatedStringExpr] $"..." # 5| 0: [StringLiteralUtf16] "C:" -# 5| 1: [IntLiteral] 12 +# 5| 1: [MethodCall] call to method ToString +# 5| -1: [IntLiteral] 12 # 6| 6: [Field] s2 # 6| -1: [TypeMention] string # 6| 1: [InterpolatedStringExpr] $"..." # 6| 0: [StringLiteralUtf16] "C:" -# 6| 1: [IntLiteral] 12 +# 6| 1: [MethodCall] call to method ToString +# 6| -1: [IntLiteral] 12 AsyncStreams.cs: # 6| [Class] AsyncStreams # 8| 5: [Method] Items diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected index ca13350d527b..643b3e813503 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected @@ -176,6 +176,7 @@ | GlobalDataFlowStringBuilder.cs:36:21:36:34 | call to method ToString | normal | GlobalDataFlowStringBuilder.cs:36:21:36:34 | call to method ToString | | GlobalDataFlowStringBuilder.cs:39:19:39:37 | object creation of type StringBuilder | normal | GlobalDataFlowStringBuilder.cs:39:19:39:37 | object creation of type StringBuilder | | GlobalDataFlowStringBuilder.cs:40:9:40:27 | call to method Append | normal | GlobalDataFlowStringBuilder.cs:40:9:40:27 | call to method Append | +| GlobalDataFlowStringBuilder.cs:40:23:40:24 | call to method ToString | normal | GlobalDataFlowStringBuilder.cs:40:23:40:24 | call to method ToString | | GlobalDataFlowStringBuilder.cs:41:21:41:34 | call to method ToString | normal | GlobalDataFlowStringBuilder.cs:41:21:41:34 | call to method ToString | | GlobalDataFlowStringBuilder.cs:44:9:44:18 | call to method Clear | normal | GlobalDataFlowStringBuilder.cs:44:9:44:18 | call to method Clear | | GlobalDataFlowStringBuilder.cs:45:23:45:35 | call to method ToString | normal | GlobalDataFlowStringBuilder.cs:45:23:45:35 | call to method ToString | diff --git a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected index f90f71d1ea95..6aa705824b79 100644 --- a/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected +++ b/csharp/ql/test/library-tests/dataflow/global/TaintTrackingPath.expected @@ -514,7 +514,7 @@ edges | GlobalDataFlowStringBuilder.cs:24:19:24:26 | (...) ... : AppendInterpolatedStringHandler | GlobalDataFlowStringBuilder.cs:24:9:24:10 | [post] access to parameter sb : StringBuilder | provenance | MaD:16 | | GlobalDataFlowStringBuilder.cs:30:31:30:32 | [post] access to local variable sb : StringBuilder | GlobalDataFlowStringBuilder.cs:31:21:31:22 | access to local variable sb : StringBuilder | provenance | | | GlobalDataFlowStringBuilder.cs:30:31:30:32 | [post] access to local variable sb : StringBuilder | GlobalDataFlowStringBuilder.cs:35:20:35:21 | access to local variable sb : StringBuilder | provenance | | -| GlobalDataFlowStringBuilder.cs:30:31:30:32 | [post] access to local variable sb : StringBuilder | GlobalDataFlowStringBuilder.cs:40:20:40:26 | (...) ... : AppendInterpolatedStringHandler | provenance | | +| GlobalDataFlowStringBuilder.cs:30:31:30:32 | [post] access to local variable sb : StringBuilder | GlobalDataFlowStringBuilder.cs:40:23:40:24 | access to local variable sb : StringBuilder | provenance | | | GlobalDataFlowStringBuilder.cs:30:35:30:48 | "taint source" : String | GlobalDataFlowStringBuilder.cs:17:64:17:64 | s : String | provenance | | | GlobalDataFlowStringBuilder.cs:30:35:30:48 | "taint source" : String | GlobalDataFlowStringBuilder.cs:30:31:30:32 | [post] access to local variable sb : StringBuilder | provenance | MaD:14 | | GlobalDataFlowStringBuilder.cs:31:13:31:17 | access to local variable sink0 : String | GlobalDataFlowStringBuilder.cs:32:15:32:19 | access to local variable sink0 | provenance | | @@ -527,6 +527,8 @@ edges | GlobalDataFlowStringBuilder.cs:36:21:36:34 | call to method ToString : String | GlobalDataFlowStringBuilder.cs:36:13:36:17 | access to local variable sink1 : String | provenance | | | GlobalDataFlowStringBuilder.cs:40:9:40:11 | [post] access to local variable sb2 : StringBuilder | GlobalDataFlowStringBuilder.cs:41:21:41:23 | access to local variable sb2 : StringBuilder | provenance | | | GlobalDataFlowStringBuilder.cs:40:20:40:26 | (...) ... : AppendInterpolatedStringHandler | GlobalDataFlowStringBuilder.cs:40:9:40:11 | [post] access to local variable sb2 : StringBuilder | provenance | MaD:16 | +| GlobalDataFlowStringBuilder.cs:40:23:40:24 | access to local variable sb : StringBuilder | GlobalDataFlowStringBuilder.cs:40:23:40:24 | call to method ToString : String | provenance | MaD:17 | +| GlobalDataFlowStringBuilder.cs:40:23:40:24 | call to method ToString : String | GlobalDataFlowStringBuilder.cs:40:20:40:26 | (...) ... : AppendInterpolatedStringHandler | provenance | | | GlobalDataFlowStringBuilder.cs:41:13:41:17 | access to local variable sink2 : String | GlobalDataFlowStringBuilder.cs:42:15:42:19 | access to local variable sink2 | provenance | | | GlobalDataFlowStringBuilder.cs:41:21:41:23 | access to local variable sb2 : StringBuilder | GlobalDataFlowStringBuilder.cs:41:21:41:34 | call to method ToString : String | provenance | MaD:17 | | GlobalDataFlowStringBuilder.cs:41:21:41:34 | call to method ToString : String | GlobalDataFlowStringBuilder.cs:41:13:41:17 | access to local variable sink2 : String | provenance | | @@ -1046,6 +1048,8 @@ nodes | GlobalDataFlowStringBuilder.cs:37:15:37:19 | access to local variable sink1 | semmle.label | access to local variable sink1 | | GlobalDataFlowStringBuilder.cs:40:9:40:11 | [post] access to local variable sb2 : StringBuilder | semmle.label | [post] access to local variable sb2 : StringBuilder | | GlobalDataFlowStringBuilder.cs:40:20:40:26 | (...) ... : AppendInterpolatedStringHandler | semmle.label | (...) ... : AppendInterpolatedStringHandler | +| GlobalDataFlowStringBuilder.cs:40:23:40:24 | access to local variable sb : StringBuilder | semmle.label | access to local variable sb : StringBuilder | +| GlobalDataFlowStringBuilder.cs:40:23:40:24 | call to method ToString : String | semmle.label | call to method ToString : String | | GlobalDataFlowStringBuilder.cs:41:13:41:17 | access to local variable sink2 : String | semmle.label | access to local variable sink2 : String | | GlobalDataFlowStringBuilder.cs:41:21:41:23 | access to local variable sb2 : StringBuilder | semmle.label | access to local variable sb2 : StringBuilder | | GlobalDataFlowStringBuilder.cs:41:21:41:34 | call to method ToString : String | semmle.label | call to method ToString : String | diff --git a/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.expected b/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.expected new file mode 100644 index 000000000000..bf2a515a8895 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.expected @@ -0,0 +1,95 @@ +implicitToString.cs: +# 3| [Class] TestClass +# 5| 5: [Class] MyClass +# 5| 4: [InstanceConstructor,PrimaryConstructor] MyClass +# 7| 5: [Method] ToString +# 7| -1: [TypeMention] string +# 8| 4: [BlockStmt] {...} +# 9| 0: [ReturnStmt] return ...; +# 9| 0: [StringLiteralUtf16] "tainted" +# 13| 6: [Method] Sink +# 13| -1: [TypeMention] Void +#-----| 2: (Parameters) +# 13| 0: [Parameter] o +# 13| -1: [TypeMention] object +# 13| 4: [BlockStmt] {...} +# 15| 7: [Method] M1 +# 15| -1: [TypeMention] Void +# 16| 4: [BlockStmt] {...} +# 17| 0: [LocalVariableDeclStmt] ... ...; +# 17| 0: [LocalVariableDeclAndInitExpr] MyClass x1 = ... +# 17| -1: [TypeMention] MyClass +# 17| 0: [LocalVariableAccess] access to local variable x1 +# 17| 1: [ObjectCreation] object creation of type MyClass +# 17| 0: [TypeMention] MyClass +# 18| 1: [LocalVariableDeclStmt] ... ...; +# 18| 0: [LocalVariableDeclAndInitExpr] String x2 = ... +# 18| -1: [TypeMention] string +# 18| 0: [LocalVariableAccess] access to local variable x2 +# 18| 1: [AddExpr] ... + ... +# 18| 0: [StringLiteralUtf16] "Hello" +# 18| 1: [MethodCall] call to method ToString +# 18| -1: [LocalVariableAccess] access to local variable x1 +# 19| 2: [ExprStmt] ...; +# 19| 0: [MethodCall] call to method Sink +# 19| 0: [LocalVariableAccess] access to local variable x2 +# 22| 8: [Method] M2 +# 22| -1: [TypeMention] Void +# 23| 4: [BlockStmt] {...} +# 24| 0: [LocalVariableDeclStmt] ... ...; +# 24| 0: [LocalVariableDeclAndInitExpr] MyClass x1 = ... +# 24| -1: [TypeMention] MyClass +# 24| 0: [LocalVariableAccess] access to local variable x1 +# 24| 1: [ObjectCreation] object creation of type MyClass +# 24| 0: [TypeMention] MyClass +# 25| 1: [LocalVariableDeclStmt] ... ...; +# 25| 0: [LocalVariableDeclAndInitExpr] String x2 = ... +# 25| -1: [TypeMention] string +# 25| 0: [LocalVariableAccess] access to local variable x2 +# 25| 1: [AddExpr] ... + ... +# 25| 0: [StringLiteralUtf16] "Hello" +# 25| 1: [MethodCall] call to method ToString +# 25| -1: [LocalVariableAccess] access to local variable x1 +# 26| 2: [ExprStmt] ...; +# 26| 0: [MethodCall] call to method Sink +# 26| 0: [LocalVariableAccess] access to local variable x2 +# 29| 9: [Method] M3 +# 29| -1: [TypeMention] Void +# 30| 4: [BlockStmt] {...} +# 31| 0: [LocalVariableDeclStmt] ... ...; +# 31| 0: [LocalVariableDeclAndInitExpr] MyClass x1 = ... +# 31| -1: [TypeMention] MyClass +# 31| 0: [LocalVariableAccess] access to local variable x1 +# 31| 1: [ObjectCreation] object creation of type MyClass +# 31| 0: [TypeMention] MyClass +# 32| 1: [LocalVariableDeclStmt] ... ...; +# 32| 0: [LocalVariableDeclAndInitExpr] String x2 = ... +# 32| -1: [TypeMention] string +# 32| 0: [LocalVariableAccess] access to local variable x2 +# 32| 1: [InterpolatedStringExpr] $"..." +# 32| 0: [StringLiteralUtf16] "Hello " +# 32| 1: [MethodCall] call to method ToString +# 32| -1: [LocalVariableAccess] access to local variable x1 +# 33| 2: [ExprStmt] ...; +# 33| 0: [MethodCall] call to method Sink +# 33| 0: [LocalVariableAccess] access to local variable x2 +# 36| 10: [Method] M4 +# 36| -1: [TypeMention] Void +# 37| 4: [BlockStmt] {...} +# 38| 0: [LocalVariableDeclStmt] ... ...; +# 38| 0: [LocalVariableDeclAndInitExpr] MyClass x1 = ... +# 38| -1: [TypeMention] MyClass +# 38| 0: [LocalVariableAccess] access to local variable x1 +# 38| 1: [ObjectCreation] object creation of type MyClass +# 38| 0: [TypeMention] MyClass +# 39| 1: [LocalVariableDeclStmt] ... ...; +# 39| 0: [LocalVariableDeclAndInitExpr] String x2 = ... +# 39| -1: [TypeMention] string +# 39| 0: [LocalVariableAccess] access to local variable x2 +# 39| 1: [InterpolatedStringExpr] $"..." +# 39| 0: [StringLiteralUtf16] "Hello " +# 39| 1: [MethodCall] call to method ToString +# 39| -1: [LocalVariableAccess] access to local variable x1 +# 40| 2: [ExprStmt] ...; +# 40| 0: [MethodCall] call to method Sink +# 40| 0: [LocalVariableAccess] access to local variable x2 diff --git a/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.qlref b/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.qlref new file mode 100644 index 000000000000..f867dd01f9f8 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.qlref @@ -0,0 +1 @@ +shared/PrintAst.ql \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.cs b/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.cs new file mode 100644 index 000000000000..84c8737204d8 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.cs @@ -0,0 +1,43 @@ +using System; + +public class TestClass +{ + public class MyClass() + { + public override string ToString() + { + return "tainted"; + } + } + + public static void Sink(object o) { } + + public void M1() + { + var x1 = new MyClass(); + var x2 = "Hello" + x1.ToString(); + Sink(x2); + } + + public void M2() + { + var x1 = new MyClass(); + var x2 = "Hello" + x1; + Sink(x2); + } + + public void M3() + { + var x1 = new MyClass(); + var x2 = $"Hello {x1.ToString()}"; + Sink(x2); + } + + public void M4() + { + var x1 = new MyClass(); + var x2 = $"Hello {x1}"; + Sink(x2); + } + +} diff --git a/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.expected b/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.expected new file mode 100644 index 000000000000..aa77f131e890 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.expected @@ -0,0 +1,34 @@ +models +edges +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:18:28:18:40 | call to method ToString : String | provenance | | +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:25:28:25:29 | call to method ToString : String | provenance | | +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:32:27:32:39 | call to method ToString : String | provenance | | +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:39:27:39:28 | call to method ToString : String | provenance | | +| implicitToString.cs:18:13:18:14 | access to local variable x2 : String | implicitToString.cs:19:14:19:15 | access to local variable x2 | provenance | | +| implicitToString.cs:18:28:18:40 | call to method ToString : String | implicitToString.cs:18:13:18:14 | access to local variable x2 : String | provenance | | +| implicitToString.cs:25:13:25:14 | access to local variable x2 : String | implicitToString.cs:26:14:26:15 | access to local variable x2 | provenance | | +| implicitToString.cs:25:28:25:29 | call to method ToString : String | implicitToString.cs:25:13:25:14 | access to local variable x2 : String | provenance | | +| implicitToString.cs:32:13:32:14 | access to local variable x2 : String | implicitToString.cs:33:14:33:15 | access to local variable x2 | provenance | | +| implicitToString.cs:32:27:32:39 | call to method ToString : String | implicitToString.cs:32:13:32:14 | access to local variable x2 : String | provenance | | +| implicitToString.cs:39:13:39:14 | access to local variable x2 : String | implicitToString.cs:40:14:40:15 | access to local variable x2 | provenance | | +| implicitToString.cs:39:27:39:28 | call to method ToString : String | implicitToString.cs:39:13:39:14 | access to local variable x2 : String | provenance | | +nodes +| implicitToString.cs:9:20:9:28 | "tainted" : String | semmle.label | "tainted" : String | +| implicitToString.cs:18:13:18:14 | access to local variable x2 : String | semmle.label | access to local variable x2 : String | +| implicitToString.cs:18:28:18:40 | call to method ToString : String | semmle.label | call to method ToString : String | +| implicitToString.cs:19:14:19:15 | access to local variable x2 | semmle.label | access to local variable x2 | +| implicitToString.cs:25:13:25:14 | access to local variable x2 : String | semmle.label | access to local variable x2 : String | +| implicitToString.cs:25:28:25:29 | call to method ToString : String | semmle.label | call to method ToString : String | +| implicitToString.cs:26:14:26:15 | access to local variable x2 | semmle.label | access to local variable x2 | +| implicitToString.cs:32:13:32:14 | access to local variable x2 : String | semmle.label | access to local variable x2 : String | +| implicitToString.cs:32:27:32:39 | call to method ToString : String | semmle.label | call to method ToString : String | +| implicitToString.cs:33:14:33:15 | access to local variable x2 | semmle.label | access to local variable x2 | +| implicitToString.cs:39:13:39:14 | access to local variable x2 : String | semmle.label | access to local variable x2 : String | +| implicitToString.cs:39:27:39:28 | call to method ToString : String | semmle.label | call to method ToString : String | +| implicitToString.cs:40:14:40:15 | access to local variable x2 | semmle.label | access to local variable x2 | +subpaths +#select +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:19:14:19:15 | access to local variable x2 | $@ | implicitToString.cs:19:14:19:15 | access to local variable x2 | access to local variable x2 | +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:26:14:26:15 | access to local variable x2 | $@ | implicitToString.cs:26:14:26:15 | access to local variable x2 | access to local variable x2 | +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:33:14:33:15 | access to local variable x2 | $@ | implicitToString.cs:33:14:33:15 | access to local variable x2 | access to local variable x2 | +| implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:9:20:9:28 | "tainted" : String | implicitToString.cs:40:14:40:15 | access to local variable x2 | $@ | implicitToString.cs:40:14:40:15 | access to local variable x2 | access to local variable x2 | diff --git a/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.ql b/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.ql new file mode 100644 index 000000000000..85797414ee5d --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/implicittostring/implicitToString.ql @@ -0,0 +1,19 @@ +import csharp +import utils.test.ProvenancePathGraph::ShowProvenance + +module TtConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr().(StringLiteral).getValue() = "tainted" } + + predicate isSink(DataFlow::Node sink) { + exists(MethodCall mc | + mc.getTarget().hasUndecoratedName("Sink") and + mc.getAnArgument() = sink.asExpr() + ) + } +} + +module Tt = TaintTracking::Global; + +from Tt::PathNode source, Tt::PathNode sink +where Tt::flowPath(source, sink) +select source, source, sink, "$@", sink, sink.toString() diff --git a/csharp/ql/test/library-tests/security/dataflow/flowsources/StoredFlowSources.expected b/csharp/ql/test/library-tests/security/dataflow/flowsources/StoredFlowSources.expected index 28156f12272e..e27ea53adbd1 100644 --- a/csharp/ql/test/library-tests/security/dataflow/flowsources/StoredFlowSources.expected +++ b/csharp/ql/test/library-tests/security/dataflow/flowsources/StoredFlowSources.expected @@ -6,6 +6,7 @@ | data.cs:28:35:28:71 | ... + ... | | data.cs:28:51:28:64 | access to local variable customerReader | | data.cs:28:51:28:71 | access to indexer | +| data.cs:28:51:28:71 | call to method ToString | | data.cs:30:13:30:26 | access to local variable customerReader | | entity.cs:31:29:31:82 | DbRawSqlQuery blogs = ... | | entity.cs:31:37:31:82 | call to method SqlQuery | 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 2f863966f351..981b36002663 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs @@ -9,5 +9,8 @@ public void M(object o) Console.WriteLine($"Hello: {o.ToString()}"); // BAD Console.WriteLine($"Hello: {o}"); // GOOD + + Console.WriteLine("Hello: " + o.ToString()); // BAD + Console.WriteLine("Hello: " + o); // 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 8021c21df2ae..28775378f049 100644 --- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected +++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected @@ -1,3 +1,4 @@ | 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. |