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

Merged
merged 22 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
261b8db
C#: Add some implicit toString data flow test cases.
michaelnebel Jan 8, 2025
f905be4
C#: Add a RedundantToStringCall test case.
michaelnebel Jan 9, 2025
908a3e3
C#: Make synthetic ToString calls in binary add expressions.
michaelnebel Jan 8, 2025
e9beeac
C#: Update test expected output.
michaelnebel Jan 8, 2025
5f539c5
C#: Make synthetic ToString calls in string interpolation expressions.
michaelnebel Jan 10, 2025
8bf67e3
C#: Update test expected output.
michaelnebel Jan 10, 2025
a4049b1
C#: Modify compiler generated strip logic to also take generated ToSt…
michaelnebel Jan 9, 2025
6c6e58b
C#: Add PrintAst test to implicit ToString test.
michaelnebel Jan 9, 2025
53c2f76
C#: Update AST printing to include generated cast (wrapping) expressi…
michaelnebel Jan 9, 2025
f239ab1
C#: Update PrintAst expected test output.
michaelnebel Jan 9, 2025
20c2b2b
C#: Update other existing tests expected output.
michaelnebel Jan 9, 2025
cd7d2d4
C#: Add change note.
michaelnebel Jan 9, 2025
6a31fd7
C#: Add ImplicitToString example in nullness tests.
michaelnebel Jan 13, 2025
e62846e
C#: Disregard compiler generated method calls as possible null derefe…
michaelnebel Jan 13, 2025
d40c1be
C#: Update expected testoutput for Nullness tests.
michaelnebel Jan 13, 2025
77f9f4c
C#: Add implicit to string usage in constructor.
michaelnebel Jan 13, 2025
2bff2d6
C#: Disregards compiler generated virtual calls as problematic virtua…
michaelnebel Jan 13, 2025
024de5e
C#: Update virtual call in constructor expected test output.
michaelnebel Jan 13, 2025
ab70a94
C#: Add a summary model for PathString.ToString.
michaelnebel Jan 13, 2025
6a406b2
C#: Do not insert a synthetic ToString call in interpolation expressi…
michaelnebel Jan 14, 2025
0c5c2a3
C#: Add implicit to string test.
michaelnebel Jan 14, 2025
3de5b22
C#: Update other existing tests expected output.
michaelnebel Jan 15, 2025
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 @@ -532,6 +532,12 @@ attribute.AttributeClass is INamedTypeSymbol nt &&
return isInline;
}

/// <summary>
/// Returns true if this type implements `System.IFormattable`.
/// </summary>
public static bool ImplementsIFormattable(this ITypeSymbol type) =>
type.AllInterfaces.Any(i => i.Name == "IFormattable" && i.ContainingNamespace.ToString() == "System");

/// <summary>
/// Holds if this type is of the form <code>System.ReadOnlySpan&lt;byte&gt;</code>.
/// </summary>
Expand Down
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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this include inherited members?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider to support this as well - should be fairly easy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also good observation @hvitved !! 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should consider to support this as well - should be fairly easy.

Yes, I think we should support that as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow up #18508

.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
@@ -1,4 +1,5 @@
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Kinds;
Expand All @@ -20,7 +21,15 @@ protected override void PopulateExpression(TextWriter trapFile)
{
case SyntaxKind.Interpolation:
var interpolation = (InterpolationSyntax)c;
Create(Context, interpolation.Expression, this, child++);
var exp = interpolation.Expression;
if (Context.GetTypeInfo(exp).Type is ITypeSymbol type && !type.ImplementsIFormattable())
{
ImplicitToString.Create(Context, exp, this, child++);
}
else
{
Create(Context, exp, 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.
6 changes: 6 additions & 0 deletions csharp/ql/lib/ext/Microsoft.AspNetCore.Http.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extensions:
- addsTo:
pack: codeql/csharp-all
extensible: summaryModel
data:
- ["Microsoft.AspNetCore.Http", "PathString", True, "ToString", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
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
6 changes: 5 additions & 1 deletion csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,11 @@ class Dereference extends G::DereferenceableExpr {
not underlyingType instanceof NullableType
)
) else (
this = any(QualifiableExpr qe | not qe.isConditional()).getQualifier() and
this =
any(QualifiableExpr qe |
not qe.isConditional() and
not qe.(MethodCall).isImplicit()
).getQualifier() and
not this instanceof ThisAccess and
not this instanceof BaseAccess and
not this instanceof TypeAccess
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ predicate overriddenSealed(RefType t, Virtualizable d) {
}

predicate virtualAccessWithThisQualifier(Expr e, Member d) {
exists(VirtualMethodCall c | c = e and c.getTarget() = d and c.hasThisQualifier())
exists(VirtualMethodCall c |
c = e and c.getTarget() = d and c.hasThisQualifier() and not c.isImplicit()
)
or
exists(VirtualMethodAccess c | c = e and c.getTarget() = d and c.hasThisQualifier())
or
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 @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand All @@ -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 | |
Expand Down Expand Up @@ -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 |
Expand Down
Loading