diff --git a/src/Analyzers/CSharp/CodeFixes/FixReturnType/CSharpFixReturnTypeCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/FixReturnType/CSharpFixReturnTypeCodeFixProvider.cs index 6353bff86f3f5..a7c0f25228290 100644 --- a/src/Analyzers/CSharp/CodeFixes/FixReturnType/CSharpFixReturnTypeCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/FixReturnType/CSharpFixReturnTypeCodeFixProvider.cs @@ -72,6 +72,7 @@ static bool IsVoid(TypeSyntax typeSyntax) if (declarationTypeToFix is null) return default; + var syntaxGenerator = document.GetRequiredLanguageService(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var returnedType = semanticModel.GetTypeInfo(returnedValue, cancellationToken).Type; @@ -85,7 +86,8 @@ static bool IsVoid(TypeSyntax typeSyntax) returnedType ??= semanticModel.Compilation.ObjectType; - TypeSyntax fixedDeclaration; + var fixedDeclaration = returnedType.GenerateTypeSyntax(allowVar: false); + if (isAsync) { var previousReturnType = semanticModel.GetTypeInfo(declarationTypeToFix, cancellationToken).Type; @@ -112,11 +114,8 @@ static bool IsVoid(TypeSyntax typeSyntax) if (taskType is null) return default; - fixedDeclaration = taskType.Construct(returnedType).GenerateTypeSyntax(allowVar: false); - } - else - { - fixedDeclaration = returnedType.GenerateTypeSyntax(allowVar: false); + var taskTypeSyntax = taskType.GenerateTypeSyntax(allowVar: false); + fixedDeclaration = (TypeSyntax)syntaxGenerator.WithTypeArguments(taskTypeSyntax, fixedDeclaration); } fixedDeclaration = fixedDeclaration.WithAdditionalAnnotations(Simplifier.Annotation).WithTriviaFrom(declarationTypeToFix); diff --git a/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs b/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs index 8a76bfc266c4a..1b13d969af304 100644 --- a/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs +++ b/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs @@ -460,4 +460,58 @@ async C M() """, ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }.RunAsync(); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79630")] + public Task TestAnonymousTypeElement1() + => VerifyCS.VerifyCodeFixAsync(""" + class C + { + public void Method() + { + var v = new { A = 0, B = 1 }; + var a = new[] { v }; + {|CS0127:return|} v; + } + } + """, """ + class C + { + public object Method() + { + var v = new { A = 0, B = 1 }; + var a = new[] { v }; + return v; + } + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79630")] + public Task TestAnonymousTypeElement2() + => VerifyCS.VerifyCodeFixAsync(""" + using System.Threading.Tasks; + + class C + { + public async Task Method() + { + await Task.CompletedTask; + var v = new { A = 0, B = 1 }; + var a = new[] { v }; + {|CS1997:return|} v; + } + } + """, """ + using System.Threading.Tasks; + + class C + { + public async Task Method() + { + await Task.CompletedTask; + var v = new { A = 0, B = 1 }; + var a = new[] { v }; + return v; + } + } + """); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs index 8f2dfb7c24712..4740457936469 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs @@ -19,15 +19,19 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions; internal partial class ITypeSymbolExtensions { - private sealed class TypeSyntaxGeneratorVisitor : SymbolVisitor + private sealed class TypeSyntaxGeneratorVisitor(bool nameOnly) : SymbolVisitor { - private readonly bool _nameOnly; - private static readonly TypeSyntaxGeneratorVisitor NameOnlyInstance = new(nameOnly: true); private static readonly TypeSyntaxGeneratorVisitor NotNameOnlyInstance = new(nameOnly: false); - private TypeSyntaxGeneratorVisitor(bool nameOnly) - => _nameOnly = nameOnly; + private static readonly QualifiedNameSyntax SystemObjectType = + QualifiedName( + AliasQualifiedName( + CreateGlobalIdentifier(), + IdentifierName("System")), + IdentifierName("Object")); + + private readonly bool _nameOnly = nameOnly; public static TypeSyntaxGeneratorVisitor Create(bool nameOnly = false) => nameOnly ? NameOnlyInstance : NotNameOnlyInstance; @@ -186,16 +190,12 @@ public TypeSyntax CreateSimpleTypeSyntax(INamedTypeSymbol symbol) } if (symbol.Name == string.Empty || symbol.IsAnonymousType) - { - return CreateSystemObject(); - } + return SystemObjectType; if (symbol.TypeParameters.Length == 0) { if (symbol.TypeKind == TypeKind.Error && symbol.Name == "var") - { - return CreateSystemObject(); - } + return SystemObjectType; return symbol.Name.ToIdentifierName(); } @@ -210,13 +210,7 @@ public TypeSyntax CreateSimpleTypeSyntax(INamedTypeSymbol symbol) } public static QualifiedNameSyntax CreateSystemObject() - { - return QualifiedName( - AliasQualifiedName( - CreateGlobalIdentifier(), - IdentifierName("System")), - IdentifierName("Object")); - } + => SystemObjectType; private static IdentifierNameSyntax CreateGlobalIdentifier() => IdentifierName(GlobalKeyword); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs index b9610a98721e0..7875d6cdcb1a6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs @@ -39,17 +39,14 @@ private static TypeSyntax GenerateTypeSyntax( var type = symbol as ITypeSymbol; var containsAnonymousType = type != null && type.ContainsAnonymousType(); + // something with an anonymous type can only be represented with 'var', regardless + // of what the user's preferences might be. if (containsAnonymousType && allowVar) - { - // something with an anonymous type can only be represented with 'var', regardless - // of what the user's preferences might be. return IdentifierName("var"); - } var syntax = containsAnonymousType ? TypeSyntaxGeneratorVisitor.CreateSystemObject() - : symbol.Accept(TypeSyntaxGeneratorVisitor.Create(nameSyntax))! - .WithAdditionalAnnotations(Simplifier.Annotation); + : symbol.Accept(TypeSyntaxGeneratorVisitor.Create(nameSyntax))!.WithAdditionalAnnotations(Simplifier.Annotation); if (!allowVar) syntax = syntax.WithAdditionalAnnotations(DoNotAllowVarAnnotation.Annotation);