using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; 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: case RefKind.RefReadOnlyParameter: trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef); break; } } protected void PopulateScopedKind(TextWriter trapFile, ScopedKind kind) { switch (kind) { case ScopedKind.ScopedRef: trapFile.scoped_annotation(this, Kinds.ScopedAnnotation.ScopedRef); break; case ScopedKind.ScopedValue: trapFile.scoped_annotation(this, Kinds.ScopedAnnotation.ScopedValue); 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 highlighting source code. /// It's generally short, e.g. a method name. /// public override Microsoft.CodeAnalysis.Location? ReportingLocation => Symbol.Locations.BestOrDefault(); /// /// The full text span of the entity, e.g. for binding comments. /// public virtual Microsoft.CodeAnalysis.Location? FullLocation => Symbol.Locations.BestOrDefault(); 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 (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 Location Location => Context.CreateLocation(ReportingLocation); } }