|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal static partial class SyntaxGeneratorExtensions
{
public static IMethodSymbol CreateBaseDelegatingConstructor(
this SyntaxGenerator factory,
IMethodSymbol constructor,
string typeName)
{
// Create a constructor that calls the base constructor. Note: if there are no
// parameters then don't bother writing out "base()" it's automatically implied.
return CodeGenerationSymbolFactory.CreateConstructorSymbol(
attributes: default,
accessibility: Accessibility.Public,
modifiers: new DeclarationModifiers(),
typeName: typeName,
parameters: constructor.Parameters,
statements: default,
baseConstructorArguments: constructor.Parameters.Length == 0
? default
: factory.CreateArguments(constructor.Parameters));
}
public static ImmutableArray<ISymbol> CreateMemberDelegatingConstructor(
this SyntaxGenerator factory,
SemanticModel semanticModel,
string typeName,
INamedTypeSymbol? containingType,
ImmutableArray<IParameterSymbol> parameters,
Accessibility accessibility,
ImmutableDictionary<string, ISymbol>? parameterToExistingMemberMap,
ImmutableDictionary<string, string>? parameterToNewMemberMap,
bool addNullChecks,
bool preferThrowExpression,
bool generateProperties,
bool isContainedInUnsafeType)
{
var newMembers = generateProperties
? CreatePropertiesForParameters(parameters, parameterToNewMemberMap, isContainedInUnsafeType)
: CreateFieldsForParameters(parameters, parameterToNewMemberMap, isContainedInUnsafeType);
var statements = factory.CreateAssignmentStatements(
semanticModel, parameters, parameterToExistingMemberMap, parameterToNewMemberMap,
addNullChecks, preferThrowExpression).SelectAsArray(
s => s.WithAdditionalAnnotations(Simplifier.Annotation));
var constructor = CodeGenerationSymbolFactory.CreateConstructorSymbol(
attributes: default,
accessibility: accessibility,
modifiers: new DeclarationModifiers(isUnsafe: !isContainedInUnsafeType && parameters.Any(static p => p.RequiresUnsafeModifier())),
typeName: typeName,
parameters: parameters,
statements: statements,
thisConstructorArguments: ShouldGenerateThisConstructorCall(containingType, parameterToExistingMemberMap)
? ImmutableArray<SyntaxNode>.Empty
: default);
return newMembers.Concat(constructor);
}
private static bool ShouldGenerateThisConstructorCall(
INamedTypeSymbol? containingType,
IDictionary<string, ISymbol>? parameterToExistingFieldMap)
{
if (containingType?.TypeKind == TypeKind.Struct)
{
// Special case. If we're generating a struct constructor, then we'll need
// to initialize all fields in the struct, not just the ones we're creating.
// If there is any field or auto-property not being set by a parameter, we
// call the default constructor.
return containingType.GetMembers()
.OfType<IFieldSymbol>()
.Where(field => !field.IsStatic)
.Select(field => field.AssociatedSymbol ?? field)
.Except(parameterToExistingFieldMap?.Values ?? SpecializedCollections.EmptyEnumerable<ISymbol>())
.Any();
}
return false;
}
public static ImmutableArray<ISymbol> CreateFieldsForParameters(
ImmutableArray<IParameterSymbol> parameters, ImmutableDictionary<string, string>? parameterToNewFieldMap, bool isContainedInUnsafeType)
{
using var _ = ArrayBuilder<ISymbol>.GetInstance(out var result);
foreach (var parameter in parameters)
{
// For non-out parameters, create a field and assign the parameter to it.
if (parameter.RefKind != RefKind.Out &&
TryGetValue(parameterToNewFieldMap, parameter.Name, out var fieldName))
{
result.Add(CodeGenerationSymbolFactory.CreateFieldSymbol(
attributes: default,
accessibility: Accessibility.Private,
modifiers: new DeclarationModifiers(isUnsafe: !isContainedInUnsafeType && parameter.RequiresUnsafeModifier()),
type: parameter.Type,
name: fieldName));
}
}
return result.ToImmutable();
}
public static ImmutableArray<ISymbol> CreatePropertiesForParameters(
ImmutableArray<IParameterSymbol> parameters, ImmutableDictionary<string, string>? parameterToNewPropertyMap, bool isContainedInUnsafeType)
{
using var _ = ArrayBuilder<ISymbol>.GetInstance(out var result);
foreach (var parameter in parameters)
{
// For non-out parameters, create a property and assign the parameter to it.
if (parameter.RefKind != RefKind.Out &&
TryGetValue(parameterToNewPropertyMap, parameter.Name, out var propertyName))
{
result.Add(CodeGenerationSymbolFactory.CreatePropertySymbol(
attributes: default,
accessibility: Accessibility.Public,
modifiers: new DeclarationModifiers(isUnsafe: !isContainedInUnsafeType && parameter.RequiresUnsafeModifier()),
type: parameter.Type,
refKind: RefKind.None,
explicitInterfaceImplementations: ImmutableArray<IPropertySymbol>.Empty,
name: propertyName,
parameters: ImmutableArray<IParameterSymbol>.Empty,
getMethod: CodeGenerationSymbolFactory.CreateAccessorSymbol(
attributes: default,
accessibility: default,
statements: default),
setMethod: null));
}
}
return result.ToImmutable();
}
private static bool TryGetValue(IDictionary<string, string>? dictionary, string key, [NotNullWhen(true)] out string? value)
{
value = null;
return
dictionary != null &&
dictionary.TryGetValue(key, out value);
}
private static bool TryGetValue(IDictionary<string, ISymbol>? dictionary, string key, [NotNullWhen(true)] out string? value)
{
value = null;
if (dictionary != null && dictionary.TryGetValue(key, out var symbol))
{
value = symbol.Name;
return true;
}
return false;
}
public static SyntaxNode CreateThrowArgumentNullExpression(this SyntaxGenerator factory, Compilation compilation, IParameterSymbol parameter)
=> factory.ThrowExpression(CreateNewArgumentNullException(factory, compilation, parameter));
private static SyntaxNode CreateNewArgumentNullException(SyntaxGenerator factory, Compilation compilation, IParameterSymbol parameter)
{
var type = compilation.GetTypeByMetadataName(typeof(ArgumentNullException).FullName!);
Contract.ThrowIfNull(type);
return factory.ObjectCreationExpression(type,
factory.NameOfExpression(
factory.IdentifierName(parameter.Name))).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation);
}
public static SyntaxNode CreateNullCheckAndThrowStatement(
this SyntaxGenerator factory,
SemanticModel semanticModel,
IParameterSymbol parameter)
{
var condition = factory.CreateNullCheckExpression(semanticModel, parameter.Name);
var throwStatement = factory.CreateThrowArgumentNullExceptionStatement(semanticModel.Compilation, parameter);
// generates: if (s is null) { throw new ArgumentNullException(nameof(s)); }
return factory.IfStatement(
condition,
SpecializedCollections.SingletonEnumerable(throwStatement));
}
public static SyntaxNode CreateThrowArgumentNullExceptionStatement(this SyntaxGenerator factory, Compilation compilation, IParameterSymbol parameter)
=> factory.ThrowStatement(CreateNewArgumentNullException(factory, compilation, parameter));
public static SyntaxNode CreateNullCheckExpression(this SyntaxGenerator factory, SemanticModel semanticModel, string identifierName)
{
var identifier = factory.IdentifierName(identifierName);
var nullExpr = factory.NullLiteralExpression();
var condition = factory.SyntaxGeneratorInternal.SupportsPatterns(semanticModel.SyntaxTree.Options)
? factory.SyntaxGeneratorInternal.IsPatternExpression(identifier, factory.SyntaxGeneratorInternal.ConstantPattern(nullExpr))
: factory.ReferenceEqualsExpression(identifier, nullExpr);
return condition;
}
public static ImmutableArray<SyntaxNode> CreateAssignmentStatements(
this SyntaxGenerator factory,
SemanticModel semanticModel,
ImmutableArray<IParameterSymbol> parameters,
IDictionary<string, ISymbol>? parameterToExistingFieldMap,
IDictionary<string, string>? parameterToNewFieldMap,
bool addNullChecks,
bool preferThrowExpression)
{
var nullCheckStatements = ArrayBuilder<SyntaxNode>.GetInstance();
var assignStatements = ArrayBuilder<SyntaxNode>.GetInstance();
foreach (var parameter in parameters)
{
var refKind = parameter.RefKind;
var parameterType = parameter.Type;
var parameterName = parameter.Name;
if (refKind == RefKind.Out)
{
// If it's an out param, then don't create a field for it. Instead, assign
// the default value for that type (i.e. "default(...)") to it.
var assignExpression = factory.AssignmentStatement(
factory.IdentifierName(parameterName),
factory.DefaultExpression(parameterType));
var statement = factory.ExpressionStatement(assignExpression);
assignStatements.Add(statement);
}
else
{
// For non-out parameters, create a field and assign the parameter to it.
// TODO: I'm not sure that's what we really want for ref parameters.
if (TryGetValue(parameterToExistingFieldMap, parameterName, out var fieldName) ||
TryGetValue(parameterToNewFieldMap, parameterName, out fieldName))
{
var fieldAccess = factory.MemberAccessExpression(factory.ThisExpression(), factory.IdentifierName(fieldName))
.WithAdditionalAnnotations(Simplifier.Annotation);
factory.AddAssignmentStatements(
semanticModel, parameter, fieldAccess,
addNullChecks, preferThrowExpression,
nullCheckStatements, assignStatements);
}
}
}
return nullCheckStatements.ToImmutableAndFree().Concat(assignStatements.ToImmutableAndFree());
}
public static void AddAssignmentStatements(
this SyntaxGenerator factory,
SemanticModel semanticModel,
IParameterSymbol parameter,
SyntaxNode fieldAccess,
bool addNullChecks,
bool preferThrowExpression,
ArrayBuilder<SyntaxNode> nullCheckStatements,
ArrayBuilder<SyntaxNode> assignStatements)
{
// Don't want to add a null check for something of the form `int?`. The type was
// already declared as nullable to indicate that null is ok. Adding a null check
// just disallows something that should be allowed.
var shouldAddNullCheck = addNullChecks && parameter.Type.CanAddNullCheck() && !parameter.Type.IsNullable();
if (shouldAddNullCheck && preferThrowExpression && factory.SupportsThrowExpression())
{
// Generate: this.x = x ?? throw ...
assignStatements.Add(CreateAssignWithNullCheckStatement(
factory, semanticModel.Compilation, parameter, fieldAccess));
}
else
{
if (shouldAddNullCheck)
{
// generate: if (x == null) throw ...
nullCheckStatements.Add(
factory.CreateNullCheckAndThrowStatement(semanticModel, parameter));
}
// generate: this.x = x;
assignStatements.Add(
factory.ExpressionStatement(
factory.AssignmentStatement(
fieldAccess,
factory.IdentifierName(parameter.Name))));
}
}
public static SyntaxNode CreateAssignWithNullCheckStatement(
this SyntaxGenerator factory, Compilation compilation, IParameterSymbol parameter, SyntaxNode fieldAccess)
{
return factory.ExpressionStatement(factory.AssignmentStatement(
fieldAccess,
factory.CoalesceExpression(
factory.IdentifierName(parameter.Name),
factory.CreateThrowArgumentNullExpression(compilation, parameter))));
}
public static async Task<IPropertySymbol> OverridePropertyAsync(
this SyntaxGenerator codeFactory,
IPropertySymbol overriddenProperty,
DeclarationModifiers modifiers,
INamedTypeSymbol containingType,
Document document,
CancellationToken cancellationToken)
{
var getAccessibility = overriddenProperty.GetMethod.ComputeResultantAccessibility(containingType);
var setAccessibility = overriddenProperty.SetMethod.ComputeResultantAccessibility(containingType);
SyntaxNode? getBody;
SyntaxNode? setBody;
// Implement an abstract property by throwing not implemented in accessors.
if (overriddenProperty.IsAbstract)
{
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var statement = codeFactory.CreateThrowNotImplementedStatement(compilation);
getBody = statement;
setBody = statement;
}
else if (overriddenProperty.IsIndexer() && document.Project.Language == LanguageNames.CSharp)
{
// Indexer: return or set base[]. Only in C#, since VB must refer to these by name.
getBody = codeFactory.ReturnStatement(
WrapWithRefIfNecessary(codeFactory, overriddenProperty,
codeFactory.ElementAccessExpression(
codeFactory.BaseExpression(),
codeFactory.CreateArguments(overriddenProperty.Parameters))));
setBody = codeFactory.ExpressionStatement(
codeFactory.AssignmentStatement(
codeFactory.ElementAccessExpression(
codeFactory.BaseExpression(),
codeFactory.CreateArguments(overriddenProperty.Parameters)),
codeFactory.IdentifierName("value")));
}
else if (overriddenProperty.GetParameters().Any())
{
// Call accessors directly if C# overriding VB
if (document.Project.Language == LanguageNames.CSharp
&& await SymbolFinder.FindSourceDefinitionAsync(overriddenProperty, document.Project.Solution, cancellationToken).ConfigureAwait(false) is { Language: LanguageNames.VisualBasic })
{
var getName = overriddenProperty.GetMethod?.Name;
var setName = overriddenProperty.SetMethod?.Name;
getBody = getName == null
? null
: codeFactory.ReturnStatement(
codeFactory.InvocationExpression(
codeFactory.MemberAccessExpression(
codeFactory.BaseExpression(),
codeFactory.IdentifierName(getName)),
codeFactory.CreateArguments(overriddenProperty.Parameters)));
setBody = setName == null
? null
: codeFactory.ExpressionStatement(
codeFactory.InvocationExpression(
codeFactory.MemberAccessExpression(
codeFactory.BaseExpression(),
codeFactory.IdentifierName(setName)),
codeFactory.CreateArguments(overriddenProperty.SetMethod.GetParameters())));
}
else
{
getBody = codeFactory.ReturnStatement(
WrapWithRefIfNecessary(codeFactory, overriddenProperty,
codeFactory.InvocationExpression(
codeFactory.MemberAccessExpression(
codeFactory.BaseExpression(),
codeFactory.IdentifierName(overriddenProperty.Name)), codeFactory.CreateArguments(overriddenProperty.Parameters))));
setBody = codeFactory.ExpressionStatement(
codeFactory.AssignmentStatement(
codeFactory.InvocationExpression(
codeFactory.MemberAccessExpression(
codeFactory.BaseExpression(),
codeFactory.IdentifierName(overriddenProperty.Name)), codeFactory.CreateArguments(overriddenProperty.Parameters)),
codeFactory.IdentifierName("value")));
}
}
else
{
// Regular property: return or set the base property
getBody = codeFactory.ReturnStatement(
WrapWithRefIfNecessary(codeFactory, overriddenProperty,
codeFactory.MemberAccessExpression(
codeFactory.BaseExpression(),
codeFactory.IdentifierName(overriddenProperty.Name))));
setBody = codeFactory.ExpressionStatement(
codeFactory.AssignmentStatement(
codeFactory.MemberAccessExpression(
codeFactory.BaseExpression(),
codeFactory.IdentifierName(overriddenProperty.Name)),
codeFactory.IdentifierName("value")));
}
// Only generate a getter if the base getter is accessible.
IMethodSymbol? accessorGet = null;
if (overriddenProperty.GetMethod != null && overriddenProperty.GetMethod.IsAccessibleWithin(containingType))
{
accessorGet = CodeGenerationSymbolFactory.CreateMethodSymbol(
overriddenProperty.GetMethod,
accessibility: getAccessibility,
statements: getBody != null ? ImmutableArray.Create(getBody) : ImmutableArray<SyntaxNode>.Empty,
modifiers: modifiers);
}
// Only generate a setter if the base setter is accessible.
IMethodSymbol? accessorSet = null;
if (overriddenProperty.SetMethod != null &&
overriddenProperty.SetMethod.IsAccessibleWithin(containingType) &&
overriddenProperty.SetMethod.DeclaredAccessibility != Accessibility.Private)
{
accessorSet = CodeGenerationSymbolFactory.CreateMethodSymbol(
overriddenProperty.SetMethod,
accessibility: setAccessibility,
statements: setBody != null ? ImmutableArray.Create(setBody) : ImmutableArray<SyntaxNode>.Empty,
modifiers: modifiers);
}
return CodeGenerationSymbolFactory.CreatePropertySymbol(
overriddenProperty,
accessibility: overriddenProperty.ComputeResultantAccessibility(containingType),
modifiers: modifiers,
name: overriddenProperty.Name,
parameters: overriddenProperty.RemoveInaccessibleAttributesAndAttributesOfTypes(containingType).Parameters,
isIndexer: overriddenProperty.IsIndexer(),
getMethod: accessorGet,
setMethod: accessorSet);
}
private static SyntaxNode WrapWithRefIfNecessary(SyntaxGenerator codeFactory, IPropertySymbol overriddenProperty, SyntaxNode body)
=> overriddenProperty.ReturnsByRef
? codeFactory.RefExpression(body)
: body;
public static IEventSymbol OverrideEvent(
IEventSymbol overriddenEvent,
DeclarationModifiers modifiers,
INamedTypeSymbol newContainingType)
{
return CodeGenerationSymbolFactory.CreateEventSymbol(
overriddenEvent,
attributes: default,
accessibility: overriddenEvent.ComputeResultantAccessibility(newContainingType),
modifiers: modifiers,
explicitInterfaceImplementations: default,
name: overriddenEvent.Name);
}
public static async Task<ISymbol> OverrideAsync(
this SyntaxGenerator generator,
ISymbol symbol,
INamedTypeSymbol containingType,
Document document,
DeclarationModifiers extraDeclarationModifiers = default,
CancellationToken cancellationToken = default)
{
var modifiers = GetOverrideModifiers(symbol) + extraDeclarationModifiers;
if (symbol is IMethodSymbol method)
{
return await generator.OverrideMethodAsync(method,
modifiers, containingType, document, cancellationToken).ConfigureAwait(false);
}
else if (symbol is IPropertySymbol property)
{
return await generator.OverridePropertyAsync(property,
modifiers, containingType, document, cancellationToken).ConfigureAwait(false);
}
else if (symbol is IEventSymbol ev)
{
return OverrideEvent(ev, modifiers, containingType);
}
else
{
throw ExceptionUtilities.Unreachable();
}
}
private static DeclarationModifiers GetOverrideModifiers(ISymbol symbol)
=> symbol.GetSymbolModifiers()
.WithIsOverride(true)
.WithIsAbstract(false)
.WithIsVirtual(false);
private static async Task<IMethodSymbol> OverrideMethodAsync(
this SyntaxGenerator codeFactory,
IMethodSymbol overriddenMethod,
DeclarationModifiers modifiers,
INamedTypeSymbol newContainingType,
Document newDocument,
CancellationToken cancellationToken)
{
// Required is not a valid modifier for methods, so clear it if the user typed it
modifiers = modifiers.WithIsRequired(false);
// Abstract: Throw not implemented
if (overriddenMethod.IsAbstract)
{
var compilation = await newDocument.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var statement = codeFactory.CreateThrowNotImplementedStatement(compilation);
return CodeGenerationSymbolFactory.CreateMethodSymbol(
overriddenMethod,
accessibility: overriddenMethod.ComputeResultantAccessibility(newContainingType),
modifiers: modifiers,
statements: ImmutableArray.Create(statement));
}
else
{
// Otherwise, call the base method with the same parameters
var typeParams = overriddenMethod.GetTypeArguments();
var body = codeFactory.InvocationExpression(
codeFactory.MemberAccessExpression(codeFactory.BaseExpression(),
typeParams.IsDefaultOrEmpty
? codeFactory.IdentifierName(overriddenMethod.Name)
: codeFactory.GenericName(overriddenMethod.Name, typeParams)),
codeFactory.CreateArguments(overriddenMethod.GetParameters()));
if (overriddenMethod.ReturnsByRef)
{
body = codeFactory.RefExpression(body);
}
return CodeGenerationSymbolFactory.CreateMethodSymbol(
method: overriddenMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(newContainingType),
accessibility: overriddenMethod.ComputeResultantAccessibility(newContainingType),
modifiers: modifiers,
statements: overriddenMethod.ReturnsVoid
? ImmutableArray.Create(codeFactory.ExpressionStatement(body))
: ImmutableArray.Create(codeFactory.ReturnStatement(body)));
}
}
/// <summary>
/// Generates a call to a method *through* an existing field or property symbol.
/// </summary>
/// <returns></returns>
public static SyntaxNode GenerateDelegateThroughMemberStatement(
this SyntaxGenerator generator, IMethodSymbol method, ISymbol throughMember)
{
var through = CreateDelegateThroughExpression(generator, method, throughMember);
var memberName = method.IsGenericMethod
? generator.GenericName(method.Name, method.TypeArguments)
: generator.IdentifierName(method.Name);
through = generator.MemberAccessExpression(through, memberName);
var arguments = generator.CreateArguments(method.Parameters.As<IParameterSymbol>());
var invocationExpression = generator.InvocationExpression(through, arguments);
return method.ReturnsVoid
? generator.ExpressionStatement(invocationExpression)
: generator.ReturnStatement(invocationExpression);
}
public static SyntaxNode CreateDelegateThroughExpression(
this SyntaxGenerator generator, ISymbol member, ISymbol throughMember)
{
var through = throughMember.IsStatic
? GenerateContainerName(generator, throughMember)
: generator.ThisExpression();
through = generator.MemberAccessExpression(
through, generator.IdentifierName(throughMember.Name));
var throughMemberType = throughMember.GetMemberType();
if (member.ContainingType.IsInterfaceType() && throughMemberType != null)
{
// In the case of 'implement interface through field / property', we need to know what
// interface we are implementing so that we can insert casts to this interface on every
// usage of the field in the generated code. Without these casts we would end up generating
// code that fails compilation in certain situations.
//
// For example consider the following code.
// class C : IReadOnlyList<int> { int[] field; }
// When applying the 'implement interface through field' code fix in the above example,
// we need to generate the following code to implement the Count property on IReadOnlyList<int>
// class C : IReadOnlyList<int> { int[] field; int Count { get { ((IReadOnlyList<int>)field).Count; } ...}
// as opposed to the following code which will fail to compile (because the array field
// doesn't have a property named .Count) -
// class C : IReadOnlyList<int> { int[] field; int Count { get { field.Count; } ...}
//
// The 'InterfaceTypes' property on the state object always contains only one item
// in the case of C# i.e. it will contain exactly the interface we are trying to implement.
// This is also the case most of the time in the case of VB, except in certain error conditions
// (recursive / circular cases) where the span of the squiggle for the corresponding
// diagnostic (BC30149) changes and 'InterfaceTypes' ends up including all interfaces
// in the Implements clause. For the purposes of inserting the above cast, we ignore the
// uncommon case and optimize for the common one - in other words, we only apply the cast
// in cases where we can unambiguously figure out which interface we are trying to implement.
var interfaceBeingImplemented = member.ContainingType;
if (!throughMemberType.Equals(interfaceBeingImplemented))
{
through = generator.CastExpression(interfaceBeingImplemented,
through.WithAdditionalAnnotations(Simplifier.Annotation));
}
else if (!throughMember.IsStatic &&
throughMember is IPropertySymbol throughMemberProperty &&
throughMemberProperty.ExplicitInterfaceImplementations.Any())
{
// If we are implementing through an explicitly implemented property, we need to cast 'this' to
// the explicitly implemented interface type before calling the member, as in:
// ((IA)this).Prop.Member();
//
var explicitlyImplementedProperty = throughMemberProperty.ExplicitInterfaceImplementations[0];
var explicitImplementationCast = generator.CastExpression(
explicitlyImplementedProperty.ContainingType,
generator.ThisExpression());
through = generator.MemberAccessExpression(explicitImplementationCast,
generator.IdentifierName(explicitlyImplementedProperty.Name));
through = through.WithAdditionalAnnotations(Simplifier.Annotation);
}
}
return through.WithAdditionalAnnotations(Simplifier.Annotation);
// local functions
static SyntaxNode GenerateContainerName(SyntaxGenerator factory, ISymbol throughMember)
{
var classOrStructType = throughMember.ContainingType;
return classOrStructType.IsGenericType
? factory.GenericName(classOrStructType.Name, classOrStructType.TypeArguments)
: factory.IdentifierName(classOrStructType.Name);
}
}
public static ImmutableArray<SyntaxNode> GetGetAccessorStatements(
this SyntaxGenerator generator, Compilation compilation,
IPropertySymbol property, ISymbol? throughMember, bool preferAutoProperties)
{
if (throughMember != null)
{
var throughExpression = CreateDelegateThroughExpression(generator, property, throughMember);
var expression = property.IsIndexer
? throughExpression
: generator.MemberAccessExpression(
throughExpression, generator.IdentifierName(property.Name));
if (property.Parameters.Length > 0)
{
var arguments = generator.CreateArguments(property.Parameters.As<IParameterSymbol>());
expression = generator.ElementAccessExpression(expression, arguments);
}
return ImmutableArray.Create(generator.ReturnStatement(expression));
}
return preferAutoProperties ? default : generator.CreateThrowNotImplementedStatementBlock(compilation);
}
public static ImmutableArray<SyntaxNode> GetSetAccessorStatements(
this SyntaxGenerator generator, Compilation compilation,
IPropertySymbol property, ISymbol? throughMember, bool preferAutoProperties)
{
if (throughMember != null)
{
var throughExpression = CreateDelegateThroughExpression(generator, property, throughMember);
var expression = property.IsIndexer
? throughExpression
: generator.MemberAccessExpression(
throughExpression, generator.IdentifierName(property.Name));
if (property.Parameters.Length > 0)
{
var arguments = generator.CreateArguments(property.Parameters.As<IParameterSymbol>());
expression = generator.ElementAccessExpression(expression, arguments);
}
expression = generator.AssignmentStatement(expression, generator.IdentifierName("value"));
return ImmutableArray.Create(generator.ExpressionStatement(expression));
}
return preferAutoProperties
? default
: generator.CreateThrowNotImplementedStatementBlock(compilation);
}
}
}
|