Skip to content

Commit c39fe59

Browse files
committed
C#: Populate UnknownType
1 parent 6b1ac73 commit c39fe59

File tree

11 files changed

+89
-28
lines changed

11 files changed

+89
-28
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp;
33
using Semmle.Extraction.CSharp.Populators;
4-
using Semmle.Extraction.Entities;
54
using System;
65
using System.Collections.Generic;
76
using System.IO;
@@ -36,6 +35,7 @@ public override void Populate(TextWriter trapFile)
3635
{
3736
if (Symbol.TypeKind == TypeKind.Error)
3837
{
38+
UnknownType.Create(Context); // make sure this exists so we can use it in `TypeRef::getReferencedType`
3939
Context.Extractor.MissingType(Symbol.ToString()!, Context.FromSource);
4040
return;
4141
}
@@ -48,7 +48,7 @@ public override void Populate(TextWriter trapFile)
4848
if (Symbol.IsBoundNullable())
4949
{
5050
// An instance of Nullable<T>
51-
trapFile.nullable_underlying_type(this, Create(Context, Symbol.TypeArguments[0]).TypeRef);
51+
trapFile.nullable_underlying_type(this, TypeArguments[0].TypeRef);
5252
}
5353
else if (Symbol.IsReallyUnbound())
5454
{
@@ -67,7 +67,7 @@ public override void Populate(TextWriter trapFile)
6767
: Type.Create(Context, Symbol.ConstructedFrom);
6868
trapFile.constructed_generic(this, unbound.TypeRef);
6969

70-
for (var i = 0; i < Symbol.TypeArguments.Length; ++i)
70+
for (var i = 0; i < TypeArguments.Length; ++i)
7171
{
7272
trapFile.type_arguments(TypeArguments[i].TypeRef, i, this);
7373
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp.Syntax;
3-
using Semmle.Util;
43
using System;
54
using System.Collections.Generic;
65
using System.IO;
@@ -24,9 +23,9 @@ public static bool ConstructedOrParentIsConstructed(INamedTypeSymbol symbol)
2423
symbol.ContainingType is not null && ConstructedOrParentIsConstructed(symbol.ContainingType);
2524
}
2625

27-
private static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool constructUnderlyingTupleType)
26+
public Kinds.TypeKind GetTypeKind(Context cx, bool constructUnderlyingTupleType)
2827
{
29-
switch (t.SpecialType)
28+
switch (Symbol.SpecialType)
3029
{
3130
case SpecialType.System_Int32: return Kinds.TypeKind.INT;
3231
case SpecialType.System_UInt32: return Kinds.TypeKind.UINT;
@@ -44,14 +43,14 @@ private static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool const
4443
case SpecialType.System_Single: return Kinds.TypeKind.FLOAT;
4544
case SpecialType.System_IntPtr: return Kinds.TypeKind.INT_PTR;
4645
default:
47-
if (t.IsBoundNullable())
46+
if (Symbol.IsBoundNullable())
4847
return Kinds.TypeKind.NULLABLE;
4948

50-
switch (t.TypeKind)
49+
switch (Symbol.TypeKind)
5150
{
5251
case TypeKind.Class: return Kinds.TypeKind.CLASS;
5352
case TypeKind.Struct:
54-
return ((INamedTypeSymbol)t).IsTupleType && !constructUnderlyingTupleType
53+
return ((INamedTypeSymbol)Symbol).IsTupleType && !constructUnderlyingTupleType
5554
? Kinds.TypeKind.TUPLE
5655
: Kinds.TypeKind.STRUCT;
5756
case TypeKind.Interface: return Kinds.TypeKind.INTERFACE;
@@ -62,7 +61,7 @@ private static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool const
6261
case TypeKind.FunctionPointer: return Kinds.TypeKind.FUNCTION_POINTER;
6362
case TypeKind.Error: return Kinds.TypeKind.UNKNOWN;
6463
default:
65-
cx.ModelError(t, $"Unhandled type kind '{t.TypeKind}'");
64+
cx.ModelError(Symbol, $"Unhandled type kind '{Symbol.TypeKind}'");
6665
return Kinds.TypeKind.UNKNOWN;
6766
}
6867
}
@@ -76,7 +75,7 @@ protected void PopulateType(TextWriter trapFile, bool constructUnderlyingTupleTy
7675
trapFile.Write("types(");
7776
trapFile.WriteColumn(this);
7877
trapFile.Write(',');
79-
trapFile.WriteColumn((int)GetClassType(Context, Symbol, constructUnderlyingTupleType));
78+
trapFile.WriteColumn((int)GetTypeKind(Context, constructUnderlyingTupleType));
8079
trapFile.Write(",\"");
8180
Symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType);
8281
trapFile.WriteLine("\")");
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.IO;
2+
using Microsoft.CodeAnalysis;
3+
4+
namespace Semmle.Extraction.CSharp.Entities
5+
{
6+
internal class UnknownType : Type
7+
{
8+
private UnknownType(Context cx)
9+
: base(cx, null) { }
10+
11+
public override void Populate(TextWriter trapFile)
12+
{
13+
trapFile.types(this, Kinds.TypeKind.UNKNOWN, "<unknown type>");
14+
}
15+
16+
public override void WriteId(EscapingTextWriter trapFile)
17+
{
18+
trapFile.Write("<unknown>;type");
19+
}
20+
21+
public override bool NeedsPopulation => true;
22+
23+
public override int GetHashCode() => 98744554;
24+
25+
public override bool Equals(object? obj)
26+
{
27+
return obj is not null && obj.GetType() == typeof(UnknownType);
28+
}
29+
30+
public static Type Create(Context cx) => UnknownTypeFactory.Instance.CreateEntity(cx, typeof(UnknownType), null);
31+
32+
private class UnknownTypeFactory : CachedEntityFactory<ITypeSymbol?, UnknownType>
33+
{
34+
public static UnknownTypeFactory Instance { get; } = new UnknownTypeFactory();
35+
36+
public override UnknownType Create(Context cx, ITypeSymbol? init) => new UnknownType(cx);
37+
}
38+
}
39+
}

csharp/ql/lib/semmle/code/csharp/TypeRef.qll

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ private class TypeRef extends @typeref {
1212

1313
string toString() { result = this.getName() }
1414

15-
Type getReferencedType() { typeref_type(this, result) }
15+
Type getReferencedType() {
16+
typeref_type(this, result)
17+
or
18+
not typeref_type(this, _) and
19+
result instanceof UnknownType and
20+
types(result, _, "<unknown type>")
21+
}
1622
}
1723

1824
/**

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,10 @@ private class DataFlowNullType extends DataFlowType {
17641764
}
17651765
}
17661766

1767+
private class DataFlowUnknownType extends DataFlowType {
1768+
DataFlowUnknownType() { this = Gvn::getGlobalValueNumber(any(UnknownType ut)) }
1769+
}
1770+
17671771
/**
17681772
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
17691773
* a node of type `t1` to a node of type `t2`.
@@ -1783,6 +1787,10 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
17831787
t1 instanceof Gvn::TypeParameterGvnType
17841788
or
17851789
t2 instanceof Gvn::TypeParameterGvnType
1790+
or
1791+
t1 instanceof DataFlowUnknownType
1792+
or
1793+
t2 instanceof DataFlowUnknownType
17861794
}
17871795

17881796
/**

csharp/ql/test/library-tests/assemblies/assemblies.ql

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import csharp
22

3+
private class KnownType extends Type {
4+
KnownType() { not this instanceof UnknownType }
5+
}
6+
37
class TypeRef extends @typeref {
48
string toString() { hasName(result) }
59

610
predicate hasName(string name) { typerefs(this, name) }
711

8-
Type getType() { typeref_type(this, result) }
12+
KnownType getType() { typeref_type(this, result) }
913
}
1014

1115
class MissingType extends TypeRef {
@@ -33,11 +37,11 @@ where
3337
a.getDeclaringType() = class1 and
3438
b.getDeclaringType() = class1 and
3539
c.getDeclaringType() = class1 and
36-
not exists(c.getParameter(0).getType()) and
37-
not exists(a.getType()) and
38-
not exists(b.getReturnType()) and
39-
not exists(c.getReturnType()) and
40-
not exists(e.getReturnType()) and
41-
not exists(g.getReturnType()) and
42-
not exists(g.getParameter(0).getType())
40+
not exists(c.getParameter(0).getType().(KnownType)) and
41+
not exists(a.getType().(KnownType)) and
42+
not exists(b.getReturnType().(KnownType)) and
43+
not exists(c.getReturnType().(KnownType)) and
44+
not exists(e.getReturnType().(KnownType)) and
45+
not exists(g.getReturnType().(KnownType)) and
46+
not exists(g.getParameter(0).getType().(KnownType))
4347
select "Test passed"

csharp/ql/test/library-tests/expressions/PrintAst.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,8 @@ expressions.cs:
825825
# 122| 9: [TypeofExpr] typeof(...)
826826
# 122| 0: [TypeAccess] access to type Y<,>
827827
# 122| 0: [TypeMention] Y<T, U>
828+
# 122| 1: [TypeMention] <unknown type>
829+
# 122| 2: [TypeMention] <unknown type>
828830
# 124| 1: [LocalVariableDeclStmt] ... ...;
829831
# 124| 0: [LocalVariableDeclAndInitExpr] T e = ...
830832
# 124| -1: [TypeMention] T

csharp/ql/test/library-tests/nestedtypes/PrintAst.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ nestedtypes.cs:
9595
# 69| 0: [TypeAccess] access to type Inner<>
9696
# 69| 0: [TypeMention] Inner<U>
9797
# 69| 1: [TypeMention] Outer<T>
98+
# 69| 1: [TypeMention] <unknown type>
99+
# 69| 2: [TypeMention] <unknown type>
98100
# 74| 5: [Class] Outer2<>
99101
#-----| 1: (Type parameters)
100102
# 74| 0: [TypeParameter] T

csharp/ql/test/library-tests/standalone/controlflow/cfg.expected

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
| ControlFlow.cs:5:10:5:10 | exit F (normal) | ControlFlow.cs:5:10:5:10 | exit F |
33
| ControlFlow.cs:6:5:11:5 | {...} | ControlFlow.cs:7:9:7:34 | ... ...; |
44
| ControlFlow.cs:7:9:7:34 | ... ...; | ControlFlow.cs:7:17:7:33 | Call (unknown target) |
5-
| ControlFlow.cs:7:13:7:33 | (unknown type) v | ControlFlow.cs:8:9:8:44 | ...; |
6-
| ControlFlow.cs:7:17:7:33 | Call (unknown target) | ControlFlow.cs:7:13:7:33 | (unknown type) v |
5+
| ControlFlow.cs:7:9:7:34 | ... ...; | ControlFlow.cs:7:17:7:33 | object creation of type <unknown type> |
6+
| ControlFlow.cs:7:13:7:33 | <unknown type> v = ... | ControlFlow.cs:8:9:8:44 | ...; |
7+
| ControlFlow.cs:7:17:7:33 | Call (unknown target) | ControlFlow.cs:7:13:7:33 | <unknown type> v = ... |
8+
| ControlFlow.cs:7:17:7:33 | object creation of type <unknown type> | ControlFlow.cs:7:13:7:33 | <unknown type> v = ... |
79
| ControlFlow.cs:8:9:8:13 | Expression | ControlFlow.cs:8:22:8:22 | access to local variable v |
810
| ControlFlow.cs:8:9:8:43 | Call (unknown target) | ControlFlow.cs:10:9:10:87 | ...; |
911
| ControlFlow.cs:8:9:8:43 | call to method | ControlFlow.cs:10:9:10:87 | ...; |
@@ -19,7 +21,9 @@
1921
| ControlFlow.cs:8:29:8:42 | "This is true" | ControlFlow.cs:8:9:8:43 | Call (unknown target) |
2022
| ControlFlow.cs:8:29:8:42 | "This is true" | ControlFlow.cs:8:9:8:43 | call to method |
2123
| ControlFlow.cs:10:9:10:86 | Call (unknown target) | ControlFlow.cs:10:51:10:62 | access to field Empty |
24+
| ControlFlow.cs:10:9:10:86 | object creation of type <unknown type> | ControlFlow.cs:10:51:10:62 | access to field Empty |
2225
| ControlFlow.cs:10:9:10:87 | ...; | ControlFlow.cs:10:9:10:86 | Call (unknown target) |
26+
| ControlFlow.cs:10:9:10:87 | ...; | ControlFlow.cs:10:9:10:86 | object creation of type <unknown type> |
2327
| ControlFlow.cs:10:35:10:86 | { ..., ... } | ControlFlow.cs:5:10:5:10 | exit F (normal) |
2428
| ControlFlow.cs:10:37:10:62 | ... = ... | ControlFlow.cs:10:79:10:79 | access to local variable v |
2529
| ControlFlow.cs:10:51:10:62 | access to field Empty | ControlFlow.cs:10:37:10:62 | ... = ... |

csharp/ql/test/library-tests/standalone/controlflow/cfg.ql

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@ class UnknownCall extends Call {
1111
override string toString() { result = "Call (unknown target)" }
1212
}
1313

14-
class UnknownLocalVariableDeclExpr extends LocalVariableDeclAndInitExpr {
15-
UnknownLocalVariableDeclExpr() { not exists(this.getVariable().getType().getName()) }
16-
17-
override string toString() { result = "(unknown type) " + this.getName() }
18-
}
19-
2014
query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) {
2115
not n1.getElement().fromLibrary() and n2 = n1.getASuccessor()
2216
}

0 commit comments

Comments
 (0)