mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge pull request #7320 from hvitved/csharp/unknown-type
C#: Populate `UnknownType`
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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("\")");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 | ... = ... |
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user