Merge pull request #7749 from michaelnebel/csharp/lambda-improvements

C# 10 - Lambda improvements.
This commit is contained in:
Michael Nebel
2022-02-08 11:28:55 +01:00
committed by GitHub
19 changed files with 10335 additions and 1916 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
description: Remove 'lambda_expr_return_type' relation.
compatibility: backwards
lambda_expr_return_type.rel: delete

View File

@@ -22,12 +22,13 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
Parameter.Create(Context, symbol, this);
}
private Lambda(ExpressionNodeInfo info, CSharpSyntaxNode body, IEnumerable<ParameterSyntax> @params)
private Lambda(ExpressionNodeInfo info, CSharpSyntaxNode body, IEnumerable<ParameterSyntax> @params, TypeSyntax? @return)
: base(info)
{
if (Context.GetModel(info.Node).GetSymbolInfo(info.Node).Symbol is IMethodSymbol symbol)
{
Modifier.ExtractModifiers(Context, info.Context.TrapWriter.Writer, this, symbol);
Attribute.ExtractAttributes(Context, symbol, this);
}
else
{
@@ -40,6 +41,13 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
foreach (var param in @params)
VisitParameter(param);
if (@return is not null)
{
var symbol = Context.GetType(@return);
var type = Entities.Type.Create(Context, symbol);
var trapFile = Context.TrapWriter.Writer;
trapFile.lambda_expr_return_type(this, type.TypeRef);
}
if (body is ExpressionSyntax exprBody)
Create(Context, exprBody, this, 0);
else if (body is BlockSyntax blockBody)
@@ -50,17 +58,17 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
}
private Lambda(ExpressionNodeInfo info, ParenthesizedLambdaExpressionSyntax node)
: this(info.SetKind(ExprKind.LAMBDA), node.Body, node.ParameterList.Parameters) { }
: this(info.SetKind(ExprKind.LAMBDA), node.Body, node.ParameterList.Parameters, node.ReturnType) { }
public static Lambda Create(ExpressionNodeInfo info, ParenthesizedLambdaExpressionSyntax node) => new Lambda(info, node);
private Lambda(ExpressionNodeInfo info, SimpleLambdaExpressionSyntax node)
: this(info.SetKind(ExprKind.LAMBDA), node.Body, Enumerators.Singleton(node.Parameter)) { }
: this(info.SetKind(ExprKind.LAMBDA), node.Body, Enumerators.Singleton(node.Parameter), null) { }
public static Lambda Create(ExpressionNodeInfo info, SimpleLambdaExpressionSyntax node) => new Lambda(info, node);
private Lambda(ExpressionNodeInfo info, AnonymousMethodExpressionSyntax node) :
this(info.SetKind(ExprKind.ANONYMOUS_METHOD), node.Body, node.ParameterList is null ? Enumerable.Empty<ParameterSyntax>() : node.ParameterList.Parameters)
this(info.SetKind(ExprKind.ANONYMOUS_METHOD), node.Body, node.ParameterList is null ? Enumerable.Empty<ParameterSyntax>() : node.ParameterList.Parameters, null)
{ }
public static Lambda Create(ExpressionNodeInfo info, AnonymousMethodExpressionSyntax node) => new Lambda(info, node);

View File

@@ -215,6 +215,9 @@ namespace Semmle.Extraction.CSharp
internal static void indexers(this TextWriter trapFile, Indexer propKey, string name, Type declaringType, Type memberType, Indexer unboundProperty) =>
trapFile.WriteTuple("indexers", propKey, name, declaringType, memberType, unboundProperty);
internal static void lambda_expr_return_type(this TextWriter trapFile, Lambda expr, Type returnType) =>
trapFile.WriteTuple("lambda_expr_return_type", expr, returnType);
internal static void local_function_stmts(this TextWriter trapFile, Entities.Statements.LocalFunction fnStmt, LocalFunction fn) =>
trapFile.WriteTuple("local_function_stmts", fnStmt, fn);

View File

@@ -0,0 +1,6 @@
---
category: majorAnalysis
---
Added support for C# 10 lambda improvements
* Explicit return types on lambda expressions.
* Lambda expression can be tagged with method and return value attributes.

View File

@@ -10,8 +10,8 @@ private import TypeRef
* An element that can have attributes. Either an assembly (`Assembly`), a field (`Field`),
* a parameter (`Parameter`), an operator (`Operator`), a method (`Method`), a constructor (`Constructor`),
* a destructor (`Destructor`), a callable accessor (`CallableAccessor`), a value or reference type
* (`ValueOrRefType`), a declaration with accessors (`DeclarationWithAccessors`), or a local function
* (`LocalFunction`).
* (`ValueOrRefType`), a declaration with accessors (`DeclarationWithAccessors`), a local function
* (`LocalFunction`) or a lambda expression (`LambdaExp`).
*/
class Attributable extends @attributable {
/** Gets an attribute attached to this element, if any. */

View File

@@ -5,6 +5,7 @@
import Expr
import semmle.code.csharp.Callable
private import semmle.code.csharp.frameworks.system.linq.Expressions
private import semmle.code.csharp.TypeRef
/**
* Either an object initializer (`ObjectInitializer`) or a collection
@@ -434,6 +435,12 @@ class AnonymousFunctionExpr extends Expr, Callable, Modifiable, @anonymous_funct
* A lambda expression, for example `(int x) => x + 1`.
*/
class LambdaExpr extends AnonymousFunctionExpr, @lambda_expr {
/* Holds if this lambda expression has explicit return type. */
predicate hasExplicitReturnType() { lambda_expr_return_type(this, _) }
/* Gets the explicit return type of this lambda expression, if any. */
Type getExplicitReturnType() { lambda_expr_return_type(this, getTypeRef(result)) }
override string toString() { result = "(...) => ..." }
override string getAPrimaryQlClass() { result = "LambdaExpr" }

View File

@@ -191,7 +191,7 @@ sourceLocationPrefix(
@attributable = @assembly | @field | @parameter | @operator | @method | @constructor
| @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors
| @local_function;
| @local_function | @lambda_expr;
/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/
@@ -1269,6 +1269,10 @@ expr_argument_name(
unique int id: @expr ref,
string name: string ref);
lambda_expr_return_type(
unique int id: @lambda_expr ref,
int type_id: @type_or_ref ref);
/** CONTROL/DATA FLOW **/
@control_flow_element = @stmt | @expr;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Add relation 'lambda_expr_return_type' for lambdas with explicit return types.
compatibility: backwards

View File

@@ -0,0 +1,20 @@
using System;
public class Lambda
{
public void M1()
{
// Examples need for implicitly typed lambdas.
Func<int, string> f1 = (int x) => x.ToString();
var f2 = (int x) => x.ToString();
// Examples need for explicit return type for implicitly and explicitly typed lambda.
var f3 = object (bool b) => b ? "1" : 0;
Func<bool, object> f4 = object (bool b) => b ? "1" : 0;
// Examples needed for explicit return type for downcast.
var f5 = int (bool b) => b ? 1 : 0;
var f6 = object (bool b) => b ? 1 : 0;
}
}

View File

@@ -0,0 +1,18 @@
using System;
public class Example : Attribute
{
public Example(int x) { }
}
public class LambdaAttributes
{
public void M1()
{
// Examples needed for attributes.
var f7 = ([Example(1)] int x) => x.ToString(); // Parameter attribute
var f8 =[Example(2)] (int x) => x.ToString(); // Lambda attribute
var f9 =[return: Example(3)] (int x) => x.ToString(); // Return value attribute
}
}

View File

@@ -0,0 +1,23 @@
lambdaDeclaration
| Func<bool, int> | Lambda.cs:16:13:16:14 | f5 | Lambda.cs:16:18:16:42 | (...) => ... |
| Func<bool, object> | Lambda.cs:12:13:12:14 | f3 | Lambda.cs:12:18:12:47 | (...) => ... |
| Func<bool, object> | Lambda.cs:13:28:13:29 | f4 | Lambda.cs:13:33:13:62 | (...) => ... |
| Func<bool, object> | Lambda.cs:17:13:17:14 | f6 | Lambda.cs:17:18:17:45 | (...) => ... |
| Func<int, string> | Lambda.cs:8:27:8:28 | f1 | Lambda.cs:8:32:8:54 | (...) => ... |
| Func<int, string> | Lambda.cs:9:13:9:14 | f2 | Lambda.cs:9:18:9:40 | (...) => ... |
| Func<int, string> | LambdaAttributes.cs:14:13:14:14 | f7 | LambdaAttributes.cs:14:18:14:53 | (...) => ... |
| Func<int, string> | LambdaAttributes.cs:15:13:15:14 | f8 | LambdaAttributes.cs:15:17:15:52 | (...) => ... |
| Func<int, string> | LambdaAttributes.cs:16:13:16:14 | f9 | LambdaAttributes.cs:16:17:16:60 | (...) => ... |
lambdaDeclarationNatural
| Func<bool, int> | Lambda.cs:16:13:16:14 | f5 | Lambda.cs:16:18:16:42 | (...) => ... |
| Func<bool, object> | Lambda.cs:12:13:12:14 | f3 | Lambda.cs:12:18:12:47 | (...) => ... |
| Func<bool, object> | Lambda.cs:17:13:17:14 | f6 | Lambda.cs:17:18:17:45 | (...) => ... |
| Func<int, string> | Lambda.cs:9:13:9:14 | f2 | Lambda.cs:9:18:9:40 | (...) => ... |
| Func<int, string> | LambdaAttributes.cs:14:13:14:14 | f7 | LambdaAttributes.cs:14:18:14:53 | (...) => ... |
| Func<int, string> | LambdaAttributes.cs:15:13:15:14 | f8 | LambdaAttributes.cs:15:17:15:52 | (...) => ... |
| Func<int, string> | LambdaAttributes.cs:16:13:16:14 | f9 | LambdaAttributes.cs:16:17:16:60 | (...) => ... |
lambdaDeclarationExplicitReturnType
| Func<bool, int> | int | int | Lambda.cs:16:13:16:14 | f5 | Lambda.cs:16:18:16:42 | (...) => ... |
| Func<bool, object> | object | object | Lambda.cs:12:13:12:14 | f3 | Lambda.cs:12:18:12:47 | (...) => ... |
| Func<bool, object> | object | object | Lambda.cs:13:28:13:29 | f4 | Lambda.cs:13:33:13:62 | (...) => ... |
| Func<bool, object> | object | object | Lambda.cs:17:13:17:14 | f6 | Lambda.cs:17:18:17:45 | (...) => ... |

View File

@@ -0,0 +1,25 @@
import csharp
private predicate getLambda(
LocalVariableDeclAndInitExpr e, string type, LocalVariable v, LambdaExpr lexp
) {
lexp = e.getRValue() and
v = e.getTargetVariable() and
type = e.getType().toStringWithTypes()
}
query predicate lambdaDeclaration(string type, LocalVariable v, LambdaExpr lexp) {
getLambda(_, type, v, lexp)
}
query predicate lambdaDeclarationNatural(string type, LocalVariable v, LambdaExpr lexp) {
exists(LocalVariableDeclAndInitExpr e | getLambda(e, type, v, lexp) and e.isImplicitlyTyped())
}
query predicate lambdaDeclarationExplicitReturnType(
string type, string explicit, string actual, LocalVariable v, LambdaExpr lexp
) {
getLambda(_, type, v, lexp) and
explicit = lexp.getExplicitReturnType().toStringWithTypes() and
actual = lexp.getReturnType().toStringWithTypes()
}

View File

@@ -0,0 +1,7 @@
allAttributes
| LambdaAttributes.cs:14:20:14:26 | [Example(...)] | LambdaAttributes.cs:14:28:14:28 | 1 | Parameter |
| LambdaAttributes.cs:15:18:15:24 | [Example(...)] | LambdaAttributes.cs:15:26:15:26 | 2 | LambdaExpr |
| LambdaAttributes.cs:16:26:16:32 | [return: Example(...)] | LambdaAttributes.cs:16:34:16:34 | 3 | LambdaExpr |
lambdaAttributes
| LambdaAttributes.cs:15:18:15:24 | [Example(...)] | LambdaAttributes.cs:15:26:15:26 | 2 | LambdaAttributes.cs:15:17:15:52 | (...) => ... |
| LambdaAttributes.cs:16:26:16:32 | [return: Example(...)] | LambdaAttributes.cs:16:34:16:34 | 3 | LambdaAttributes.cs:16:17:16:60 | (...) => ... |

View File

@@ -0,0 +1,11 @@
import csharp
query predicate allAttributes(Attribute a, Expr arg, string c) {
a.fromSource() and
arg = a.getArgument(0) and
c = a.getTarget().(Element).getAPrimaryQlClass()
}
query predicate lambdaAttributes(Attribute a, Expr arg, LambdaExpr l) {
allAttributes(a, arg, _) and a.getTarget() = l
}