Skip to content

Commit

Permalink
Fix code generation on init-only property
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed May 8, 2020
1 parent fa105f2 commit 5af14a6
Show file tree
Hide file tree
Showing 25 changed files with 141 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,58 @@ public override ref int X()
}", new[] { "X", "Y", "this[]" });
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateOverrides)]
public async Task TestInitOnlyProperty()
{
await TestWithPickMembersDialogAsync(
@"
class Base
{
public virtual int Property { init => throw new NotImplementedException(); }
}
class Derived : Base
{
[||]
}",
@"
class Base
{
public virtual int Property { init => throw new NotImplementedException(); }
}
class Derived : Base
{
public override int Property { init => base.Property = value; }
}", new[] { "Property" });
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateOverrides)]
public async Task TestInitOnlyIndexer()
{
await TestWithPickMembersDialogAsync(
@"
class Base
{
public virtual int this[int i] { init => throw new NotImplementedException(); }
}
class Derived : Base
{
[||]
}",
@"
class Base
{
public virtual int this[int i] { init => throw new NotImplementedException(); }
}
class Derived : Base
{
public override int this[int i] { init => base[i] = value; }
}", new[] { "this[]" });
}

[WorkItem(21601, "https://github.com/dotnet/roslyn/issues/21601")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateOverrides)]
public async Task TestMissingInStaticClass1()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8417,6 +8417,30 @@ void I.M2()
}", index: 2);
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task ImplementInitOnlyProperty()
{
await TestInRegularAndScriptAsync(@"
interface I
{
int Property { get; init; }
}
class C : [|I|]
{
}",
@"
interface I
{
int Property { get; init; }
}
class C : [|I|]
{
public int Property { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task ImplementRemainingExplicitlyMissingWhenAllImplemented()
{
Expand Down
3 changes: 3 additions & 0 deletions src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ internal static async Task TestAddMethodAsync(
modifiers,
GetTypeSymbol(returnType)(context.SemanticModel),
RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations,
name,
typeParameters,
Expand Down Expand Up @@ -379,6 +380,7 @@ internal static async Task TestAddPropertyAsync(
new Editing.DeclarationModifiers(isAbstract: getStatements == null),
typeSymbol,
RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
"get_" + name,
typeParameters: default,
Expand All @@ -390,6 +392,7 @@ internal static async Task TestAddPropertyAsync(
new Editing.DeclarationModifiers(isAbstract: setStatements == null),
GetTypeSymbol(typeof(void))(context.SemanticModel),
RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
"set_" + name,
typeParameters: default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ public class [|C|]
}}");
}

[Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)]
public async Task TestInitOnlyProperty()
{
var metadataSource = @"public class C { public int Property { get; init; } }
namespace System.Runtime.CompilerServices
{
public sealed class IsExternalInit { }
}
";
var symbolName = "C";

await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview",
expected: $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// {CodeAnalysisResources.InMemoryAssembly}
#endregion
public class [|C|]
{{
public C();
public int Property {{ get; init; }}
}}");
}

[Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)]
public async Task TestTupleWithNames()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.NavigationBar
modifiers:=New DeclarationModifiers(),
returnType:=delegateInvokeMethod.ReturnType,
refKind:=delegateInvokeMethod.RefKind,
isInitOnly:=False,
explicitInterfaceImplementations:=Nothing,
name:=methodName,
typeParameters:=Nothing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.NavigationBar
modifiers:=New DeclarationModifiers(isOverride:=True),
returnType:=compilation.GetSpecialType(SpecialType.System_Void),
refKind:=RefKind.None,
isInitOnly:=False,
explicitInterfaceImplementations:=Nothing,
name:=WellKnownMemberNames.DestructorName,
typeParameters:=Nothing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ private static async Task<Document> UpdateDocumentAsync(
modifiers: new DeclarationModifiers(isStatic, isAsync: declaredSymbol.IsAsync),
returnType: declaredSymbol.ReturnType,
refKind: default,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: methodName,
typeParameters: typeParameters.ToImmutableArray(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ protected override OperationStatus<IMethodSymbol> GenerateMethodDefinition(bool
modifiers: CreateMethodModifiers(),
returnType: AnalyzerResult.ReturnType,
refKind: RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: _methodName.ToString(),
typeParameters: CreateMethodTypeParameters(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ private static IMethodSymbol GenerateMethodSymbol(
modifiers: default,
returnType: typeToGenerateIn,
refKind: RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: null,
typeParameters: ImmutableArray<ITypeParameterSymbol>.Empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ protected override async Task<ISymbol> GenerateMemberAsync(ISymbol member, IName
modifiers: MemberInsertionCompletionItem.GetModifiers(item),
returnType: semanticModel.Compilation.GetSpecialType(SpecialType.System_Void),
refKind: method.RefKind,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: member.Name,
typeParameters: method.TypeParameters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ private static string GetLegalName(string name, Document document)
private static IMethodSymbol CreateAccessorSymbol(IPropertySymbol prop, MethodKind kind)
=> CodeGenerationSymbolFactory.CreateMethodSymbol(
attributes: default, Accessibility.Public, DeclarationModifiers.Abstract,
prop.Type, refKind: default, explicitInterfaceImplementations: default,
prop.Type, refKind: default, isInitOnly: false, explicitInterfaceImplementations: default,
name: "", typeParameters: default, parameters: default, methodKind: kind);

private static IMethodSymbol CreateConstructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,7 @@ private static IMethodSymbol GenerateDeconstructMethod(
modifiers: default,
model.Compilation.GetSpecialType(SpecialType.System_Void),
RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
WellKnownMemberNames.DeconstructMethodName,
typeParameters: default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ private ImmutableArray<ISymbol> CreateInterfaceMembers(IEnumerable<ISymbol> incl
modifiers: new DeclarationModifiers(isAbstract: true, isUnsafe: method.RequiresUnsafeModifier()),
returnType: method.ReturnType,
refKind: method.RefKind,
isInitOnly: method.IsInitOnly,
explicitInterfaceImplementations: default,
name: method.Name,
typeParameters: method.TypeParameters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ private IMethodSymbol CreateGetHashCodeMethod(
modifiers: new DeclarationModifiers(isOverride: true),
returnType: compilation.GetSpecialType(SpecialType.System_Int32),
refKind: RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: GetHashCodeName,
typeParameters: default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ private async Task<bool> TryInitializeMethodAsync(
modifiers: default,
returnType: semanticModel.Compilation.GetSpecialType(SpecialType.System_Void),
refKind: RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: null,
typeParameters: default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ private static IMethodSymbol CreateMethodSymbolWithReturnType(
modifiers: default,
returnType: expressionType,
refKind: RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: null,
typeParameters: ImmutableArray<ITypeParameterSymbol>.Empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public async ValueTask<IMethodSymbol> GenerateMethodAsync(
modifiers: new DeclarationModifiers(isStatic: State.IsStatic, isAbstract: isAbstract, isUnsafe: isUnsafe),
returnType: returnType,
refKind: DetermineRefKind(cancellationToken),
isInitOnly: false,
explicitInterfaceImplementations: default,
name: State.IdentifierToken.ValueText,
typeParameters: DetermineTypeParameters(cancellationToken),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public WrappedMethodSymbol(IMethodSymbol methodSymbol, bool canImplementImplicit
public IMethodSymbol ConstructedFrom => _symbol.ConstructedFrom;

public bool IsReadOnly => _symbol.IsReadOnly;
public bool IsInitOnly => _symbol.IsInitOnly; // TODO2: test MetadataAsSource
public bool IsInitOnly => _symbol.IsInitOnly;

public ImmutableArray<IMethodSymbol> ExplicitInterfaceImplementations
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
modifiers:=CreateMethodModifiers(),
returnType:=Me.AnalyzerResult.ReturnType,
refKind:=RefKind.None,
isInitOnly:=False,
explicitInterfaceImplementations:=Nothing,
name:=_methodName.ToString(),
typeParameters:=CreateMethodTypeParameters(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateMethod
modifiers:=Nothing,
returnType:=typeToGenerateIn,
refKind:=RefKind.None,
isInitOnly:=False,
explicitInterfaceImplementations:=Nothing,
name:=Nothing,
typeParameters:=ImmutableArray(Of ITypeParameterSymbol).Empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,11 @@ private static AccessorListSyntax GenerateAccessorList(
IPropertySymbol property, CodeGenerationDestination destination,
Workspace workspace, CodeGenerationOptions options, ParseOptions parseOptions)
{
var setAccessorKind = property.SetMethod?.IsInitOnly == true ? SyntaxKind.InitAccessorDeclaration : SyntaxKind.SetAccessorDeclaration;
var accessors = new List<AccessorDeclarationSyntax>
{
GenerateAccessorDeclaration(property, property.GetMethod, SyntaxKind.GetAccessorDeclaration, destination, workspace, options, parseOptions),
GenerateAccessorDeclaration(property, property.SetMethod, SyntaxKind.SetAccessorDeclaration, destination, workspace, options, parseOptions),
GenerateAccessorDeclaration(property, property.SetMethod, setAccessorKind, destination, workspace, options, parseOptions),
};

return accessors[0] == null && accessors[1] == null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ internal static IMethodSymbol CreateMethodSymbol(
Accessibility accessibility, DeclarationModifiers modifiers,
ITypeSymbol returnType,
RefKind refKind,
bool isInitOnly,
ImmutableArray<IMethodSymbol> explicitInterfaceImplementations, string name,
ImmutableArray<ITypeParameterSymbol> typeParameters,
ImmutableArray<IParameterSymbol> parameters,
Expand All @@ -156,8 +157,7 @@ internal static IMethodSymbol CreateMethodSymbol(
ImmutableArray<AttributeData> returnTypeAttributes = default,
MethodKind methodKind = MethodKind.Ordinary)
{
// TODO2
var result = new CodeGenerationMethodSymbol(containingType, attributes, accessibility, modifiers, returnType, refKind, isInitOnly: false, explicitInterfaceImplementations, name, typeParameters, parameters, returnTypeAttributes, methodKind);
var result = new CodeGenerationMethodSymbol(containingType, attributes, accessibility, modifiers, returnType, refKind, isInitOnly, explicitInterfaceImplementations, name, typeParameters, parameters, returnTypeAttributes, methodKind);
CodeGenerationMethodInfo.Attach(result, modifiers.IsNew, modifiers.IsUnsafe, modifiers.IsPartial, modifiers.IsAsync, statements, handlesExpressions);
return result;
}
Expand All @@ -169,6 +169,7 @@ public static IMethodSymbol CreateMethodSymbol(
ImmutableArray<AttributeData> attributes, Accessibility accessibility, DeclarationModifiers modifiers,
ITypeSymbol returnType,
RefKind refKind,
bool isInitOnly,
ImmutableArray<IMethodSymbol> explicitInterfaceImplementations, string name,
ImmutableArray<ITypeParameterSymbol> typeParameters,
ImmutableArray<IParameterSymbol> parameters,
Expand All @@ -177,7 +178,7 @@ public static IMethodSymbol CreateMethodSymbol(
ImmutableArray<AttributeData> returnTypeAttributes = default,
MethodKind methodKind = MethodKind.Ordinary)
{
return CreateMethodSymbol(null, attributes, accessibility, modifiers, returnType, refKind, explicitInterfaceImplementations, name, typeParameters, parameters, statements, handlesExpressions, returnTypeAttributes, methodKind);
return CreateMethodSymbol(null, attributes, accessibility, modifiers, returnType, refKind, isInitOnly, explicitInterfaceImplementations, name, typeParameters, parameters, statements, handlesExpressions, returnTypeAttributes, methodKind);
}

/// <summary>
Expand Down Expand Up @@ -325,12 +326,13 @@ internal static IMethodSymbol CreateAccessorSymbol(
ImmutableArray<IMethodSymbol> explicitInterfaceImplementations = default,
ImmutableArray<SyntaxNode> statements = default)
{
return CodeGenerationSymbolFactory.CreateMethodSymbol(
return CreateMethodSymbol(
attributes,
accessibility ?? accessor.DeclaredAccessibility,
accessor.GetSymbolModifiers().WithIsAbstract(statements == null),
accessor.ReturnType,
accessor.RefKind,
isInitOnly: accessor.IsInitOnly,
explicitInterfaceImplementations.IsDefault ? accessor.ExplicitInterfaceImplementations : explicitInterfaceImplementations,
accessor.Name,
accessor.TypeParameters,
Expand All @@ -347,12 +349,13 @@ public static IMethodSymbol CreateAccessorSymbol(
Accessibility accessibility,
ImmutableArray<SyntaxNode> statements)
{
return CodeGenerationSymbolFactory.CreateMethodSymbol(
return CreateMethodSymbol(
attributes,
accessibility,
new DeclarationModifiers(isAbstract: statements == null),
returnType: null,
refKind: RefKind.None,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: string.Empty,
typeParameters: default,
Expand Down Expand Up @@ -416,6 +419,7 @@ public static CodeGenerationNamedTypeSymbol CreateDelegateTypeSymbol(
modifiers: new DeclarationModifiers(),
returnType: returnType,
refKind: refKind,
isInitOnly: false,
explicitInterfaceImplementations: default,
name: "Invoke",
typeParameters: default,
Expand Down Expand Up @@ -468,6 +472,7 @@ internal static IMethodSymbol CreateMethodSymbol(
modifiers ?? method.GetSymbolModifiers(),
returnType ?? method.ReturnType,
method.RefKind,
isInitOnly: method.IsInitOnly,
explicitInterfaceImplementations,
name ?? method.Name,
method.TypeParameters,
Expand All @@ -488,7 +493,7 @@ internal static IPropertySymbol CreatePropertySymbol(
IMethodSymbol getMethod = null,
IMethodSymbol setMethod = null)
{
return CodeGenerationSymbolFactory.CreatePropertySymbol(
return CreatePropertySymbol(
attributes,
accessibility ?? property.DeclaredAccessibility,
modifiers ?? property.GetSymbolModifiers(),
Expand All @@ -512,7 +517,7 @@ internal static IEventSymbol CreateEventSymbol(
IMethodSymbol addMethod = null,
IMethodSymbol removeMethod = null)
{
return CodeGenerationSymbolFactory.CreateEventSymbol(
return CreateEventSymbol(
attributes,
accessibility ?? @event.DeclaredAccessibility,
modifiers ?? @event.GetSymbolModifiers(),
Expand Down
Loading

0 comments on commit 5af14a6

Please sign in to comment.