mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
95 lines
3.5 KiB
C#
95 lines
3.5 KiB
C#
using System.IO;
|
|
using System.Linq;
|
|
using Microsoft.CodeAnalysis;
|
|
|
|
namespace Semmle.Extraction.CSharp.Entities
|
|
{
|
|
internal class Accessor : Method
|
|
{
|
|
private readonly IPropertySymbol property;
|
|
protected Accessor(Context cx, IMethodSymbol init, IPropertySymbol property)
|
|
: base(cx, init)
|
|
{
|
|
this.property = property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the property symbol associated with accessor `symbol`, or `null`
|
|
/// if there is no associated symbol.
|
|
/// </summary>
|
|
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<IPropertySymbol>();
|
|
props = props.Where(p => SymbolEqualityComparer.Default.Equals(symbol, p.GetMethod) || SymbolEqualityComparer.Default.Equals(symbol, p.SetMethod));
|
|
return props.SingleOrDefault();
|
|
}
|
|
|
|
public override bool NeedsPopulation =>
|
|
base.NeedsPopulation &&
|
|
!Symbol.IsPartialDefinition; // Accessors always have an implementing declaration as well.
|
|
|
|
public override void Populate(TextWriter trapFile)
|
|
{
|
|
PopulateMethod(trapFile);
|
|
PopulateModifiers(trapFile);
|
|
ContainingType!.PopulateGenerics();
|
|
|
|
var parent = Property.Create(Context, property);
|
|
int kind;
|
|
Accessor unboundAccessor;
|
|
if (SymbolEqualityComparer.Default.Equals(Symbol, property.GetMethod))
|
|
{
|
|
kind = 1;
|
|
var orig = property.OriginalDefinition;
|
|
unboundAccessor = Create(Context, orig.GetMethod!, orig);
|
|
}
|
|
else if (SymbolEqualityComparer.Default.Equals(Symbol, property.SetMethod))
|
|
{
|
|
kind = 2;
|
|
var orig = property.OriginalDefinition;
|
|
unboundAccessor = Create(Context, orig.SetMethod!, orig);
|
|
}
|
|
else
|
|
{
|
|
Context.ModelError(Symbol, $"Unhandled accessor method {Symbol.ToDisplayString()}");
|
|
return;
|
|
}
|
|
|
|
trapFile.accessors(this, kind, Symbol.Name, parent, unboundAccessor);
|
|
|
|
if (Context.ExtractLocation(Symbol))
|
|
{
|
|
WriteLocationsToTrap(trapFile.accessor_location, this, Locations);
|
|
}
|
|
|
|
Overrides(trapFile);
|
|
|
|
if (Symbol.FromSource() && Block is null)
|
|
{
|
|
trapFile.compiler_generated(this);
|
|
}
|
|
|
|
if (Symbol.IsInitOnly)
|
|
{
|
|
trapFile.init_only_accessors(this);
|
|
}
|
|
}
|
|
|
|
public static Accessor Create(Context cx, IMethodSymbol symbol, IPropertySymbol prop) =>
|
|
AccessorFactory.Instance.CreateEntity(cx, symbol, (symbol, prop));
|
|
|
|
private class AccessorFactory : CachedEntityFactory<(IMethodSymbol, IPropertySymbol), Accessor>
|
|
{
|
|
public static AccessorFactory Instance { get; } = new AccessorFactory();
|
|
|
|
public override Accessor Create(Context cx, (IMethodSymbol, IPropertySymbol) init) => new(cx, init.Item1, init.Item2);
|
|
}
|
|
}
|
|
}
|