using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Util; namespace Semmle.Extraction.CSharp.Entities { internal class Indexer : Property, IExpressionParentEntity { protected Indexer(Context cx, IPropertySymbol init) : base(cx, init) { } private Indexer OriginalDefinition => IsSourceDeclaration ? this : Create(Context, Symbol.OriginalDefinition); public override void Populate(TextWriter trapFile) { PopulateNullability(trapFile, Symbol.GetAnnotatedType()); var type = Type.Create(Context, Symbol.Type); trapFile.indexers(this, Symbol.GetName(useMetadataName: true), ContainingType!, type.TypeRef, OriginalDefinition); var getter = BodyDeclaringSymbol.GetMethod; var setter = BodyDeclaringSymbol.SetMethod; if (getter is null && setter is null) Context.ModelError(Symbol, "No indexer accessor defined"); if (getter is not null) Method.Create(Context, getter); if (setter is not null) Method.Create(Context, setter); for (var i = 0; i < Symbol.Parameters.Length; ++i) { var original = Parameter.Create(Context, Symbol.OriginalDefinition.Parameters[i], OriginalDefinition); Parameter.Create(Context, Symbol.Parameters[i], this, original); } PopulateAttributes(); PopulateModifiers(trapFile); var declSyntaxReferences = IsSourceDeclaration ? Symbol.DeclaringSyntaxReferences. Select(d => d.GetSyntax()).OfType().ToArray() : Enumerable.Empty(); foreach (var explicitInterface in Symbol.ExplicitInterfaceImplementations.Select(impl => Type.Create(Context, impl.ContainingType))) { trapFile.explicitly_implements(this, explicitInterface.TypeRef); foreach (var syntax in declSyntaxReferences) TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier!.Name, this, explicitInterface); } if (Context.OnlyScaffold) { return; } if (Context.ExtractLocation(Symbol)) { WriteLocationsToTrap(trapFile.indexer_location, this, Locations); } if (IsSourceDeclaration) { var expressionBody = ExpressionBody; if (expressionBody is not null) { // The expression may need to reference parameters in the getter. // So we need to arrange that the expression is populated after the getter. Context.PopulateLater(() => Expression.CreateFromNode(new ExpressionNodeInfo(Context, expressionBody, this, 0).SetType(Symbol.GetAnnotatedType()))); } } BindComments(); foreach (var syntax in declSyntaxReferences) TypeMention.Create(Context, syntax.Type, this, type); } public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntityFromSymbol(cx, prop); public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); trapFile.Write(Symbol.MetadataName); trapFile.Write('('); trapFile.BuildList(",", Symbol.Parameters, p => trapFile.WriteSubId(Type.Create(Context, p.Type))); trapFile.Write(");indexer"); } public override Microsoft.CodeAnalysis.Location FullLocation { get { return Symbol.DeclaringSyntaxReferences .Select(r => r.GetSyntax()) .OfType() .Select(s => s.GetLocation()) .Concat(Symbol.Locations) .Best(); } } bool IExpressionParentEntity.IsTopLevelParent => true; private class IndexerFactory : CachedEntityFactory { public static IndexerFactory Instance { get; } = new IndexerFactory(); public override Indexer Create(Context cx, IPropertySymbol init) => new Indexer(cx, init); } } }