提交 28dc5966 编写于 作者: C CyrusNajmabadi

Fix issue with definite-assignment checking in 'inline-decl' with 'var-typed' locals.

上级 6744226e
...@@ -1967,6 +1967,44 @@ void M() ...@@ -1967,6 +1967,44 @@ void M()
Console.WriteLine(_); Console.WriteLine(_);
} }
} }
}");
}
[WorkItem(18668, "https://github.com/dotnet/roslyn/issues/18668")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineDeclaration)]
public async Task TestDefiniteAssignmentIssueWithVar()
{
await TestMissingInRegularAndScriptAsync(
@"
using System;
class C
{
static void M(bool condition)
{
[|var|] x = 1;
var result = condition && int.TryParse(""2"", out x);
Console.WriteLine(x);
}
}");
}
[WorkItem(18668, "https://github.com/dotnet/roslyn/issues/18668")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineDeclaration)]
public async Task TestDefiniteAssignmentIssueWithNonVar()
{
await TestMissingInRegularAndScriptAsync(
@"
using System;
class C
{
static void M(bool condition)
{
[|int|] x = 1;
var result = condition && int.TryParse(""2"", out x);
Console.WriteLine(x);
}
}"); }");
} }
} }
......
...@@ -127,8 +127,8 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb ...@@ -127,8 +127,8 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb
} }
var semanticModel = context.SemanticModel; var semanticModel = context.SemanticModel;
var outSymbol = semanticModel.GetSymbolInfo(argumentExpression, cancellationToken).Symbol; var outLocalSymbol = semanticModel.GetSymbolInfo(argumentExpression, cancellationToken).Symbol as ILocalSymbol;
if (outSymbol?.Kind != SymbolKind.Local) if (outLocalSymbol == null)
{ {
// The out-argument wasn't referencing a local. So we don't have an local // The out-argument wasn't referencing a local. So we don't have an local
// declaration that we can attempt to inline here. // declaration that we can attempt to inline here.
...@@ -139,7 +139,7 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb ...@@ -139,7 +139,7 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb
// Trying to do things like inline a var-decl in a for-statement is just too // Trying to do things like inline a var-decl in a for-statement is just too
// esoteric and would make us have to write a lot more complex code to support // esoteric and would make us have to write a lot more complex code to support
// that scenario. // that scenario.
var localReference = outSymbol.DeclaringSyntaxReferences.FirstOrDefault(); var localReference = outLocalSymbol.DeclaringSyntaxReferences.FirstOrDefault();
var localDeclarator = localReference?.GetSyntax(cancellationToken) as VariableDeclaratorSyntax; var localDeclarator = localReference?.GetSyntax(cancellationToken) as VariableDeclaratorSyntax;
if (localDeclarator == null) if (localDeclarator == null)
{ {
...@@ -196,7 +196,7 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb ...@@ -196,7 +196,7 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb
// Make sure that variable is not accessed outside of that scope. // Make sure that variable is not accessed outside of that scope.
var dataFlow = semanticModel.AnalyzeDataFlow(outArgumentScope); var dataFlow = semanticModel.AnalyzeDataFlow(outArgumentScope);
if (dataFlow.ReadOutside.Contains(outSymbol) || dataFlow.WrittenOutside.Contains(outSymbol)) if (dataFlow.ReadOutside.Contains(outLocalSymbol) || dataFlow.WrittenOutside.Contains(outLocalSymbol))
{ {
// The variable is read or written from outside the block that the new variable // The variable is read or written from outside the block that the new variable
// would be scoped in. This would cause a break. // would be scoped in. This would cause a break.
...@@ -207,7 +207,7 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb ...@@ -207,7 +207,7 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb
} }
// Make sure the variable isn't ever accessed before the usage in this out-var. // Make sure the variable isn't ever accessed before the usage in this out-var.
if (IsAccessed(semanticModel, outSymbol, enclosingBlockOfLocalStatement, if (IsAccessed(semanticModel, outLocalSymbol, enclosingBlockOfLocalStatement,
localStatement, argumentNode, cancellationToken)) localStatement, argumentNode, cancellationToken))
{ {
return; return;
...@@ -216,8 +216,8 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb ...@@ -216,8 +216,8 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb
// See if inlining this variable would make it so that some variables were no // See if inlining this variable would make it so that some variables were no
// longer definitely assigned. // longer definitely assigned.
if (WouldCauseDefiniteAssignmentErrors( if (WouldCauseDefiniteAssignmentErrors(
semanticModel, localDeclarator, enclosingBlockOfLocalStatement, semanticModel, localDeclaration, localDeclarator,
outSymbol, cancellationToken)) enclosingBlockOfLocalStatement, outLocalSymbol, cancellationToken))
{ {
return; return;
} }
...@@ -243,8 +243,12 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb ...@@ -243,8 +243,12 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb
} }
private bool WouldCauseDefiniteAssignmentErrors( private bool WouldCauseDefiniteAssignmentErrors(
SemanticModel semanticModel, VariableDeclaratorSyntax localDeclarator, SemanticModel semanticModel,
BlockSyntax enclosingBlock, ISymbol outSymbol, CancellationToken cancellationToken) VariableDeclarationSyntax localDeclaration,
VariableDeclaratorSyntax localDeclarator,
BlockSyntax enclosingBlock,
ILocalSymbol outLocalSymbol,
CancellationToken cancellationToken)
{ {
// See if we have something like: // See if we have something like:
// //
...@@ -260,12 +264,12 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb ...@@ -260,12 +264,12 @@ private void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context, INamedTypeSymb
// Find all the current read-references to the local. // Find all the current read-references to the local.
var query = from t in enclosingBlock.DescendantTokens() var query = from t in enclosingBlock.DescendantTokens()
where t.Kind() == SyntaxKind.IdentifierToken where t.Kind() == SyntaxKind.IdentifierToken
where t.ValueText == outSymbol.Name where t.ValueText == outLocalSymbol.Name
let id = t.Parent as IdentifierNameSyntax let id = t.Parent as IdentifierNameSyntax
where id != null where id != null
where !id.IsOnlyWrittenTo() where !id.IsOnlyWrittenTo()
let symbol = semanticModel.GetSymbolInfo(id).GetAnySymbol() let symbol = semanticModel.GetSymbolInfo(id).GetAnySymbol()
where outSymbol.Equals(symbol) where outLocalSymbol.Equals(symbol)
select id; select id;
var references = query.ToImmutableArray<SyntaxNode>(); var references = query.ToImmutableArray<SyntaxNode>();
...@@ -274,16 +278,29 @@ where outSymbol.Equals(symbol) ...@@ -274,16 +278,29 @@ where outSymbol.Equals(symbol)
// Ensure we can track the references and the local variable as we make edits // Ensure we can track the references and the local variable as we make edits
// to the tree. // to the tree.
var rootWithTrackedNodes = root.TrackNodes(references.Concat(localDeclarator).Concat(enclosingBlock)); var rootWithTrackedNodes = root.TrackNodes(
references.Concat(ImmutableArray.Create<SyntaxNode>(localDeclarator, localDeclaration, enclosingBlock)));
// Now, take the local variable and remove it's initializer. Then go to all // Now, take the local variable and remove it's initializer. Then go to all
// the locations where we read from it. If they're definitely assigned, then // the locations where we read from it. If they're definitely assigned, then
// that means the out-var did it's work and assigned the variable across all // that means the out-var did it's work and assigned the variable across all
// paths. If it's not definitely assigned, then we can't inline this variable. // paths. If it's not definitely assigned, then we can't inline this variable.
var currentLocalDeclarator = rootWithTrackedNodes.GetCurrentNode(localDeclarator); var currentLocalDeclarator = rootWithTrackedNodes.GetCurrentNode(localDeclarator);
var currentLocalDeclaration = rootWithTrackedNodes.GetCurrentNode(localDeclaration);
var updatedDeclaration = currentLocalDeclaration
.ReplaceNode(currentLocalDeclarator, currentLocalDeclarator.WithInitializer(null));
// If the declaration was a "var" declaration, then replace "var" with the actual
// type of the local. This way we don't get a "'var v' requires an initializer" which
// will suppress the message about definite assignment later.
if (updatedDeclaration.Type.IsVar)
{
updatedDeclaration = updatedDeclaration.WithType(
outLocalSymbol.Type.GenerateTypeSyntax());
}
var rootWithoutInitializer = rootWithTrackedNodes.ReplaceNode( var rootWithoutInitializer = rootWithTrackedNodes.ReplaceNode(
currentLocalDeclarator, currentLocalDeclaration, updatedDeclaration);
currentLocalDeclarator.WithInitializer(null));
var rootWithoutInitializerTree = root.SyntaxTree.WithRootAndOptions( var rootWithoutInitializerTree = root.SyntaxTree.WithRootAndOptions(
rootWithoutInitializer, root.SyntaxTree.Options); rootWithoutInitializer, root.SyntaxTree.Options);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册