Merge pull request #12103 from michaelnebel/csharp/scopedmodfier

C# 11: Scoped parameters and local variables.
This commit is contained in:
Michael Nebel
2023-02-10 10:04:09 +01:00
committed by GitHub
21 changed files with 8573 additions and 9 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
description: Remove a relation for scoped annotations.
compatibility: backwards
scoped_annotation.rel: delete

View File

@@ -59,6 +59,19 @@ namespace Semmle.Extraction.CSharp.Entities
}
}
protected void PopulateScopedKind(TextWriter trapFile, ScopedKind kind)
{
switch (kind)
{
case ScopedKind.ScopedRef:
trapFile.scoped_annotation(this, Kinds.ScopedAnnotation.ScopedRef);
break;
case ScopedKind.ScopedValue:
trapFile.scoped_annotation(this, Kinds.ScopedAnnotation.ScopedValue);
break;
}
}
protected void ExtractCompilerGenerated(TextWriter trapFile)
{
if (Symbol.IsImplicitlyDeclared)

View File

@@ -33,7 +33,8 @@ namespace Semmle.Extraction.CSharp.Entities
PopulateRefKind(trapFile, Symbol.RefKind);
var unboundFieldKey = Field.Create(Context, Symbol.OriginalDefinition);
trapFile.fields(this, (Symbol.IsConst ? 2 : 1), Symbol.Name, ContainingType, Type.TypeRef, unboundFieldKey);
var kind = Symbol.IsConst ? VariableKind.Const : VariableKind.None;
trapFile.fields(this, kind, Symbol.Name, ContainingType, Type.TypeRef, unboundFieldKey);
PopulateModifiers(trapFile);

View File

@@ -23,17 +23,23 @@ namespace Semmle.Extraction.CSharp.Entities
public void PopulateManual(Expression parent, bool isVar)
{
var trapFile = Context.TrapWriter.Writer;
var (kind, type) = Symbol is ILocalSymbol l
? (l.IsRef ? 3 : l.IsConst ? 2 : 1, l.GetAnnotatedType())
: (1, parent.Type);
trapFile.localvars(this, kind, Symbol.Name, isVar ? 1 : 0, Type.Create(Context, type).TypeRef, parent);
var @var = isVar ? 1 : 0;
if (Symbol is ILocalSymbol local)
{
var kind = local.IsRef ? Kinds.VariableKind.Ref : local.IsConst ? Kinds.VariableKind.Const : Kinds.VariableKind.None;
var type = local.GetAnnotatedType();
trapFile.localvars(this, kind, Symbol.Name, @var, Type.Create(Context, type).TypeRef, parent);
PopulateNullability(trapFile, local.GetAnnotatedType());
PopulateScopedKind(trapFile, local.ScopedKind);
if (local.IsRef)
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
}
else
{
trapFile.localvars(this, Kinds.VariableKind.None, Symbol.Name, @var, Type.Create(Context, parent.Type).TypeRef, parent);
}
trapFile.localvar_location(this, Location);

View File

@@ -100,6 +100,7 @@ namespace Semmle.Extraction.CSharp.Entities
PopulateAttributes();
PopulateNullability(trapFile, Symbol.GetAnnotatedType());
PopulateRefKind(trapFile, Symbol.RefKind);
PopulateScopedKind(trapFile, Symbol.ScopedKind);
if (Symbol.Name != Original.Symbol.Name)
Context.ModelError(Symbol, "Inconsistent parameter declaration");

View File

@@ -0,0 +1,8 @@
namespace Semmle.Extraction.Kinds;
public enum ScopedAnnotation
{
None = 0,
ScopedRef = 1,
ScopedValue = 2
}

View File

@@ -0,0 +1,8 @@
namespace Semmle.Extraction.Kinds;
public enum VariableKind
{
None = 1,
Const = 2,
Ref = 3
}

View File

@@ -191,8 +191,8 @@ namespace Semmle.Extraction.CSharp
internal static void field_location(this TextWriter trapFile, Field field, Location location) =>
trapFile.WriteTuple("field_location", field, location);
internal static void fields(this TextWriter trapFile, Field field, int @const, string name, Type declaringType, Type fieldType, Field unboundKey) =>
trapFile.WriteTuple("fields", field, @const, name, declaringType, fieldType, unboundKey);
internal static void fields(this TextWriter trapFile, Field field, VariableKind kind, string name, Type declaringType, Type fieldType, Field unboundKey) =>
trapFile.WriteTuple("fields", field, (int)kind, name, declaringType, fieldType, unboundKey);
internal static void general_type_parameter_constraints(this TextWriter trapFile, TypeParameterConstraints constraints, int hasKind) =>
trapFile.WriteTuple("general_type_parameter_constraints", constraints, hasKind);
@@ -227,8 +227,8 @@ namespace Semmle.Extraction.CSharp
internal static void localvar_location(this TextWriter trapFile, LocalVariable var, Location location) =>
trapFile.WriteTuple("localvar_location", var, location);
internal static void localvars(this TextWriter trapFile, LocalVariable key, int @const, string name, int @var, Type type, Expression expr) =>
trapFile.WriteTuple("localvars", key, @const, name, @var, type, expr);
internal static void localvars(this TextWriter trapFile, LocalVariable key, VariableKind kind, string name, int @var, Type type, Expression expr) =>
trapFile.WriteTuple("localvars", key, (int)kind, name, @var, type, expr);
public static void metadata_handle(this TextWriter trapFile, IEntity entity, Location assembly, int handleValue) =>
trapFile.WriteTuple("metadata_handle", entity, assembly, handleValue);
@@ -462,5 +462,8 @@ namespace Semmle.Extraction.CSharp
internal static void file_extraction_mode(this System.IO.TextWriter trapFile, Entities.File file, ExtractorMode mode) =>
trapFile.WriteTuple("file_extraction_mode", file, mode);
internal static void scoped_annotation(this TextWriter trapFile, IEntity element, ScopedAnnotation @scoped) =>
trapFile.WriteTuple("scoped_annotation", element, (int)@scoped);
}
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* C# 11: Added extractor support for the `scoped` modifier annotation on parameters and local variables.

View File

@@ -71,6 +71,11 @@ class LocalScopeVariable extends Variable, @local_scope_variable {
*/
predicate isRef() { none() }
/**
* Holds if this local variable or parameter is `scoped`.
*/
predicate isScoped() { scoped_annotation(this, _) }
override predicate hasQualifiedName(string qualifier, string name) { none() }
}

View File

@@ -878,6 +878,13 @@ param_location(
int id: @parameter ref,
int loc: @location ref);
@has_scoped_annotation = @local_scope_variable
scoped_annotation(
int id: @has_scoped_annotation ref,
int kind: int ref // scoped ref = 1, scoped value = 2
);
/** STATEMENTS **/
@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent;

View File

@@ -26228,6 +26228,59 @@
</dep>
</dependencies>
</relation>
<relation>
<name>scoped_annotation</name>
<cardinality>43498</cardinality>
<columnsizes>
<e>
<k>id</k>
<v>43498</v>
</e>
<e>
<k>kind</k>
<v>18</v>
</e>
</columnsizes>
<dependencies>
<dep>
<src>id</src>
<trg>kind</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>1</a>
<b>2</b>
<v>43498</v>
</b>
</bs>
</hist>
</val>
</dep>
<dep>
<src>kind</src>
<trg>id</trg>
<val>
<hist>
<budget>12</budget>
<bs>
<b>
<a>11</a>
<b>12</b>
<v>9</v>
</b>
<b>
<a>4599</a>
<b>4600</b>
<v>9</v>
</b>
</bs>
</hist>
</val>
</dep>
</dependencies>
</relation>
<relation>
<name>statements</name>
<cardinality>2458000</cardinality>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Add a relation for scoped annotations.
compatibility: backwards

View File

@@ -343,6 +343,87 @@ PatternMatchSpan.cs:
# 16| 2: [DefaultCase] default:
# 16| 3: [BlockStmt] {...}
# 16| 0: [BreakStmt] break;
Scoped.cs:
# 1| [Struct] S1
# 2| [Struct] S2
# 7| [Class] ScopedModifierTest
# 9| 5: [Method] M1
# 9| -1: [TypeMention] int
#-----| 2: (Parameters)
# 9| 0: [Parameter] x1
# 9| -1: [TypeMention] int
# 9| 1: [Parameter] y1
# 9| -1: [TypeMention] int
# 10| 4: [BlockStmt] {...}
# 13| 0: [ReturnStmt] return ...;
# 13| 0: [RefExpr] ref ...
# 13| 0: [ParameterAccess] access to parameter y1
# 16| 6: [Method] M2
# 16| -1: [TypeMention] int
#-----| 2: (Parameters)
# 16| 0: [Parameter] x2
# 16| -1: [TypeMention] int
# 16| 1: [Parameter] y2
# 16| -1: [TypeMention] int
# 17| 4: [BlockStmt] {...}
# 18| 0: [ExprStmt] ...;
# 18| 0: [AssignExpr] ... = ...
# 18| 0: [ParameterAccess] access to parameter x2
# 18| 1: [IntLiteral] 0
# 21| 1: [ReturnStmt] return ...;
# 21| 0: [RefExpr] ref ...
# 21| 0: [ParameterAccess] access to parameter y2
# 24| 7: [Method] M3
# 24| -1: [TypeMention] int
#-----| 2: (Parameters)
# 24| 0: [Parameter] x3
# 24| -1: [TypeMention] int
# 25| 4: [BlockStmt] {...}
# 27| 0: [ReturnStmt] return ...;
# 27| 0: [ParameterAccess] access to parameter x3
# 30| 8: [Method] M4
# 30| -1: [TypeMention] S1
#-----| 2: (Parameters)
# 30| 0: [Parameter] x4
# 30| -1: [TypeMention] S1
# 31| 4: [BlockStmt] {...}
# 33| 0: [ReturnStmt] return ...;
# 33| 0: [ParameterAccess] access to parameter x4
# 36| 9: [Method] M5
# 36| -1: [TypeMention] S2
#-----| 2: (Parameters)
# 36| 0: [Parameter] x5
# 36| -1: [TypeMention] S2
# 37| 4: [BlockStmt] {...}
# 40| 0: [ReturnStmt] return ...;
# 40| 0: [ObjectCreation] object creation of type S2
# 40| 0: [TypeMention] S2
# 43| 10: [Method] M6
# 43| -1: [TypeMention] S2
#-----| 2: (Parameters)
# 43| 0: [Parameter] x6
# 43| -1: [TypeMention] S2
# 44| 4: [BlockStmt] {...}
# 47| 0: [ReturnStmt] return ...;
# 47| 0: [ObjectCreation] object creation of type S2
# 47| 0: [TypeMention] S2
# 50| 11: [Method] Locals
# 50| -1: [TypeMention] S2
# 51| 4: [BlockStmt] {...}
# 52| 0: [LocalVariableDeclStmt] ... ...;
# 52| 0: [LocalVariableDeclAndInitExpr] S2 x7 = ...
# 52| -1: [TypeMention] null
# 52| 0: [LocalVariableAccess] access to local variable x7
# 52| 1: [ObjectCreation] object creation of type S2
# 52| 0: [TypeMention] S2
# 55| 1: [LocalVariableDeclStmt] ... ...;
# 55| 0: [LocalVariableDeclAndInitExpr] S2 y7 = ...
# 55| -1: [TypeMention] S2
# 55| 0: [LocalVariableAccess] access to local variable y7
# 55| 1: [ObjectCreation] object creation of type S2
# 55| 0: [TypeMention] S2
# 56| 2: [ReturnStmt] return ...;
# 56| 0: [LocalVariableAccess] access to local variable y7
SignAnalysis.cs:
# 1| [Class] MySignAnalysis
# 4| 5: [Method] UnsignedRightShiftSign

View File

@@ -0,0 +1,59 @@
public struct S1 { }
public ref struct S2 { }
// The `scoped` modifier can be applied to parameters
// or local variables. The type of the parameter or
// local variable must be either a `ref` value or `ref struct` value.
public class ScopedModifierTest
{
public ref int M1(scoped ref int x1, ref int y1)
{
// Not allowed.
// return ref x1;
return ref y1;
}
public ref int M2(scoped out int x2, ref int y2)
{
x2 = 0;
// Not allowed.
// return ref x;
return ref y2;
}
public int M3(scoped ref int x3)
{
// Allowed is it is not returned by reference.
return x3;
}
public S1 M4(scoped ref S1 x4)
{
// Allowed as it is not returned by reference.
return x4;
}
public S2 M5(scoped S2 x5)
{
// Not allowed.
// return x5;
return new S2();
}
public S2 M6(scoped ref S2 x6)
{
// Not allowed.
// return x6;
return new S2();
}
public S2 Locals()
{
scoped S2 x7 = new S2();
// Not allowed.
// return x7;
S2 y7 = new S2();
return y7;
}
}

View File

@@ -0,0 +1,7 @@
| Scoped.cs:9:38:9:39 | x1 | Parameter |
| Scoped.cs:16:38:16:39 | x2 | Parameter |
| Scoped.cs:24:34:24:35 | x3 | Parameter |
| Scoped.cs:30:32:30:33 | x4 | Parameter |
| Scoped.cs:36:28:36:29 | x5 | Parameter |
| Scoped.cs:43:32:43:33 | x6 | Parameter |
| Scoped.cs:52:19:52:20 | x7 | LocalVariable |

View File

@@ -0,0 +1,5 @@
import csharp
from LocalScopeVariable v
where v.getFile().getStem() = "Scoped" and v.isScoped()
select v, v.getAPrimaryQlClass()