Merge pull request #7320 from hvitved/csharp/unknown-type

C#: Populate `UnknownType`
This commit is contained in:
Tom Hvitved
2021-12-10 09:57:55 +01:00
committed by GitHub
11 changed files with 88 additions and 28 deletions

View File

@@ -1,7 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Semmle.Extraction.CSharp.Populators;
using Semmle.Extraction.Entities;
using System;
using System.Collections.Generic;
using System.IO;
@@ -36,6 +35,7 @@ namespace Semmle.Extraction.CSharp.Entities
{
if (Symbol.TypeKind == TypeKind.Error)
{
UnknownType.Create(Context); // make sure this exists so we can use it in `TypeRef::getReferencedType`
Context.Extractor.MissingType(Symbol.ToString()!, Context.FromSource);
return;
}
@@ -48,7 +48,7 @@ namespace Semmle.Extraction.CSharp.Entities
if (Symbol.IsBoundNullable())
{
// An instance of Nullable<T>
trapFile.nullable_underlying_type(this, Create(Context, Symbol.TypeArguments[0]).TypeRef);
trapFile.nullable_underlying_type(this, TypeArguments[0].TypeRef);
}
else if (Symbol.IsReallyUnbound())
{
@@ -67,7 +67,7 @@ namespace Semmle.Extraction.CSharp.Entities
: Type.Create(Context, Symbol.ConstructedFrom);
trapFile.constructed_generic(this, unbound.TypeRef);
for (var i = 0; i < Symbol.TypeArguments.Length; ++i)
for (var i = 0; i < TypeArguments.Length; ++i)
{
trapFile.type_arguments(TypeArguments[i].TypeRef, i, this);
}

View File

@@ -1,6 +1,5 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Util;
using System;
using System.Collections.Generic;
using System.IO;
@@ -24,9 +23,9 @@ namespace Semmle.Extraction.CSharp.Entities
symbol.ContainingType is not null && ConstructedOrParentIsConstructed(symbol.ContainingType);
}
private static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool constructUnderlyingTupleType)
public Kinds.TypeKind GetTypeKind(Context cx, bool constructUnderlyingTupleType)
{
switch (t.SpecialType)
switch (Symbol.SpecialType)
{
case SpecialType.System_Int32: return Kinds.TypeKind.INT;
case SpecialType.System_UInt32: return Kinds.TypeKind.UINT;
@@ -44,14 +43,14 @@ namespace Semmle.Extraction.CSharp.Entities
case SpecialType.System_Single: return Kinds.TypeKind.FLOAT;
case SpecialType.System_IntPtr: return Kinds.TypeKind.INT_PTR;
default:
if (t.IsBoundNullable())
if (Symbol.IsBoundNullable())
return Kinds.TypeKind.NULLABLE;
switch (t.TypeKind)
switch (Symbol.TypeKind)
{
case TypeKind.Class: return Kinds.TypeKind.CLASS;
case TypeKind.Struct:
return ((INamedTypeSymbol)t).IsTupleType && !constructUnderlyingTupleType
return ((INamedTypeSymbol)Symbol).IsTupleType && !constructUnderlyingTupleType
? Kinds.TypeKind.TUPLE
: Kinds.TypeKind.STRUCT;
case TypeKind.Interface: return Kinds.TypeKind.INTERFACE;
@@ -62,7 +61,7 @@ namespace Semmle.Extraction.CSharp.Entities
case TypeKind.FunctionPointer: return Kinds.TypeKind.FUNCTION_POINTER;
case TypeKind.Error: return Kinds.TypeKind.UNKNOWN;
default:
cx.ModelError(t, $"Unhandled type kind '{t.TypeKind}'");
cx.ModelError(Symbol, $"Unhandled type kind '{Symbol.TypeKind}'");
return Kinds.TypeKind.UNKNOWN;
}
}
@@ -76,7 +75,7 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.Write("types(");
trapFile.WriteColumn(this);
trapFile.Write(',');
trapFile.WriteColumn((int)GetClassType(Context, Symbol, constructUnderlyingTupleType));
trapFile.WriteColumn((int)GetTypeKind(Context, constructUnderlyingTupleType));
trapFile.Write(",\"");
Symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType);
trapFile.WriteLine("\")");

