Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,53 @@
// The .NET Foundation licenses this file to you under the MIT license.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

view with whitespce off.

// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.LanguageService;
using Microsoft.CodeAnalysis.CSharp.OrderModifiers;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.OrderModifiers;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.HideBase;

using static CSharpSyntaxTokens;

internal sealed partial class HideBaseCodeFixProvider
{
private sealed class AddNewKeywordAction(Document document, SyntaxNode node) : CodeAction
private static async Task<Document> GetChangedDocumentAsync(
Document document, SyntaxNode node, CancellationToken cancellationToken)
{
private readonly Document _document = document;
private readonly SyntaxNode _node = node;
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var modifierOrder = await GetModifierOrderAsync(document, cancellationToken).ConfigureAwait(false);

public override string Title => CSharpCodeFixesResources.Hide_base_member;

protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{
var root = await _document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var configOptions = await _document.GetHostAnalyzerConfigOptionsAsync(cancellationToken).ConfigureAwait(false);

var newNode = GetNewNode(_node, configOptions.GetOption(CSharpCodeStyleOptions.PreferredModifierOrder).Value);
var newRoot = root.ReplaceNode(_node, newNode);
return document.WithSyntaxRoot(root.ReplaceNode(node, GetNewNode(node, modifierOrder)));
}

return _document.WithSyntaxRoot(newRoot);
}
private static SyntaxNode GetNewNode(SyntaxNode node, Dictionary<int, int>? preferredOrder)
{
var syntaxFacts = CSharpSyntaxFacts.Instance;
var modifiers = syntaxFacts.GetModifiers(node);
var newModifiers = modifiers.Add(NewKeyword);

private static SyntaxNode GetNewNode(SyntaxNode node, string preferredModifierOrder)
if (preferredOrder is null ||
!AbstractOrderModifiersHelpers.IsOrdered(preferredOrder, modifiers))
{
var syntaxFacts = CSharpSyntaxFacts.Instance;
var modifiers = syntaxFacts.GetModifiers(node);
var newModifiers = modifiers.Add(NewKeyword);

if (!CSharpOrderModifiersHelper.Instance.TryGetOrComputePreferredOrder(preferredModifierOrder, out var preferredOrder) ||
!AbstractOrderModifiersHelpers.IsOrdered(preferredOrder, modifiers))
{
return syntaxFacts.WithModifiers(node, newModifiers);
}
return syntaxFacts.WithModifiers(node, newModifiers);
}

var orderedModifiers = new SyntaxTokenList(
newModifiers.OrderBy(CompareModifiers));
var orderedModifiers = new SyntaxTokenList(
newModifiers.OrderBy(CompareModifiers));

return syntaxFacts.WithModifiers(node, orderedModifiers);
return syntaxFacts.WithModifiers(node, orderedModifiers);

int CompareModifiers(SyntaxToken left, SyntaxToken right)
=> GetOrder(left) - GetOrder(right);
int CompareModifiers(SyntaxToken left, SyntaxToken right)
=> GetOrder(left) - GetOrder(right);

int GetOrder(SyntaxToken token)
=> preferredOrder.TryGetValue(token.RawKind, out var value) ? value : int.MaxValue;
}
int GetOrder(SyntaxToken token)
=> preferredOrder.TryGetValue(token.RawKind, out var value) ? value : int.MaxValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,81 @@
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.OrderModifiers;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;

namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.HideBase;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddNew), Shared]
[method: ImportingConstructor]
[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
internal sealed partial class HideBaseCodeFixProvider() : CodeFixProvider
internal sealed partial class HideBaseCodeFixProvider() : SyntaxEditorBasedCodeFixProvider
{
internal const string CS0108 = nameof(CS0108); // 'SomeClass.SomeMember' hides inherited member 'SomeClass.SomeMember'. Use the new keyword if hiding was intended.

public override ImmutableArray<string> FixableDiagnosticIds => [CS0108];

public override FixAllProvider GetFixAllProvider()
=> WellKnownFixAllProviders.BatchFixer;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
private static SyntaxNode? GetOriginalNode(SyntaxNode root, Diagnostic diagnostic)
{
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;

var token = root.FindToken(diagnosticSpan.Start);

var originalNode = token.GetAncestor<PropertyDeclarationSyntax>() ??
token.GetAncestor<MethodDeclarationSyntax>() ??
(SyntaxNode?)token.GetAncestor<FieldDeclarationSyntax>();
return originalNode;
}

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

var originalNode = GetOriginalNode(root, context.Diagnostics.First());
if (originalNode == null)
return;

context.RegisterCodeFix(new AddNewKeywordAction(context.Document, originalNode), context.Diagnostics);
context.RegisterCodeFix(CodeAction.Create(
CSharpCodeFixesResources.Hide_base_member,
cancellationToken => GetChangedDocumentAsync(context.Document, originalNode, cancellationToken),
nameof(CSharpCodeFixesResources.Hide_base_member)),
context.Diagnostics);
}

protected override async Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken)
{
var modifierOrder = await GetModifierOrderAsync(document, cancellationToken).ConfigureAwait(false);

var root = editor.OriginalRoot;
foreach (var diagnostic in diagnostics)
{
var originalNode = GetOriginalNode(root, diagnostic);
if (originalNode == null)
continue;

var newNode = GetNewNode(originalNode, modifierOrder);
editor.ReplaceNode(originalNode, newNode);
}
}

private static async Task<Dictionary<int, int>?> GetModifierOrderAsync(Document document, CancellationToken cancellationToken)
{
var configOptions = await document.GetHostAnalyzerConfigOptionsAsync(cancellationToken).ConfigureAwait(false);
var modifierOrder = configOptions.GetOption(CSharpCodeStyleOptions.PreferredModifierOrder).Value;
CSharpOrderModifiersHelper.Instance.TryGetOrComputePreferredOrder(modifierOrder, out var preferredOrder);
return preferredOrder;
}
}
Loading