diff --git a/ChangeLog.md b/ChangeLog.md index a7cc22e4bc..bb697c7e16 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -34,7 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix formatting of argument list ([#952](https://github.com/josefpihrt/roslynator/pull/952). - Do not remove async/await when 'using declaration' is used ([#953](https://github.com/josefpihrt/roslynator/pull/953). - Convert if-else to return statement when pattern matching is used ([RCS1073](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1073.md)) ([#956](https://github.com/josefpihrt/roslynator/pull/956). -- Do not simplify 'default' expression if the type is inferred ([RCS1244](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/1244.md)) ([#956](https://github.com/josefpihrt/roslynator/pull/966). +- Do not simplify 'default' expression if the type is inferred ([RCS1244](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1244.md)) ([#966](https://github.com/josefpihrt/roslynator/pull/966). +- Use explicit type from lambda expression ([RCS1008](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1008.md)) ([#967](https://github.com/josefpihrt/roslynator/pull/967). ----- diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseExplicitTypeInsteadOfVarCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseExplicitTypeInsteadOfVarCodeFixProvider.cs index a74ae5f23e..12d5afb734 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseExplicitTypeInsteadOfVarCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/UseExplicitTypeInsteadOfVarCodeFixProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; @@ -34,15 +35,38 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) if (!TryFindFirstAncestorOrSelf(root, context.Span, out SyntaxNode node, predicate: f => f.IsKind(SyntaxKind.VariableDeclaration, SyntaxKind.DeclarationExpression))) return; + SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); + if (node is VariableDeclarationSyntax variableDeclaration) { - TypeSyntax type = variableDeclaration.Type; + ExpressionSyntax value = variableDeclaration.Variables[0].Initializer.Value; + ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(value, context.CancellationToken); - SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); + if (typeSymbol is null) + { + var localSymbol = semanticModel.GetDeclaredSymbol(variableDeclaration.Variables[0], context.CancellationToken) as ILocalSymbol; - ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(variableDeclaration.Variables[0].Initializer.Value, context.CancellationToken); + if (localSymbol is not null) + { + typeSymbol = localSymbol.Type; - RegisterCodeFix(context, type, typeSymbol, semanticModel); + value = value.WalkDownParentheses(); + + Debug.Assert( + value.IsKind(SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression), + value.Kind().ToString()); + + if (value.IsKind(SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression)) + typeSymbol = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated); + } + else + { + SyntaxDebug.Fail(variableDeclaration.Variables[0]); + return; + } + } + + RegisterCodeFix(context, variableDeclaration.Type, typeSymbol, semanticModel); } else { @@ -50,8 +74,6 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) TypeSyntax type = declarationExpression.Type; - SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); - var localSymbol = semanticModel.GetDeclaredSymbol(declarationExpression.Designation, context.CancellationToken) as ILocalSymbol; ITypeSymbol typeSymbol = (localSymbol?.Type) ?? semanticModel.GetTypeSymbol(declarationExpression, context.CancellationToken); diff --git a/src/Tests/Analyzers.Tests/RCS1008UseExplicitTypeInsteadOfVarWhenTypeIsNotObviousTests.cs b/src/Tests/Analyzers.Tests/RCS1008UseExplicitTypeInsteadOfVarWhenTypeIsNotObviousTests.cs index 6507754e8b..78c447ef7f 100644 --- a/src/Tests/Analyzers.Tests/RCS1008UseExplicitTypeInsteadOfVarWhenTypeIsNotObviousTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1008UseExplicitTypeInsteadOfVarWhenTypeIsNotObviousTests.cs @@ -249,6 +249,66 @@ void M() "); } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseExplicitTypeInsteadOfVarWhenTypeIsNotObvious)] + public async Task Test_Func_Lambda() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M() + { + [|var|] x = () => + { + return default(object); + }; + } +} +", @" +class C +{ + void M() + { + System.Func x = () => + { + return default(object); + }; + } +} +", options: Options.AddAllowedCompilerDiagnosticId("CS8603")); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseExplicitTypeInsteadOfVarWhenTypeIsNotObvious)] + public async Task Test_Func_Lambda_Nullable() + { + await VerifyDiagnosticAndFixAsync(@" +#nullable enable + +class C +{ + void M() + { + [|var|] x = () => + { + return default(object); + }; + } +} +", @" +#nullable enable + +class C +{ + void M() + { + System.Func x = () => + { + return default(object); + }; + } +} +", options: Options.AddAllowedCompilerDiagnosticId("CS8603")); + } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseExplicitTypeInsteadOfVarWhenTypeIsNotObvious)] public async Task TestNoDiagnostic() {