提交 7c064e5d 编写于 作者: M Manish Vasani

Fix couple of issues found during dogfooding:

1. Do not invoke SyntaxEditorBasedCodeFixProvider.FixAllAsync if there are no diagnostics to fixed.
2. Handle flow captures that are both lvalue and rvalue captures (left of a compound assignment operation where right has conditional code).
上级 c2d4bac0
......@@ -697,6 +697,43 @@ int M(int x)
}}");
}
[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedValues)]
[InlineData("true")] // Constant
[InlineData("M2()")] // Non-constant
public async Task CompoundLogicalOrOperator_ValueUsed_LaterStatement(string rightHandSide)
{
await TestMissingInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
bool M(bool x)
{{
[|x|] |= {rightHandSide} && {rightHandSide};
return x;
}}
bool M2() => true;
}}");
}
[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedValues)]
[InlineData("true")] // Constant
[InlineData("M2()")] // Non-constant
public async Task CompoundLogicalOrOperator_ValueUsed_LaterStatement_02(string rightHandSide)
{
await TestMissingInRegularAndScriptWithAllOptionsAsync(
$@"class C
{{
bool M()
{{
bool [|x|] = false;
x |= {rightHandSide} && {rightHandSide};
return x;
}}
bool M2() => true;
}}");
}
[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedValues)]
[InlineData(nameof(PreferDiscard))]
[InlineData(nameof(PreferUnusedLocal))]
......
......@@ -58,7 +58,7 @@ public sealed override async Task<CodeAction> GetFixAsync(FixAllContext fixAllCo
return new CodeAction.SolutionChangeAction(title, _ => Task.FromResult(currentSolution));
}
private Task<Document> FixDocumentAsync(
private async Task<Document> FixDocumentAsync(
FixAllState fixAllState, Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken)
{
// Ensure that diagnostics for this document are always in document location
......@@ -66,7 +66,14 @@ public sealed override async Task<CodeAction> GetFixAsync(FixAllContext fixAllCo
// that want to update a document.
var filteredDiagnostics = diagnostics.WhereAsArray(d => _codeFixProvider.IncludeDiagnosticDuringFixAll(fixAllState, d, cancellationToken))
.Sort((d1, d2) => d1.Location.SourceSpan.Start - d2.Location.SourceSpan.Start);
return _codeFixProvider.FixAllAsync(document, filteredDiagnostics, cancellationToken);
// PERF: Do not invoke FixAllAsync on the code fix provider if there are no diagnostics to be fixed.
if (filteredDiagnostics.Length == 0)
{
return document;
}
return await _codeFixProvider.FixAllAsync(document, filteredDiagnostics, cancellationToken).ConfigureAwait(false);
}
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.FlowAnalysis
{
/// <summary>
/// Indicates the kind of flow capture in an <see cref="IFlowCaptureOperation"/>.
/// </summary>
internal enum FlowCaptureKind
{
/// <summary>
/// Indicates an R-Value flow capture, i.e. capture of a symbol's value.
/// </summary>
RValueCapture,
/// <summary>
/// Indicates an L-Value flow capture, i.e. captures of a symbol's location/address.
/// </summary>
LValueCapture,
/// <summary>
/// Indicates both an R-Value and an L-Value flow capture, i.e. captures of a symbol's value and location/address.
/// These are generated for left of a compound assignment operation, such that there is conditional code on the right side of the compound assignment.
/// </summary>
LValueAndRValueCapture
}
}
......@@ -30,7 +30,7 @@ namespace Microsoft.CodeAnalysis.FlowAnalysis
/// </remarks>
internal static class LValueFlowCapturesProvider
{
public static ImmutableHashSet<CaptureId> CreateLValueFlowCaptures(ControlFlowGraph cfg)
public static ImmutableDictionary<CaptureId, FlowCaptureKind> CreateLValueFlowCaptures(ControlFlowGraph cfg)
{
// This method identifies flow capture reference operations that are target of an assignment
// and marks them as lvalue flow captures.
......@@ -39,7 +39,7 @@ public static ImmutableHashSet<CaptureId> CreateLValueFlowCaptures(ControlFlowGr
// the flow graph. The debug only asserts in this method ensure this invariant.
// If these asserts fire, we should adjust this algorithm.
ImmutableHashSet<CaptureId>.Builder lvalueFlowCaptureIdBuilder = null;
ImmutableDictionary<CaptureId, FlowCaptureKind>.Builder lvalueFlowCaptureIdBuilder = null;
#if DEBUG
var rvalueFlowCaptureIds = new HashSet<CaptureId>();
#endif
......@@ -49,8 +49,9 @@ public static ImmutableHashSet<CaptureId> CreateLValueFlowCaptures(ControlFlowGr
assignment.Target == flowCaptureReference ||
flowCaptureReference.IsInLeftOfDeconstructionAssignment(out _))
{
lvalueFlowCaptureIdBuilder = lvalueFlowCaptureIdBuilder ?? ImmutableHashSet.CreateBuilder<CaptureId>();
lvalueFlowCaptureIdBuilder.Add(flowCaptureReference.Id);
lvalueFlowCaptureIdBuilder = lvalueFlowCaptureIdBuilder ?? ImmutableDictionary.CreateBuilder<CaptureId, FlowCaptureKind>();
var captureKind = flowCaptureReference.Parent is ICompoundAssignmentOperation ? FlowCaptureKind.LValueAndRValueCapture : FlowCaptureKind.LValueCapture;
lvalueFlowCaptureIdBuilder.Add(flowCaptureReference.Id, captureKind);
}
#if DEBUG
else
......@@ -63,14 +64,14 @@ public static ImmutableHashSet<CaptureId> CreateLValueFlowCaptures(ControlFlowGr
#if DEBUG
if (lvalueFlowCaptureIdBuilder != null)
{
foreach (var captureId in lvalueFlowCaptureIdBuilder)
foreach (var captureId in lvalueFlowCaptureIdBuilder.Keys)
{
Debug.Assert(!rvalueFlowCaptureIds.Contains(captureId), "Flow capture used as both an r-value and an l-value?");
}
}
#endif
return lvalueFlowCaptureIdBuilder != null ? lvalueFlowCaptureIdBuilder.ToImmutable() : ImmutableHashSet<CaptureId>.Empty;
return lvalueFlowCaptureIdBuilder != null ? lvalueFlowCaptureIdBuilder.ToImmutable() : ImmutableDictionary<CaptureId, FlowCaptureKind>.Empty;
}
}
}
......@@ -114,6 +114,7 @@ public BasicBlockAnalysisData AnalyzeLambdaInvocation(IFlowAnonymousFunctionOper
// Methods specific to flow capture analysis for CFG based dataflow analysis.
public abstract bool IsLValueFlowCapture(CaptureId captureId);
public abstract bool IsRValueFlowCapture(CaptureId captureId);
public abstract void OnLValueCaptureFound(ISymbol symbol, IOperation operation, CaptureId captureId);
public abstract void OnLValueDereferenceFound(CaptureId captureId);
......
......@@ -85,6 +85,8 @@ private sealed class FlowGraphAnalysisData : AnalysisData
_lValueFlowCapturesMap = PooledDictionary<CaptureId, PooledHashSet<(ISymbol, IOperation)>>.GetInstance();
LValueFlowCapturesInGraph = LValueFlowCapturesProvider.CreateLValueFlowCaptures(controlFlowGraph);
Debug.Assert(LValueFlowCapturesInGraph.Values.All(kind => kind == FlowCaptureKind.LValueCapture || kind == FlowCaptureKind.LValueAndRValueCapture));
_symbolWritesInsideBlockRangeMap = PooledDictionary<(int firstBlockOrdinal, int lastBlockOrdinal), PooledHashSet<(ISymbol, IOperation)>>.GetInstance();
}
......@@ -149,7 +151,7 @@ private sealed class FlowGraphAnalysisData : AnalysisData
/// <summary>
/// Flow captures for l-value or address captures.
/// </summary>
public ImmutableHashSet<CaptureId> LValueFlowCapturesInGraph { get; }
public ImmutableDictionary<CaptureId, FlowCaptureKind> LValueFlowCapturesInGraph { get; }
public BasicBlockAnalysisData GetBlockAnalysisData(BasicBlock basicBlock)
=> _analysisDataByBasicBlockMap[basicBlock];
......@@ -310,7 +312,10 @@ public void SetAnalysisDataOnExitBlockEnd()
}
public override bool IsLValueFlowCapture(CaptureId captureId)
=> LValueFlowCapturesInGraph.Contains(captureId);
=> LValueFlowCapturesInGraph.ContainsKey(captureId);
public override bool IsRValueFlowCapture(CaptureId captureId)
=> !LValueFlowCapturesInGraph.TryGetValue(captureId, out var captureKind) || captureKind != FlowCaptureKind.LValueCapture;
public override void OnLValueCaptureFound(ISymbol symbol, IOperation operation, CaptureId captureId)
{
......
......@@ -49,6 +49,8 @@ protected override BasicBlockAnalysisData AnalyzeLambdaInvocationCore(IFlowAnony
public override bool IsLValueFlowCapture(CaptureId captureId)
=> throw ExceptionUtilities.Unreachable;
public override bool IsRValueFlowCapture(CaptureId captureId)
=> throw ExceptionUtilities.Unreachable;
public override void OnLValueCaptureFound(ISymbol symbol, IOperation operation, CaptureId captureId)
=> throw ExceptionUtilities.Unreachable;
public override void OnLValueDereferenceFound(CaptureId captureId)
......
......@@ -119,6 +119,12 @@ private void OnReferenceFound(ISymbol symbol, IOperation operation)
_currentAnalysisData.IsLValueFlowCapture(flowCapture.Id))
{
OnLValueCaptureFound(symbol, operation, flowCapture.Id);
// For compound assignments, the flow capture can be both an R-Value and an L-Value capture.
if (_currentAnalysisData.IsRValueFlowCapture(flowCapture.Id))
{
OnReadReferenceFound(symbol);
}
}
else
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册