mirror of
https://github.com/github/codeql.git
synced 2025-12-17 17:23:36 +01:00
291 lines
11 KiB
C#
291 lines
11 KiB
C#
using System;
|
|
using Microsoft.CodeAnalysis;
|
|
using Semmle.Extraction.CSharp.Populators;
|
|
using System.Linq;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Semmle.Extraction.Entities;
|
|
|
|
namespace Semmle.Extraction.CSharp.Entities
|
|
{
|
|
public class Parameter : CachedSymbol<IParameterSymbol>, IExpressionParentEntity
|
|
{
|
|
protected IEntity Parent;
|
|
protected readonly Parameter Original;
|
|
|
|
protected Parameter(Context cx, IParameterSymbol init, IEntity parent, Parameter original)
|
|
: base(cx, init)
|
|
{
|
|
Parent = parent;
|
|
Original = original ?? this;
|
|
}
|
|
|
|
public override Microsoft.CodeAnalysis.Location ReportingLocation => symbol.GetSymbolLocation();
|
|
|
|
public enum Kind
|
|
{
|
|
None, Ref, Out, Params, This, In
|
|
}
|
|
|
|
protected virtual int Ordinal
|
|
{
|
|
get
|
|
{
|
|
// For some reason, methods of kind ReducedExtension
|
|
// omit the "this" parameter, so the parameters are
|
|
// actually numbered from 1.
|
|
// This is to be consistent from the original (unreduced) extension method.
|
|
var method = symbol.ContainingSymbol as IMethodSymbol;
|
|
bool isReducedExtension = method != null && method.MethodKind == MethodKind.ReducedExtension;
|
|
return symbol.Ordinal + (isReducedExtension ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
Kind ParamKind
|
|
{
|
|
get
|
|
{
|
|
switch (symbol.RefKind)
|
|
{
|
|
case RefKind.Out:
|
|
return Kind.Out;
|
|
case RefKind.Ref:
|
|
return Kind.Ref;
|
|
case RefKind.In:
|
|
return Kind.In;
|
|
default:
|
|
if (symbol.IsParams) return Kind.Params;
|
|
|
|
if (Ordinal == 0)
|
|
{
|
|
var method = symbol.ContainingSymbol as IMethodSymbol;
|
|
if (method != null && method.IsExtensionMethod) return Kind.This;
|
|
}
|
|
return Kind.None;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) =>
|
|
ParameterFactory.Instance.CreateEntity(cx, param, parent, original);
|
|
|
|
/// <summary>
|
|
/// Gets the parameter entity for <paramref name="param"/> which must
|
|
/// already have been created.
|
|
/// </summary>
|
|
public static Parameter GetAlreadyCreated(Context cx, IParameterSymbol param) =>
|
|
ParameterFactory.Instance.CreateEntity(cx, param, null, null);
|
|
|
|
public override IId Id
|
|
{
|
|
get
|
|
{
|
|
// This is due to a bug in Roslyn when ValueTuple.cs is extracted.
|
|
// The parameter symbols don't match up properly so we don't have a parent.
|
|
if (Parent == null)
|
|
Parent = Method.Create(Context, symbol.ContainingSymbol as IMethodSymbol);
|
|
return new Key(Parent, "_", Ordinal, ";parameter");
|
|
}
|
|
}
|
|
|
|
public override bool NeedsPopulation => true;
|
|
|
|
string Name
|
|
{
|
|
get
|
|
{
|
|
// Very rarely, two parameters have the same name according to the data model.
|
|
// This breaks our database constraints.
|
|
// Generate an impossible name to ensure that it doesn't conflict.
|
|
int conflictingCount = symbol.ContainingSymbol.GetParameters().Count(p => p.Ordinal < symbol.Ordinal && p.Name == symbol.Name);
|
|
return conflictingCount > 0 ? symbol.Name + "`" + conflictingCount : symbol.Name;
|
|
}
|
|
}
|
|
|
|
public override void Populate()
|
|
{
|
|
ExtractAttributes();
|
|
|
|
if (symbol.Name != Original.symbol.Name)
|
|
Context.ModelError(symbol, "Inconsistent parameter declaration");
|
|
|
|
var type = Type.Create(Context, symbol.Type);
|
|
Context.Emit(Tuples.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent, Original));
|
|
|
|
foreach (var l in symbol.Locations)
|
|
Context.Emit(Tuples.param_location(this, Context.Create(l)));
|
|
|
|
if (!IsSourceDeclaration || !symbol.FromSource())
|
|
return;
|
|
|
|
BindComments();
|
|
|
|
if (IsSourceDeclaration)
|
|
foreach (var syntax in symbol.DeclaringSyntaxReferences.
|
|
Select(d => d.GetSyntax()).
|
|
OfType<ParameterSyntax>().
|
|
Where(s => s.Type != null))
|
|
TypeMention.Create(Context, syntax.Type, this, type);
|
|
|
|
if (symbol.HasExplicitDefaultValue && Context.Defines(symbol))
|
|
{
|
|
// This is a slight bug in the dbscheme
|
|
// We should really define param_default(param, string)
|
|
// And use parameter child #0 to encode the default expression.
|
|
var defaultValue = GetParameterDefaultValue(symbol);
|
|
if (defaultValue == null)
|
|
{
|
|
// In case this parameter belongs to an accessor of an indexer, we need
|
|
// to get the default value from the corresponding parameter belonging
|
|
// to the indexer itself
|
|
var method = (IMethodSymbol)symbol.ContainingSymbol;
|
|
if (method != null)
|
|
{
|
|
var i = method.Parameters.IndexOf(symbol);
|
|
var indexer = (IPropertySymbol)method.AssociatedSymbol;
|
|
if (indexer != null)
|
|
defaultValue = GetParameterDefaultValue(indexer.Parameters[i]);
|
|
}
|
|
}
|
|
|
|
if (defaultValue != null)
|
|
{
|
|
Context.PopulateLater(() =>
|
|
{
|
|
Expression.Create(Context, defaultValue.Value, this, 0);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
public override bool IsSourceDeclaration => symbol.IsSourceDeclaration();
|
|
|
|
bool IExpressionParentEntity.IsTopLevelParent => true;
|
|
|
|
static EqualsValueClauseSyntax GetParameterDefaultValue(IParameterSymbol parameter)
|
|
{
|
|
var syntax = parameter.DeclaringSyntaxReferences.Select(@ref => @ref.GetSyntax()).OfType<ParameterSyntax>().FirstOrDefault();
|
|
return syntax != null ? syntax.Default : null;
|
|
}
|
|
|
|
class ParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), Parameter>
|
|
{
|
|
public static readonly ParameterFactory Instance = new ParameterFactory();
|
|
|
|
public Parameter Create(Context cx, (IParameterSymbol, IEntity, Parameter) init) => new Parameter(cx, init.Item1, init.Item2, init.Item3);
|
|
}
|
|
|
|
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;
|
|
}
|
|
|
|
class VarargsType : Type
|
|
{
|
|
VarargsType(Context cx)
|
|
: base(cx, null) { }
|
|
|
|
public override void Populate()
|
|
{
|
|
Context.Emit(Tuples.types(this, Kinds.TypeKind.ARGLIST, "__arglist"));
|
|
Context.Emit(Tuples.parent_namespace(this, Namespace.Create(Context, Context.Compilation.GlobalNamespace)));
|
|
Modifier.HasModifier(Context, this, "public");
|
|
}
|
|
|
|
public override bool NeedsPopulation => true;
|
|
|
|
public sealed override IId Id => new Key("__arglist;type");
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return 98735267;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj != null && obj.GetType() == typeof(VarargsType);
|
|
}
|
|
|
|
public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateEntity(cx, null);
|
|
|
|
class VarargsTypeFactory : ICachedEntityFactory<string, VarargsType>
|
|
{
|
|
public static readonly VarargsTypeFactory Instance = new VarargsTypeFactory();
|
|
|
|
public VarargsType Create(Context cx, string init) => new VarargsType(cx);
|
|
}
|
|
}
|
|
|
|
class VarargsParam : Parameter
|
|
{
|
|
VarargsParam(Context cx, Method methodKey)
|
|
: base(cx, null, methodKey, null) { }
|
|
|
|
public override void Populate()
|
|
{
|
|
var typeKey = VarargsType.Create(Context);
|
|
// !! Maybe originaldefinition is wrong
|
|
Context.Emit(Tuples.@params(this, "", typeKey, Ordinal, Kind.None, Parent, this));
|
|
Context.Emit(Tuples.param_location(this, GeneratedLocation.Create(Context)));
|
|
}
|
|
|
|
protected override int Ordinal => ((Method)Parent).OriginalDefinition.symbol.Parameters.Length;
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return 9873567;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj != null && obj.GetType() == typeof(VarargsParam);
|
|
}
|
|
|
|
public static VarargsParam Create(Context cx, Method method) => VarargsParamFactory.Instance.CreateEntity(cx, method);
|
|
|
|
class VarargsParamFactory : ICachedEntityFactory<Method, VarargsParam>
|
|
{
|
|
public static readonly VarargsParamFactory Instance = new VarargsParamFactory();
|
|
|
|
public VarargsParam Create(Context cx, Method init) => new VarargsParam(cx, init);
|
|
}
|
|
}
|
|
|
|
class ConstructedExtensionParameter : Parameter
|
|
{
|
|
readonly ITypeSymbol ConstructedType;
|
|
|
|
ConstructedExtensionParameter(Context cx, Method method, Parameter original)
|
|
: base(cx, original.symbol, method, original)
|
|
{
|
|
ConstructedType = method.symbol.ReceiverType;
|
|
}
|
|
|
|
public override void Populate()
|
|
{
|
|
var typeKey = Type.Create(Context, ConstructedType);
|
|
Context.Emit(Tuples.@params(this, Original.symbol.Name, typeKey.TypeRef, 0, Kind.This, Parent, Original));
|
|
Context.Emit(Tuples.param_location(this, Original.Location));
|
|
}
|
|
|
|
public override int GetHashCode() => symbol.GetHashCode() + 31 * ConstructedType.GetHashCode();
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
var other = obj as ConstructedExtensionParameter;
|
|
if (other == null || other.GetType() != typeof(ConstructedExtensionParameter))
|
|
return false;
|
|
|
|
return Equals(symbol, other.symbol) && Equals(ConstructedType, other.ConstructedType);
|
|
}
|
|
|
|
public static ConstructedExtensionParameter Create(Context cx, Method method, Parameter parameter) =>
|
|
ExtensionParamFactory.Instance.CreateEntity(cx, (method, parameter));
|
|
|
|
class ExtensionParamFactory : ICachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter>
|
|
{
|
|
public static readonly ExtensionParamFactory Instance = new ExtensionParamFactory();
|
|
|
|
public ConstructedExtensionParameter Create(Context cx, (Method, Parameter) init) =>
|
|
new ConstructedExtensionParameter(cx, init.Item1, init.Item2);
|
|
}
|
|
}
|
|
}
|