View File

@@ -0,0 +1,39 @@
using System.IO;
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction.CSharp.Entities
{
internal class UnknownType : Type
{
private UnknownType(Context cx)
: base(cx, null) { }
public override void Populate(TextWriter trapFile)
{
trapFile.types(this, Kinds.TypeKind.UNKNOWN, "<unknown type>");
}
public override void WriteId(EscapingTextWriter trapFile)
{
trapFile.Write("<unknown>;type");
}
public override bool NeedsPopulation => true;
public override int GetHashCode() => 98744554;
public override bool Equals(object? obj)
{
return obj is not null && obj.GetType() == typeof(UnknownType);
}
public static Type Create(Context cx) => UnknownTypeFactory.Instance.CreateEntity(cx, typeof(UnknownType), null);
private class UnknownTypeFactory : CachedEntityFactory<ITypeSymbol?, UnknownType>
{
public static UnknownTypeFactory Instance { get; } = new UnknownTypeFactory();
public override UnknownType Create(Context cx, ITypeSymbol? init) => new UnknownType(cx);
}
}
}

View File

@@ -12,7 +12,12 @@ private class TypeRef extends @typeref {
string toString() { result = this.getName() }
Type getReferencedType() { typeref_type(this, result) }
Type getReferencedType() {
typeref_type(this, result)
or
not typeref_type(this, _) and
result instanceof UnknownType
}
}
/**

View File

@@ -1780,6 +1780,10 @@ private class DataFlowNullType extends DataFlowType {
}
}
private class DataFlowUnknownType extends DataFlowType {
DataFlowUnknownType() { this = Gvn::getGlobalValueNumber(any(UnknownType ut)) }
}
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
@@ -1799,6 +1803,10 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
t1 instanceof Gvn::TypeParameterGvnType
or
t2 instanceof Gvn::TypeParameterGvnType
or
t1 instanceof DataFlowUnknownType
or
t2 instanceof DataFlowUnknownType
}
/**

View File

@@ -1,11 +1,15 @@
import csharp
private class KnownType extends Type {
KnownType() { not this instanceof UnknownType }
}
class TypeRef extends @typeref {
string toString() { hasName(result) }
predicate hasName(string name) { typerefs(this, name) }
Type getType() { typeref_type(this, result) }
KnownType getType() { typeref_type(this, result) }
}
class MissingType extends TypeRef {
@@ -33,11 +37,11 @@ where
a.getDeclaringType() = class1 and
b.getDeclaringType() = class1 and
c.getDeclaringType() = class1 and
not exists(c.getParameter(0).getType()) and
not exists(a.getType()) and
not exists(b.getReturnType()) and
not exists(c.getReturnType()) and
not exists(e.getReturnType()) and
not exists(g.getReturnType()) and
not exists(g.getParameter(0).getType())
not exists(c.getParameter(0).getType().(KnownType)) and
not exists(a.getType().(KnownType)) and
not exists(b.getReturnType().(KnownType)) and
not exists(c.getReturnType().(KnownType)) and
not exists(e.getReturnType().(KnownType)) and
not exists(g.getReturnType().(KnownType)) and
not exists(g.getParameter(0).getType().(KnownType))
select "Test passed"

View File

@@ -825,6 +825,8 @@ expressions.cs:
# 122| 9: [TypeofExpr] typeof(...)
# 122| 0: [TypeAccess] access to type Y<,>
# 122| 0: [TypeMention] Y<T, U>
# 122| 1: [TypeMention] <unknown type>
# 122| 2: [TypeMention] <unknown type>
# 124| 1: [LocalVariableDeclStmt] ... ...;
# 124| 0: [LocalVariableDeclAndInitExpr] T e = ...
# 124| -1: [TypeMention] T

View File

@@ -95,6 +95,8 @@ nestedtypes.cs:
# 69| 0: [TypeAccess] access to type Inner<>
# 69| 0: [TypeMention] Inner<U>
# 69| 1: [TypeMention] Outer<T>
# 69| 1: [TypeMention] <unknown type>
# 69| 2: [TypeMention] <unknown type>
# 74| 5: [Class] Outer2<>
#-----| 1: (Type parameters)
# 74| 0: [TypeParameter] T

View File

@@ -2,8 +2,10 @@
| ControlFlow.cs:5:10:5:10 | exit F (normal) | ControlFlow.cs:5:10:5:10 | exit F |
| ControlFlow.cs:6:5:11:5 | {...} | ControlFlow.cs:7:9:7:34 | ... ...; |
| ControlFlow.cs:7:9:7:34 | ... ...; | ControlFlow.cs:7:17:7:33 | Call (unknown target) |
| ControlFlow.cs:7:13:7:33 | (unknown type) v | ControlFlow.cs:8:9:8:44 | ...; |
| ControlFlow.cs:7:17:7:33 | Call (unknown target) | ControlFlow.cs:7:13:7:33 | (unknown type) v |
| ControlFlow.cs:7:9:7:34 | ... ...; | ControlFlow.cs:7:17:7:33 | object creation of type <unknown type> |
| ControlFlow.cs:7:13:7:33 | <unknown type> v = ... | ControlFlow.cs:8:9:8:44 | ...; |
| ControlFlow.cs:7:17:7:33 | Call (unknown target) | ControlFlow.cs:7:13:7:33 | <unknown type> v = ... |
| ControlFlow.cs:7:17:7:33 | object creation of type <unknown type> | ControlFlow.cs:7:13:7:33 | <unknown type> v = ... |
| ControlFlow.cs:8:9:8:13 | Expression | ControlFlow.cs:8:22:8:22 | access to local variable v |
| ControlFlow.cs:8:9:8:43 | Call (unknown target) | ControlFlow.cs:10:9:10:87 | ...; |
| ControlFlow.cs:8:9:8:43 | call to method | ControlFlow.cs:10:9:10:87 | ...; |
@@ -19,7 +21,9 @@
| ControlFlow.cs:8:29:8:42 | "This is true" | ControlFlow.cs:8:9:8:43 | Call (unknown target) |
| ControlFlow.cs:8:29:8:42 | "This is true" | ControlFlow.cs:8:9:8:43 | call to method |
| ControlFlow.cs:10:9:10:86 | Call (unknown target) | ControlFlow.cs:10:51:10:62 | access to field Empty |
| ControlFlow.cs:10:9:10:86 | object creation of type <unknown type> | ControlFlow.cs:10:51:10:62 | access to field Empty |
| ControlFlow.cs:10:9:10:87 | ...; | ControlFlow.cs:10:9:10:86 | Call (unknown target) |
| ControlFlow.cs:10:9:10:87 | ...; | ControlFlow.cs:10:9:10:86 | object creation of type <unknown type> |
| ControlFlow.cs:10:35:10:86 | { ..., ... } | ControlFlow.cs:5:10:5:10 | exit F (normal) |
| ControlFlow.cs:10:37:10:62 | ... = ... | ControlFlow.cs:10:79:10:79 | access to local variable v |
| ControlFlow.cs:10:51:10:62 | access to field Empty | ControlFlow.cs:10:37:10:62 | ... = ... |

View File

@@ -11,12 +11,6 @@ class UnknownCall extends Call {
override string toString() { result = "Call (unknown target)" }
}
class UnknownLocalVariableDeclExpr extends LocalVariableDeclAndInitExpr {
UnknownLocalVariableDeclExpr() { not exists(this.getVariable().getType().getName()) }
override string toString() { result = "(unknown type) " + this.getName() }
}
query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) {
not n1.getElement().fromLibrary() and n2 = n1.getASuccessor()
}

View File

@@ -1,7 +1,10 @@
| errors.cs:16:19:16:20 | f1 | <unknown type> |
| errors.cs:22:17:22:27 | unknownType | <unknown type> |
| errors.cs:35:21:35:21 | p | Int32 |
| errors.cs:41:16:41:17 | c1 | C1 |
| errors.cs:50:12:50:13 | c1 | C1 |
| errors.cs:51:12:51:13 | c2 | C2 |
| errors.cs:53:31:53:31 | x | <unknown type> |
| errors.cs:59:20:59:20 | x | Int32 |
| errors.cs:72:28:72:29 | p1 | Int32 |
| errors.cs:72:39:72:40 | p2 | String |