Merge pull request #4617 from tamasvajk/feature/csharp9-implicit-obj-creation

C#: Extract 'ImplicitObjectCreationExpressionSyntax'
This commit is contained in:
Tamás Vajk
2020-12-17 15:20:13 +01:00
committed by GitHub
18 changed files with 7603 additions and 3531 deletions

View File

@@ -84,6 +84,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case SyntaxKind.ObjectCreationExpression:
return ExplicitObjectCreation.Create(info);
case SyntaxKind.ImplicitObjectCreationExpression:
return ImplicitObjectCreation.Create(info);
case SyntaxKind.ArrayCreationExpression:
return NormalArrayCreation.Create(info);
@@ -179,7 +182,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
return ImplicitArrayCreation.Create(info);
case SyntaxKind.AnonymousObjectCreationExpression:
return ImplicitObjectCreation.Create(info);
return AnonymousObjectCreation.Create(info);
case SyntaxKind.ComplexElementInitializerExpression:
return CollectionInitializer.Create(info);

View File

@@ -1,137 +0,0 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Entities;
using Semmle.Extraction.Kinds;
using System.IO;
using System.Linq;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal abstract class ObjectCreation<TExpressionSyntax> : Expression<TExpressionSyntax>
where TExpressionSyntax : ExpressionSyntax
{
protected ObjectCreation(ExpressionNodeInfo info)
: base(info) { }
}
// new Foo(...) { ... }.
internal class ExplicitObjectCreation : ObjectCreation<ObjectCreationExpressionSyntax>
{
private static bool IsDynamicObjectCreation(Context cx, ObjectCreationExpressionSyntax node)
{
return node.ArgumentList != null && node.ArgumentList.Arguments.Any(arg => IsDynamic(cx, arg.Expression));
}
private static ExprKind GetKind(Context cx, ObjectCreationExpressionSyntax node)
{
var si = cx.GetModel(node).GetSymbolInfo(node.Type);
return Entities.Type.IsDelegate(si.Symbol as INamedTypeSymbol) ? ExprKind.EXPLICIT_DELEGATE_CREATION : ExprKind.OBJECT_CREATION;
}
private ExplicitObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(GetKind(info.Context, (ObjectCreationExpressionSyntax)info.Node))) { }
public static Expression Create(ExpressionNodeInfo info) => new ExplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
if (Syntax.ArgumentList != null)
{
PopulateArguments(trapFile, Syntax.ArgumentList, 0);
}
var target = cx.GetModel(Syntax).GetSymbolInfo(Syntax);
var method = (IMethodSymbol)target.Symbol;
if (method != null)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
if (IsDynamicObjectCreation(cx, Syntax))
{
var name = GetDynamicName(Syntax.Type);
if (name.HasValue)
trapFile.dynamic_member_name(this, name.Value.Text);
else
cx.ModelError(Syntax, "Unable to get name for dynamic object creation.");
}
if (Syntax.Initializer != null)
{
switch (Syntax.Initializer.Kind())
{
case SyntaxKind.CollectionInitializerExpression:
CollectionInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
case SyntaxKind.ObjectInitializerExpression:
ObjectInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
default:
cx.ModelError("Unhandled initializer in object creation");
break;
}
}
TypeMention.Create(cx, Syntax.Type, this, Type);
}
private static SyntaxToken? GetDynamicName(CSharpSyntaxNode name)
{
switch (name.Kind())
{
case SyntaxKind.IdentifierName:
return ((IdentifierNameSyntax)name).Identifier;
case SyntaxKind.GenericName:
return ((GenericNameSyntax)name).Identifier;
case SyntaxKind.QualifiedName:
// We ignore any qualifiers, for now
return GetDynamicName(((QualifiedNameSyntax)name).Right);
default:
return null;
}
}
}
internal class ImplicitObjectCreation : ObjectCreation<AnonymousObjectCreationExpressionSyntax>
{
public ImplicitObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(ExprKind.OBJECT_CREATION)) { }
public static Expression Create(ExpressionNodeInfo info) =>
new ImplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
var target = cx.GetSymbolInfo(Syntax);
var method = (IMethodSymbol)target.Symbol;
if (method != null)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
var child = 0;
var objectInitializer = Syntax.Initializers.Any() ?
new Expression(new ExpressionInfo(cx, Type, Location, ExprKind.OBJECT_INIT, this, -1, false, null)) :
null;
foreach (var init in Syntax.Initializers)
{
// Create an "assignment"
var property = cx.GetModel(init).GetDeclaredSymbol(init);
var propEntity = Property.Create(cx, property);
var type = Entities.Type.Create(cx, property.GetAnnotatedType());
var loc = cx.Create(init.GetLocation());
var assignment = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, false, null));
Create(cx, init.Expression, assignment, 0);
Property.Create(cx, property);
var access = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 1, false, null));
trapFile.expr_access(access, propEntity);
}
}
}
}

