From c8169f576fd380d7700931d80228e9203bd5e576 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 20 Mar 2026 12:21:21 +0100 Subject: [PATCH] C#: Don't extract expanded assignments and swap child indices for assignments. --- .../Entities/Expressions/Assignment.cs | 75 ++----------------- .../Entities/Expressions/Initializer.cs | 29 +++---- .../ObjectCreation/AnonymousObjectCreation.cs | 6 +- .../Entities/Expressions/Query.cs | 6 +- .../Expressions/VariableDeclaration.cs | 6 +- .../Entities/Field.cs | 4 +- .../Entities/Property.cs | 4 +- 7 files changed, 33 insertions(+), 97 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs index f3e2e510cd6..67e49b2919c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs @@ -22,26 +22,12 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions protected override void PopulateExpression(TextWriter trapFile) { - var operatorKind = OperatorKind; - if (operatorKind.HasValue) - { - // Convert assignment such as `a += b` into `a = a + b`. - var simpleAssignExpr = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.SIMPLE_ASSIGN, this, 2, isCompilerGenerated: true, null)); - Create(Context, Syntax.Left, simpleAssignExpr, 1); - var opexpr = new Expression(new ExpressionInfo(Context, Type, Location, operatorKind.Value, simpleAssignExpr, 0, isCompilerGenerated: true, null)); - Create(Context, Syntax.Left, opexpr, 0, isCompilerGenerated: true); - Create(Context, Syntax.Right, opexpr, 1); - opexpr.OperatorCall(trapFile, Syntax); - } - else - { - Create(Context, Syntax.Left, this, 1); - Create(Context, Syntax.Right, this, 0); + Create(Context, Syntax.Left, this, 0); + Create(Context, Syntax.Right, this, 1); - if (Kind == ExprKind.ADD_EVENT || Kind == ExprKind.REMOVE_EVENT) - { - OperatorCall(trapFile, Syntax); - } + if (Kind != ExprKind.SIMPLE_ASSIGN && Kind != ExprKind.ASSIGN_COALESCE) + { + OperatorCall(trapFile, Syntax); } } @@ -108,56 +94,5 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions return kind; } - - /// - /// Gets the kind of this assignment operator (null if the - /// assignment is not an assignment operator). For example, the operator - /// kind of `*=` is `*`. - /// - private ExprKind? OperatorKind - { - get - { - var kind = Kind; - if (kind == ExprKind.REMOVE_EVENT || kind == ExprKind.ADD_EVENT || kind == ExprKind.SIMPLE_ASSIGN) - return null; - - if (CallType.AdjustKind(kind) == ExprKind.OPERATOR_INVOCATION) - return ExprKind.OPERATOR_INVOCATION; - - switch (kind) - { - case ExprKind.ASSIGN_ADD: - return ExprKind.ADD; - case ExprKind.ASSIGN_AND: - return ExprKind.BIT_AND; - case ExprKind.ASSIGN_DIV: - return ExprKind.DIV; - case ExprKind.ASSIGN_LSHIFT: - return ExprKind.LSHIFT; - case ExprKind.ASSIGN_MUL: - return ExprKind.MUL; - case ExprKind.ASSIGN_OR: - return ExprKind.BIT_OR; - case ExprKind.ASSIGN_REM: - return ExprKind.REM; - case ExprKind.ASSIGN_RSHIFT: - return ExprKind.RSHIFT; - case ExprKind.ASSIGN_URSHIFT: - return ExprKind.URSHIFT; - case ExprKind.ASSIGN_SUB: - return ExprKind.SUB; - case ExprKind.ASSIGN_XOR: - return ExprKind.BIT_XOR; - case ExprKind.ASSIGN_COALESCE: - return ExprKind.NULL_COALESCING; - default: - Context.ModelError(Syntax, $"Couldn't unfold assignment of type {kind}"); - return ExprKind.UNKNOWN; - } - } - } - - public new CallType CallType => GetCallType(Context, Syntax); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs index 92e2b910f99..63024cd47fc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs @@ -83,8 +83,22 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { var assignmentInfo = new ExpressionNodeInfo(Context, init, this, child++).SetKind(ExprKind.SIMPLE_ASSIGN); var assignmentEntity = new Expression(assignmentInfo); + var target = Context.GetSymbolInfo(assignment.Left); + + // If the target is null, then assume that this is an array initializer (of the form `[...] = ...`) + var access = target.Symbol is null ? + new Expression(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 0).SetKind(ExprKind.ARRAY_ACCESS)) : + Access.Create(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 0), target.Symbol, false, Context.CreateEntity(target.Symbol)); + + if (assignment.Left is ImplicitElementAccessSyntax iea) + { + // An array/indexer initializer of the form `[...] = ...` + access.PopulateArguments(trapFile, iea.ArgumentList.Arguments, 0); + } + var typeInfoRight = Context.GetTypeInfo(assignment.Right); if (typeInfoRight.Type is null) + { // The type may be null for nested initializers such as // ```csharp // new ClassWithArrayField() { As = { [0] = a } } @@ -92,21 +106,8 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions // In this case we take the type from the assignment // `As = { [0] = a }` instead typeInfoRight = assignmentInfo.TypeInfo; - CreateFromNode(new ExpressionNodeInfo(Context, assignment.Right, assignmentEntity, 0, typeInfoRight)); - - var target = Context.GetSymbolInfo(assignment.Left); - - // If the target is null, then assume that this is an array initializer (of the form `[...] = ...`) - - var access = target.Symbol is null ? - new Expression(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 1).SetKind(ExprKind.ARRAY_ACCESS)) : - Access.Create(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 1), target.Symbol, false, Context.CreateEntity(target.Symbol)); - - if (assignment.Left is ImplicitElementAccessSyntax iea) - { - // An array/indexer initializer of the form `[...] = ...` - access.PopulateArguments(trapFile, iea.ArgumentList.Arguments, 0); } + CreateFromNode(new ExpressionNodeInfo(Context, assignment.Right, assignmentEntity, 1, typeInfoRight)); } else { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs index a6f94f53338..1fdf03171b9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs @@ -41,11 +41,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var loc = Context.CreateLocation(init.GetLocation()); var assignment = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, isCompilerGenerated: false, null)); - Create(Context, init.Expression, assignment, 0); Property.Create(Context, property); - - var access = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 1, isCompilerGenerated: false, null)); + var access = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 0, isCompilerGenerated: false, null)); trapFile.expr_access(access, propEntity); + + Create(Context, init.Expression, assignment, 1); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs index 85a1ceda47c..aadf06f2dee 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs @@ -94,12 +94,12 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions child ); - Expression.Create(cx, Expr, decl, 0); - var nameLoc = cx.CreateLocation(name.GetLocation()); - var access = new Expression(new ExpressionInfo(cx, type, nameLoc, ExprKind.LOCAL_VARIABLE_ACCESS, decl, 1, isCompilerGenerated: false, null)); + var access = new Expression(new ExpressionInfo(cx, type, nameLoc, ExprKind.LOCAL_VARIABLE_ACCESS, decl, 0, isCompilerGenerated: false, null)); cx.TrapWriter.Writer.expr_access(access, LocalVariable.Create(cx, variableSymbol)); + Expression.Create(cx, Expr, decl, 1); + return decl; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs index c44f9e2b946..47ecee3e037 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs @@ -176,11 +176,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions if (d.Initializer is not null) { - Create(cx, d.Initializer.Value, ret, 0); - // Create an access - var access = new Expression(new ExpressionInfo(cx, type, localVar.Location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 1, isCompilerGenerated: false, null)); + var access = new Expression(new ExpressionInfo(cx, type, localVar.Location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 0, isCompilerGenerated: false, null)); cx.TrapWriter.Writer.expr_access(access, localVar); + + Create(cx, d.Initializer.Value, ret, 1); } if (d.Parent is VariableDeclarationSyntax decl) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 329115f11c7..708c00d2f73 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -116,9 +116,9 @@ namespace Semmle.Extraction.CSharp.Entities { var type = Symbol.GetAnnotatedType(); var simpleAssignExpr = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, isCompilerGenerated: true, constValue)); - Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 0)); - var access = new Expression(new ExpressionInfo(Context, type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, isCompilerGenerated: true, constValue)); + var access = new Expression(new ExpressionInfo(Context, type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 0, isCompilerGenerated: true, constValue)); trapFile.expr_access(access, this); + Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 1)); return access; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index 57eb5efc007..988ca843927 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -94,9 +94,9 @@ namespace Semmle.Extraction.CSharp.Entities var loc = Context.CreateLocation(initializer!.GetLocation()); var annotatedType = AnnotatedTypeSymbol.CreateNotAnnotated(Symbol.Type); var simpleAssignExpr = new Expression(new ExpressionInfo(Context, annotatedType, loc, ExprKind.SIMPLE_ASSIGN, this, child++, isCompilerGenerated: true, null)); - Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 0)); - var access = new Expression(new ExpressionInfo(Context, annotatedType, Location, ExprKind.PROPERTY_ACCESS, simpleAssignExpr, 1, isCompilerGenerated: true, null)); + var access = new Expression(new ExpressionInfo(Context, annotatedType, Location, ExprKind.PROPERTY_ACCESS, simpleAssignExpr, 0, isCompilerGenerated: true, null)); trapFile.expr_access(access, this); + Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 1)); if (!Symbol.IsStatic) { This.CreateImplicit(Context, Symbol.ContainingType, Location, access, -1);