mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge pull request #11996 from michaelnebel/csharp/refstructreffield
C# 11: Extractor support for `ref` fields in `ref struct`.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
class AnnotatedElement extends @cil_has_type_annotation {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Field extends @cil_field {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AnnotatedElement element, int annotation
|
||||
where cil_type_annotation(element, annotation) and not element instanceof Field
|
||||
select element, annotation
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
class AnnotatedElement extends @has_type_annotation {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Field extends @field {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AnnotatedElement element, int annotation
|
||||
where type_annotation(element, annotation) and not element instanceof Field
|
||||
select element, annotation
|
||||
@@ -0,0 +1,4 @@
|
||||
description: Remove CIL fields as entities that can have type annotations and remove field type annotations.
|
||||
compatibility: backwards
|
||||
cil_type_annotation.rel: run cil_type_annotation.qlo
|
||||
type_annotation.rel: run type_annotation.qlo
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
@@ -38,6 +35,11 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
t = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
if (t is ByRefType brt)
|
||||
{
|
||||
t = brt.ElementType;
|
||||
yield return Tuples.cil_type_annotation(this, TypeAnnotation.Ref);
|
||||
}
|
||||
yield return Tuples.cil_field(this, DeclaringType, Name, t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
PopulateAttributes();
|
||||
ContainingType!.PopulateGenerics();
|
||||
PopulateNullability(trapFile, Symbol.GetAnnotatedType());
|
||||
PopulateRefKind(trapFile, Symbol.RefKind);
|
||||
|
||||
var unboundFieldKey = Field.Create(Context, Symbol.OriginalDefinition);
|
||||
trapFile.fields(this, (Symbol.IsConst ? 2 : 1), Symbol.Name, ContainingType, Type.TypeRef, unboundFieldKey);
|
||||
|
||||
@@ -25,19 +25,14 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
public static string AccessibilityModifier(Accessibility access)
|
||||
{
|
||||
switch (access)
|
||||
return access switch
|
||||
{
|
||||
case Accessibility.Private:
|
||||
return "private";
|
||||
case Accessibility.Protected:
|
||||
return "protected";
|
||||
case Accessibility.Public:
|
||||
return "public";
|
||||
case Accessibility.Internal:
|
||||
return "internal";
|
||||
default:
|
||||
throw new InternalError("Unavailable modifier combination");
|
||||
}
|
||||
Accessibility.Private => Modifiers.Private,
|
||||
Accessibility.Protected => Modifiers.Protected,
|
||||
Accessibility.Public => Modifiers.Public,
|
||||
Accessibility.Internal => Modifiers.Internal,
|
||||
_ => throw new InternalError("Unavailable modifier combination"),
|
||||
};
|
||||
}
|
||||
|
||||
public static void HasAccessibility(Context cx, TextWriter trapFile, IEntity type, Accessibility access)
|
||||
@@ -48,17 +43,17 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
case Accessibility.Public:
|
||||
case Accessibility.Protected:
|
||||
case Accessibility.Internal:
|
||||
HasModifier(cx, trapFile, type, Modifier.AccessibilityModifier(access));
|
||||
HasModifier(cx, trapFile, type, AccessibilityModifier(access));
|
||||
break;
|
||||
case Accessibility.NotApplicable:
|
||||
break;
|
||||
case Accessibility.ProtectedOrInternal:
|
||||
HasModifier(cx, trapFile, type, "protected");
|
||||
HasModifier(cx, trapFile, type, "internal");
|
||||
HasModifier(cx, trapFile, type, Modifiers.Protected);
|
||||
HasModifier(cx, trapFile, type, Modifiers.Internal);
|
||||
break;
|
||||
case Accessibility.ProtectedAndInternal:
|
||||
HasModifier(cx, trapFile, type, "protected");
|
||||
HasModifier(cx, trapFile, type, "private");
|
||||
HasModifier(cx, trapFile, type, Modifiers.Protected);
|
||||
HasModifier(cx, trapFile, type, Modifiers.Private);
|
||||
break;
|
||||
default:
|
||||
throw new InternalError($"Unhandled Microsoft.CodeAnalysis.Accessibility value: {access}");
|
||||
@@ -70,6 +65,27 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.has_modifiers(target, Modifier.Create(cx, modifier));
|
||||
}
|
||||
|
||||
private static void ExtractNamedTypeModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol)
|
||||
{
|
||||
if (symbol.Kind != SymbolKind.NamedType)
|
||||
return;
|
||||
|
||||
if (symbol is not INamedTypeSymbol nt)
|
||||
throw new InternalError(symbol, "Symbol kind is inconsistent with its type");
|
||||
|
||||
if (nt.IsRecord)
|
||||
HasModifier(cx, trapFile, key, Modifiers.Record);
|
||||
|
||||
if (nt.TypeKind == TypeKind.Struct)
|
||||
{
|
||||
if (nt.IsReadOnly)
|
||||
HasModifier(cx, trapFile, key, Modifiers.Readonly);
|
||||
|
||||
if (nt.IsRefLikeType)
|
||||
HasModifier(cx, trapFile, key, Modifiers.Ref);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol)
|
||||
{
|
||||
HasAccessibility(cx, trapFile, key, symbol.DeclaredAccessibility);
|
||||
@@ -77,51 +93,35 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.has_modifiers(key, Modifier.Create(cx, Accessibility.Public));
|
||||
|
||||
if (symbol.IsAbstract && (symbol.Kind != SymbolKind.NamedType || ((INamedTypeSymbol)symbol).TypeKind != TypeKind.Interface))
|
||||
HasModifier(cx, trapFile, key, "abstract");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Abstract);
|
||||
|
||||
if (symbol.IsSealed)
|
||||
HasModifier(cx, trapFile, key, "sealed");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Sealed);
|
||||
|
||||
var fromSource = symbol.DeclaringSyntaxReferences.Length > 0;
|
||||
|
||||
if (symbol.IsStatic && !(symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsConst && !fromSource))
|
||||
HasModifier(cx, trapFile, key, "static");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Static);
|
||||
|
||||
if (symbol.IsVirtual)
|
||||
HasModifier(cx, trapFile, key, "virtual");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Virtual);
|
||||
|
||||
if (symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsReadOnly)
|
||||
HasModifier(cx, trapFile, key, "readonly");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Readonly);
|
||||
|
||||
if (symbol.IsOverride)
|
||||
HasModifier(cx, trapFile, key, "override");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Override);
|
||||
|
||||
if (symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).IsAsync)
|
||||
HasModifier(cx, trapFile, key, "async");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Async);
|
||||
|
||||
if (symbol.IsExtern)
|
||||
HasModifier(cx, trapFile, key, "extern");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Extern);
|
||||
|
||||
foreach (var modifier in symbol.GetSourceLevelModifiers())
|
||||
HasModifier(cx, trapFile, key, modifier);
|
||||
|
||||
if (symbol.Kind == SymbolKind.NamedType)
|
||||
{
|
||||
var nt = symbol as INamedTypeSymbol;
|
||||
if (nt is null)
|
||||
throw new InternalError(symbol, "Symbol kind is inconsistent with its type");
|
||||
|
||||
if (nt.IsRecord)
|
||||
HasModifier(cx, trapFile, key, "record");
|
||||
|
||||
if (nt.TypeKind == TypeKind.Struct)
|
||||
{
|
||||
if (nt.IsReadOnly)
|
||||
HasModifier(cx, trapFile, key, "readonly");
|
||||
if (nt.IsRefLikeType)
|
||||
HasModifier(cx, trapFile, key, "ref");
|
||||
}
|
||||
}
|
||||
ExtractNamedTypeModifiers(cx, trapFile, key, symbol);
|
||||
}
|
||||
|
||||
public static Modifier Create(Context cx, string modifier)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
internal static class Modifiers
|
||||
{
|
||||
public const string Abstract = "abstract";
|
||||
public const string Async = "async";
|
||||
public const string Const = "const";
|
||||
public const string Extern = "extern";
|
||||
public const string Internal = "internal";
|
||||
public const string New = "new";
|
||||
public const string Override = "override";
|
||||
public const string Partial = "partial";
|
||||
public const string Private = "private";
|
||||
public const string Protected = "protected";
|
||||
public const string Public = "public";
|
||||
public const string Readonly = "readonly";
|
||||
public const string Record = "record";
|
||||
public const string Ref = "ref";
|
||||
public const string Sealed = "sealed";
|
||||
public const string Static = "static";
|
||||
public const string Virtual = "virtual";
|
||||
}
|
||||
4
csharp/ql/lib/change-notes/2023-01-26-reffields.md
Normal file
4
csharp/ql/lib/change-notes/2023-01-26-reffields.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* C# 11: Added extractor support for `ref` fields in `ref struct` declarations.
|
||||
@@ -152,4 +152,7 @@ class Field extends DotNet::Field, Variable, Member, CustomModifierReceiver, @ci
|
||||
override ValueOrRefType getDeclaringType() { cil_field(this, result, _, _) }
|
||||
|
||||
override Location getLocation() { result = this.getDeclaringType().getLocation() }
|
||||
|
||||
/** Holds if this declaration is `ref`. */
|
||||
predicate isRef() { cil_type_annotation(this, 32) }
|
||||
}
|
||||
|
||||
@@ -399,6 +399,12 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent
|
||||
/** Holds if this field is `volatile`. */
|
||||
predicate isVolatile() { this.hasModifier("volatile") }
|
||||
|
||||
/** Holds if this is a `ref` field. */
|
||||
predicate isRef() { this.getAnnotatedType().isRef() }
|
||||
|
||||
/** Holds if this is a `ref readonly` field. */
|
||||
predicate isReadonlyRef() { this.getAnnotatedType().isReadonlyRef() }
|
||||
|
||||
/** Holds if this field is `readonly`. */
|
||||
predicate isReadOnly() { this.hasModifier("readonly") }
|
||||
|
||||
|
||||
@@ -1868,7 +1868,7 @@ cil_field(
|
||||
@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event;
|
||||
@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type;
|
||||
@cil_parameterizable = @cil_method | @cil_function_pointer_type;
|
||||
@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_method | @cil_function_pointer_type;
|
||||
@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_field | @cil_method | @cil_function_pointer_type;
|
||||
|
||||
#keyset[parameterizable, index]
|
||||
cil_parameter(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Add CIL fields as entities that can have type annotations.
|
||||
compatibility: backwards
|
||||
15
csharp/ql/test/library-tests/csharp11/Struct.cs_
Normal file
15
csharp/ql/test/library-tests/csharp11/Struct.cs_
Normal file
@@ -0,0 +1,15 @@
|
||||
// TODO: Test needs to be enabled when .NET 7 is used as the runtime.
|
||||
namespace structassembly;
|
||||
|
||||
public class MyEmptyClass { }
|
||||
|
||||
public ref struct RefStruct
|
||||
{
|
||||
public int MyInt;
|
||||
public ref byte MyByte;
|
||||
public ref object MyObject;
|
||||
internal ref MyEmptyClass MyEmptyClass;
|
||||
public ref readonly byte MyReadonlyByte;
|
||||
public readonly ref object MyReadonlyObject;
|
||||
public readonly ref readonly string MyReadonlyString;
|
||||
}
|
||||
14
csharp/ql/test/library-tests/csharp11/cil/Struct.cs_
Normal file
14
csharp/ql/test/library-tests/csharp11/cil/Struct.cs_
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace structassembly;
|
||||
|
||||
public class MyEmptyClass { }
|
||||
|
||||
public ref struct RefStruct
|
||||
{
|
||||
public int MyInt;
|
||||
public ref byte MyByte;
|
||||
public ref object MyObject;
|
||||
internal ref MyEmptyClass MyEmptyClass;
|
||||
public ref readonly byte MyReadonlyByte;
|
||||
public readonly ref object MyReadonlyObject;
|
||||
public readonly ref readonly string MyReadonlyString;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
| file://:0:0:0:0 | MyByte | Byte |
|
||||
| file://:0:0:0:0 | MyEmptyClass | MyEmptyClass |
|
||||
| file://:0:0:0:0 | MyObject | Object |
|
||||
| file://:0:0:0:0 | MyReadonlyByte | Byte |
|
||||
| file://:0:0:0:0 | MyReadonlyObject | Object |
|
||||
| file://:0:0:0:0 | MyReadonlyString | String |
|
||||
5
csharp/ql/test/library-tests/csharp11/cil/refField.ql
Normal file
5
csharp/ql/test/library-tests/csharp11/cil/refField.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import cil
|
||||
|
||||
query predicate cilfields(CIL::Field f, string type) {
|
||||
f.isRef() and type = f.getType().toString() and f.getDeclaringType().getName() = "RefStruct"
|
||||
}
|
||||
BIN
csharp/ql/test/library-tests/csharp11/cil/structassembly.dll
Normal file
BIN
csharp/ql/test/library-tests/csharp11/cil/structassembly.dll
Normal file
Binary file not shown.
3
csharp/ql/test/library-tests/csharp11/refField.expected
Normal file
3
csharp/ql/test/library-tests/csharp11/refField.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
reffields
|
||||
readonlyreffields
|
||||
readonlyfield
|
||||
16
csharp/ql/test/library-tests/csharp11/refField.ql
Normal file
16
csharp/ql/test/library-tests/csharp11/refField.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
import csharp
|
||||
|
||||
query predicate reffields(Field f) {
|
||||
f.getFile().getStem() = "Struct" and
|
||||
f.isRef()
|
||||
}
|
||||
|
||||
query predicate readonlyreffields(Field f) {
|
||||
f.getFile().getStem() = "Struct" and
|
||||
f.isReadonlyRef()
|
||||
}
|
||||
|
||||
query predicate readonlyfield(Field f) {
|
||||
f.getFile().getStem() = "Struct" and
|
||||
f.isReadOnly()
|
||||
}
|
||||
Reference in New Issue
Block a user