未验证 提交 28688494 编写于 作者: N Neal Gafter 提交者: GitHub

Track enclosing symbol in data flow analysis, which might be a field or property. (#28252)

* Track enclosing symbol in data flow analysis, which might be a field or property.
Code used to assume it was (and typed it as) a method.
Fixes #19845
Fixes #27969
上级 80f9d816
......@@ -118,9 +118,9 @@ private int RootSlot(int slot)
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc)
{
var oldMethodOrLambda = this.currentMethodOrLambda;
var oldSymbol = this.currentSymbol;
var localFuncSymbol = localFunc.Symbol;
this.currentMethodOrLambda = localFuncSymbol;
this.currentSymbol = localFuncSymbol;
var oldPending = SavePending(); // we do not support branches into a lambda
......@@ -205,14 +205,14 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
}
this.State = savedState;
this.currentMethodOrLambda = oldMethodOrLambda;
this.currentSymbol = oldSymbol;
return null;
}
private void RecordReadInLocalFunction(int slot)
{
var localFunc = GetNearestLocalFunctionOpt(currentMethodOrLambda);
var localFunc = GetNearestLocalFunctionOpt(currentSymbol);
Debug.Assert(localFunc != null);
......@@ -294,7 +294,7 @@ private bool IsCapturedInLocalFunction(int slot)
// A variable is captured in a local function iff its
// container is higher in the tree than the nearest
// local function
var nearestLocalFunc = GetNearestLocalFunctionOpt(currentMethodOrLambda);
var nearestLocalFunc = GetNearestLocalFunctionOpt(currentSymbol);
return !(nearestLocalFunc is null) && IsCaptured(rootSymbol, nearestLocalFunc);
}
......
......@@ -114,9 +114,9 @@ internal partial class DataFlowPass : AbstractFlowPass<DataFlowPass.LocalState>
private BitVector _alreadyReported;
/// <summary>
/// Reflects the enclosing method or lambda at the current location (in the bound tree).
/// Reflects the enclosing member or lambda at the current location (in the bound tree).
/// </summary>
protected MethodSymbol currentMethodOrLambda { get; private set; }
protected Symbol currentSymbol { get; private set; }
/// <summary>
/// A cache for remember which structs are empty.
......@@ -160,7 +160,7 @@ protected override void Free()
{
this.initiallyAssignedVariables = null;
_sourceAssembly = ((object)member == null) ? null : (SourceAssemblySymbol)member.ContainingAssembly;
this.currentMethodOrLambda = member as MethodSymbol;
this.currentSymbol = member;
_unassignedVariableAddressOfSyntaxes = unassignedVariableAddressOfSyntaxes;
bool strict = compilation.FeatureStrictEnabled; // Compiler flag /features:strict removes the relaxed DA checking we have for backward compatibility
_emptyStructTypeCache = new EmptyStructTypeCache(compilation, !strict);
......@@ -179,7 +179,7 @@ protected override void Free()
{
this.initiallyAssignedVariables = initiallyAssignedVariables;
_sourceAssembly = ((object)member == null) ? null : (SourceAssemblySymbol)member.ContainingAssembly;
this.currentMethodOrLambda = member as MethodSymbol;
this.currentSymbol = member;
_unassignedVariableAddressOfSyntaxes = null;
bool strict = compilation.FeatureStrictEnabled; // Compiler flag /features:strict removes the relaxed DA checking we have for backward compatibility
_emptyStructTypeCache = emptyStructs ?? new EmptyStructTypeCache(compilation, !strict);
......@@ -203,7 +203,7 @@ protected override void Free()
{
this.initiallyAssignedVariables = initiallyAssignedVariables;
_sourceAssembly = null;
this.currentMethodOrLambda = member as MethodSymbol;
this.currentSymbol = member;
_unassignedVariableAddressOfSyntaxes = unassignedVariableAddressOfSyntaxes;
_emptyStructTypeCache = new NeverEmptyStructTypeCache();
}
......@@ -262,17 +262,16 @@ protected override ImmutableArray<PendingBranch> RemoveReturns()
{
var result = base.RemoveReturns();
if (currentMethodOrLambda?.IsAsync == true &&
!currentMethodOrLambda.IsImplicitlyDeclared)
if (currentSymbol is MethodSymbol currentMethod && currentMethod.IsAsync && !currentMethod.IsImplicitlyDeclared)
{
var foundAwait = result.Any(pending => pending.Branch?.Kind == BoundKind.AwaitExpression);
if (!foundAwait)
{
// If we're on a LambdaSymbol, then use its 'DiagnosticLocation'. That will be
// much better than using its 'Location' (which is the entire span of the lambda).
var diagnosticLocation = currentMethodOrLambda is LambdaSymbol lambda
var diagnosticLocation = currentSymbol is LambdaSymbol lambda
? lambda.DiagnosticLocation
: currentMethodOrLambda.Locations[0];
: currentSymbol.Locations[0];
Diagnostics.Add(ErrorCode.WRN_AsyncLacksAwaits, diagnosticLocation);
}
......@@ -283,7 +282,7 @@ protected override ImmutableArray<PendingBranch> RemoveReturns()
protected virtual void ReportUnassignedOutParameter(ParameterSymbol parameter, SyntaxNode node, Location location)
{
if (!_requireOutParamsAssigned && topLevelMethod == currentMethodOrLambda)
if (!_requireOutParamsAssigned && ReferenceEquals(topLevelMethod, currentSymbol))
{
return;
}
......@@ -399,13 +398,13 @@ protected void Analyze(ref bool badRegion, DiagnosticBag diagnostics)
/// <param name="rangeVariableUnderlyingParameter">If variable.Kind is RangeVariable, its underlying lambda parameter. Else null.</param>
private void CheckCaptured(Symbol variable, ParameterSymbol rangeVariableUnderlyingParameter = null)
{
if (IsCaptured(rangeVariableUnderlyingParameter ?? variable, currentMethodOrLambda))
if (IsCaptured(rangeVariableUnderlyingParameter ?? variable, currentSymbol))
{
NoteCaptured(variable);
}
}
private static bool IsCaptured(Symbol variable, MethodSymbol containingMethodOrLambda)
private static bool IsCaptured(Symbol variable, Symbol containingSymbol)
{
switch (variable.Kind)
{
......@@ -436,10 +435,10 @@ private static bool IsCaptured(Symbol variable, MethodSymbol containingMethodOrL
// case the variable is not captured by the target function, or null, in which
// case it is.
for (var currentFunction = variable.ContainingSymbol;
currentFunction != null;
(object)currentFunction != null;
currentFunction = currentFunction.ContainingSymbol)
{
if (currentFunction == containingMethodOrLambda)
if (ReferenceEquals(currentFunction, containingSymbol))
{
return false;
}
......@@ -888,7 +887,7 @@ protected int MakeSlot(BoundExpression node)
{
var propAccess = (BoundPropertyAccess)node;
if (Binder.AccessingAutoPropertyFromConstructor(propAccess, this.currentMethodOrLambda))
if (Binder.AccessingAutoPropertyFromConstructor(propAccess, this.currentSymbol))
{
var propSymbol = propAccess.PropertySymbol;
var backingField = (propSymbol as SourcePropertySymbol)?.BackingField;
......@@ -1090,7 +1089,7 @@ private bool IsAssigned(BoundExpression node, out int unassignedSlot)
case BoundKind.PropertyAccess:
{
var propertyAccess = (BoundPropertyAccess)node;
if (Binder.AccessingAutoPropertyFromConstructor(propertyAccess, this.currentMethodOrLambda))
if (Binder.AccessingAutoPropertyFromConstructor(propertyAccess, this.currentSymbol))
{
var property = propertyAccess.PropertySymbol;
var backingField = (property as SourcePropertySymbol)?.BackingField;
......@@ -1443,7 +1442,7 @@ private void EnterParameters(ImmutableArray<ParameterSymbol> parameters)
protected virtual void EnterParameter(ParameterSymbol parameter)
{
if (parameter.RefKind == RefKind.Out && !this.currentMethodOrLambda.IsAsync) // out parameters not allowed in async
if (parameter.RefKind == RefKind.Out && !(this.currentSymbol is MethodSymbol currentMethod && currentMethod.IsAsync)) // out parameters not allowed in async
{
int slot = GetOrCreateSlot(parameter);
if (slot > 0) SetSlotState(slot, initiallyAssignedVariables?.Contains(parameter) == true);
......@@ -1750,9 +1749,9 @@ public override BoundNode VisitLocal(BoundLocal node)
LocalSymbol localSymbol = node.LocalSymbol;
CheckAssigned(localSymbol, node.Syntax);
if (localSymbol.IsFixed &&
(this.currentMethodOrLambda.MethodKind == MethodKind.AnonymousFunction ||
this.currentMethodOrLambda.MethodKind == MethodKind.LocalFunction) &&
if (localSymbol.IsFixed && this.currentSymbol is MethodSymbol currentMethod &&
(currentMethod.MethodKind == MethodKind.AnonymousFunction ||
currentMethod.MethodKind == MethodKind.LocalFunction) &&
_capturedVariables.Contains(localSymbol))
{
Diagnostics.Add(ErrorCode.ERR_FixedLocalInLambda, new SourceLocation(node.Syntax), localSymbol);
......@@ -1830,8 +1829,8 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node)
public override BoundNode VisitLambda(BoundLambda node)
{
var oldMethodOrLambda = this.currentMethodOrLambda;
this.currentMethodOrLambda = node.Symbol;
var oldSymbol = this.currentSymbol;
this.currentSymbol = node.Symbol;
var oldPending = SavePending(); // we do not support branches into a lambda
......@@ -1866,7 +1865,7 @@ public override BoundNode VisitLambda(BoundLambda node)
this.State = stateAfterLambda;
this.currentMethodOrLambda = oldMethodOrLambda;
this.currentSymbol = oldSymbol;
return null;
}
......@@ -2189,7 +2188,7 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node)
public override BoundNode VisitPropertyAccess(BoundPropertyAccess node)
{
var result = base.VisitPropertyAccess(node);
if (Binder.AccessingAutoPropertyFromConstructor(node, this.currentMethodOrLambda))
if (Binder.AccessingAutoPropertyFromConstructor(node, this.currentSymbol))
{
var property = node.PropertySymbol;
var backingField = (property as SourcePropertySymbol)?.BackingField;
......
......@@ -66,7 +66,7 @@ internal class ReadWriteWalker : AbstractRegionDataFlowPass
protected override void EnterRegion()
{
for (MethodSymbol m = this.currentMethodOrLambda; (object)m != null; m = m.ContainingSymbol as MethodSymbol)
for (var m = this.currentSymbol as MethodSymbol; (object)m != null; m = m.ContainingSymbol as MethodSymbol)
{
foreach (var p in m.Parameters)
{
......
......@@ -6152,6 +6152,83 @@ struct MyStruct
Assert.Equal("this, value", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
}
[Fact]
[WorkItem(27969, "https://github.com/dotnet/roslyn/issues/27969")]
public void CodeInInitializer01()
{
var analysisResults = CompileAndAnalyzeDataFlowExpression(@"
using System;
class C
{
object P { get; } = Create(nameof(P), /*<bind>*/x => true/*</bind>*/);
static object Create(string name, Func<string, bool> f) => throw null;
}
");
var dataFlowAnalysisResults = analysisResults;
Assert.Equal("x", GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal("x", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
}
[Fact]
[WorkItem(27969, "https://github.com/dotnet/roslyn/issues/27969")]
public void CodeInInitializer02()
{
var analysisResults = CompileAndAnalyzeDataFlowExpression(@"
using System;
class C
{
object P { get; } = Create(P, /*<bind>*/x => true/*</bind>*/);
static object Create(object name, Func<string, bool> f) => throw null;
}
");
var dataFlowAnalysisResults = analysisResults;
Assert.Equal("x", GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal("x", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
}
[Fact]
[WorkItem(19845, "https://github.com/dotnet/roslyn/issues/19845")]
public void CodeInInitializer03()
{
var analysisResults = CompileAndAnalyzeDataFlowExpression(@"
class C {
static int X { get; set; }
int Y = /*<bind>*/X/*</bind>*/;
}");
var dataFlowAnalysisResults = analysisResults;
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.AlwaysAssigned));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
}
#endregion
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册