未验证 提交 63819a6f 编写于 作者: J Jason Malinowski 提交者: GitHub

Merge pull request #38038 from jasonmalinowski/remove-nullable-attributes-when-generating-code

Remove inaccessible attributes when generating code
......@@ -24,7 +24,7 @@ private async Task TestAllowUnsafeEnabledIfDisabledAsync(string initialMarkup)
using (var workspace = CreateWorkspaceFromOptions(initialMarkup, parameters))
{
var (_, action) = await GetCodeActionsAsync(workspace, parameters);
var operations = await VerifyActionAndGetOperationsAsync(action, default);
var operations = await VerifyActionAndGetOperationsAsync(workspace, action, default);
var (oldSolution, newSolution) = ApplyOperationsAndGetSolution(workspace, operations);
Assert.True(((CSharpCompilationOptions)newSolution.Projects.Single().CompilationOptions).AllowUnsafe);
......
......@@ -31,7 +31,7 @@ internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProvider
using (var workspace = CreateWorkspaceFromOptions(initialMarkup, parameters))
{
var (_, action) = await GetCodeActionsAsync(workspace, parameters);
var operations = await VerifyActionAndGetOperationsAsync(action, default);
var operations = await VerifyActionAndGetOperationsAsync(workspace, action, default);
var appliedChanges = ApplyOperationsAndGetSolution(workspace, operations);
var oldSolution = appliedChanges.Item1;
......
......@@ -337,6 +337,63 @@ public object Method1()
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task NoNullableAttributesInMethodFromMetadata()
{
var initial = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<MetadataReferenceFromSource Language=""C#"" CommonReferences=""true"">
<Document>
#nullable enable
public interface IInterface
{
void M(string? s1, string s2);
string this[string? s1, string s2] { get; set; }
}
</Document>
</MetadataReferenceFromSource>
<Document>
#nullable enable
using System;
class C : [|IInterface|]
{
}</Document>
</Project>
</Workspace>";
var expected = @"
#nullable enable
using System;
class C : IInterface
{
public string this[string? s1, string s2]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public void M(string? s1, string s2)
{
throw new NotImplementedException();
}
}";
await TestWithAllCodeStyleOptionsOffAsync(initial, expected, index: 0);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestMethodWhenClassBracesAreMissing()
{
......@@ -6036,6 +6093,56 @@ public void set_P(int x, object Value)
await TestWithAllCodeStyleOptionsOffAsync(initial, expected, index: 0);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestImplementationOfIndexerWithInaccessibleAttributes()
{
var initial = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
using System;
internal class ShouldBeRemovedAttribute : Attribute { }
public interface I
{
string this[[ShouldBeRemovedAttribute] int i] { get; set; }
}
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<ProjectReference>Assembly1</ProjectReference>
<Document>
using System;
class C : [|I|]
{
}
</Document>
</Project>
</Workspace>";
var expected = @"
using System;
class C : I
{
public string this[int i]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
";
await TestWithAllCodeStyleOptionsOffAsync(initial, expected, index: 0);
}
#if false
[WorkItem(13677)]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
......@@ -7996,7 +8103,7 @@ public class Test : ITest
}");
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/36101"), Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public async Task TestWithNullableDisabled()
{
await TestInRegularAndScriptAsync(
......
......@@ -398,7 +398,7 @@ Imports IVT = System.Runtime.CompilerServices.InternalsVisibleToAttribute
<Project Language="Visual Basic" CommonReferences="true" AssemblyName="ClassLibrary1">
<CompilationOptions
CryptoKeyFile=<%= SigningTestHelpers.PublicKeyFile %>
StrongNameProvider=<%= SigningTestHelpers.DefaultDesktopStrongNameProvider.GetType().AssemblyQualifiedName %>/>
StrongNameProvider=<%= SigningTestHelpers.DefaultDesktopStrongNameProvider.GetType().AssemblyQualifiedName %>
DelaySign="True"
/>
</Project>
......
......@@ -70,6 +70,9 @@ public TestParameters WithFixProviderData(object fixProviderData)
public TestParameters WithIndex(int index)
=> new TestParameters(parseOptions, compilationOptions, options, fixProviderData, index, priority, title: title);
public TestParameters WithRetainNonFixableDiagnostics(bool retainNonFixableDiagnostics)
=> new TestParameters(parseOptions, compilationOptions, options, fixProviderData, index, priority, title: title, retainNonFixableDiagnostics: retainNonFixableDiagnostics);
}
protected abstract string GetLanguage();
......@@ -241,7 +244,7 @@ protected Task TestSmartTagGlyphTagsAsync(string initialMarkup, ImmutableArray<s
string expectedDocumentName,
CodeAction action)
{
var operations = await VerifyActionAndGetOperationsAsync(action, default);
var operations = await VerifyActionAndGetOperationsAsync(workspace, action, default);
return await TestAddDocument(
workspace,
expectedMarkup,
......@@ -398,7 +401,7 @@ protected Task TestSmartTagGlyphTagsAsync(string initialMarkup, ImmutableArray<s
ImmutableArray<TextSpan> navigationSpans,
TestParameters parameters)
{
var operations = await VerifyActionAndGetOperationsAsync(action, parameters);
var operations = await VerifyActionAndGetOperationsAsync(workspace, action, parameters);
return await TestOperationsAsync(
workspace, expected, operations, conflictSpans, renameSpans,
warningSpans, navigationSpans, expectedChangedDocumentId: null, parseOptions: parameters.parseOptions);
......@@ -539,10 +542,15 @@ static void VerifyExpectedDocumentText(string expected, string actual)
}
}
internal static Task<ImmutableArray<CodeActionOperation>> VerifyActionAndGetOperationsAsync(
CodeAction action, TestParameters parameters)
internal async Task<ImmutableArray<CodeActionOperation>> VerifyActionAndGetOperationsAsync(
TestWorkspace workspace, CodeAction action, TestParameters parameters)
{
Assert.False(action is null, "No action was offered when one was expected.");
if (action is null)
{
var diagnostics = await GetDiagnosticsWorkerAsync(workspace, parameters.WithRetainNonFixableDiagnostics(true));
throw new Exception("No action was offered when one was expected. Diagnostics from the compilation: " + string.Join("", diagnostics.Select(d => Environment.NewLine + d.ToString())));
}
if (parameters.priority != null)
{
......@@ -554,7 +562,7 @@ static void VerifyExpectedDocumentText(string expected, string actual)
Assert.Equal(parameters.title, action.Title);
}
return action.GetOperationsAsync(CancellationToken.None);
return await action.GetOperationsAsync(CancellationToken.None);
}
protected Tuple<Solution, Solution> ApplyOperationsAndGetSolution(
......
......@@ -79,7 +79,7 @@ protected override Task<ImmutableArray<Diagnostic>> GetDiagnosticsWorkerAsync(Te
CodeAction action,
string expectedPreviewContents = null)
{
var operations = await VerifyActionAndGetOperationsAsync(action, default);
var operations = await VerifyActionAndGetOperationsAsync(workspace, action, default);
await VerifyPreviewContents(workspace, expectedPreviewContents, operations);
......
......@@ -259,6 +259,8 @@ public static TestWorkspace Create(string xmlDefinition, bool completed = true,
ref int projectId,
ref int documentId)
{
AssertNoChildText(projectElement);
var language = GetLanguage(workspace, projectElement);
var assemblyName = GetAssemblyName(workspace, projectElement, ref projectId);
......@@ -802,6 +804,8 @@ private static MetadataReference CreateMetadataReferenceFromSource(TestWorkspace
private static Compilation CreateCompilation(TestWorkspace workspace, XElement referencedSource)
{
AssertNoChildText(referencedSource);
var languageName = GetLanguage(workspace, referencedSource);
var assemblyName = "ReferencedAssembly";
......@@ -947,5 +951,16 @@ public static bool IsWorkspaceElement(string text)
{
return text.TrimStart('\r', '\n', ' ').StartsWith("<Workspace>", StringComparison.Ordinal);
}
private static void AssertNoChildText(XElement element)
{
foreach (var node in element.Nodes())
{
if (node is XText text && !string.IsNullOrWhiteSpace(text.Value))
{
throw new Exception($"Element {element} has child text that isn't recognized. The XML syntax is invalid.");
}
}
}
}
}
......@@ -60,7 +60,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.NavigationBar
explicitInterfaceImplementations:=Nothing,
name:=methodName,
typeParameters:=Nothing,
parameters:=delegateInvokeMethod.Parameters.WithAttributesToBeCopied(destinationType),
parameters:=delegateInvokeMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(destinationType).Parameters,
handlesExpressions:=ImmutableArray.Create(Of SyntaxNode)(handlesSyntax))
methodSymbol = GeneratedSymbolAnnotation.AddAnnotationToSymbol(methodSymbol)
......
......@@ -29,8 +29,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.NavigationBar
Dim codeGenerationSymbol = GeneratedSymbolAnnotation.AddAnnotationToSymbol(
CodeGenerationSymbolFactory.CreateMethodSymbol(
methodToReplicate,
parameters:=methodToReplicate.Parameters.WithAttributesToBeCopied(destinationType)))
methodToReplicate.RemoveInaccessibleAttributesAndAttributesOfTypes(destinationType)))
Return Await CodeGenerator.AddMethodDeclarationAsync(document.Project.Solution,
destinationType,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
......@@ -9,6 +11,7 @@
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ImplementInterface
{
......@@ -44,7 +47,7 @@ internal partial class ImplementInterfaceCodeAction
var updatedProperty = property.RenameParameters(parameterNames);
updatedProperty = updatedProperty.RemoveAttributeFromParameters(attributesToRemove);
updatedProperty = updatedProperty.RemoveInaccessibleAttributesAndAttributesOfTypes(compilation.Assembly, attributesToRemove);
// TODO(cyrusn): Delegate through throughMember if it's non-null.
return CodeGenerationSymbolFactory.CreatePropertySymbol(
......@@ -66,10 +69,10 @@ internal partial class ImplementInterfaceCodeAction
private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
{
return new[] { compilation.ComAliasNameAttributeType(), compilation.TupleElementNamesAttributeType(),
compilation.DynamicAttributeType() };
compilation.DynamicAttributeType() }.WhereNotNull().ToArray()!;
}
private IMethodSymbol GenerateSetAccessor(
private IMethodSymbol? GenerateSetAccessor(
Compilation compilation,
IPropertySymbol property,
Accessibility accessibility,
......@@ -103,7 +106,7 @@ private INamedTypeSymbol[] AttributesToRemove(Compilation compilation)
compilation, property, generateAbstractly, propertyGenerationBehavior, cancellationToken));
}
private IMethodSymbol GenerateGetAccessor(
private IMethodSymbol? GenerateGetAccessor(
Compilation compilation,
IPropertySymbol property,
Accessibility accessibility,
......
......@@ -265,17 +265,18 @@ private void GenerateAndAddEventHandler(ITextView textView, ITextBuffer subjectB
}
var syntaxFactory = semanticDocument.Document.GetLanguageService<SyntaxGenerator>();
var delegateInvokeMethod = delegateType.DelegateInvokeMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(semanticDocument.SemanticModel.Compilation.Assembly);
return CodeGenerationSymbolFactory.CreateMethodSymbol(
attributes: default,
accessibility: Accessibility.Private,
modifiers: new DeclarationModifiers(isStatic: eventHookupExpression.IsInStaticContext()),
returnType: delegateType.DelegateInvokeMethod.ReturnType,
refKind: delegateType.DelegateInvokeMethod.RefKind,
returnType: delegateInvokeMethod.ReturnType,
refKind: delegateInvokeMethod.RefKind,
explicitInterfaceImplementations: default,
name: eventHandlerMethodName,
typeParameters: default,
parameters: delegateType.DelegateInvokeMethod.Parameters,
parameters: delegateInvokeMethod.Parameters,
statements: ImmutableArray.Create(
CodeGenerationHelpers.GenerateThrowStatement(syntaxFactory, semanticDocument, "System.NotImplementedException", cancellationToken)));
}
......
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
......@@ -905,6 +906,59 @@ private void C_MyEvent()
testState.AssertCodeIs(expectedCode);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.EventHookup)]
public async Task EventHookupRemovesInaccessibleAttributes()
{
var workspaceXml = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""A"" CommonReferences=""true"">
<Document>
using System;
public static class C
{
public static event DelegateType E;
public delegate void DelegateType([ShouldBeRemovedInternalAttribute] object o);
}
internal class ShouldBeRemovedInternalAttribute : Attribute { }
</Document>
</Project>
<Project Language=""C#"" CommonReferences=""true"">
<ProjectReference>A</ProjectReference>
<Document>
class D
{
void M()
{
C.E +$$
}
}</Document>
</Project>
</Workspace>";
using var testState = new EventHookupTestState(XElement.Parse(workspaceXml), options: null);
testState.SendTypeChar('=');
testState.SendTab();
await testState.WaitForAsynchronousOperationsAsync();
var expectedCode = @"
class D
{
void M()
{
C.E += C_E;
}
private void C_E(object o)
{
throw new System.NotImplementedException();
}
}";
testState.AssertCodeIs(expectedCode);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.EventHookup)]
public async Task EventHookupWithQualifiedMethodAccessAndNotificationOptionSilent()
{
......
......@@ -428,7 +428,8 @@ public static INamespaceSymbol CreateNamespaceSymbol(string name, IList<ISymbol>
ImmutableArray<IParameterSymbol>? parameters = default,
ImmutableArray<SyntaxNode> statements = default,
INamedTypeSymbol containingType = null,
ITypeSymbol returnType = null)
ITypeSymbol returnType = null,
Optional<ImmutableArray<AttributeData>> returnTypeAttributes = default)
{
return CreateMethodSymbol(
containingType,
......@@ -442,7 +443,7 @@ public static INamespaceSymbol CreateNamespaceSymbol(string name, IList<ISymbol>
method.TypeParameters,
parameters ?? method.Parameters,
statements,
returnTypeAttributes: method.GetReturnTypeAttributes());
returnTypeAttributes: returnTypeAttributes.HasValue ? returnTypeAttributes.Value : method.GetReturnTypeAttributes());
}
internal static IPropertySymbol CreatePropertySymbol(
......
......@@ -101,13 +101,13 @@ public static IMethodSymbol RenameTypeParameters(this IMethodSymbol method, ILis
method.GetAttributes(),
method.DeclaredAccessibility,
method.GetSymbolModifiers(),
method.ReturnType.SubstituteTypes(mapping, typeGenerator),
method.GetReturnTypeWithAnnotatedNullability().SubstituteTypes(mapping, typeGenerator),
method.RefKind,
method.ExplicitInterfaceImplementations,
method.Name,
updatedTypeParameters,
method.Parameters.SelectAsArray(p =>
CodeGenerationSymbolFactory.CreateParameterSymbol(p.GetAttributes(), p.RefKind, p.IsParams, p.Type.SubstituteTypes(mapping, typeGenerator), p.Name, p.IsOptional,
CodeGenerationSymbolFactory.CreateParameterSymbol(p.GetAttributes(), p.RefKind, p.IsParams, p.GetTypeWithAnnotatedNullability().SubstituteTypes(mapping, typeGenerator), p.Name, p.IsOptional,
p.HasExplicitDefaultValue, p.HasExplicitDefaultValue ? p.ExplicitDefaultValue : null)));
}
......@@ -206,18 +206,8 @@ public static IMethodSymbol RenameTypeParameters(this IMethodSymbol method, ILis
params INamedTypeSymbol[] removeAttributeTypes)
{
bool shouldRemoveAttribute(AttributeData a) =>
removeAttributeTypes.Any(attr => attr != null && attr.Equals(a.AttributeClass)) || !a.AttributeClass.IsAccessibleWithin(accessibleWithin);
removeAttributeTypes.Any(attr => attr.Equals(a.AttributeClass)) || !a.AttributeClass.IsAccessibleWithin(accessibleWithin);
return method.RemoveAttributesCore(
shouldRemoveAttribute,
statements: default,
handlesExpressions: default);
}
private static IMethodSymbol RemoveAttributesCore(
this IMethodSymbol method, Func<AttributeData, bool> shouldRemoveAttribute,
ImmutableArray<SyntaxNode> statements, ImmutableArray<SyntaxNode> handlesExpressions)
{
var methodHasAttribute = method.GetAttributes().Any(shouldRemoveAttribute);
var someParameterHasAttribute = method.Parameters
......@@ -231,23 +221,16 @@ public static IMethodSymbol RenameTypeParameters(this IMethodSymbol method, ILis
}
return CodeGenerationSymbolFactory.CreateMethodSymbol(
method.ContainingType,
method.GetAttributes().WhereAsArray(a => !shouldRemoveAttribute(a)),
method.DeclaredAccessibility,
method.GetSymbolModifiers(),
method.ReturnType,
method.RefKind,
method.ExplicitInterfaceImplementations,
method.Name,
method.TypeParameters,
method.Parameters.SelectAsArray(p =>
method,
containingType: method.ContainingType,
explicitInterfaceImplementations: method.ExplicitInterfaceImplementations,
attributes: method.GetAttributes().WhereAsArray(a => !shouldRemoveAttribute(a)),
parameters: method.Parameters.SelectAsArray(p =>
CodeGenerationSymbolFactory.CreateParameterSymbol(
p.GetAttributes().WhereAsArray(a => !shouldRemoveAttribute(a)),
p.RefKind, p.IsParams, p.Type, p.Name, p.IsOptional,
p.RefKind, p.IsParams, p.GetTypeWithAnnotatedNullability(), p.Name, p.IsOptional,
p.HasExplicitDefaultValue, p.HasExplicitDefaultValue ? p.ExplicitDefaultValue : null)),
statements,
handlesExpressions,
method.GetReturnTypeAttributes().WhereAsArray(a => !shouldRemoveAttribute(a)));
returnTypeAttributes: method.GetReturnTypeAttributes().WhereAsArray(a => !shouldRemoveAttribute(a)));
}
public static bool? IsMoreSpecificThan(this IMethodSymbol method1, IMethodSymbol method2)
......
......@@ -53,11 +53,6 @@ public static IParameterSymbol WithAttributes(this IParameterSymbol parameter, I
parameter.HasExplicitDefaultValue ? parameter.ExplicitDefaultValue : null);
}
public static ImmutableArray<IParameterSymbol> WithAttributesToBeCopied(
this ImmutableArray<IParameterSymbol> parameters, INamedTypeSymbol containingType)
=> parameters.SelectAsArray(
p => p.WithAttributes(p.GetAttributes().WhereAsArray(a => a.ShouldKeepAttribute(containingType))));
public static ImmutableArray<IParameterSymbol> RenameParameters(this IList<IParameterSymbol> parameters, IList<string> parameterNames)
{
var result = ArrayBuilder<IParameterSymbol>.GetInstance();
......@@ -68,8 +63,5 @@ public static ImmutableArray<IParameterSymbol> RenameParameters(this IList<IPara
return result.ToImmutableAndFree();
}
private static bool ShouldKeepAttribute(this AttributeData attributeData, INamedTypeSymbol containingType)
=> attributeData.AttributeClass.IsAccessibleWithin(containingType);
}
}
......@@ -37,16 +37,11 @@ public static IPropertySymbol RenameParameters(this IPropertySymbol property, IL
property.IsIndexer);
}
public static IPropertySymbol RemoveAttributeFromParameters(
this IPropertySymbol property, INamedTypeSymbol?[]? attributesToRemove)
public static IPropertySymbol RemoveInaccessibleAttributesAndAttributesOfTypes(
this IPropertySymbol property, ISymbol accessibleWithin, params INamedTypeSymbol[] attributesToRemove)
{
if (attributesToRemove == null)
{
return property;
}
bool shouldRemoveAttribute(AttributeData a) =>
attributesToRemove.Any(attr => attr?.Equals(a.AttributeClass) ?? false);
attributesToRemove.Any(attr => attr.Equals(a.AttributeClass)) || !a.AttributeClass.IsAccessibleWithin(accessibleWithin);
var someParameterHasAttribute = property.Parameters
.Any(p => p.GetAttributes().Any(shouldRemoveAttribute));
......@@ -67,7 +62,7 @@ public static IPropertySymbol RenameParameters(this IPropertySymbol property, IL
property.Parameters.SelectAsArray(p =>
CodeGenerationSymbolFactory.CreateParameterSymbol(
p.GetAttributes().WhereAsArray(a => !shouldRemoveAttribute(a)),
p.RefKind, p.IsParams, p.Type, p.Name, p.IsOptional,
p.RefKind, p.IsParams, p.GetTypeWithAnnotatedNullability(), p.Name, p.IsOptional,
p.HasExplicitDefaultValue, p.HasExplicitDefaultValue ? p.ExplicitDefaultValue : null)),
property.GetMethod,
property.SetMethod,
......
......@@ -426,7 +426,7 @@ private static SyntaxNode CreateNewArgumentNullException(SyntaxGenerator factory
accessibility: overriddenProperty.ComputeResultantAccessibility(containingType),
modifiers: modifiers,
name: overriddenProperty.Name,
parameters: overriddenProperty.Parameters.WithAttributesToBeCopied(containingType),
parameters: overriddenProperty.RemoveInaccessibleAttributesAndAttributesOfTypes(containingType).Parameters,
isIndexer: overriddenProperty.IsIndexer(),
getMethod: accessorGet,
setMethod: accessorSet);
......@@ -525,10 +525,9 @@ private static DeclarationModifiers GetOverrideModifiers(ISymbol symbol)
}
return CodeGenerationSymbolFactory.CreateMethodSymbol(
method: overriddenMethod,
method: overriddenMethod.RemoveInaccessibleAttributesAndAttributesOfTypes(newContainingType),
accessibility: overriddenMethod.ComputeResultantAccessibility(newContainingType),
modifiers: modifiers,
parameters: overriddenMethod.Parameters.WithAttributesToBeCopied(newContainingType),
statements: overriddenMethod.ReturnsVoid
? ImmutableArray.Create(codeFactory.ExpressionStatement(body))
: ImmutableArray.Create(codeFactory.ReturnStatement(body)));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册