提交 d4e2332f 编写于 作者: J Julien 提交者: GitHub

Fix implement-interface with tuple names attribute (#13035)

上级 44dac61b
......@@ -27,6 +27,55 @@ public async Task TestMethod()
@"using System; interface IInterface { void Method1 ( ) ; } class Class : IInterface { public void Method1 ( ) { throw new NotImplementedException ( ) ; } } ");
}
private static readonly string s_tupleElementNamesAttribute =
@"namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event )]
public sealed class TupleElementNamesAttribute : Attribute { }
}
";
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface), Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.Tuples)]
public async Task TupleWithNamesInMethod()
{
await TestAsync(
@"interface IInterface { [return: System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] (int a, int b)[] Method1 ((int c, string) x); } class Class : [|IInterface|] { } " + s_tupleElementNamesAttribute,
@"using System; interface IInterface { [return: System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] (int a, int b)[] Method1 ((int c, string) x) ; } class Class : IInterface { public (int a, int b)[] Method1 ((int c, string) x) { throw new NotImplementedException ( ) ; } } " + s_tupleElementNamesAttribute);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface), Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.Tuples)]
public async Task TupleWithNamesInMethod_Explicitly()
{
await TestAsync(
@"interface IInterface { [return: System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] (int a, int b)[] Method1 ((int c, string) x); } class Class : [|IInterface|] { } " + s_tupleElementNamesAttribute,
@"using System; interface IInterface { [return: System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] (int a, int b)[] Method1 ((int c, string) x) ; } class Class : IInterface { (int a, int b)[] IInterface.Method1 ((int c, string) x) { throw new NotImplementedException ( ) ; } } " + s_tupleElementNamesAttribute,
index: 1);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface), Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.Tuples)]
public async Task TupleWithNamesInProperty()
{
await TestAsync(
@"interface IInterface { [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] (int a, int b)[] Property1 { [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] get; [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] set; } } class Class : [|IInterface|] { } " + s_tupleElementNamesAttribute,
@"using System; interface IInterface { [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] (int a, int b)[] Property1 { [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] get; [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] set; } } class Class : IInterface { public (int a, int b)[] Property1 { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } } " + s_tupleElementNamesAttribute);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface), Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.Tuples)]
public async Task TupleWithNamesInEvent()
{
await TestAsync(
@"interface IInterface { [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] event Func<(int a, int b)> Event1; } class Class : [|IInterface|] { } " + s_tupleElementNamesAttribute,
@"interface IInterface { [System.Runtime.CompilerServices.TupleElementNames(new[] { ""a"", ""b"" })] event Func<(int a, int b)> Event1; } class Class : IInterface { public event Func<(int a, int b)> Event1; } " + s_tupleElementNamesAttribute);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task NoDynamicAttributeInMethod()
{
await TestAsync(
@"interface IInterface { [return: System.Runtime.CompilerServices.DynamicAttribute()] object Method1 (); } class Class : [|IInterface|] { } ",
@"using System; interface IInterface { [return: System.Runtime.CompilerServices.DynamicAttribute()] object Method1 (); } class Class : IInterface { public object Method1 () { throw new NotImplementedException ( ) ; } } ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestMethodWhenClassBracesAreMissing()
{
......
......@@ -28,9 +28,9 @@ internal partial class ImplementInterfaceCodeAction
var updatedMethod = method.EnsureNonConflictingNames(
this.State.ClassOrStructType, syntaxFacts, cancellationToken);
updatedMethod = updatedMethod.RemoveInaccessibleAttributesAndAttributesOfType(
accessibleWithin: this.State.ClassOrStructType,
removeAttributeType: compilation.ComAliasNameAttributeType());
updatedMethod = updatedMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(
this.State.ClassOrStructType,
AttributesToRemove(compilation));
return CodeGenerationSymbolFactory.CreateMethodSymbol(
updatedMethod,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
......@@ -26,29 +27,13 @@ internal partial class ImplementInterfaceCodeAction
CancellationToken cancellationToken)
{
var factory = this.Document.GetLanguageService<SyntaxGenerator>();
var comAliasNameAttribute = compilation.ComAliasNameAttributeType();
var getAccessor = property.GetMethod == null
? null
: CodeGenerationSymbolFactory.CreateAccessorSymbol(
property.GetMethod.RemoveInaccessibleAttributesAndAttributesOfType(
accessibleWithin: this.State.ClassOrStructType,
removeAttributeType: comAliasNameAttribute),
attributes: null,
accessibility: accessibility,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.GetMethod : null,
statements: GetGetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
var setAccessor = property.SetMethod == null
? null
: CodeGenerationSymbolFactory.CreateAccessorSymbol(
property.SetMethod.RemoveInaccessibleAttributesAndAttributesOfType(
accessibleWithin: this.State.ClassOrStructType,
removeAttributeType: comAliasNameAttribute),
attributes: null,
accessibility: accessibility,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.SetMethod : null,
statements: GetSetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
var attributesToRemove = AttributesToRemove(compilation);
var getAccessor = GenerateGetAccessor(compilation, property, accessibility, generateAbstractly,
useExplicitInterfaceSymbol, attributesToRemove, cancellationToken);
var setAccessor = GenerateSetAccessor(compilation, property, accessibility,
generateAbstractly, useExplicitInterfaceSymbol, attributesToRemove, cancellationToken);
var syntaxFacts = Document.GetLanguageService<ISyntaxFactsService>();
var parameterNames = NameGenerator.EnsureUniqueness(
......@@ -56,7 +41,7 @@ internal partial class ImplementInterfaceCodeAction
var updatedProperty = property.RenameParameters(parameterNames);
updatedProperty = updatedProperty.RemoveAttributeFromParameters(comAliasNameAttribute);
updatedProperty = updatedProperty.RemoveAttributeFromParameters(attributesToRemove);
// TODO(cyrusn): Delegate through throughMember if it's non-null.
return CodeGenerationSymbolFactory.CreatePropertySymbol(
......@@ -69,6 +54,70 @@ internal partial class ImplementInterfaceCodeAction
setMethod: setAccessor);
}
/// <summary>
/// Lists compiler attributes that we want to remove.
/// The TupleElementNames attribute is compiler generated (it is used for naming tuple element names).
/// We never want to place it in source code.
/// Same thing for the Dynamic attribute.
/// </summary>
private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
{
return new[] { compilation.ComAliasNameAttributeType(), compilation.TupleElementNamesAttributeType(),
compilation.DynamicAttributeType() };
}
private IMethodSymbol GenerateSetAccessor(
Compilation compilation,
IPropertySymbol property,
Accessibility accessibility,
bool generateAbstractly,
bool useExplicitInterfaceSymbol,
INamedTypeSymbol[] attributesToRemove,
CancellationToken cancellationToken)
{
if (property.SetMethod == null)
{
return null;
}
var setMethod = property.SetMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(
this.State.ClassOrStructType,
attributesToRemove);
return CodeGenerationSymbolFactory.CreateAccessorSymbol(
setMethod,
attributes: null,
accessibility: accessibility,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.SetMethod : null,
statements: GetSetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
}
private IMethodSymbol GenerateGetAccessor(
Compilation compilation,
IPropertySymbol property,
Accessibility accessibility,
bool generateAbstractly,
bool useExplicitInterfaceSymbol,
INamedTypeSymbol[] attributesToRemove,
CancellationToken cancellationToken)
{
if (property.GetMethod == null)
{
return null;
}
var getMethod = property.GetMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(
this.State.ClassOrStructType,
attributesToRemove);
return CodeGenerationSymbolFactory.CreateAccessorSymbol(
getMethod,
attributes: null,
accessibility: accessibility,
explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.GetMethod : null,
statements: GetGetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
}
private IList<SyntaxNode> GetSetAccessorStatements(
Compilation compilation,
IPropertySymbol property,
......
......@@ -138,5 +138,15 @@ public static INamedTypeSymbol SuppressMessageAttributeType(this Compilation com
{
return compilation.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.SuppressMessageAttribute");
}
}
public static INamedTypeSymbol TupleElementNamesAttributeType(this Compilation compilation)
{
return compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.TupleElementNamesAttribute");
}
public static INamedTypeSymbol DynamicAttributeType(this Compilation compilation)
{
return compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.DynamicAttribute");
}
}
}
......@@ -168,13 +168,13 @@ public static IMethodSymbol RenameParameters(this IMethodSymbol method, IList<st
return updatedMethod.RenameParameters(parameterNames);
}
public static IMethodSymbol RemoveInaccessibleAttributesAndAttributesOfType(
this IMethodSymbol method, ISymbol accessibleWithin, INamedTypeSymbol removeAttributeType,
IList<SyntaxNode> statements = null, IList<SyntaxNode> handlesExpressions = null)
public static IMethodSymbol RemoveInaccessibleAttributesAndAttributesOfTypes(
this IMethodSymbol method, ISymbol accessibleWithin, params INamedTypeSymbol[] removeAttributeTypes)
{
Func<AttributeData, bool> shouldRemoveAttribute = a =>
a.AttributeClass.Equals(removeAttributeType) || !a.AttributeClass.IsAccessibleWithin(accessibleWithin);
return method.RemoveAttributesCore(shouldRemoveAttribute, statements, handlesExpressions);
removeAttributeTypes.Any(attr => attr != null && attr.Equals(a.AttributeClass)) || !a.AttributeClass.IsAccessibleWithin(accessibleWithin);
return method.RemoveAttributesCore(shouldRemoveAttribute, statements: null, handlesExpressions: null);
}
private static IMethodSymbol RemoveAttributesCore(
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
......@@ -34,15 +35,18 @@ public static IPropertySymbol RenameParameters(this IPropertySymbol property, IL
}
public static IPropertySymbol RemoveAttributeFromParameters(
this IPropertySymbol property, INamedTypeSymbol attributeType)
this IPropertySymbol property, INamedTypeSymbol[] attributesToRemove)
{
if (attributeType == null)
if (attributesToRemove == null)
{
return property;
}
Func<AttributeData, bool> shouldRemoveAttribute = a =>
attributesToRemove.Where(attr => attr != null).Any(attr => attr.Equals(a.AttributeClass));
var someParameterHasAttribute = property.Parameters
.Any(p => p.GetAttributes().Any(a => a.AttributeClass.Equals(attributeType)));
.Any(p => p.GetAttributes().Any(shouldRemoveAttribute));
if (!someParameterHasAttribute)
{
return property;
......@@ -58,7 +62,7 @@ public static IPropertySymbol RenameParameters(this IPropertySymbol property, IL
property.Name,
property.Parameters.Select(p =>
CodeGenerationSymbolFactory.CreateParameterSymbol(
p.GetAttributes().Where(a => !a.AttributeClass.Equals(attributeType)).ToList(),
p.GetAttributes().Where(a => !shouldRemoveAttribute(a)).ToList(),
p.RefKind, p.IsParams, p.Type, p.Name, p.IsOptional,
p.HasExplicitDefaultValue, p.HasExplicitDefaultValue ? p.ExplicitDefaultValue : null)).ToList(),
property.GetMethod,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册