using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Entities; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; namespace Semmle.Extraction.CSharp.Entities { internal abstract class CachedSymbol : CachedEntity where T : class, ISymbol { protected CachedSymbol(Context cx, T init) : base(cx, init) { } public virtual Type? ContainingType => Symbol.ContainingType is not null ? Symbol.ContainingType.IsTupleType ? NamedType.CreateNamedTypeFromTupleType(Context, Symbol.ContainingType) : Type.Create(Context, Symbol.ContainingType) : null; public void PopulateModifiers(TextWriter trapFile) { Modifier.ExtractModifiers(Context, trapFile, this, Symbol); } protected void PopulateAttributes() { // Only extract attributes for source declarations if (ReferenceEquals(Symbol, Symbol.OriginalDefinition)) Attribute.ExtractAttributes(Context, Symbol, this); } protected void PopulateNullability(TextWriter trapFile, AnnotatedTypeSymbol type) { var n = NullabilityEntity.Create(Context, Nullability.Create(type)); if (!type.HasObliviousNullability()) { trapFile.type_nullability(this, n); } } protected void PopulateRefKind(TextWriter trapFile, RefKind kind) { switch (kind) { case RefKind.Out: trapFile.type_annotation(this, Kinds.TypeAnnotation.Out); break; case RefKind.Ref: trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref); break; case RefKind.RefReadOnly: trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef); break; } } protected void ExtractCompilerGenerated(TextWriter trapFile) { if (Symbol.IsImplicitlyDeclared) trapFile.compiler_generated(this); } /// /// The location which is stored in the database and is used when highlighing source code. /// It's generally short, e.g. a method name. /// public override Microsoft.CodeAnalysis.Location? ReportingLocation => Symbol.Locations.FirstOrDefault(); /// /// The full text span of the entity, e.g. for binding comments. /// public virtual Microsoft.CodeAnalysis.Location? FullLocation => Symbol.Locations.FirstOrDefault(); public virtual IEnumerable Locations { get { var loc = ReportingLocation; if (loc is not null) { // Some built in operators lack locations, so loc is null. yield return Context.CreateLocation(ReportingLocation); if (!Context.Extractor.Standalone && loc.Kind == LocationKind.SourceFile) yield return Assembly.CreateOutputAssembly(Context); } } } /// /// Bind comments to this symbol. /// Comments are only bound to source declarations. /// protected void BindComments() { if (!Symbol.IsImplicitlyDeclared && IsSourceDeclaration && Symbol.FromSource()) Context.BindComments(this, FullLocation); } protected virtual T BodyDeclaringSymbol => Symbol; public BlockSyntax? Block { get { return BodyDeclaringSymbol.DeclaringSyntaxReferences .SelectMany(r => r.GetSyntax().ChildNodes()) .OfType() .FirstOrDefault(); } } public ExpressionSyntax? ExpressionBody { get { return BodyDeclaringSymbol.DeclaringSyntaxReferences .SelectMany(r => r.GetSyntax().ChildNodes()) .OfType() .Select(arrow => arrow.Expression) .FirstOrDefault(); } } public virtual bool IsSourceDeclaration => Symbol.IsSourceDeclaration(); public override bool NeedsPopulation => Context.Defines(Symbol); public Extraction.Entities.Location Location => Context.CreateLocation(ReportingLocation); protected void PopulateMetadataHandle(TextWriter trapFile) { var handle = MetadataHandle; if (handle.HasValue) trapFile.metadata_handle(this, Location, MetadataTokens.GetToken(handle.Value)); } private static System.Reflection.PropertyInfo? GetPropertyInfo(object o, string name) { return o.GetType().GetProperty(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty); } public Handle? MetadataHandle { get { var handleProp = GetPropertyInfo(Symbol, "Handle"); object handleObj = Symbol; if (handleProp is null) { var underlyingSymbolProp = GetPropertyInfo(Symbol, "UnderlyingSymbol"); if (underlyingSymbolProp?.GetValue(Symbol) is object underlying) { handleProp = GetPropertyInfo(underlying, "Handle"); handleObj = underlying; } } if (handleProp is not null) { switch (handleProp.GetValue(handleObj)) { case MethodDefinitionHandle md: return md; case TypeDefinitionHandle td: return td; case PropertyDefinitionHandle pd: return pd; case FieldDefinitionHandle fd: return fd; } } return null; } } } }