Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue with 'convert typeof(x) to nameof(x)' with 'nint' #67151

Merged
merged 1 commit into from
Mar 4, 2023
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 @@ -15,7 +15,8 @@
namespace Microsoft.CodeAnalysis.CSharp.ConvertTypeOfToNameOf
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.ConvertTypeOfToNameOf), Shared]
internal class CSharpConvertTypeOfToNameOfCodeFixProvider : AbstractConvertTypeOfToNameOfCodeFixProvider
internal class CSharpConvertTypeOfToNameOfCodeFixProvider : AbstractConvertTypeOfToNameOfCodeFixProvider<
MemberAccessExpressionSyntax>
{
[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
Expand All @@ -26,13 +27,13 @@ public CSharpConvertTypeOfToNameOfCodeFixProvider()
protected override string GetCodeFixTitle()
=> CSharpCodeFixesResources.Convert_typeof_to_nameof;

protected override SyntaxNode GetSymbolTypeExpression(SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
protected override SyntaxNode GetSymbolTypeExpression(SemanticModel model, MemberAccessExpressionSyntax node, CancellationToken cancellationToken)
{
// The corresponding analyzer (CSharpConvertTypeOfToNameOfDiagnosticAnalyzer) validated the syntax
var typeOfExpression = (TypeOfExpressionSyntax)((MemberAccessExpressionSyntax)node).Expression;
var typeOfExpression = (TypeOfExpressionSyntax)node.Expression;
var typeSymbol = model.GetSymbolInfo(typeOfExpression.Type, cancellationToken).Symbol.GetSymbolType();
Contract.ThrowIfNull(typeSymbol);
return typeSymbol.GenerateExpressionSyntax();
return typeSymbol.GenerateExpressionSyntax(nameSyntax: true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.ConvertTypeOfToNameOf;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
Expand Down Expand Up @@ -329,5 +330,36 @@ void M()
""";
await VerifyCS.VerifyCodeFixAsync(text, text);
}

[Fact, WorkItem(47128, "https://github.com/dotnet/roslyn/issues/47128")]
public async Task TestNint()
{
await new VerifyCS.Test
{
TestCode = """
using System;

class C
{
void M()
{
Console.WriteLine([|typeof(nint).Name|]);
}
}
""",
FixedCode = """
using System;

class C
{
void M()
{
Console.WriteLine(nameof(IntPtr));
}
}
""",
LanguageVersion = LanguageVersion.CSharp10,
}.RunAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@
// 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.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.ConvertTypeOfToNameOf
{
internal abstract class AbstractConvertTypeOfToNameOfCodeFixProvider : SyntaxEditorBasedCodeFixProvider
internal abstract class AbstractConvertTypeOfToNameOfCodeFixProvider<
TMemberAccessExpressionSyntax> : SyntaxEditorBasedCodeFixProvider
where TMemberAccessExpressionSyntax : SyntaxNode
{
protected abstract string GetCodeFixTitle();

protected abstract SyntaxNode GetSymbolTypeExpression(SemanticModel model, TMemberAccessExpressionSyntax node, CancellationToken cancellationToken);

public sealed override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.ConvertTypeOfToNameOfDiagnosticId);

Expand All @@ -35,23 +38,21 @@ protected override async Task FixAllAsync(
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
foreach (var diagnostic in diagnostics)
{
var node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
if (editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) is not TMemberAccessExpressionSyntax node)
continue;

ConvertTypeOfToNameOf(semanticModel, editor, node, cancellationToken);
}
}

/// <Summary>
/// Method converts typeof(...).Name to nameof(...)
/// </Summary>
public void ConvertTypeOfToNameOf(SemanticModel semanticModel, SyntaxEditor editor, SyntaxNode nodeToReplace, CancellationToken cancellationToken)
public void ConvertTypeOfToNameOf(SemanticModel semanticModel, SyntaxEditor editor, TMemberAccessExpressionSyntax nodeToReplace, CancellationToken cancellationToken)
{
var typeExpression = GetSymbolTypeExpression(semanticModel, nodeToReplace, cancellationToken);
var nameOfSyntax = editor.Generator.NameOfExpression(typeExpression);
editor.ReplaceNode(nodeToReplace, nameOfSyntax);
}

protected abstract SyntaxNode GetSymbolTypeExpression(SemanticModel model, SyntaxNode node, CancellationToken cancellationToken);

protected abstract string GetCodeFixTitle();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertTypeOfToNameOf

<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.ConvertTypeOfToNameOf), [Shared]>
Friend Class VisualBasicConvertGetTypeToNameOfCodeFixProvider
Inherits AbstractConvertTypeOfToNameOfCodeFixProvider
Inherits AbstractConvertTypeOfToNameOfCodeFixProvider(
Of MemberAccessExpressionSyntax)

<ImportingConstructor>
<SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification:="Used in test code: https://github.com/dotnet/roslyn/issues/42814")>
Expand All @@ -26,9 +26,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertTypeOfToNameOf
Return VisualBasicCodeFixesResources.Convert_GetType_to_NameOf
End Function

Protected Overrides Function GetSymbolTypeExpression(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As SyntaxNode

Dim expression = DirectCast(node, MemberAccessExpressionSyntax).Expression
Protected Overrides Function GetSymbolTypeExpression(semanticModel As SemanticModel, node As MemberAccessExpressionSyntax, cancellationToken As CancellationToken) As SyntaxNode
Dim expression = node.Expression
Dim type = DirectCast(expression, GetTypeExpressionSyntax).Type
Dim symbolType = semanticModel.GetSymbolInfo(type, cancellationToken).Symbol.GetSymbolType()
Dim symbolExpression = symbolType.GenerateExpressionSyntax()
Expand All @@ -39,8 +38,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertTypeOfToNameOf

If TypeOf symbolExpression Is QualifiedNameSyntax Then
Dim qualifiedName = DirectCast(symbolExpression, QualifiedNameSyntax)
Return SyntaxFactory.SimpleMemberAccessExpression(qualifiedName.Left, qualifiedName.Right) _
.WithAdditionalAnnotations(Simplifier.Annotation)
Return SyntaxFactory.
SimpleMemberAccessExpression(qualifiedName.Left, qualifiedName.Right).
WithAdditionalAnnotations(Simplifier.Annotation)
End If

' Corresponding analyzer VisualBasicConvertTypeOfToNameOfDiagnosticAnalyzer validated the above syntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ internal partial class ITypeSymbolExtensions
{
private class ExpressionSyntaxGeneratorVisitor : SymbolVisitor<ExpressionSyntax>
{
public static readonly ExpressionSyntaxGeneratorVisitor Instance = new();
private static readonly ExpressionSyntaxGeneratorVisitor NameOnlyInstance = new(nameOnly: true);
private static readonly ExpressionSyntaxGeneratorVisitor NotNameOnlyInstance = new(nameOnly: false);

private ExpressionSyntaxGeneratorVisitor()
{
}
private readonly bool _nameOnly;

private ExpressionSyntaxGeneratorVisitor(bool nameOnly)
=> _nameOnly = nameOnly;

public static ExpressionSyntaxGeneratorVisitor Create(bool nameOnly)
=> nameOnly ? NameOnlyInstance : NotNameOnlyInstance;

public override ExpressionSyntax DefaultVisit(ISymbol symbol)
=> symbol.Accept(TypeSyntaxGeneratorVisitor.Create())!;
=> symbol.Accept(TypeSyntaxGeneratorVisitor.Create(_nameOnly))!;

private static TExpressionSyntax AddInformationTo<TExpressionSyntax>(TExpressionSyntax syntax, ISymbol symbol)
where TExpressionSyntax : ExpressionSyntax
Expand All @@ -32,7 +37,7 @@ private static TExpressionSyntax AddInformationTo<TExpressionSyntax>(TExpression

public override ExpressionSyntax VisitNamedType(INamedTypeSymbol symbol)
{
if (TypeSyntaxGeneratorVisitor.TryCreateNativeIntegerType(symbol, out var typeSyntax))
if (!_nameOnly && TypeSyntaxGeneratorVisitor.TryCreateNativeIntegerType(symbol, out var typeSyntax))
return typeSyntax;

typeSyntax = TypeSyntaxGeneratorVisitor.Create().CreateSimpleTypeSyntax(symbol);
Expand Down Expand Up @@ -96,7 +101,7 @@ public override ExpressionSyntax VisitNamespace(INamespaceSymbol symbol)
}
}

private static ExpressionSyntax CreateMemberAccessExpression(
private static MemberAccessExpressionSyntax CreateMemberAccessExpression(
ISymbol symbol, ExpressionSyntax container, SimpleNameSyntax syntax)
{
return AddInformationTo(SyntaxFactory.MemberAccessExpression(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,16 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions
{
internal static partial class ITypeSymbolExtensions
{
public static ExpressionSyntax GenerateExpressionSyntax(
this ITypeSymbol typeSymbol)
{
return typeSymbol.Accept(ExpressionSyntaxGeneratorVisitor.Instance)!.WithAdditionalAnnotations(Simplifier.Annotation);
}
/// <paramref name="nameSyntax"><see langword="true"/> if only normal name-syntax nodes should be returned.
/// <see langword="false"/> if special nodes (like predefined types) can be used.</paramref>
public static ExpressionSyntax GenerateExpressionSyntax(this ITypeSymbol typeSymbol, bool nameSyntax = false)
=> typeSymbol.Accept(ExpressionSyntaxGeneratorVisitor.Create(nameSyntax))!.WithAdditionalAnnotations(Simplifier.Annotation);

public static NameSyntax GenerateNameSyntax(
this INamespaceOrTypeSymbol symbol, bool allowVar = true)
{
return (NameSyntax)GenerateTypeSyntax(symbol, nameSyntax: true, allowVar: allowVar);
}
public static NameSyntax GenerateNameSyntax(this INamespaceOrTypeSymbol symbol, bool allowVar = true)
=> (NameSyntax)GenerateTypeSyntax(symbol, nameSyntax: true, allowVar: allowVar);

public static TypeSyntax GenerateTypeSyntax(
this INamespaceOrTypeSymbol symbol, bool allowVar = true)
{
return GenerateTypeSyntax(symbol, nameSyntax: false, allowVar: allowVar);
}
public static TypeSyntax GenerateTypeSyntax(this INamespaceOrTypeSymbol symbol, bool allowVar = true)
=> GenerateTypeSyntax(symbol, nameSyntax: false, allowVar: allowVar);

private static TypeSyntax GenerateTypeSyntax(
INamespaceOrTypeSymbol symbol, bool nameSyntax, bool allowVar = true)
Expand Down