Files
codeql/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs

149 lines
5.7 KiB
C#

using System;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.CSharp.Entities.Expressions;
using Semmle.Extraction.Kinds;
namespace Semmle.Extraction.CSharp.Entities
{
internal class Field : CachedSymbol<IFieldSymbol>, IExpressionParentEntity
{
private Field(Context cx, IFieldSymbol init)
: base(cx, init)
{
type = new Lazy<Type>(() => Entities.Type.Create(cx, Symbol.Type));
}
public static Field Create(Context cx, IFieldSymbol field) => FieldFactory.Instance.CreateEntityFromSymbol(cx, field.CorrespondingTupleField ?? field);
// Do not populate backing fields.
// Populate Tuple fields.
public override bool NeedsPopulation =>
(base.NeedsPopulation && !Symbol.IsImplicitlyDeclared) || Symbol.ContainingType.IsTupleType;
public override void Populate(TextWriter trapFile)
{
PopulateAttributes();
ContainingType!.PopulateGenerics();
PopulateNullability(trapFile, Symbol.GetAnnotatedType());
PopulateRefKind(trapFile, Symbol.RefKind);
var unboundFieldKey = Field.Create(Context, Symbol.OriginalDefinition);
var kind = Symbol.IsConst ? VariableKind.Const : VariableKind.None;
trapFile.fields(this, kind, Symbol.Name, ContainingType, Type.TypeRef, unboundFieldKey);
PopulateModifiers(trapFile);
if (Symbol.IsVolatile)
Modifier.HasModifier(Context, trapFile, this, "volatile");
if (Symbol.IsConst)
{
Modifier.HasModifier(Context, trapFile, this, "const");
if (Symbol.HasConstantValue)
{
trapFile.constant_value(this, Expression.ValueAsString(Symbol.ConstantValue));
}
}
if (Context.OnlyScaffold)
{
return;
}
if (Context.ExtractLocation(Symbol))
{
WriteLocationsToTrap(trapFile.field_location, this, Locations);
}
if (!IsSourceDeclaration || !Symbol.FromSource())
return;
Context.BindComments(this, Location.Symbol);
var child = 0;
foreach (var initializer in Symbol.DeclaringSyntaxReferences
.Select(n => n.GetSyntax())
.OfType<VariableDeclaratorSyntax>()
.Where(n => n.Initializer is not null))
{
Context.PopulateLater(() =>
{
var loc = Context.CreateLocation(initializer.GetLocation());
var fieldAccess = AddInitializerAssignment(trapFile, initializer.Initializer!.Value, loc, null, ref child);
if (!Symbol.IsStatic)
{
This.CreateImplicit(Context, Symbol.ContainingType, Location, fieldAccess, -1);
}
});
}
foreach (var initializer in Symbol.DeclaringSyntaxReferences
.Select(n => n.GetSyntax())
.OfType<EnumMemberDeclarationSyntax>()
.Where(n => n.EqualsValue is not null))
{
// Mark fields that have explicit initializers.
var constValue = Symbol.HasConstantValue
? Expression.ValueAsString(Symbol.ConstantValue)
: null;
var loc = Context.CreateLocation(initializer.GetLocation());
AddInitializerAssignment(trapFile, initializer.EqualsValue!.Value, loc, constValue, ref child);
}
if (IsSourceDeclaration)
{
foreach (var syntax in Symbol.DeclaringSyntaxReferences
.Select(d => d.GetSyntax())
.OfType<VariableDeclaratorSyntax>()
.Select(d => d.Parent)
.OfType<VariableDeclarationSyntax>())
{
TypeMention.Create(Context, syntax.Type, this, Type);
}
}
}
private Expression AddInitializerAssignment(TextWriter trapFile, ExpressionSyntax initializer, Location loc,
string? constValue, ref int child)
{
var type = Symbol.GetAnnotatedType();
var simpleAssignExpr = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, isCompilerGenerated: true, constValue));
Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 0));
var access = new Expression(new ExpressionInfo(Context, type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, isCompilerGenerated: true, constValue));
trapFile.expr_access(access, this);
return access;
}
private readonly Lazy<Type> type;
public Type Type => type.Value;
public override void WriteId(EscapingTextWriter trapFile)
{
trapFile.WriteSubId(Type);
trapFile.Write(" ");
trapFile.WriteSubId(ContainingType!);
trapFile.Write('.');
trapFile.Write(Symbol.Name);
trapFile.Write(";field");
}
bool IExpressionParentEntity.IsTopLevelParent => true;
private class FieldFactory : CachedEntityFactory<IFieldSymbol, Field>
{
public static FieldFactory Instance { get; } = new FieldFactory();
public override Field Create(Context cx, IFieldSymbol init) => new Field(cx, init);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.PushesLabel;
}
}