From cd10095ff94e6c3194915c1c46844b844d4ae063 Mon Sep 17 00:00:00 2001 From: Neme12 Date: Mon, 16 Apr 2018 19:47:18 +0200 Subject: [PATCH] Fix for UseDeconstruction with default literal (#26140) Merged on behalf of @Neme12. Thanks --- .../UseDeconstructionTests.cs | 25 +++++++++++++++++- ...harpUseDeconstructionDiagnosticAnalyzer.cs | 26 ++++++++++++++----- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseDeconstruction/UseDeconstructionTests.cs b/src/EditorFeatures/CSharpTest/UseDeconstruction/UseDeconstructionTests.cs index 833957f2d15..b1bdb893422 100644 --- a/src/EditorFeatures/CSharpTest/UseDeconstruction/UseDeconstructionTests.cs +++ b/src/EditorFeatures/CSharpTest/UseDeconstruction/UseDeconstructionTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.UseDeconstruction; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; @@ -481,7 +482,7 @@ void M() (string name, int age) [|person|] = default; Console.WriteLine(person.name + "" "" + person.age); } -}"); +}", new TestParameters(parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_1))); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDeconstruction)] @@ -610,6 +611,28 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDeconstruction)] + public async Task TestWithTupleLiteralConversion() + { + await TestInRegularAndScriptAsync( +@"class C +{ + void M() + { + (object name, double age) [|person|] = (null, 0); + Console.WriteLine(person.name + "" "" + person.age); + } +}", +@"class C +{ + void M() + { + (object name, double age) = (null, 0); + Console.WriteLine(name + "" "" + age); + } +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseDeconstruction)] public async Task TestWithImplicitTupleConversion() { diff --git a/src/Features/CSharp/Portable/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs index 848b1b420ed..d9289331249 100644 --- a/src/Features/CSharp/Portable/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs @@ -125,10 +125,10 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) } var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(declarator, cancellationToken); - var expressionType = semanticModel.GetTypeInfo(declarator.Initializer.Value, cancellationToken).Type; + var initializerConversion = semanticModel.GetConversion(declarator.Initializer.Value, cancellationToken); return TryAnalyze( - semanticModel, local, variableDeclaration.Type, declarator.Identifier, expressionType, + semanticModel, local, variableDeclaration.Type, declarator.Identifier, initializerConversion, variableDeclaration.Parent.Parent, out tupleType, out memberAccessExpressions, cancellationToken); } @@ -140,10 +140,10 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) CancellationToken cancellationToken) { var local = semanticModel.GetDeclaredSymbol(forEachStatement, cancellationToken); - var elementType = semanticModel.GetForEachStatementInfo(forEachStatement).ElementType; + var elementConversion = semanticModel.GetForEachStatementInfo(forEachStatement).ElementConversion; return TryAnalyze( - semanticModel, local, forEachStatement.Type, forEachStatement.Identifier, elementType, + semanticModel, local, forEachStatement.Type, forEachStatement.Identifier, elementConversion, forEachStatement, out tupleType, out memberAccessExpressions, cancellationToken); } @@ -152,7 +152,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) ILocalSymbol local, TypeSyntax typeNode, SyntaxToken identifier, - ITypeSymbol initializerType, + Conversion conversion, SyntaxNode searchScope, out INamedTypeSymbol tupleType, out ImmutableArray memberAccessExpressions, @@ -171,9 +171,21 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) return false; } + if (conversion.Exists && + !conversion.IsIdentity && + !conversion.IsTupleConversion && + !conversion.IsTupleLiteralConversion) + { + // If there is any other conversion, we bail out because the source type might not be a tuple + // or it is a tuple but only thanks to target type inference, which won't occur in a deconstruction. + // Interesting case that illustrates this is initialization with a default literal: + // (int a, int b) t = default; + // This is classified as conversion.IsNullLiteral. + return false; + } + var type = semanticModel.GetTypeInfo(typeNode, cancellationToken).Type; - if (type == null || !type.IsTupleType || - initializerType == null || !initializerType.IsTupleType) + if (type == null || !type.IsTupleType) { return false; } -- GitLab