View File

@@ -0,0 +1,50 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Entities;
using Semmle.Extraction.Kinds;
using System.IO;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal class AnonymousObjectCreation : Expression<AnonymousObjectCreationExpressionSyntax>
{
public AnonymousObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(ExprKind.OBJECT_CREATION)) { }
public static Expression Create(ExpressionNodeInfo info) =>
new AnonymousObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
var target = cx.GetSymbolInfo(Syntax);
var method = (IMethodSymbol)target.Symbol;
if (method != null)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
var child = 0;
var objectInitializer = Syntax.Initializers.Any() ?
new Expression(new ExpressionInfo(cx, Type, Location, ExprKind.OBJECT_INIT, this, -1, false, null)) :
null;
foreach (var init in Syntax.Initializers)
{
// Create an "assignment"
var property = cx.GetModel(init).GetDeclaredSymbol(init);
var propEntity = Property.Create(cx, property);
var type = Entities.Type.Create(cx, property.GetAnnotatedType());
var loc = cx.Create(init.GetLocation());
var assignment = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, false, null));
Create(cx, init.Expression, assignment, 0);
Property.Create(cx, property);
var access = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 1, false, null));
trapFile.expr_access(access, propEntity);
}
}
}
}

View File

@@ -0,0 +1,75 @@
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Kinds;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal abstract class BaseObjectCreation<TExpressionSyntax> : Expression<TExpressionSyntax>
where TExpressionSyntax : BaseObjectCreationExpressionSyntax
{
protected BaseObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(GetKind(info.Context, (BaseObjectCreationExpressionSyntax)info.Node)))
{
}
protected override void PopulateExpression(TextWriter trapFile)
{
if (Syntax.ArgumentList != null)
{
PopulateArguments(trapFile, Syntax.ArgumentList, 0);
}
var target = cx.GetModel(Syntax).GetSymbolInfo(Syntax);
if (target.Symbol is IMethodSymbol method)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
if (IsDynamicObjectCreation(cx, Syntax))
{
if (cx.GetModel(Syntax).GetTypeInfo(Syntax).Type is INamedTypeSymbol type &&
!string.IsNullOrEmpty(type.Name))
{
trapFile.dynamic_member_name(this, type.Name);
}
else
{
cx.ModelError(Syntax, "Unable to get name for dynamic object creation.");
}
}
if (Syntax.Initializer != null)
{
switch (Syntax.Initializer.Kind())
{
case SyntaxKind.CollectionInitializerExpression:
CollectionInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
case SyntaxKind.ObjectInitializerExpression:
ObjectInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
default:
cx.ModelError("Unhandled initializer in object creation");
break;
}
}
}
private static ExprKind GetKind(Context cx, BaseObjectCreationExpressionSyntax node)
{
var type = cx.GetModel(node).GetTypeInfo(node).Type;
return Entities.Type.IsDelegate(type as INamedTypeSymbol)
? ExprKind.EXPLICIT_DELEGATE_CREATION
: ExprKind.OBJECT_CREATION;
}
private static bool IsDynamicObjectCreation(Context cx, BaseObjectCreationExpressionSyntax node)
{
return node.ArgumentList != null &&
node.ArgumentList.Arguments.Any(arg => IsDynamic(cx, arg.Expression));
}
}
}

