using Microsoft.CodeAnalysis;
using System.IO;
using System.Linq;
namespace Semmle.Extraction.CSharp.Entities
{
internal class Accessor : Method
{
protected Accessor(Context cx, IMethodSymbol init)
: base(cx, init) { }
///
/// Gets the property symbol associated accessor `symbol`, or `null`
/// if there is no associated symbol.
///
public static IPropertySymbol? GetPropertySymbol(IMethodSymbol symbol)
{
// Usually, the property/indexer can be fetched from the associated symbol
if (symbol.AssociatedSymbol is IPropertySymbol prop)
return prop;
// But for properties/indexers that implement explicit interfaces, Roslyn
// does not properly populate `AssociatedSymbol`
var props = symbol.ContainingType.GetMembers().OfType();
props = props.Where(p => SymbolEqualityComparer.Default.Equals(symbol, p.GetMethod) || SymbolEqualityComparer.Default.Equals(symbol, p.SetMethod));
return props.SingleOrDefault();
}
///
/// Gets the property symbol associated with this accessor.
///
private IPropertySymbol? PropertySymbol => GetPropertySymbol(Symbol);
public new Accessor OriginalDefinition => Create(Context, Symbol.OriginalDefinition);
public override void Populate(TextWriter trapFile)
{
PopulateMethod(trapFile);
PopulateModifiers(trapFile);
ContainingType!.PopulateGenerics();
var prop = PropertySymbol;
if (prop is null)
{
Context.ModelError(Symbol, "Unhandled accessor associated symbol");
return;
}
var parent = Property.Create(Context, prop);
int kind;
Accessor unboundAccessor;
if (SymbolEqualityComparer.Default.Equals(Symbol, prop.GetMethod))
{
kind = 1;
unboundAccessor = Create(Context, prop.OriginalDefinition.GetMethod!);
}
else if (SymbolEqualityComparer.Default.Equals(Symbol, prop.SetMethod))
{
kind = 2;
unboundAccessor = Create(Context, prop.OriginalDefinition.SetMethod!);
}
else
{
Context.ModelError(Symbol, "Unhandled accessor kind");
return;
}
trapFile.accessors(this, kind, Symbol.Name, parent, unboundAccessor);
foreach (var l in Locations)
trapFile.accessor_location(this, l);
Overrides(trapFile);
if (Symbol.FromSource() && Block is null)
{
trapFile.compiler_generated(this);
}
if (Symbol.IsInitOnly)
{
trapFile.init_only_accessors(this);
}
}
public static new Accessor Create(Context cx, IMethodSymbol symbol) =>
AccessorFactory.Instance.CreateEntityFromSymbol(cx, symbol);
private class AccessorFactory : CachedEntityFactory
{
public static AccessorFactory Instance { get; } = new AccessorFactory();
public override Accessor Create(Context cx, IMethodSymbol init) => new Accessor(cx, init);
}
}
}