未验证 提交 734f1d82 编写于 作者: C Charles Stoner 提交者: GitHub

Infer method group based on receiver nullability (#35601)

上级 eb04e507
......@@ -193,7 +193,7 @@ private CSharpAttributeData GetAttribute(BoundAttribute boundAttribute, Diagnost
Debug.Assert((object)attributeType != null);
NullableWalker.AnalyzeIfNeeded(Compilation, boundAttribute, Conversions, diagnostics);
NullableWalker.AnalyzeIfNeeded(this, boundAttribute, diagnostics);
bool hasErrors = boundAttribute.HasAnyErrors;
......
......@@ -98,9 +98,9 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, Diagnosti
}
}
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Conversions conversions, DiagnosticBag diagnostics)
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Binder binder, DiagnosticBag diagnostics)
{
return NullableWalker.AnalyzeAndRewrite(Compilation, symbol: null, boundRoot, conversions, diagnostics);
return NullableWalker.AnalyzeAndRewrite(Compilation, symbol: null, boundRoot, binder, diagnostics);
}
internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel)
......
......@@ -258,9 +258,9 @@ internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTre
return false;
}
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Conversions conversions, DiagnosticBag diagnostics)
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Binder binder, DiagnosticBag diagnostics)
{
return NullableWalker.AnalyzeAndRewrite(Compilation, MemberSymbol, boundRoot, conversions, diagnostics);
return NullableWalker.AnalyzeAndRewrite(Compilation, MemberSymbol, boundRoot, binder, diagnostics);
}
}
}
......@@ -22,14 +22,14 @@ public SpeculativeMemberSemanticModel(SyntaxTreeSemanticModel parentSemanticMode
{
}
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Conversions conversions, DiagnosticBag diagnostics)
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Binder binder, DiagnosticBag diagnostics)
{
// https://github.com/dotnet/roslyn/issues/35037: Speculative models are going to have to do something more advanced
// here. They will need to run nullable analysis up to the point that is being speculated on, and
// then take that state and run analysis on the statement or expression being speculated on.
// Currently, it will return incorrect info because it's just running analysis on the speculated
// part.
return NullableWalker.AnalyzeAndRewrite(Compilation, MemberSymbol as MethodSymbol, boundRoot, conversions, diagnostics);
return NullableWalker.AnalyzeAndRewrite(Compilation, MemberSymbol as MethodSymbol, boundRoot, binder, diagnostics);
}
internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel)
......
......@@ -1625,7 +1625,7 @@ private BoundNode GetBoundLambdaOrQuery(CSharpSyntaxNode lambdaOrQuery)
// https://github.com/dotnet/roslyn/issues/35038: We need to do a rewrite here, and create a test that can hit this.
#if DEBUG
var diagnostics = new DiagnosticBag();
_ = RewriteNullableBoundNodes(boundOuterExpression, incrementalBinder.Conversions, diagnostics);
_ = RewriteNullableBoundNodes(boundOuterExpression, incrementalBinder, diagnostics);
#endif
nodes = GuardedAddBoundTreeAndGetBoundNodeFromMap(lambdaOrQuery, boundOuterExpression);
......@@ -1850,7 +1850,7 @@ private void EnsureRootBoundForNullabilityIfNecessary()
// part.
var binder = GetEnclosingBinder(GetAdjustedNodePosition(bindableRoot));
var boundRoot = Bind(binder, bindableRoot, diagnostics);
boundRoot = RewriteNullableBoundNodes(boundRoot, binder.Conversions, diagnostics);
boundRoot = RewriteNullableBoundNodes(boundRoot, binder, diagnostics);
if (Compilation.NullableAnalysisEnabled && !IsSpeculativeSemanticModel)
{
......@@ -1858,7 +1858,7 @@ private void EnsureRootBoundForNullabilityIfNecessary()
}
}
protected abstract BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Conversions conversions, DiagnosticBag diagnostics);
protected abstract BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Binder binder, DiagnosticBag diagnostics);
/// <summary>
/// Get all bounds nodes associated with a node, ordered from highest to lowest in the bound tree.
......
......@@ -205,9 +205,9 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode
return false;
}
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Conversions conversions, DiagnosticBag diagnostics)
protected override BoundNode RewriteNullableBoundNodes(BoundNode boundRoot, Binder binder, DiagnosticBag diagnostics)
{
return NullableWalker.AnalyzeAndRewrite(Compilation, MemberSymbol, boundRoot, conversions, diagnostics);
return NullableWalker.AnalyzeAndRewrite(Compilation, MemberSymbol, boundRoot, binder, diagnostics);
}
}
}
......@@ -1653,7 +1653,7 @@ internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSt
// also run the nullable walker, and issue duplicate warnings. We should try to only run the pass
// once.
// https://github.com/dotnet/roslyn/issues/35041
methodBody = NullableWalker.AnalyzeAndRewrite(bodyBinder.Compilation, method, methodBody, bodyBinder.Conversions, new DiagnosticBag());
methodBody = NullableWalker.AnalyzeAndRewrite(bodyBinder.Compilation, method, methodBody, bodyBinder, new DiagnosticBag());
}
forSemanticModel = (syntaxNode, methodBody, bodyBinder);
......
......@@ -108,6 +108,11 @@ public VisitArgumentResult(VisitResult visitResult, Optional<LocalState> stateFo
/// </summary>
private readonly PooledDictionary<Symbol, TypeWithAnnotations> _variableTypes = PooledDictionary<Symbol, TypeWithAnnotations>.GetInstance();
/// <summary>
/// Binder for symbol being analyzed.
/// </summary>
private readonly Binder _binder;
/// <summary>
/// Conversions with nullability and unknown matching any.
/// </summary>
......@@ -308,12 +313,14 @@ protected override void Free()
bool useMethodSignatureParameterTypes,
MethodSymbol methodSignatureOpt,
BoundNode node,
Binder binder,
Conversions conversions,
ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> returnTypesOpt,
VariableState initialState,
Dictionary<BoundExpression, (NullabilityInfo, TypeSymbol)> analyzedNullabilityMapOpt)
: base(compilation, symbol, node, EmptyStructTypeCache.CreatePrecise(), trackUnassignments: true)
{
_binder = binder;
_conversions = (Conversions)conversions.WithNullability(true);
_useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes;
_methodSignatureOpt = methodSignatureOpt;
......@@ -387,10 +394,12 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
{
return;
}
var conversions = compilation.GetBinderFactory(node.SyntaxTree).GetBinder(node.Syntax).Conversions;
var binder = compilation.GetBinderFactory(node.SyntaxTree).GetBinder(node.Syntax);
var conversions = binder.Conversions;
Analyze(compilation,
method,
node,
binder,
conversions,
diagnostics,
useMethodSignatureParameterTypes: false,
......@@ -404,7 +413,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
CSharpCompilation compilation,
Symbol symbol,
BoundNode node,
Conversions conversions,
Binder binder,
DiagnosticBag diagnostics)
{
var analyzedNullabilities = PooledDictionary<BoundExpression, (NullabilityInfo, TypeSymbol)>.GetInstance();
......@@ -413,7 +422,8 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
compilation,
symbol,
node,
conversions,
binder,
binder.Conversions,
diagnostics,
useMethodSignatureParameterTypes: !(methodSymbol is null),
methodSignatureOpt: methodSymbol,
......@@ -434,17 +444,28 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
}
internal static void AnalyzeIfNeeded(
CSharpCompilation compilation,
Binder binder,
BoundAttribute attribute,
Conversions conversions,
DiagnosticBag diagnostics)
{
var compilation = binder.Compilation;
if (compilation.LanguageVersion < MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion())
{
return;
}
Analyze(compilation, null, attribute, conversions, diagnostics, useMethodSignatureParameterTypes: false, methodSignatureOpt: null, returnTypes: null, initialState: null, analyzedNullabilityMapOpt: null);
Analyze(
compilation,
symbol: null,
attribute,
binder,
binder.Conversions,
diagnostics,
useMethodSignatureParameterTypes: false,
methodSignatureOpt: null,
returnTypes: null,
initialState: null,
analyzedNullabilityMapOpt: null);
}
internal static void Analyze(
......@@ -461,6 +482,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
compilation,
lambda.Symbol,
lambda.Body,
lambda.Binder,
conversions,
diagnostics,
useMethodSignatureParameterTypes: !lambda.UnboundLambda.HasExplicitlyTypedParameterList,
......@@ -474,6 +496,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
CSharpCompilation compilation,
Symbol symbol,
BoundNode node,
Binder binder,
Conversions conversions,
DiagnosticBag diagnostics,
bool useMethodSignatureParameterTypes,
......@@ -489,6 +512,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
useMethodSignatureParameterTypes,
methodSignatureOpt,
node,
binder,
conversions,
returnTypes,
initialState,
......@@ -1884,6 +1908,7 @@ private ArrayTypeSymbol VisitArrayInitializer(BoundArrayCreation node)
useMethodSignatureParameterTypes: false,
methodSignatureOpt: null,
node,
binder: null,
conversions: conversions,
returnTypesOpt: null,
initialState: null,
......@@ -2867,7 +2892,12 @@ private static bool HasImplicitTypeArguments(BoundExpression node)
// Unexpected syntax kind.
return false;
}
var nameSyntax = Binder.GetNameSyntax(((InvocationExpressionSyntax)syntax).Expression, out var _);
return HasImplicitTypeArguments(((InvocationExpressionSyntax)syntax).Expression);
}
private static bool HasImplicitTypeArguments(SyntaxNode syntax)
{
var nameSyntax = Binder.GetNameSyntax(syntax, out _);
if (nameSyntax == null)
{
// Unexpected syntax kind.
......@@ -3386,40 +3416,50 @@ private VariableState GetVariableState(Optional<LocalState> localState)
private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol method, ImmutableArray<BoundExpression> arguments)
{
return InferMethodTypeArguments(node.BinderOpt, method, arguments, node.ArgumentRefKindsOpt, node.ArgsToParamsOpt, node.Expanded);
}
private MethodSymbol InferMethodTypeArguments(
Binder binder,
MethodSymbol method,
ImmutableArray<BoundExpression> arguments,
ImmutableArray<RefKind> argumentRefKindsOpt,
ImmutableArray<int> argsToParamsOpt,
bool expanded)
{
Debug.Assert(binder != null);
Debug.Assert(method.IsGenericMethod);
// https://github.com/dotnet/roslyn/issues/27961 OverloadResolution.IsMemberApplicableInNormalForm and
// IsMemberApplicableInExpandedForm use the least overridden method. We need to do the same here.
var definition = method.ConstructedFrom;
var refKinds = ArrayBuilder<RefKind>.GetInstance();
if (node.ArgumentRefKindsOpt != null)
if (argumentRefKindsOpt != null)
{
refKinds.AddRange(node.ArgumentRefKindsOpt);
refKinds.AddRange(argumentRefKindsOpt);
}
Debug.Assert(node.BinderOpt != null);
// https://github.com/dotnet/roslyn/issues/27961 Do we really need OverloadResolution.GetEffectiveParameterTypes?
// Aren't we doing roughly the same calculations in GetCorrespondingParameter?
OverloadResolution.GetEffectiveParameterTypes(
definition,
arguments.Length,
node.ArgsToParamsOpt,
argsToParamsOpt,
refKinds,
isMethodGroupConversion: false,
// https://github.com/dotnet/roslyn/issues/27961 `allowRefOmittedArguments` should be
// false for constructors and several other cases (see Binder use). Should we
// capture the original value in the BoundCall?
allowRefOmittedArguments: true,
binder: node.BinderOpt,
expanded: node.Expanded,
binder: binder,
expanded: expanded,
parameterTypes: out ImmutableArray<TypeWithAnnotations> parameterTypes,
parameterRefKinds: out ImmutableArray<RefKind> parameterRefKinds);
refKinds.Free();
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
var result = MethodTypeInferrer.Infer(
node.BinderOpt,
binder,
_conversions,
definition.TypeParameters,
definition.ContainingType,
......@@ -4213,23 +4253,16 @@ private static BoundConversion GetConversionIfApplicable(BoundExpression convers
{
case ConversionKind.MethodGroup:
{
var receiverOpt = ((BoundMethodGroup)conversionOperand)?.ReceiverOpt;
// https://github.com/dotnet/roslyn/issues/33637: Should update method based on inferred receiver type.
var group = conversionOperand as BoundMethodGroup;
var delegateType = targetType.GetDelegateType();
var method = conversion.Method;
if (TryGetMethodGroupReceiverNullability(receiverOpt, out TypeWithState receiverType))
if (group != null)
{
if (conversion.IsExtensionMethod)
{
CheckExtensionMethodThisNullability(receiverOpt, Conversion.Identity, method.Parameters[0], receiverType);
}
else
{
CheckPossibleNullReceiver(receiverOpt, receiverType, checkNullableValueType: false);
}
method = CheckMethodGroupReceiverNullability(group, delegateType, method, conversion.IsExtensionMethod);
}
if (reportRemainingWarnings)
{
ReportNullabilityMismatchWithTargetDelegate(diagnosticLocationOpt, targetType.GetDelegateType(), method, conversion.IsExtensionMethod);
ReportNullabilityMismatchWithTargetDelegate(diagnosticLocationOpt, delegateType, method, conversion.IsExtensionMethod);
}
}
resultState = NullableFlowState.NotNull;
......@@ -4721,23 +4754,11 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE
{
case BoundMethodGroup group:
{
// https://github.com/dotnet/roslyn/issues/33637: Should update method based on inferred receiver type.
VisitMethodGroup(group);
var method = node.MethodOpt;
if (method is object)
{
var receiverOpt = group.ReceiverOpt;
if (receiverOpt != null)
{
VisitRvalue(receiverOpt);
if (node.IsExtensionMethod)
{
CheckExtensionMethodThisNullability(receiverOpt, Conversion.Identity, method.Parameters[0], ResultType);
}
else
{
CheckPossibleNullReceiver(receiverOpt);
}
}
method = CheckMethodGroupReceiverNullability(group, delegateType, method, node.IsExtensionMethod);
if (!group.IsSuppressed)
{
ReportNullabilityMismatchWithTargetDelegate(group.Syntax.Location, delegateType, method, node.IsExtensionMethod);
......@@ -4804,6 +4825,48 @@ private void SetMethodGroupReceiverNullability(BoundExpression receiver, TypeWit
_methodGroupReceiverMapOpt[receiver] = type;
}
private MethodSymbol CheckMethodGroupReceiverNullability(BoundMethodGroup group, NamedTypeSymbol delegateType, MethodSymbol method, bool invokedAsExtensionMethod)
{
var receiverOpt = group.ReceiverOpt;
if (TryGetMethodGroupReceiverNullability(receiverOpt, out TypeWithState receiverType))
{
var syntax = group.Syntax;
if (!invokedAsExtensionMethod)
{
method = (MethodSymbol)AsMemberOfType(receiverType.Type, method);
}
if (method.IsGenericMethod && HasImplicitTypeArguments(group.Syntax))
{
var arguments = ArrayBuilder<BoundExpression>.GetInstance();
if (invokedAsExtensionMethod)
{
arguments.Add(CreatePlaceholderIfNecessary(receiverOpt, receiverType.ToTypeWithAnnotations()));
}
// Create placeholders for the arguments. (See Conversions.GetDelegateArguments()
// which is used for that purpose in initial binding.)
foreach (var parameter in delegateType.DelegateInvokeMethod.Parameters)
{
var parameterType = parameter.TypeWithAnnotations;
arguments.Add(new BoundExpressionWithNullability(syntax, new BoundParameter(syntax, parameter), parameterType.NullableAnnotation, parameterType.Type));
}
method = InferMethodTypeArguments(_binder, method, arguments.ToImmutableAndFree(), argumentRefKindsOpt: default, argsToParamsOpt: default, expanded: false);
}
if (invokedAsExtensionMethod)
{
CheckExtensionMethodThisNullability(receiverOpt, Conversion.Identity, method.Parameters[0], receiverType);
}
else
{
CheckPossibleNullReceiver(receiverOpt, receiverType, checkNullableValueType: false);
}
if (ConstraintsHelper.RequiresChecking(method))
{
CheckMethodConstraints(syntax, method);
}
}
return method;
}
public override BoundNode VisitLambda(BoundLambda node)
{
// It's possible to reach VisitLambda without having analyzed the lambda body in error scenarios,
......@@ -4856,6 +4919,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
Analyze(compilation,
node.Symbol,
body,
_binder,
_conversions,
Diagnostics,
useMethodSignatureParameterTypes: false,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册