View File

@@ -0,0 +1,20 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.IO;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
// new Foo(...) { ... }.
internal class ExplicitObjectCreation : BaseObjectCreation<ObjectCreationExpressionSyntax>
{
private ExplicitObjectCreation(ExpressionNodeInfo info) : base(info) { }
public static Expression Create(ExpressionNodeInfo info) => new ExplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
base.PopulateExpression(trapFile);
TypeMention.Create(cx, Syntax.Type, this, Type);
}
}
}

View File

@@ -0,0 +1,19 @@
using System.IO;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal class ImplicitObjectCreation : BaseObjectCreation<ImplicitObjectCreationExpressionSyntax>
{
private ImplicitObjectCreation(ExpressionNodeInfo info) : base(info) { }
public static Expression Create(ExpressionNodeInfo info) => new ImplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
base.PopulateExpression(trapFile);
trapFile.implicitly_typed_object_creation(this);
}
}
}

View File

@@ -301,6 +301,11 @@ namespace Semmle.Extraction.CSharp
trapFile.WriteTuple("implicitly_typed_array_creation", array);
}
internal static void implicitly_typed_object_creation(this TextWriter trapFile, Expression expression)
{
trapFile.WriteTuple("implicitly_typed_object_creation", expression);
}
internal static void indexer_location(this TextWriter trapFile, Indexer indexer, Location location)
{
trapFile.WriteTuple("indexer_location", indexer, location);

View File

@@ -205,6 +205,9 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr {
*/
ObjectOrCollectionInitializer getInitializer() { result = this.getChild(-1) }
/** Holds if the type of the created object is inferred. */
predicate isImplicitlyTyped() { implicitly_typed_object_creation(this) }
override string toString() { result = "object creation of type " + this.getType().getName() }
override Expr getRawArgument(int i) {
@@ -271,6 +274,9 @@ class DelegateCreation extends Expr, @delegate_creation_expr {
*/
class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_creation_expr {
override string getAPrimaryQlClass() { result = "ExplicitDelegateCreation" }
/** Holds if the type of the created delegate is inferred. */
predicate isImplicitlyTyped() { implicitly_typed_object_creation(this) }
}
/**

View File

@@ -1078,6 +1078,8 @@ case @expr.kind of
@throw_element = @throw_expr | @throw_stmt;
@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr;
implicitly_typed_array_creation(
unique int id: @array_creation_expr ref);
@@ -1087,6 +1089,9 @@ explicitly_sized_array_creation(
stackalloc_array_creation(
unique int id: @array_creation_expr ref);
implicitly_typed_object_creation(
unique int id: @implicitly_typeable_object_creation_expr ref);
mutator_invocation_mode(
unique int id: @operator_invocation_expr ref,
int mode: int ref /* prefix = 1, postfix = 2*/);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
public class AnonObj
{
private List<AnonObj> l = new();
public int Prop1 { get; set; }
public AnonObj M1(AnonObj t)
{
this.M1(new() { Prop1 = 1 });
return new();
}
delegate void D(int x);
void M2(int x) { }
D GetM() { return new(M2); }
void MethodAdd()
{
List<int> list = new();// { 1, 2, 3 }; todo: the initializer causes an extraction error
}
}

View File

@@ -0,0 +1,7 @@
implicitlyTypedObjectCreation
| AnonymousObjectCreation.cs:7:31:7:35 | object creation of type List<AnonObj> |
| AnonymousObjectCreation.cs:13:17:13:35 | object creation of type AnonObj |
| AnonymousObjectCreation.cs:14:16:14:20 | object creation of type AnonObj |
| AnonymousObjectCreation.cs:25:26:25:30 | object creation of type List<Int32> |
implicitlyTypedDelegateCreation
| AnonymousObjectCreation.cs:21:23:21:29 | delegate creation of type D |

View File

@@ -0,0 +1,9 @@
import csharp
query predicate implicitlyTypedObjectCreation(ObjectCreation creation) {
creation.isImplicitlyTyped()
}
query predicate implicitlyTypedDelegateCreation(ExplicitDelegateCreation creation) {
creation.isImplicitlyTyped()
}

View File

@@ -1,3 +1,59 @@
AnonymousObjectCreation.cs:
# 5| [Class] AnonObj
# 7| 5: [Field] l
# 7| -1: [TypeMention] List<AnonObj>
# 7| 1: [TypeMention] AnonObj
# 7| 1: [AssignExpr] ... = ...
# 7| 0: [FieldAccess] access to field l
# 7| 1: [ObjectCreation] object creation of type List<AnonObj>
# 9| 6: [Property] Prop1
# 9| -1: [TypeMention] int
# 9| 3: [Getter] get_Prop1
# 9| 4: [Setter] set_Prop1
#-----| 2: (Parameters)
# 9| 0: [Parameter] value
# 11| 7: [Method] M1
# 11| -1: [TypeMention] AnonObj
#-----| 2: (Parameters)
# 11| 0: [Parameter] t
# 11| -1: [TypeMention] AnonObj
# 12| 4: [BlockStmt] {...}
# 13| 0: [ExprStmt] ...;
# 13| 0: [MethodCall] call to method M1
# 13| -1: [ThisAccess] this access
# 13| 0: [ObjectCreation] object creation of type AnonObj
# 13| -1: [ObjectInitializer] { ..., ... }
# 13| 0: [MemberInitializer] ... = ...
# 13| 0: [PropertyCall] access to property Prop1
# 13| 1: [IntLiteral] 1
# 14| 1: [ReturnStmt] return ...;
# 14| 0: [ObjectCreation] object creation of type AnonObj
# 17| 8: [DelegateType] D
#-----| 2: (Parameters)
# 17| 0: [Parameter] x
# 17| -1: [TypeMention] int
# 19| 9: [Method] M2
# 19| -1: [TypeMention] Void
#-----| 2: (Parameters)
# 19| 0: [Parameter] x
# 19| -1: [TypeMention] int
# 19| 4: [BlockStmt] {...}
# 21| 10: [Method] GetM
# 21| -1: [TypeMention] D
# 21| 4: [BlockStmt] {...}
# 21| 0: [ReturnStmt] return ...;
# 21| 0: [ExplicitDelegateCreation] delegate creation of type D
# 21| 0: [ImplicitDelegateCreation] delegate creation of type D
# 21| 0: [MethodAccess] access to method M2
# 23| 11: [Method] MethodAdd
# 23| -1: [TypeMention] Void
# 24| 4: [BlockStmt] {...}
# 25| 0: [LocalVariableDeclStmt] ... ...;
# 25| 0: [LocalVariableDeclAndInitExpr] List<Int32> list = ...
# 25| -1: [TypeMention] List<Int32>
# 25| 1: [TypeMention] int
# 25| 0: [LocalVariableAccess] access to local variable list
# 25| 1: [ObjectCreation] object creation of type List<Int32>
Discard.cs:
# 3| [Class] Discard
# 5| 5: [Method] M1

View File

@@ -1,3 +1,4 @@
| AnonymousObjectCreation.cs:9:29:9:31 | set_Prop1 | set |
| InitOnlyProperty.cs:12:42:12:45 | set_Prop0 | init |
| InitOnlyProperty.cs:13:37:13:40 | set_Prop1 | init |
| InitOnlyProperty.cs:14:37:14:39 | set_Prop2 | set |

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Added 'implicitly_typed_object_creation' and 'implicitly_typeable_object_creation_expr'.
compatibility: backwards