Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C#: Adding synthetic implicit ToString calls in binary- and string interpolation expressions. #18446

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,13 @@ public Location Location

public ExprKind Kind { get; set; } = ExprKind.UNKNOWN;

public bool IsCompilerGenerated { get; set; }
public bool IsCompilerGenerated { get; init; }

/// <summary>
/// Whether the expression should have a compiler generated `ToString` call added,
/// if there is no suitable implicit cast.
/// </summary>
public bool ImplicitToString { get; private set; }

public ExpressionNodeInfo SetParent(IExpressionParentEntity parent, int child)
{
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/// <summary>
/// Creates an expression from a syntax node.
/// Inserts type conversion as required.
/// Population is deferred to avoid overflowing the stack.
/// </summary>
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Gets the `ToString` method for the given type.
/// </summary>
private static IMethodSymbol? GetToStringMethod(ITypeSymbol? type)
{
return type?
.GetMembers()
.OfType<IMethodSymbol>()
.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;

/// <summary>
/// Creates a new expression, adding a compiler generated `ToString` call if required.
/// </summary>
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)));
}

/// <summary>
/// Wraps the resulting expression in a `ToString` call, if a suitable `ToString` method is available.
/// </summary>
public static Expression Wrap(ExpressionNodeInfo info)
{
if (GetToStringMethod(info.Type?.Symbol) is IMethodSymbol toString)
{
return new ImplicitToString(info, toString);
}
return Factory.Create(info);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion csharp/ql/examples/snippets/ternary_conditional.ql
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions csharp/ql/lib/change-notes/2025-01-09-implicit-to-string.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added extractor support for extracting implicit `ToString` calls in binary `+` expressions and string interpolation expressions.
4 changes: 3 additions & 1 deletion csharp/ql/lib/semmle/code/csharp/PrintAst.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion csharp/ql/lib/semmle/code/csharp/commons/Constants.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
6 changes: 3 additions & 3 deletions csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
2 changes: 1 addition & 1 deletion csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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, _)
)
}
Expand Down
4 changes: 4 additions & 0 deletions csharp/ql/lib/semmle/code/csharp/exprs/Call.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

/**
Expand Down
14 changes: 11 additions & 3 deletions csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}

Expand Down
4 changes: 2 additions & 2 deletions csharp/ql/src/Likely Bugs/Dynamic/BadDynamicCall.ql
Original file line number Diff line number Diff line change
Expand Up @@ -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()
)
)
}
Expand Down
2 changes: 1 addition & 1 deletion csharp/ql/src/Likely Bugs/ObjectComparison.ql
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (...) ... |
Expand Down Expand Up @@ -3179,15 +3180,17 @@ 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 ...: |
| Patterns.cs:28:17:28:46 | call to method WriteLine | Patterns.cs:29:17:29:22 | break; |
| 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 ...: |
Expand Down Expand Up @@ -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 |
Expand All @@ -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 | $"..." |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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 |
Expand All @@ -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 |
Expand Down
Loading
Loading