NullableWalker.cs 260.2 KB
Newer Older
1 2 3
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

#if DEBUG
A
Andy Gocke 已提交
4
// See comment in DefiniteAssignment.
5 6 7 8
#define REFERENCE_STATE
#endif

using System;
9
using System.Collections.Generic;
10 11
using System.Collections.Immutable;
using System.Diagnostics;
12
using System.Linq;
J
Jared Parsons 已提交
13
using System.Runtime.CompilerServices;
14
using Microsoft.CodeAnalysis.CSharp.Symbols;
15
using Microsoft.CodeAnalysis.CSharp.Syntax;
16 17 18 19 20 21
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
22
    /// Nullability flow analysis.
23
    /// </summary>
24
    [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
A
Andy Gocke 已提交
25
    internal sealed partial class NullableWalker : LocalDataFlowPass<NullableWalker.LocalState>
26 27
    {
        /// <summary>
28 29
        /// Used to copy variable slots and types from the NullableWalker for the containing method
        /// or lambda to the NullableWalker created for a nested lambda or local function.
30
        /// </summary>
31 32
        internal sealed class VariableState
        {
33
            // Consider referencing the collections directly from the original NullableWalker
34
            // rather than copying the collections. (Items are added to the collections
35 36 37
            // but never replaced so the collections are lazily populated but otherwise immutable.)
            internal readonly ImmutableDictionary<VariableIdentifier, int> VariableSlot;
            internal readonly ImmutableArray<VariableIdentifier> VariableBySlot;
38
            internal readonly ImmutableDictionary<Symbol, TypeWithAnnotations> VariableTypes;
39 40 41 42

            internal VariableState(
                ImmutableDictionary<VariableIdentifier, int> variableSlot,
                ImmutableArray<VariableIdentifier> variableBySlot,
43
                ImmutableDictionary<Symbol, TypeWithAnnotations> variableTypes)
44 45 46 47 48 49 50
            {
                VariableSlot = variableSlot;
                VariableBySlot = variableBySlot;
                VariableTypes = variableTypes;
            }
        }

51 52 53 54 55
        /// <summary>
        /// Represents the result of visiting an expression.
        /// Contains a result type which tells us whether the expression may be null,
        /// and an l-value type which tells us whether we can assign null to the expression.
        /// </summary>
56
        [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
57 58
        private readonly struct VisitResult
        {
59
            public readonly TypeWithState RValueType;
60
            public readonly TypeWithAnnotations LValueType;
61

62
            public VisitResult(TypeWithState rValueType, TypeWithAnnotations lValueType)
63
            {
64
                RValueType = rValueType;
65 66
                LValueType = lValueType;
            }
67 68 69

            private string GetDebuggerDisplay() =>
                $"RValueType={RValueType.GetDebuggerDisplay()}, LValueType={LValueType.GetDebuggerDisplay()}";
70 71
        }

72 73 74
        /// <summary>
        /// The inferred type at the point of declaration of var locals and parameters.
        /// </summary>
75
        private readonly PooledDictionary<Symbol, TypeWithAnnotations> _variableTypes = PooledDictionary<Symbol, TypeWithAnnotations>.GetInstance();
76

77
        private readonly Binder _binder;
78 79 80 81

        /// <summary>
        /// Conversions with nullability and unknown matching any.
        /// </summary>
82 83
        private readonly Conversions _conversions;

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
        /// <summary>
        /// Use the return type and nullability from _methodSignatureOpt to calculate return
        /// expression conversions. If false, the signature of _member is used instead.
        /// </summary>
        private readonly bool _useMethodSignatureReturnType;

        /// <summary>
        /// Use the the parameter types and nullability from _methodSignatureOpt for initial
        /// parameter state. If false, the signature of _member is used instead.
        /// </summary>
        private readonly bool _useMethodSignatureParameterTypes;

        /// <summary>
        /// Method signature used for return type or parameter types. Distinct from _member
        /// signature when _member is a lambda and type is inferred from MethodTypeInferrer.
        /// </summary>
        private readonly MethodSymbol _methodSignatureOpt;

        /// <summary>
103
        /// Return statements and the result types from analyzing the returned expressions. Used when inferring lambda return type in MethodTypeInferrer.
104
        /// </summary>
105
        private readonly ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> _returnTypesOpt;
106 107 108 109 110 111 112

        /// <summary>
        /// An optional callback for callers to receive notification of the inferred type and nullability
        /// of each expression in the method. Since the walker may require multiple passes, the callback
        /// may be invoked multiple times for a single expression, potentially with different nullability
        /// each time. The last call for each expression will include the final inferred type and nullability.
        /// </summary>
113
        private readonly Action<BoundExpression, TypeWithAnnotations> _callbackOpt;
114

115 116
        /// <summary>
        /// Invalid type, used only to catch Visit methods that do not set
117
        /// _result.Type. See VisitExpressionWithoutStackGuard.
118
        /// </summary>
119
        private static readonly TypeWithState _invalidType = new TypeWithState(ErrorTypeSymbol.UnknownResultType, NullableFlowState.NotNull);
120

121 122 123 124 125
        /// <summary>
        /// The result and l-value type of the last visited expression.
        /// </summary>
        private VisitResult _visitResult;

126 127 128 129 130 131 132 133
        /// <summary>
        /// The visit result of the receiver for the current conditional access.
        ///
        /// For example: A conditional invocation uses a placeholder as a receiver. By storing the
        /// visit result from the actual receiver ahead of time, we can give this placeholder a correct result.
        /// </summary>
        private VisitResult _currentConditionalReceiverVisitResult;

134 135 136
        /// <summary>
        /// The result type represents the state of the last visited expression.
        /// </summary>
137
        private TypeWithState ResultType
138 139 140 141
        {
            get => _visitResult.RValueType;
            set
            {
142
                SetResult(rvalueType: value, lvalueType: value.ToTypeWithAnnotations());
143 144 145
            }
        }

N
Neal Gafter 已提交
146 147 148 149 150 151 152 153
        /// <summary>
        /// Force the inference of the LValueResultType from ResultType.
        /// </summary>
        private void UseRvalueOnly()
        {
            ResultType = ResultType;
        }

154
        private TypeWithAnnotations LvalueResultType
N
Neal Gafter 已提交
155 156 157 158
        {
            get => _visitResult.LValueType;
            set
            {
159
                SetResult(rvalueType: value.ToTypeWithState(), lvalueType: value);
N
Neal Gafter 已提交
160 161 162
            }
        }

N
Neal Gafter 已提交
163 164 165 166 167 168 169 170
        /// <summary>
        /// Force the inference of the ResultType from LValueResultType.
        /// </summary>
        private void UseLvalueOnly()
        {
            LvalueResultType = LvalueResultType;
        }

171
        private void SetResult(TypeWithState rvalueType, TypeWithAnnotations lvalueType)
172
        {
173
            _visitResult = new VisitResult(rvalueType, lvalueType);
174
        }
175

176 177 178
        /// <summary>
        /// Instances being constructed.
        /// </summary>
179
        private PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal> _placeholderLocalsOpt;
180

181 182 183 184 185 186
        /// <summary>
        /// For methods with annotations, we'll need to visit the arguments twice.
        /// Once for diagnostics and once for result state (but disabling diagnostics).
        /// </summary>
        private bool _disableDiagnostics = false;

187 188 189 190 191 192
        /// <summary>
        /// Used to allow <see cref="MakeSlot(BoundExpression)"/> to substitute the correct slot for a <see cref="BoundConditionalReceiver"/> when
        /// it's encountered.
        /// </summary>
        private int _lastConditionalAccessSlot = -1;

193 194
        protected override void Free()
        {
195
            _variableTypes.Free();
196
            _placeholderLocalsOpt?.Free();
197 198 199
            base.Free();
        }

200
        private NullableWalker(
201
            CSharpCompilation compilation,
202 203 204 205
            MethodSymbol method,
            bool useMethodSignatureReturnType,
            bool useMethodSignatureParameterTypes,
            MethodSymbol methodSignatureOpt,
206
            BoundNode node,
207
            ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> returnTypesOpt,
208
            VariableState initialState,
209
            Action<BoundExpression, TypeWithAnnotations> callbackOpt)
210
            : base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true)
211
        {
212
            _callbackOpt = callbackOpt;
213
            _binder = compilation.GetBinderFactory(node.SyntaxTree).GetBinder(node.Syntax);
214
            Debug.Assert(!_binder.Conversions.IncludeNullability);
215
            _conversions = (Conversions)_binder.Conversions.WithNullability(true);
216 217 218
            _useMethodSignatureReturnType = (object)methodSignatureOpt != null && useMethodSignatureReturnType;
            _useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes;
            _methodSignatureOpt = methodSignatureOpt;
219
            _returnTypesOpt = returnTypesOpt;
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
            if (initialState != null)
            {
                var variableBySlot = initialState.VariableBySlot;
                nextVariableSlot = variableBySlot.Length;
                foreach (var (variable, slot) in initialState.VariableSlot)
                {
                    Debug.Assert(slot < nextVariableSlot);
                    _variableSlot.Add(variable, slot);
                }
                this.variableBySlot = variableBySlot.ToArray();
                foreach (var pair in initialState.VariableTypes)
                {
                    _variableTypes.Add(pair.Key, pair.Value);
                }
            }
235 236
        }

237 238 239 240 241 242 243 244 245 246 247
        public string GetDebuggerDisplay()
        {
            if (this.IsConditionalState)
            {
                return $"{{{GetType().Name} WhenTrue:{Dump(StateWhenTrue)} WhenFalse:{Dump(StateWhenFalse)}{"}"}";
            }
            else
            {
                return $"{{{GetType().Name} {Dump(State)}{"}"}";
            }
        }
N
Neal Gafter 已提交
248

249 250
        // For purpose of nullability analysis, awaits create pending branches, so async usings and foreachs do too
        public sealed override bool AwaitUsingAndForeachAddsPendingBranch => true;
251

252 253 254 255 256 257 258
        protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()
        {
            return true;
        }

        protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
        {
259
            if (_returnTypesOpt != null)
260
            {
261
                _returnTypesOpt.Clear();
262
            }
263 264
            this.Diagnostics.Clear();
            ParameterSymbol methodThisParameter = MethodThisParameter;
A
Andy Gocke 已提交
265
            this.State = TopState();                   // entry point is reachable
266
            this.regionPlace = RegionPlace.Before;
267
            EnterParameters();                               // with parameters assigned
C
Chris Sienkiewicz 已提交
268
            if (!(methodThisParameter is null))
269
            {
270
                EnterParameter(methodThisParameter, methodThisParameter.TypeWithAnnotations);
271 272 273 274 275 276
            }

            ImmutableArray<PendingBranch> pendingReturns = base.Scan(ref badRegion);
            return pendingReturns;
        }

277 278 279 280 281
        internal static void Analyze(
            CSharpCompilation compilation,
            MethodSymbol method,
            BoundNode node,
            DiagnosticBag diagnostics,
282
            Action<BoundExpression, TypeWithAnnotations> callbackOpt = null)
283
        {
284
            if (method.IsImplicitlyDeclared && (!method.IsImplicitConstructor || method.ContainingType.IsImplicitlyDeclared))
285 286 287
            {
                return;
            }
288 289
            Analyze(compilation, method, node, diagnostics, useMethodSignatureReturnType: false, useMethodSignatureParameterTypes: false, methodSignatureOpt: null, returnTypes: null, initialState: null, callbackOpt);
        }
290

C
Chris Sienkiewicz 已提交
291
        internal static void AnalyzeIfNeeded(
292 293 294 295
            CSharpCompilation compilation,
            BoundAttribute attribute,
            DiagnosticBag diagnostics)
        {
C
Chris Sienkiewicz 已提交
296
            if (compilation.LanguageVersion < MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion())
297 298 299 300
            {
                return;
            }

C
Chris Sienkiewicz 已提交
301
            Analyze(compilation, null, attribute, diagnostics, useMethodSignatureReturnType: false, useMethodSignatureParameterTypes: false, methodSignatureOpt: null, returnTypes: null, initialState: null, callbackOpt: null);
302 303
        }

304 305 306 307 308
        internal static void Analyze(
            CSharpCompilation compilation,
            BoundLambda lambda,
            DiagnosticBag diagnostics,
            MethodSymbol delegateInvokeMethod,
309
            ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> returnTypes,
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
            VariableState initialState)
        {
            Analyze(
                compilation,
                lambda.Symbol,
                lambda.Body,
                diagnostics,
                useMethodSignatureReturnType: true,
                useMethodSignatureParameterTypes: !lambda.UnboundLambda.HasExplicitlyTypedParameterList,
                methodSignatureOpt: delegateInvokeMethod,
                returnTypes, initialState,
                callbackOpt: null);
        }

        private static void Analyze(
            CSharpCompilation compilation,
            MethodSymbol method,
            BoundNode node,
            DiagnosticBag diagnostics,
            bool useMethodSignatureReturnType,
            bool useMethodSignatureParameterTypes,
            MethodSymbol methodSignatureOpt,
332
            ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> returnTypes,
333
            VariableState initialState,
334
            Action<BoundExpression, TypeWithAnnotations> callbackOpt)
335 336 337
        {
            Debug.Assert(diagnostics != null);
            var walker = new NullableWalker(compilation, method, useMethodSignatureReturnType, useMethodSignatureParameterTypes, methodSignatureOpt, node, returnTypes, initialState, callbackOpt);
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
            try
            {
                bool badRegion = false;
                ImmutableArray<PendingBranch> returns = walker.Analyze(ref badRegion);
                diagnostics.AddRange(walker.Diagnostics);
                Debug.Assert(!badRegion);
            }
            catch (BoundTreeVisitor.CancelledByStackGuardException ex) when (diagnostics != null)
            {
                ex.AddAnError(diagnostics);
            }
            finally
            {
                walker.Free();
            }
        }

        protected override void Normalize(ref LocalState state)
        {
357 358 359
            if (!state.Reachable)
                return;

360
            int oldNext = state.Capacity;
361
            state.EnsureCapacity(nextVariableSlot);
362 363 364 365 366 367 368
            Populate(ref state, oldNext);
        }

        private void Populate(ref LocalState state, int start)
        {
            int capacity = state.Capacity;
            for (int slot = start; slot < capacity; slot++)
369
            {
370
                state[slot] = GetDefaultState(ref state, slot);
371 372 373
            }
        }

374
        private NullableFlowState GetDefaultState(ref LocalState state, int slot)
375
        {
376 377 378
            if (!state.Reachable)
                return NullableFlowState.NotNull;

379
            if (slot == 0)
380
                return NullableFlowState.MaybeNull;
381 382 383 384 385 386 387

            var variable = variableBySlot[slot];
            var symbol = variable.Symbol;

            switch (symbol.Kind)
            {
                case SymbolKind.Local:
388
                    // Locals are considered not null before they are definitely assigned
389
                    return NullableFlowState.NotNull;
390 391 392
                case SymbolKind.Parameter:
                    {
                        var parameter = (ParameterSymbol)symbol;
393 394
                        if (parameter.RefKind == RefKind.Out)
                        {
395
                            return NullableFlowState.NotNull;
396
                        }
397

398
                        if (!_variableTypes.TryGetValue(parameter, out TypeWithAnnotations parameterType))
399
                        {
400
                            parameterType = parameter.TypeWithAnnotations;
401
                        }
402

403
                        return parameterType.ToTypeWithState().State;
404 405 406
                    }
                case SymbolKind.Field:
                case SymbolKind.Property:
407
                case SymbolKind.Event:
408
                    return symbol.GetTypeOrReturnType().ToTypeWithState().State;
409 410 411
                default:
                    throw ExceptionUtilities.UnexpectedValue(symbol.Kind);
            }
412 413 414 415
        }

        protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundExpression receiver, out Symbol member)
        {
416 417 418 419
            receiver = null;
            member = null;

            switch (expr.Kind)
420
            {
421
                case BoundKind.FieldAccess:
422
                    {
423 424
                        var fieldAccess = (BoundFieldAccess)expr;
                        var fieldSymbol = fieldAccess.FieldSymbol;
425 426
                        member = fieldSymbol;
                        if (fieldSymbol.IsFixedSizeBuffer)
427 428 429
                        {
                            return false;
                        }
430 431 432 433
                        if (fieldSymbol.IsStatic)
                        {
                            return true;
                        }
434 435 436 437 438 439 440
                        receiver = fieldAccess.ReceiverOpt;
                        break;
                    }
                case BoundKind.EventAccess:
                    {
                        var eventAccess = (BoundEventAccess)expr;
                        var eventSymbol = eventAccess.EventSymbol;
441 442
                        // https://github.com/dotnet/roslyn/issues/29901 Use AssociatedField for field-like events?
                        member = eventSymbol;
443 444
                        if (eventSymbol.IsStatic)
                        {
445
                            return true;
446 447 448 449 450 451 452 453
                        }
                        receiver = eventAccess.ReceiverOpt;
                        break;
                    }
                case BoundKind.PropertyAccess:
                    {
                        var propAccess = (BoundPropertyAccess)expr;
                        var propSymbol = propAccess.PropertySymbol;
454 455
                        member = GetBackingFieldIfStructProperty(propSymbol);
                        if (member is null)
456 457 458
                        {
                            return false;
                        }
459 460 461 462
                        if (propSymbol.IsStatic)
                        {
                            return true;
                        }
463 464
                        receiver = propAccess.ReceiverOpt;
                        break;
465 466
                    }
            }
467

468 469
            Debug.Assert(member?.IsStatic != true);

470 471 472 473
            return (object)member != null &&
                (object)receiver != null &&
                receiver.Kind != BoundKind.TypeExpression &&
                (object)receiver.Type != null;
474 475
        }

476
        // https://github.com/dotnet/roslyn/issues/29619 Use backing field for struct property
477 478 479 480
        // for now, to avoid cycles if the struct type contains a property of the struct type.
        // Remove this and populate struct members lazily to match classes.
        private Symbol GetBackingFieldIfStructProperty(Symbol symbol)
        {
481
            if (symbol.Kind == SymbolKind.Property && !symbol.ContainingType.IsNullableType())
482 483 484 485 486
            {
                var property = (PropertySymbol)symbol;
                var containingType = property.ContainingType;
                if (containingType.TypeKind == TypeKind.Struct)
                {
487
                    // https://github.com/dotnet/roslyn/issues/29619 Relying on field name
488 489
                    // will not work for properties declared in other languages.
                    var fieldName = GeneratedNames.MakeBackingFieldName(property.Name);
490
                    return _emptyStructTypeCache.GetStructFields(containingType, includeStatic: symbol.IsStatic).FirstOrDefault(f => f.Name == fieldName);
491 492 493 494 495
                }
            }
            return symbol;
        }

496
        // https://github.com/dotnet/roslyn/issues/29619 Temporary, until we're using
497
        // properties on structs directly.
498
        protected override int GetOrCreateSlot(Symbol symbol, int containingSlot = 0)
499 500
        {
            symbol = GetBackingFieldIfStructProperty(symbol);
N
Neal Gafter 已提交
501
            if (symbol is null)
502 503 504 505 506 507
            {
                return -1;
            }
            return base.GetOrCreateSlot(symbol, containingSlot);
        }

508 509 510 511
        protected override int MakeSlot(BoundExpression node)
        {
            switch (node.Kind)
            {
512 513 514 515 516 517 518
                case BoundKind.ThisReference:
                case BoundKind.BaseReference:
                    {
                        var method = getTopLevelMethod(_symbol as MethodSymbol);
                        var thisParameter = method?.ThisParameter;
                        return (object)thisParameter != null ? GetOrCreateSlot(thisParameter) : -1;
                    }
519 520 521
                case BoundKind.Conversion:
                    {
                        var conv = (BoundConversion)node;
522
                        switch (conv.Conversion.Kind)
523
                        {
524 525 526 527 528
                            case ConversionKind.ExplicitNullable:
                                {
                                    var operand = conv.Operand;
                                    var operandType = operand.Type;
                                    var convertedType = conv.Type;
529
                                    if (AreNullableAndUnderlyingTypes(operandType, convertedType, out _))
530 531 532 533 534 535 536 537
                                    {
                                        // Explicit conversion of Nullable<T> to T is equivalent to Nullable<T>.Value.
                                        // For instance, in the following, when evaluating `((A)a).B` we need to recognize
                                        // the nullability of `(A)a` (not nullable) and the slot (the slot for `a.Value`).
                                        //   struct A { B? B; }
                                        //   struct B { }
                                        //   if (a?.B != null) _ = ((A)a).B.Value; // no warning
                                        int containingSlot = MakeSlot(operand);
538
                                        return containingSlot < 0 ? -1 : GetNullableOfTValueSlot(operandType, containingSlot, out _);
539
                                    }
540 541 542 543 544 545 546 547 548 549 550 551
                                    else if (AreNullableAndUnderlyingTypes(convertedType, operandType, out _))
                                    {
                                        // Explicit conversion of T to Nullable<T> is equivalent to new Nullable<T>(t).
                                        return getPlaceholderSlot(node);
                                    }
                                }
                                break;
                            case ConversionKind.ImplicitNullable:
                                // Implicit conversion of T to Nullable<T> is equivalent to new Nullable<T>(t).
                                if (AreNullableAndUnderlyingTypes(conv.Type, conv.Operand.Type, out _))
                                {
                                    return getPlaceholderSlot(node);
552 553 554
                                }
                                break;
                            case ConversionKind.Identity:
555 556
                            case ConversionKind.ImplicitReference:
                            case ConversionKind.ExplicitReference:
557
                            case ConversionKind.ImplicitTupleLiteral:
558
                            case ConversionKind.ExplicitTupleLiteral:
559
                            case ConversionKind.ImplicitTuple:
560
                            case ConversionKind.ExplicitTuple:
561 562
                            case ConversionKind.Boxing:
                            case ConversionKind.Unboxing:
563 564
                                if (isSupportedConversion(conv.Conversion, conv.Operand))
                                {
565 566
                                    // No need to create a slot for the boxed value (in the Boxing case) since assignment already
                                    // clones slots and there is not another scenario where creating a slot is observable.
567 568 569
                                    return MakeSlot(conv.Operand);
                                }
                                break;
570 571 572
                        }
                    }
                    break;
573
                case BoundKind.DefaultExpression:
574
                case BoundKind.ObjectCreationExpression:
575
                case BoundKind.DynamicObjectCreationExpression:
576
                case BoundKind.AnonymousObjectCreationExpression:
577
                case BoundKind.NewT:
578 579
                case BoundKind.TupleLiteral:
                case BoundKind.ConvertedTupleLiteral:
580
                    return getPlaceholderSlot(node);
581 582
                case BoundKind.ConditionalReceiver:
                    {
583
                        return _lastConditionalAccessSlot;
584
                    }
585
                default:
586 587 588
                    // If there was a placeholder local for this node, we should
                    // use the placeholder for the slot. See other cases above.
                    Debug.Assert(_placeholderLocalsOpt?.TryGetValue(node, out _) != true);
589
                    return base.MakeSlot(node);
590
            }
591 592

            return -1;
593

594 595 596 597 598 599 600 601 602
            int getPlaceholderSlot(BoundExpression expr)
            {
                if (_placeholderLocalsOpt != null && _placeholderLocalsOpt.TryGetValue(expr, out ObjectCreationPlaceholderLocal placeholder))
                {
                    return GetOrCreateSlot(placeholder);
                }
                return -1;
            }

603
            static MethodSymbol getTopLevelMethod(MethodSymbol method)
604 605 606 607 608 609 610 611 612 613 614 615
            {
                while ((object)method != null)
                {
                    var container = method.ContainingSymbol;
                    if (container.Kind == SymbolKind.NamedType)
                    {
                        return method;
                    }
                    method = container as MethodSymbol;
                }
                return null;
            }
616 617 618

            // Returns true if the nullable state from the operand of the conversion
            // can be used as is, and we can create a slot from the conversion.
619
            static bool isSupportedConversion(Conversion conversion, BoundExpression operandOpt)
620 621 622 623 624 625 626 627
            {
                // https://github.com/dotnet/roslyn/issues/32599: Allow implicit and explicit
                // conversions where the nullable state of the operand remains valid.
                switch (conversion.Kind)
                {
                    case ConversionKind.Identity:
                    case ConversionKind.DefaultOrNullLiteral:
                    case ConversionKind.ImplicitReference:
628
                    case ConversionKind.ExplicitReference:
629 630
                    case ConversionKind.Boxing:
                    case ConversionKind.Unboxing:
631
                        return true;
632
                    case ConversionKind.ImplicitTupleLiteral:
633 634
                    case ConversionKind.ExplicitTupleLiteral:
                        switch (operandOpt?.Kind)
635
                        {
636
                            case BoundKind.Conversion:
637
                                {
638 639
                                    var operandConversion = (BoundConversion)operandOpt;
                                    return isSupportedConversion(operandConversion.Conversion, operandConversion.Operand);
640
                                }
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
                            case BoundKind.ConvertedTupleLiteral:
                                {
                                    var arguments = ((BoundConvertedTupleLiteral)operandOpt).Arguments;
                                    var conversions = conversion.UnderlyingConversions;
                                    for (int i = 0; i < arguments.Length; i++)
                                    {
                                        // https://github.com/dotnet/roslyn/issues/32600: Copy nullable
                                        // state of tuple elements independently.
                                        if (!isSupportedConversion(conversions[i], (arguments[i] as BoundConversion)?.Operand))
                                        {
                                            return false;
                                        }
                                    }
                                    return true;
                                }
                            default:
                                return false;
658 659
                        }
                    case ConversionKind.ImplicitTuple:
660
                    case ConversionKind.ExplicitTuple:
661 662 663 664 665 666 667
                        // https://github.com/dotnet/roslyn/issues/32600: Copy nullable
                        // state of tuple elements independently.
                        return conversion.UnderlyingConversions.All(c => isSupportedConversion(c, null));
                    default:
                        return false;
                }
            }
668 669
        }

J
Cleanup  
Jared Parsons 已提交
670
        protected override void VisitRvalue(BoundExpression node)
671 672
        {
            Visit(node);
J
Cleanup  
Jared Parsons 已提交
673
            VisitRvalueEpilogue();
J
Jared Parsons 已提交
674 675
        }

J
Cleanup  
Jared Parsons 已提交
676 677 678 679 680 681 682
        /// <summary>
        /// The contents of this method, particularly <see cref="UseRvalueOnly"/>, are problematic when 
        /// inlined. The methods themselves are small but they end up allocating significantly larger 
        /// frames due to the use of biggish value types within them. The <see cref="VisitRvalue"/> method
        /// is used on a hot path for fluent calls and this size change is enough that it causes us
        /// to exceed our thresholds in EndToEndTests.OverflowOnFluentCall.
        /// </summary>
J
Jared Parsons 已提交
683
        [MethodImpl(MethodImplOptions.NoInlining)]
J
Cleanup  
Jared Parsons 已提交
684
        private void VisitRvalueEpilogue()
J
Jared Parsons 已提交
685
        {
686
            Unsplit();
N
Neal Gafter 已提交
687
            UseRvalueOnly(); // drop lvalue part
688 689 690
        }

        private TypeWithState VisitRvalueWithState(BoundExpression node)
691
        {
J
Cleanup  
Jared Parsons 已提交
692
            VisitRvalue(node);
693
            return ResultType;
694 695
        }

696
        private TypeWithAnnotations VisitLvalueWithAnnotations(BoundExpression node)
697 698 699
        {
            Visit(node);
            Unsplit();
N
Neal Gafter 已提交
700
            return LvalueResultType;
701 702
        }

703 704 705 706 707
        private static object GetTypeAsDiagnosticArgument(TypeSymbol typeOpt)
        {
            return typeOpt ?? (object)"<null>";
        }

708 709 710 711 712 713 714
        private enum AssignmentKind
        {
            Assignment,
            Return,
            Argument
        }

715 716 717
        /// <summary>
        /// Reports top-level nullability problem in assignment.
        /// </summary>
718 719
        private bool ReportNullableAssignmentIfNecessary(
            BoundExpression value,
720
            TypeWithAnnotations targetType,
721 722 723 724 725
            TypeWithState valueType,
            bool useLegacyWarnings,
            AssignmentKind assignmentKind = AssignmentKind.Assignment,
            Symbol target = null,
            Conversion conversion = default)
726
        {
727 728
            Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);

729 730
            if (value == null ||
                !targetType.HasType ||
731
                targetType.Type.IsValueType ||
732
                targetType.CanBeAssignedNull ||
733
                valueType.IsNotNull)
734 735 736 737 738
            {
                return false;
            }

            var unwrappedValue = SkipReferenceConversions(value);
739
            if (unwrappedValue.IsSuppressed)
740 741 742
            {
                return false;
            }
743 744

            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
745
            if (RequiresSafetyWarningWhenNullIntroduced(targetType.Type))
746
            {
N
Neal Gafter 已提交
747
                if (conversion.Kind == ConversionKind.UnsetConversionKind)
748
                    conversion = this._conversions.ClassifyImplicitConversionFromType(valueType.Type, targetType.Type, ref useSiteDiagnostics);
N
Neal Gafter 已提交
749

750 751 752 753 754 755 756 757 758
                if (conversion.IsImplicit && !conversion.IsDynamic)
                {
                    // For type parameters that cannot be annotated, the analysis must report those
                    // places where null values first sneak in, like `default`, `null`, and `GetFirstOrDefault`,
                    // as a safety diagnostic.  This is NOT one of those places.
                    return false;
                }

                useLegacyWarnings = false;
759
            }
760

761
            if (reportNullLiteralAssignmentIfNecessary(value))
762
            {
763
                return true;
764 765
            }

766 767
            if (assignmentKind == AssignmentKind.Argument)
            {
768
                ReportSafetyDiagnostic(ErrorCode.WRN_NullReferenceArgument, value.Syntax,
769 770 771 772 773
                    new FormattedSymbol(target, SymbolDisplayFormat.ShortFormat),
                    new FormattedSymbol(target.ContainingSymbol, SymbolDisplayFormat.MinimallyQualifiedFormat));
            }
            else if (useLegacyWarnings)
            {
774
                ReportNonSafetyDiagnostic(value.Syntax);
775 776 777
            }
            else
            {
778
                ReportSafetyDiagnostic(assignmentKind == AssignmentKind.Return ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullReferenceAssignment, value.Syntax);
779 780 781 782 783 784
            }

            return true;

            // Report warning converting null literal to non-nullable reference type.
            // target (e.g.: `object x = null;` or calling `void F(object y)` with `F(null)`).
785
            bool reportNullLiteralAssignmentIfNecessary(BoundExpression expr)
786
            {
787
                if (expr.ConstantValue?.IsNull != true)
788 789 790 791
                {
                    return false;
                }

792 793 794 795
                // For type parameters that cannot be annotated, the analysis must report those
                // places where null values first sneak in, like `default`, `null`, and `GetFirstOrDefault`,
                // as a safety diagnostic.  This is one of those places.
                if (useLegacyWarnings && !RequiresSafetyWarningWhenNullIntroduced(expr.Type))
796
                {
797
                    ReportNonSafetyDiagnostic(expr.Syntax);
798
                }
799
                else
800
                {
801
                    ReportSafetyDiagnostic(assignmentKind == AssignmentKind.Return ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullAsNonNullable, expr.Syntax);
802 803 804
                }
                return true;
            }
805
        }
806

807 808 809 810 811 812 813 814 815 816 817 818 819 820
        private static bool IsDefaultValue(BoundExpression expr)
        {
            switch (expr.Kind)
            {
                case BoundKind.Conversion:
                    {
                        var conversion = (BoundConversion)expr;
                        return conversion.Conversion.Kind == ConversionKind.DefaultOrNullLiteral &&
                            IsDefaultValue(conversion.Operand);
                    }
                case BoundKind.DefaultExpression:
                    return true;
                default:
                    return false;
821
            }
822 823
        }

824
        // Maybe this method can be replaced by VisitOptionalImplicitConversion or ApplyConversion
825 826
        private void ReportAssignmentWarnings(
            BoundExpression value,
827
            TypeWithAnnotations targetType,
828 829
            TypeWithState valueType,
            bool useLegacyWarnings)
C
Charles Stoner 已提交
830 831 832 833 834
        {
            Debug.Assert(value != null);

            if (this.State.Reachable)
            {
N
Neal Gafter 已提交
835
                if (!targetType.HasType || valueType.HasNullType)
C
Charles Stoner 已提交
836 837 838 839
                {
                    return;
                }

840
                // Report top-level nullability issues
841
                ReportNullableAssignmentIfNecessary(value, targetType, valueType, useLegacyWarnings, AssignmentKind.Assignment);
842 843

                // Report nested nullability issues
844
                var sourceType = valueType.Type;
845
                var destinationType = targetType.Type;
846 847
                if ((object)sourceType != null && IsNullabilityMismatch(destinationType, sourceType))
                {
848
                    ReportNullabilityMismatchInAssignment(value.Syntax, sourceType, destinationType);
849
                }
C
Charles Stoner 已提交
850 851 852
            }
        }

853 854 855 856 857
        private void ReportNullabilityMismatchInAssignment(SyntaxNode syntaxNode, object sourceType, object destinationType)
        {
            ReportSafetyDiagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, syntaxNode, sourceType, destinationType);
        }

858
        /// <summary>
C
Charles Stoner 已提交
859
        /// Update tracked value on assignment.
860
        /// </summary>
861 862
        private void TrackNullableStateForAssignment(
            BoundExpression value,
863
            TypeWithAnnotations targetType,
864 865 866
            int targetSlot,
            TypeWithState valueType,
            int valueSlot = -1)
867
        {
C
Charles Stoner 已提交
868
            Debug.Assert(value != null);
869
            Debug.Assert(!IsConditionalState);
C
Charles Stoner 已提交
870

871 872
            if (this.State.Reachable)
            {
N
Neal Gafter 已提交
873
                if (!targetType.HasType)
874 875 876
                {
                    return;
                }
877

878
                if (targetSlot <= 0 || targetSlot == valueSlot)
879
                {
C
Charles Stoner 已提交
880 881
                    return;
                }
882

C
Charles Stoner 已提交
883
                if (targetSlot >= this.State.Capacity) Normalize(ref this.State);
884

885
                var newState = valueType.State;
886
                SetStateAndTrackForFinally(ref this.State, targetSlot, newState);
C
Charles Stoner 已提交
887
                InheritDefaultState(targetSlot);
888

889 890
                // https://github.com/dotnet/roslyn/issues/33428: Can the areEquivalentTypes check be removed
                // if InheritNullableStateOfMember asserts the member is valid for target and value?
891
                if (areEquivalentTypes(targetType, valueType))
892
                {
893 894 895
                    // https://github.com/dotnet/roslyn/issues/31395: We should copy all tracked state from `value` regardless of
                    // BoundNode type but we'll need to handle cycles (see NullableReferenceTypesTests.Members_FieldCycle_07).
                    // For now, we copy a limited set of BoundNode types that shouldn't contain cycles.
896
                    if (((targetType.Type.IsReferenceType || targetType.TypeKind == TypeKind.TypeParameter) && (isSupportedReferenceTypeValue(value) || targetType.Type.IsAnonymousType)) ||
897
                        targetType.IsNullableType())
898
                    {
899 900 901
                        // Nullable<T> is handled here rather than in InheritNullableStateOfTrackableStruct since that
                        // method only clones auto-properties (see https://github.com/dotnet/roslyn/issues/29619).
                        // When that issue is fixed, Nullable<T> should be handled there instead.
C
Charles Stoner 已提交
902
                        if (valueSlot > 0)
903
                        {
C
Charles Stoner 已提交
904
                            InheritNullableStateOfTrackableType(targetSlot, valueSlot, skipSlot: targetSlot);
905
                        }
906
                    }
907
                    else if (EmptyStructTypeCache.IsTrackableStructType(targetType.Type))
908
                    {
909
                        InheritNullableStateOfTrackableStruct(targetType.Type, targetSlot, valueSlot, isDefaultValue: IsDefaultValue(value), skipSlot: targetSlot);
910 911
                    }
                }
912
            }
913

914
            static bool areEquivalentTypes(TypeWithAnnotations target, TypeWithState assignedValue) =>
915
                target.Type.Equals(assignedValue.Type, TypeCompareKind.AllIgnoreOptions);
916 917 918 919 920 921 922 923 924 925 926

            // https://github.com/dotnet/roslyn/issues/31395: See comment above.
            static bool isSupportedReferenceTypeValue(BoundExpression value)
            {
                switch (value.Kind)
                {
                    case BoundKind.Conversion:
                        return isSupportedReferenceTypeValue(((BoundConversion)value).Operand);
                    case BoundKind.ObjectCreationExpression:
                    case BoundKind.AnonymousObjectCreationExpression:
                    case BoundKind.DynamicObjectCreationExpression:
927
                    case BoundKind.NewT:
928 929 930 931 932
                        return true;
                    default:
                        return false;
                }
            }
933 934
        }

935
        private void ReportNonSafetyDiagnostic(SyntaxNode syntax)
936
        {
937
            ReportNonSafetyDiagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, syntax);
938 939
        }

940 941 942 943 944 945 946 947 948 949 950
        private void ReportNonSafetyDiagnostic(ErrorCode errorCode, SyntaxNode syntax)
        {
            // All warnings should be in the `#pragma warning ... nullable` set.
            Debug.Assert(!ErrorFacts.NullableFlowAnalysisSafetyWarnings.Contains(MessageProvider.Instance.GetIdForErrorCode((int)errorCode)));
            Debug.Assert(ErrorFacts.NullableFlowAnalysisNonSafetyWarnings.Contains(MessageProvider.Instance.GetIdForErrorCode((int)errorCode)));
#pragma warning disable CS0618
            ReportDiagnostic(errorCode, syntax);
#pragma warning restore CS0618
        }

        private void ReportSafetyDiagnostic(ErrorCode errorCode, SyntaxNode syntaxNode, params object[] arguments)
951
        {
952
            // All warnings should be in the `#pragma warning ... nullable` set.
953 954 955 956 957 958
            Debug.Assert(ErrorFacts.NullableFlowAnalysisSafetyWarnings.Contains(MessageProvider.Instance.GetIdForErrorCode((int)errorCode)));
            Debug.Assert(!ErrorFacts.NullableFlowAnalysisNonSafetyWarnings.Contains(MessageProvider.Instance.GetIdForErrorCode((int)errorCode)));
#pragma warning disable CS0618
            ReportDiagnostic(errorCode, syntaxNode, arguments);
#pragma warning restore CS0618
        }
959

960 961 962
        [Obsolete("Use ReportSafetyDiagnostic/ReportNonSafetyDiagnostic instead", error: false)]
        private void ReportDiagnostic(ErrorCode errorCode, SyntaxNode syntaxNode, params object[] arguments)
        {
963 964
            Debug.Assert(!IsConditionalState);
            if (this.State.Reachable && !_disableDiagnostics)
965 966 967
            {
                Diagnostics.Add(errorCode, syntaxNode.GetLocation(), arguments);
            }
968 969
        }

C
Charles Stoner 已提交
970
        private void InheritNullableStateOfTrackableStruct(TypeSymbol targetType, int targetSlot, int valueSlot, bool isDefaultValue, int skipSlot = -1)
971 972 973 974
        {
            Debug.Assert(targetSlot > 0);
            Debug.Assert(EmptyStructTypeCache.IsTrackableStructType(targetType));

C
Charles Stoner 已提交
975 976 977 978 979
            if (skipSlot < 0)
            {
                skipSlot = targetSlot;
            }

980
            // https://github.com/dotnet/roslyn/issues/29619 Handle properties not backed by fields.
981
            // See ModifyMembers_StructPropertyNoBackingField and PropertyCycle_Struct tests.
982 983
            foreach (var field in _emptyStructTypeCache.GetStructInstanceFields(targetType))
            {
C
Charles Stoner 已提交
984
                InheritNullableStateOfMember(targetSlot, valueSlot, field, isDefaultValue: isDefaultValue, skipSlot);
985 986 987
            }
        }

C
Charles Stoner 已提交
988 989
        // 'skipSlot' is the original target slot that should be skipped in case of cycles.
        private void InheritNullableStateOfMember(int targetContainerSlot, int valueContainerSlot, Symbol member, bool isDefaultValue, int skipSlot)
990
        {
991
            Debug.Assert(targetContainerSlot > 0);
C
Charles Stoner 已提交
992
            Debug.Assert(skipSlot > 0);
993
            // https://github.com/dotnet/roslyn/issues/33428: Ensure member is valid for target and value.
994

995
            TypeWithAnnotations fieldOrPropertyType = member.GetTypeOrReturnType();
996

997 998 999
            // Nullable<T> is handled here rather than in InheritNullableStateOfTrackableStruct since that
            // method only clones auto-properties (see https://github.com/dotnet/roslyn/issues/29619).
            // When that issue is fixed, Nullable<T> should be handled there instead.
1000 1001 1002
            if (fieldOrPropertyType.Type.IsReferenceType ||
                fieldOrPropertyType.TypeKind == TypeKind.TypeParameter ||
                fieldOrPropertyType.IsNullableType())
1003
            {
1004
                int targetMemberSlot = GetOrCreateSlot(member, targetContainerSlot);
C
Charles Stoner 已提交
1005 1006
                Debug.Assert(targetMemberSlot > 0);

1007
                NullableFlowState value = isDefaultValue ? NullableFlowState.MaybeNull : fieldOrPropertyType.ToTypeWithState().State;
C
Charles Stoner 已提交
1008 1009
                int valueMemberSlot = -1;

1010
                if (valueContainerSlot > 0)
1011
                {
C
Charles Stoner 已提交
1012 1013 1014 1015 1016
                    valueMemberSlot = VariableSlot(member, valueContainerSlot);
                    if (valueMemberSlot == skipSlot)
                    {
                        return;
                    }
1017 1018
                    value = valueMemberSlot > 0 && valueMemberSlot < this.State.Capacity ?
                        this.State[valueMemberSlot] :
1019
                        NullableFlowState.NotNull;
1020 1021
                }

1022
                SetStateAndTrackForFinally(ref this.State, targetMemberSlot, value);
C
Charles Stoner 已提交
1023
                if (valueMemberSlot > 0)
1024
                {
C
Charles Stoner 已提交
1025
                    InheritNullableStateOfTrackableType(targetMemberSlot, valueMemberSlot, skipSlot);
1026 1027
                }
            }
1028
            else if (EmptyStructTypeCache.IsTrackableStructType(fieldOrPropertyType.Type))
1029
            {
C
Misc.  
Charles Stoner 已提交
1030
                int targetMemberSlot = GetOrCreateSlot(member, targetContainerSlot);
1031
                if (targetMemberSlot > 0)
1032
                {
C
Charles Stoner 已提交
1033 1034
                    int valueMemberSlot = (valueContainerSlot > 0) ? GetOrCreateSlot(member, valueContainerSlot) : -1;
                    if (valueMemberSlot == skipSlot)
1035
                    {
C
Charles Stoner 已提交
1036
                        return;
1037
                    }
1038
                    InheritNullableStateOfTrackableStruct(fieldOrPropertyType.Type, targetMemberSlot, valueMemberSlot, isDefaultValue: isDefaultValue, skipSlot);
1039 1040 1041 1042
                }
            }
        }

1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
        /// <summary>
        /// Whenever setting the state of a variable, and that variable is not declared at the point the state is being set,
        /// and the new state might be <see cref="NullableFlowState.MaybeNull"/>, this method should be called to perform the
        /// state setting and to ensure the mutation is visible outside the finally block when the mutation occurs in a finally block.
        /// </summary>
        private void SetStateAndTrackForFinally(ref LocalState state, int slot, NullableFlowState newState)
        {
            state[slot] = newState;
            if (newState == NullableFlowState.MaybeNull && _tryState.HasValue)
            {
                var tryState = _tryState.Value;
                tryState[slot] = NullableFlowState.MaybeNull;
                _tryState = tryState;
            }
        }

1059
        private void InheritDefaultState(int targetSlot)
1060 1061 1062
        {
            Debug.Assert(targetSlot > 0);

1063 1064
            // Reset the state of any members of the target.
            for (int slot = targetSlot + 1; slot < nextVariableSlot; slot++)
1065
            {
1066 1067
                var variable = variableBySlot[slot];
                if (variable.ContainingSlot != targetSlot)
1068 1069 1070
                {
                    continue;
                }
1071
                SetStateAndTrackForFinally(ref this.State, slot, variable.Symbol.GetTypeOrReturnType().ToTypeWithState().State);
1072 1073 1074
                InheritDefaultState(slot);
            }
        }
1075

C
Charles Stoner 已提交
1076
        private void InheritNullableStateOfTrackableType(int targetSlot, int valueSlot, int skipSlot)
1077 1078 1079
        {
            Debug.Assert(targetSlot > 0);
            Debug.Assert(valueSlot > 0);
1080

1081
            // Clone the state for members that have been set on the value.
C
Charles Stoner 已提交
1082
            for (int slot = valueSlot + 1; slot < nextVariableSlot; slot++)
1083 1084 1085
            {
                var variable = variableBySlot[slot];
                if (variable.ContainingSlot != valueSlot)
1086 1087 1088
                {
                    continue;
                }
1089
                var member = variable.Symbol;
C
Misc.  
Charles Stoner 已提交
1090
                Debug.Assert(member.Kind == SymbolKind.Field || member.Kind == SymbolKind.Property || member.Kind == SymbolKind.Event);
C
Charles Stoner 已提交
1091
                InheritNullableStateOfMember(targetSlot, valueSlot, member, isDefaultValue: false, skipSlot);
1092 1093 1094
            }
        }

1095 1096
        private TypeSymbol GetSlotType(int slot)
        {
1097
            return VariableTypeWithAnnotations(variableBySlot[slot].Symbol).Type;
1098 1099
        }

A
Andy Gocke 已提交
1100
        protected override LocalState TopState()
1101
        {
1102
            var state = new LocalState(reachable: true, new ArrayBuilder<NullableFlowState>(nextVariableSlot));
1103 1104
            Populate(ref state, start: 0);
            return state;
1105 1106 1107 1108
        }

        protected override LocalState UnreachableState()
        {
1109
            return new LocalState(reachable: false, null);
1110 1111
        }

A
Andy Gocke 已提交
1112
        protected override LocalState ReachableBottomState()
1113
        {
1114
            // Create a reachable state in which all variables are known to be non-null.
1115 1116 1117
            var builder = new ArrayBuilder<NullableFlowState>(nextVariableSlot);
            builder.AddMany(NullableFlowState.NotNull, nextVariableSlot);
            return new LocalState(reachable: true, builder);
1118 1119
        }

1120
        private void EnterParameters()
1121
        {
C
Chris Sienkiewicz 已提交
1122 1123 1124 1125 1126 1127 1128
            var methodSymbol = _symbol as MethodSymbol;
            if (methodSymbol is null)
            {
                return;
            }

            var methodParameters = methodSymbol.Parameters;
1129 1130 1131 1132
            var signatureParameters = _useMethodSignatureParameterTypes ? _methodSignatureOpt.Parameters : methodParameters;
            Debug.Assert(signatureParameters.Length == methodParameters.Length);
            int n = methodParameters.Length;
            for (int i = 0; i < n; i++)
1133
            {
1134
                var parameter = methodParameters[i];
1135
                var parameterType = signatureParameters[i].TypeWithAnnotations;
1136
                EnterParameter(parameter, parameterType);
1137 1138 1139
            }
        }

1140
        private void EnterParameter(ParameterSymbol parameter, TypeWithAnnotations parameterType)
1141
        {
F
Fredric Silberberg 已提交
1142
            _variableTypes[parameter] = parameterType;
1143
            int slot = GetOrCreateSlot(parameter);
F
Fredric Silberberg 已提交
1144

1145 1146
            Debug.Assert(!IsConditionalState);
            if (slot > 0 && parameter.RefKind != RefKind.Out)
1147
            {
1148
                if (EmptyStructTypeCache.IsTrackableStructType(parameterType.Type))
1149
                {
1150
                    InheritNullableStateOfTrackableStruct(
1151
                        parameterType.Type,
1152 1153
                        slot,
                        valueSlot: -1,
C
Charles Stoner 已提交
1154
                        isDefaultValue: parameter.ExplicitDefaultConstantValue?.IsNull == true);
1155 1156 1157 1158 1159 1160
                }
            }
        }

        public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node)
        {
1161
            var resultType = VisitRvalueWithState(node.Expression);
N
Neal Gafter 已提交
1162
            VisitPattern(node.Expression, resultType, node.Pattern);
1163
            SetNotNullResult(node);
1164
            return node;
1165 1166
        }

1167 1168 1169
        /// <summary>
        /// Examples:
        /// `x is Point p`
1170
        /// `switch (x) ... case Point p:` // https://github.com/dotnet/roslyn/issues/29873 not yet handled
1171 1172 1173 1174 1175
        ///
        /// If the expression is trackable, we'll return with different null-states for that expression in the two conditional states.
        /// If the pattern is a `var` pattern, we'll also have re-inferred the `var` type with nullability and
        /// updated the state for that declared local.
        /// </summary>
1176
        private void VisitPattern(BoundExpression expression, TypeWithState expressionResultType, BoundPattern pattern)
1177
        {
1178 1179 1180
            NullableFlowState whenTrue = expressionResultType.State;
            NullableFlowState whenFalse = expressionResultType.State;

1181 1182 1183
            switch (pattern.Kind)
            {
                case BoundKind.ConstantPattern:
1184 1185 1186
                    // If the constant is null, the pattern tells us the expression is null.
                    // If the constant is not null, the pattern tells us the expression is not null.
                    // If there is no constant, we don't know.
1187 1188 1189
                    switch (((BoundConstantPattern)pattern).ConstantValue?.IsNull)
                    {
                        case true:
1190 1191
                            whenTrue = NullableFlowState.MaybeNull;
                            whenFalse = NullableFlowState.NotNull;
1192 1193
                            break;
                        case false:
1194 1195
                            whenTrue = NullableFlowState.NotNull;
                            whenFalse = NullableFlowState.MaybeNull;
1196 1197
                            break;
                    }
1198 1199 1200 1201
                    break;
                case BoundKind.DeclarationPattern:
                    var declarationPattern = (BoundDeclarationPattern)pattern;
                    if (declarationPattern.IsVar)
1202
                    {
1203 1204 1205 1206 1207
                        // The result type and state of the expression carry into the variable declared by var pattern
                        Symbol variable = declarationPattern.Variable;
                        // No variable declared for discard (`i is var _`)
                        if ((object)variable != null)
                        {
N
Neal Gafter 已提交
1208
                            var variableType = expressionResultType.ToTypeWithAnnotations();
1209 1210
                            _variableTypes[variable] = variableType;
                            TrackNullableStateForAssignment(expression, variableType, GetOrCreateSlot(variable), expressionResultType);
1211
                        }
N
Neal Gafter 已提交
1212

1213
                        whenFalse = NullableFlowState.NotNull; // whenFalse is unreachable
1214 1215 1216
                    }
                    else
                    {
1217
                        whenTrue = NullableFlowState.NotNull; // the pattern tells us the expression is not null
1218 1219
                    }
                    break;
1220 1221 1222
                default:
                    // https://github.com/dotnet/roslyn/issues/29909 : handle other kinds of patterns
                    break;
1223
            }
1224 1225

            Debug.Assert(!IsConditionalState);
1226

1227 1228 1229
            // Create slot since EnsureCapacity should be
            // called on all fields and that is simpler if state is limited to this.State.
            int mainSlot = MakeSlot(expression);
1230

1231
            base.VisitPattern(pattern);
1232 1233
            Debug.Assert(IsConditionalState);

1234
            if (mainSlot > 0)
1235
            {
1236 1237
                SetStateAndTrackForFinally(ref this.StateWhenTrue, mainSlot, whenTrue);
                SetStateAndTrackForFinally(ref this.StateWhenFalse, mainSlot, whenFalse);
1238 1239
            }

1240
            if (whenTrue.IsNotNull() || whenFalse.IsNotNull())
1241 1242 1243 1244 1245
            {
                var slotBuilder = ArrayBuilder<int>.GetInstance();
                GetSlotsToMarkAsNotNullable(expression, slotBuilder);

                // Set all nested conditional slots. For example in a?.b?.c we'll set a, b, and c.
1246
                if (whenTrue.IsNotNull())
1247
                {
1248
                    MarkSlotsAsNotNull(slotBuilder, ref StateWhenTrue);
1249
                }
1250
                else if (whenFalse.IsNotNull())
1251
                {
1252
                    MarkSlotsAsNotNull(slotBuilder, ref StateWhenFalse);
1253 1254 1255
                }

                slotBuilder.Free();
1256
            }
1257 1258 1259 1260 1261
        }

        protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement node)
        {
            Debug.Assert(!IsConditionalState);
1262 1263 1264 1265 1266 1267 1268

            BoundExpression expr = node.ExpressionOpt;
            if (expr == null)
            {
                return null;
            }

1269 1270
            // Should not convert to method return type when inferring return type (when _returnTypesOpt != null).
            if (_returnTypesOpt == null &&
1271
                TryGetReturnType(out TypeWithAnnotations returnType))
1272
            {
1273 1274 1275 1276 1277 1278 1279 1280 1281
                if (node.RefKind == RefKind.None)
                {
                    VisitOptionalImplicitConversion(expr, returnType, useLegacyWarnings: false, AssignmentKind.Return);
                }
                else
                {
                    // return ref expr;
                    VisitRefExpression(expr, returnType);
                }
1282 1283
            }
            else
1284
            {
N
Neal Gafter 已提交
1285
                var result = VisitRvalueWithState(expr);
1286 1287
                if (_returnTypesOpt != null)
                {
N
Neal Gafter 已提交
1288
                    _returnTypesOpt.Add((node, result.ToTypeWithAnnotations()));
1289
                }
1290
            }
1291 1292 1293 1294

            return null;
        }

1295
        private TypeWithState VisitRefExpression(BoundExpression expr, TypeWithAnnotations destinationType)
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316
        {
            Visit(expr);
            TypeWithState resultType = ResultType;
            if (!expr.IsSuppressed && RemoveConversion(expr, includeExplicitConversions: false).expression.Kind != BoundKind.ThrowExpression)
            {
                var lvalueResultType = LvalueResultType;
                if (IsNullabilityMismatch(lvalueResultType, destinationType))
                {
                    // declared types must match
                    ReportNullabilityMismatchInAssignment(expr.Syntax, lvalueResultType, destinationType);
                }
                else
                {
                    // types match, but state would let a null in
                    ReportNullableAssignmentIfNecessary(expr, destinationType, resultType, useLegacyWarnings: false);
                }
            }

            return resultType;
        }

1317
        private bool TryGetReturnType(out TypeWithAnnotations type)
1318
        {
1319
            var method = (MethodSymbol)_symbol;
1320
            var returnType = (_useMethodSignatureReturnType ? _methodSignatureOpt : method).ReturnTypeWithAnnotations;
1321
            Debug.Assert((object)returnType != LambdaSymbol.ReturnTypeIsBeingInferred);
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334

            if (returnType.SpecialType == SpecialType.System_Void)
            {
                type = default;
                return false;
            }

            if (!method.IsAsync)
            {
                type = returnType;
                return true;
            }

1335 1336
            if (method.IsGenericTaskReturningAsync(compilation))
            {
1337
                type = ((NamedTypeSymbol)returnType.Type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.Single();
1338
                return true;
1339 1340
            }

1341 1342
            type = default;
            return false;
1343 1344
        }

1345
        private static bool RequiresSafetyWarningWhenNullIntroduced(TypeSymbol typeOpt)
1346
        {
1347
            return typeOpt?.IsTypeParameterDisallowingAnnotation() == true && !typeOpt.IsNullableTypeOrTypeParameter();
1348 1349
        }

1350 1351
        public override BoundNode VisitLocal(BoundLocal node)
        {
1352 1353 1354
            var local = node.LocalSymbol;
            int slot = GetOrCreateSlot(local);
            var type = GetDeclaredLocalResult(local);
1355
            SetResult(GetAdjustedResult(type, slot), type);
1356 1357 1358 1359 1360
            return null;
        }

        public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node)
        {
1361 1362 1363 1364
            var local = node.LocalSymbol;
            int slot = GetOrCreateSlot(local);

            var initializer = node.InitializerOpt;
1365
            if (initializer is null)
1366
            {
1367 1368
                return null;
            }
1369

1370
            TypeWithAnnotations type = local.TypeWithAnnotations;
1371 1372 1373 1374 1375 1376
            TypeWithState valueType;
            if (local.IsRef)
            {
                valueType = VisitRefExpression(initializer, type);
            }
            else
1377
            {
1378
                bool inferredType = node.DeclaredType.InferredType;
1379 1380
                valueType = VisitOptionalImplicitConversion(initializer, targetTypeOpt: inferredType ? default : type, useLegacyWarnings: true, AssignmentKind.Assignment);
                if (inferredType)
1381
                {
1382 1383
                    if (valueType.HasNullType)
                    {
1384
                        Debug.Assert(type.Type.IsErrorType());
1385 1386
                        valueType = type.ToTypeWithState();
                    }
1387

N
Neal Gafter 已提交
1388
                    type = valueType.ToTypeWithAnnotations();
1389 1390
                    _variableTypes[local] = type;
                }
1391
            }
1392

1393
            TrackNullableStateForAssignment(initializer, type, slot, valueType, MakeSlot(initializer));
1394
            return null;
1395 1396 1397 1398 1399
        }

        protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node)
        {
            Debug.Assert(!IsConditionalState);
1400
            bool wasReachable = this.State.Reachable;
1401
            ResultType = _invalidType;
1402
            _ = base.VisitExpressionWithoutStackGuard(node);
1403
            TypeWithState resultType = ResultType;
1404

1405
#if DEBUG
1406
            // Verify Visit method set _result.
1407 1408
            Debug.Assert((object)resultType.Type != _invalidType.Type);
            Debug.Assert(AreCloseEnough(resultType.Type, node.Type));
1409
#endif
1410
            if (node.IsSuppressed || node.HasAnyErrors || !wasReachable)
1411
            {
1412 1413
                resultType = resultType.WithNotNullState();
                SetResult(resultType, LvalueResultType);
1414
            }
1415

1416
            _callbackOpt?.Invoke(node, resultType.ToTypeWithAnnotations());
1417
            return null;
1418 1419
        }

1420 1421 1422 1423
#if DEBUG
        // For asserts only.
        private static bool AreCloseEnough(TypeSymbol typeA, TypeSymbol typeB)
        {
1424 1425 1426 1427 1428 1429
            if ((object)typeA == typeB)
            {
                return true;
            }
            if (typeA is null || typeB is null)
            {
1430
                return typeA?.IsErrorType() != false && typeB?.IsErrorType() != false;
1431
            }
1432 1433
            return canIgnoreAnyType(typeA) ||
                canIgnoreAnyType(typeB) ||
1434
                typeA.Equals(typeB, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamicAndTupleNames); // Ignore TupleElementNames (see https://github.com/dotnet/roslyn/issues/23651).
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445

            bool canIgnoreAnyType(TypeSymbol type)
            {
                return (object)type.VisitType((t, unused1, unused2) => canIgnoreType(t), (object)null) != null;
            }
            bool canIgnoreType(TypeSymbol type)
            {
                return type.IsErrorType() || type.IsDynamic() || type.HasUseSiteError || (type.IsAnonymousType && canIgnoreAnonymousType((NamedTypeSymbol)type));
            }
            bool canIgnoreAnonymousType(NamedTypeSymbol type)
            {
1446
                return AnonymousTypeManager.GetAnonymousTypePropertyTypesWithAnnotations(type).Any(t => canIgnoreAnyType(t.Type));
1447
            }
1448 1449 1450 1451
        }
#endif

        protected override void VisitStatement(BoundStatement statement)
1452
        {
1453
            ResultType = _invalidType;
1454
            base.VisitStatement(statement);
1455
            ResultType = _invalidType;
1456
        }
1457

1458 1459
        public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
        {
1460
            Debug.Assert(!IsConditionalState);
1461
            var arguments = node.Arguments;
1462 1463
            var argumentResults = VisitArguments(node, arguments, node.ArgumentRefKindsOpt, node.Constructor, node.ArgsToParamsOpt, node.Expanded);
            VisitObjectOrDynamicObjectCreation(node, arguments, argumentResults, node.InitializerExpressionOpt);
1464 1465 1466
            return null;
        }

1467 1468 1469
        private void VisitObjectOrDynamicObjectCreation(
            BoundExpression node,
            ImmutableArray<BoundExpression> arguments,
1470
            ImmutableArray<VisitResult> argumentResults,
1471
            BoundExpression initializerOpt)
1472
        {
1473 1474 1475
            Debug.Assert(node.Kind == BoundKind.ObjectCreationExpression ||
                node.Kind == BoundKind.DynamicObjectCreationExpression ||
                node.Kind == BoundKind.NewT);
1476
            var argumentTypes = argumentResults.SelectAsArray(ar => ar.RValueType);
1477 1478 1479

            int slot = -1;
            TypeSymbol type = node.Type;
1480
            NullableFlowState resultState = NullableFlowState.NotNull;
1481
            if ((object)type != null)
1482
            {
1483 1484
                slot = GetOrCreateObjectCreationPlaceholderSlot(node);
                if (slot > 0)
1485
                {
1486 1487 1488 1489
                    var constructor = (node as BoundObjectCreationExpression)?.Constructor;
                    bool isDefaultValueTypeConstructor = constructor?.IsDefaultValueTypeConstructor() == true;

                    if (EmptyStructTypeCache.IsTrackableStructType(type))
1490
                    {
1491
                        this.State[slot] = NullableFlowState.NotNull;
1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
                        var tupleType = constructor?.ContainingType as TupleTypeSymbol;
                        if ((object)tupleType != null && !isDefaultValueTypeConstructor)
                        {
                            // new System.ValueTuple<T1, ..., TN>(e1, ..., eN)
                            TrackNullableStateOfTupleElements(slot, tupleType, arguments, argumentTypes, useRestField: true);
                        }
                        else
                        {
                            InheritNullableStateOfTrackableStruct(
                                type,
                                slot,
                                valueSlot: -1,
C
Charles Stoner 已提交
1504
                                isDefaultValue: isDefaultValueTypeConstructor);
1505
                        }
1506
                    }
1507
                    else if (type.IsNullableType())
1508
                    {
1509
                        if (isDefaultValueTypeConstructor)
1510
                        {
1511 1512 1513 1514 1515 1516
                            // a nullable value type created with its default constructor is by definition null
                            resultState = NullableFlowState.MaybeNull;
                        }
                        else if (constructor.ParameterCount == 1)
                        {
                            // if we deal with one-parameter ctor that takes underlying, then Value state is inferred from the argument.
1517 1518
                            var parameterType = constructor.ParameterTypesWithAnnotations[0];
                            if (AreNullableAndUnderlyingTypes(type, parameterType.Type, out TypeWithAnnotations underlyingType))
1519 1520 1521
                            {
                                TrackNullableStateOfNullableValue(node, arguments[0], type, underlyingType);
                            }
1522 1523
                        }
                    }
1524
                }
1525 1526 1527 1528
            }

            if (initializerOpt != null)
            {
1529
                VisitObjectCreationInitializer(null, slot, initializerOpt);
1530 1531
            }

1532
            ResultType = new TypeWithState(type, resultState);
1533 1534 1535 1536 1537 1538 1539 1540 1541 1542
        }

        private void VisitObjectCreationInitializer(Symbol containingSymbol, int containingSlot, BoundExpression node)
        {
            switch (node.Kind)
            {
                case BoundKind.ObjectInitializerExpression:
                    foreach (var initializer in ((BoundObjectInitializerExpression)node).Initializers)
                    {
                        switch (initializer.Kind)
1543
                        {
1544
                            case BoundKind.AssignmentOperator:
1545
                                VisitObjectElementInitializer(containingSlot, (BoundAssignmentOperator)initializer);
1546 1547
                                break;
                            default:
J
Cleanup  
Jared Parsons 已提交
1548
                                VisitRvalue(initializer);
1549
                                break;
1550 1551
                        }
                    }
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561
                    break;
                case BoundKind.CollectionInitializerExpression:
                    foreach (var initializer in ((BoundCollectionInitializerExpression)node).Initializers)
                    {
                        switch (initializer.Kind)
                        {
                            case BoundKind.CollectionElementInitializer:
                                VisitCollectionElementInitializer((BoundCollectionElementInitializer)initializer);
                                break;
                            default:
J
Cleanup  
Jared Parsons 已提交
1562
                                VisitRvalue(initializer);
1563 1564 1565 1566 1567
                                break;
                        }
                    }
                    break;
                default:
1568
                    TypeWithState resultType = VisitRvalueWithState(node);
1569
                    Debug.Assert((object)containingSymbol != null);
1570 1571
                    if ((object)containingSymbol != null)
                    {
1572
                        var type = containingSymbol.GetTypeOrReturnType();
1573 1574
                        ReportAssignmentWarnings(node, type, resultType, useLegacyWarnings: false);
                        TrackNullableStateForAssignment(node, type, containingSlot, resultType, MakeSlot(node));
1575
                    }
1576
                    break;
1577
            }
1578
        }
1579

1580
        private void VisitObjectElementInitializer(int containingSlot, BoundAssignmentOperator node)
1581 1582 1583 1584 1585 1586 1587 1588 1589 1590
        {
            var left = node.Left;
            switch (left.Kind)
            {
                case BoundKind.ObjectInitializerMember:
                    {
                        var objectInitializer = (BoundObjectInitializerMember)left;
                        var symbol = objectInitializer.MemberSymbol;
                        if (!objectInitializer.Arguments.IsDefaultOrEmpty)
                        {
1591
                            VisitArguments(objectInitializer, objectInitializer.Arguments, objectInitializer.ArgumentRefKindsOpt, (PropertySymbol)symbol, objectInitializer.ArgsToParamsOpt, objectInitializer.Expanded);
1592
                        }
1593 1594 1595 1596 1597
                        if ((object)symbol != null)
                        {
                            int slot = (containingSlot < 0) ? -1 : GetOrCreateSlot(symbol, containingSlot);
                            VisitObjectCreationInitializer(symbol, slot, node.Right);
                        }
1598 1599 1600
                    }
                    break;
                default:
1601
                    Visit(node);
1602 1603 1604
                    break;
            }
        }
1605

1606 1607
        private new void VisitCollectionElementInitializer(BoundCollectionElementInitializer node)
        {
1608 1609
            // Note: we analyze even omitted calls
            VisitArguments(node, node.Arguments, refKindsOpt: default, node.AddMethod, node.ArgsToParamsOpt, node.Expanded);
1610
            SetUnknownResultNullability();
1611 1612
        }

1613
        private void SetNotNullResult(BoundExpression node)
1614
        {
1615
            ResultType = new TypeWithState(node.Type, NullableFlowState.NotNull);
1616 1617
        }

1618
        private int GetOrCreateObjectCreationPlaceholderSlot(BoundExpression node)
1619 1620
        {
            ObjectCreationPlaceholderLocal placeholder;
1621
            if (_placeholderLocalsOpt == null)
1622
            {
1623
                _placeholderLocalsOpt = PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal>.GetInstance();
1624 1625 1626 1627
                placeholder = null;
            }
            else
            {
1628
                _placeholderLocalsOpt.TryGetValue(node, out placeholder);
1629 1630
            }

N
Neal Gafter 已提交
1631
            if (placeholder is null)
1632
            {
1633
                placeholder = new ObjectCreationPlaceholderLocal(_symbol, node);
1634
                _placeholderLocalsOpt.Add(node, placeholder);
1635 1636
            }

1637
            return GetOrCreateSlot(placeholder);
1638 1639 1640 1641 1642
        }

        public override BoundNode VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node)
        {
            Debug.Assert(!IsConditionalState);
1643

1644 1645
            var anonymousType = (NamedTypeSymbol)node.Type;
            Debug.Assert(anonymousType.IsAnonymousType);
1646

1647
            var arguments = node.Arguments;
1648 1649 1650
            var argumentTypes = arguments.SelectAsArray((arg, self) =>
                self.VisitRvalueWithState(arg), this);
            var argumentsWithAnnotations = argumentTypes.SelectAsArray((arg, self) =>
N
Neal Gafter 已提交
1651
                arg.ToTypeWithAnnotations(), this);
1652

1653
            if (argumentsWithAnnotations.All(argType => argType.HasType))
1654
            {
1655
                anonymousType = AnonymousTypeManager.ConstructAnonymousTypeSymbol(anonymousType, argumentsWithAnnotations);
1656 1657
                int receiverSlot = GetOrCreateObjectCreationPlaceholderSlot(node);
                for (int i = 0; i < arguments.Length; i++)
1658
                {
1659 1660 1661
                    var argument = arguments[i];
                    var argumentType = argumentTypes[i];
                    var property = AnonymousTypeManager.GetAnonymousTypeProperty(anonymousType, i);
1662
                    TrackNullableStateForAssignment(argument, property.TypeWithAnnotations, GetOrCreateSlot(property, receiverSlot), argumentType, MakeSlot(argument));
1663
                }
1664
            }
1665

1666
            ResultType = new TypeWithState(anonymousType, NullableFlowState.NotNull);
1667
            return null;
1668 1669 1670 1671
        }

        public override BoundNode VisitArrayCreation(BoundArrayCreation node)
        {
1672 1673
            foreach (var expr in node.Bounds)
            {
J
Cleanup  
Jared Parsons 已提交
1674
                VisitRvalue(expr);
1675 1676
            }
            TypeSymbol resultType = (node.InitializerOpt == null) ? node.Type : VisitArrayInitializer(node);
1677
            ResultType = new TypeWithState(resultType, NullableFlowState.NotNull);
1678
            return null;
1679 1680
        }

1681
        private ArrayTypeSymbol VisitArrayInitializer(BoundArrayCreation node)
1682
        {
1683 1684 1685 1686 1687 1688 1689 1690
            BoundArrayInitialization initialization = node.InitializerOpt;
            var expressions = ArrayBuilder<BoundExpression>.GetInstance(initialization.Initializers.Length);
            GetArrayElements(initialization, expressions);
            int n = expressions.Count;

            // Consider recording in the BoundArrayCreation
            // whether the array was implicitly typed, rather than relying on syntax.
            bool isInferred = node.Syntax.Kind() == SyntaxKind.ImplicitArrayCreationExpression;
1691
            var arrayType = (ArrayTypeSymbol)node.Type;
1692
            var elementType = arrayType.ElementTypeWithAnnotations;
1693 1694 1695 1696 1697 1698
            if (!isInferred)
            {
                for (int i = 0; i < n; i++)
                {
                    _ = VisitOptionalImplicitConversion(expressions[i], elementType, useLegacyWarnings: false, AssignmentKind.Assignment);
                }
1699

1700
                ResultType = _invalidType;
1701 1702 1703 1704
                return arrayType;
            }

            var conversions = ArrayBuilder<Conversion>.GetInstance(n);
1705
            var resultTypes = ArrayBuilder<TypeWithState>.GetInstance(n);
1706
            for (int i = 0; i < n; i++)
1707
            {
1708 1709 1710 1711
                // collect expressions, conversions and result types
                (BoundExpression expression, Conversion conversion) = RemoveConversion(expressions[i], includeExplicitConversions: false);
                expressions[i] = expression;
                conversions.Add(conversion);
1712
                var resultType = VisitRvalueWithState(expression);
1713
                resultTypes.Add(resultType);
1714 1715
            }

1716 1717
            var placeholderBuilder = ArrayBuilder<BoundExpression>.GetInstance(n);
            for (int i = 0; i < n; i++)
1718
            {
N
Neal Gafter 已提交
1719
                placeholderBuilder.Add(CreatePlaceholderIfNecessary(expressions[i], resultTypes[i].ToTypeWithAnnotations()));
1720 1721 1722 1723 1724 1725 1726
            }
            var placeholders = placeholderBuilder.ToImmutableAndFree();

            TypeSymbol bestType = null;
            if (!node.HasErrors)
            {
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
1727
                bestType = BestTypeInferrer.InferBestType(placeholders, _conversions, ref useSiteDiagnostics);
1728 1729
            }

1730
            TypeWithAnnotations inferredType;
1731 1732 1733 1734 1735 1736
            if (bestType is null)
            {
                inferredType = elementType.SetUnknownNullabilityForReferenceTypes();
            }
            else
            {
1737
                inferredType = TypeWithAnnotations.Create(bestType);
1738 1739
            }

1740
            if ((object)bestType != null)
1741 1742 1743
            {
                // Convert elements to best type to determine element top-level nullability and to report nested nullability warnings
                for (int i = 0; i < n; i++)
1744
                {
1745 1746
                    var placeholder = placeholders[i];
                    resultTypes[i] = ApplyConversion(placeholder, placeholder, conversions[i], inferredType, resultTypes[i], checkConversion: true,
1747
                        fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: true, reportTopLevelWarnings: false);
1748
                }
1749 1750

                // Set top-level nullability on inferred element type
1751
                inferredType = TypeWithAnnotations.Create(inferredType.Type, BestTypeInferrer.GetNullableAnnotation(resultTypes));
1752 1753

                for (int i = 0; i < n; i++)
1754
                {
1755 1756
                    var nodeForSyntax = expressions[i];
                    // Report top-level warnings
1757
                    _ = ApplyConversion(nodeForSyntax, operandOpt: nodeForSyntax, Conversion.Identity, targetTypeWithNullability: inferredType, operandType: resultTypes[i],
1758
                        checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: false);
1759 1760
                }
            }
1761

1762 1763 1764
            resultTypes.Free();
            expressions.Free();

1765
            ResultType = _invalidType;
1766 1767 1768 1769 1770 1771 1772 1773 1774
            arrayType = arrayType.WithElementType(inferredType);
            return arrayType;
        }

        /// <summary>
        /// Applies a method similar to <see cref="VisitArrayInitializer(BoundArrayCreation)"/>
        /// The expressions returned from a lambda are not converted though, so we'll have to classify fresh conversions.
        /// Note: even if some conversions fail, we'll proceed to infer top-level nullability. That is reasonable in common cases.
        /// </summary>
1775 1776
        internal static TypeWithAnnotations BestTypeForLambdaReturns(
            ArrayBuilder<(BoundExpression, TypeWithAnnotations)> returns,
1777 1778 1779 1780 1781 1782 1783 1784
            CSharpCompilation compilation,
            BoundNode node)
        {
            var walker = new NullableWalker(compilation, method: null,
                useMethodSignatureReturnType: false, useMethodSignatureParameterTypes: false, methodSignatureOpt: null,
                node, returnTypesOpt: null, initialState: null, callbackOpt: null);

            int n = returns.Count;
1785
            var resultTypes = ArrayBuilder<TypeWithAnnotations>.GetInstance(n);
1786 1787 1788 1789 1790 1791 1792 1793 1794 1795
            var placeholdersBuilder = ArrayBuilder<BoundExpression>.GetInstance(n);
            for (int i = 0; i < n; i++)
            {
                var (returnExpr, resultType) = returns[i];
                resultTypes.Add(resultType);
                placeholdersBuilder.Add(CreatePlaceholderIfNecessary(returnExpr, resultType));
            }

            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
            var placeholders = placeholdersBuilder.ToImmutableAndFree();
1796
            TypeSymbol bestType = BestTypeInferrer.InferBestType(placeholders, walker._conversions, ref useSiteDiagnostics);
1797

1798
            TypeWithAnnotations inferredType;
1799
            if ((object)bestType != null)
1800
            {
1801
                // Note: so long as we have a best type, we can proceed.
1802
                var bestTypeWithObliviousAnnotation = TypeWithAnnotations.Create(bestType);
1803
                ConversionsBase conversionsWithoutNullability = walker._conversions.WithNullability(false);
1804
                for (int i = 0; i < n; i++)
1805
                {
1806 1807
                    BoundExpression placeholder = placeholders[i];
                    Conversion conversion = conversionsWithoutNullability.ClassifyConversionFromExpression(placeholder, bestType, ref useSiteDiagnostics);
1808
                    resultTypes[i] = walker.ApplyConversion(placeholder, placeholder, conversion, bestTypeWithObliviousAnnotation, resultTypes[i].ToTypeWithState(),
1809
                        checkConversion: false, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Return,
N
Neal Gafter 已提交
1810
                        reportRemainingWarnings: false, reportTopLevelWarnings: false).ToTypeWithAnnotations();
1811
                }
1812 1813

                // Set top-level nullability on inferred type
1814
                inferredType = TypeWithAnnotations.Create(bestType, BestTypeInferrer.GetNullableAnnotation(resultTypes));
1815 1816 1817 1818
            }
            else
            {
                inferredType = default;
1819 1820
            }

1821 1822 1823 1824
            resultTypes.Free();
            walker.Free();

            return inferredType;
1825 1826
        }

1827
        private static void GetArrayElements(BoundArrayInitialization node, ArrayBuilder<BoundExpression> builder)
1828
        {
1829
            foreach (var child in node.Initializers)
1830
            {
1831
                if (child.Kind == BoundKind.ArrayInitialization)
1832
                {
1833 1834 1835 1836 1837
                    GetArrayElements((BoundArrayInitialization)child, builder);
                }
                else
                {
                    builder.Add(child);
1838 1839 1840 1841
                }
            }
        }

1842
        public override BoundNode VisitArrayAccess(BoundArrayAccess node)
1843
        {
1844 1845
            Debug.Assert(!IsConditionalState);

1846
            Visit(node.Expression);
1847 1848

            Debug.Assert(!IsConditionalState);
F
Fredric Silberberg 已提交
1849
            Debug.Assert(!node.Expression.Type.IsValueType);
1850 1851
            // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
            // after indices have been visited, and only if the receiver has not changed.
1852
            CheckPossibleNullReceiver(node.Expression);
1853

1854
            var type = ResultType.Type as ArrayTypeSymbol;
1855

1856 1857
            foreach (var i in node.Indices)
            {
J
Cleanup  
Jared Parsons 已提交
1858
                VisitRvalue(i);
1859 1860
            }

1861
            TypeWithAnnotations result;
A
Andy Gocke 已提交
1862
            if (node.Indices.Length == 1 &&
1863
                TypeSymbol.Equals(node.Indices[0].Type, compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything2))
A
Andy Gocke 已提交
1864
            {
1865
                result = TypeWithAnnotations.Create(type);
A
Andy Gocke 已提交
1866 1867 1868
            }
            else
            {
1869
                result = type?.ElementTypeWithAnnotations ?? default;
A
Andy Gocke 已提交
1870
            }
N
Neal Gafter 已提交
1871
            LvalueResultType = result;
A
Andy Gocke 已提交
1872

1873 1874 1875
            return null;
        }

1876
        private TypeWithState InferResultNullability(BoundBinaryOperator node, TypeWithState leftType, TypeWithState rightType)
1877 1878 1879 1880
        {
            return InferResultNullability(node.OperatorKind, node.MethodOpt, node.Type, leftType, rightType);
        }

1881
        private TypeWithState InferResultNullability(BinaryOperatorKind operatorKind, MethodSymbol methodOpt, TypeSymbol resultType, TypeWithState leftType, TypeWithState rightType)
1882
        {
1883
            NullableFlowState resultState = NullableFlowState.NotNull;
1884 1885
            if (operatorKind.IsUserDefined())
            {
1886
                // Update method based on operand types: see https://github.com/dotnet/roslyn/issues/29605.
1887
                if ((object)methodOpt != null && methodOpt.ParameterCount == 2)
1888
                {
1889
                    return operatorKind.IsLifted() && !operatorKind.IsComparison()
1890 1891
                        ? LiftedReturnType(methodOpt.ReturnTypeWithAnnotations, leftType.State.Join(rightType.State))
                        : methodOpt.ReturnTypeWithAnnotations.ToTypeWithState();
1892 1893
                }
            }
F
Fredric Silberberg 已提交
1894
            else if (!operatorKind.IsDynamic() && !resultType.IsValueType)
1895 1896 1897 1898
            {
                switch (operatorKind.Operator() | operatorKind.OperandTypes())
                {
                    case BinaryOperatorKind.DelegateCombination:
1899
                        resultState = leftType.State.Meet(rightType.State);
1900
                        break;
1901
                    case BinaryOperatorKind.DelegateRemoval:
1902
                        resultState = NullableFlowState.MaybeNull; // Delegate removal can produce null.
1903 1904
                        break;
                    default:
1905
                        resultState = NullableFlowState.NotNull;
1906
                        break;
1907 1908
                }
            }
1909

1910 1911
            if (operatorKind.IsLifted())
            {
1912
                resultState = leftType.State.Join(rightType.State);
1913 1914 1915
            }

            return new TypeWithState(resultType, resultState);
1916 1917 1918 1919 1920
        }

        protected override void AfterLeftChildHasBeenVisited(BoundBinaryOperator binary)
        {
            Debug.Assert(!IsConditionalState);
1921
            TypeWithState leftType = ResultType;
1922

1923 1924 1925 1926
            var rightType = VisitRvalueWithState(binary.Right);
            Debug.Assert(!IsConditionalState);
            // At this point, State.Reachable may be false for
            // invalid code such as `s + throw new Exception()`.
1927

1928
            if (binary.OperatorKind.IsUserDefined() && binary.MethodOpt?.ParameterCount == 2)
1929
            {
1930 1931 1932
                var parameters = binary.MethodOpt.Parameters;
                ReportArgumentWarnings(binary.Left, leftType, parameters[0]);
                ReportArgumentWarnings(binary.Right, rightType, parameters[1]);
1933
            }
1934

1935 1936
            Debug.Assert(!IsConditionalState);
            ResultType = InferResultNullability(binary, leftType, rightType);
1937

1938
            BinaryOperatorKind op = binary.OperatorKind.Operator();
1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972

            // learn from non-null constant
            BoundExpression operandComparedToNonNull = null;
            if (isNonNullConstant(binary.Left))
            {
                operandComparedToNonNull = binary.Right;
            }
            else if (isNonNullConstant(binary.Right))
            {
                operandComparedToNonNull = binary.Left;
            }

            if (operandComparedToNonNull != null)
            {
                switch (op)
                {
                    case BinaryOperatorKind.Equal:
                    case BinaryOperatorKind.GreaterThan:
                    case BinaryOperatorKind.LessThan:
                    case BinaryOperatorKind.GreaterThanOrEqual:
                    case BinaryOperatorKind.LessThanOrEqual:
                        operandComparedToNonNull = SkipReferenceConversions(operandComparedToNonNull);
                        splitAndLearnFromNonNullTest(operandComparedToNonNull, whenTrue: true);
                        return;
                    case BinaryOperatorKind.NotEqual:
                        operandComparedToNonNull = SkipReferenceConversions(operandComparedToNonNull);
                        splitAndLearnFromNonNullTest(operandComparedToNonNull, whenTrue: false);
                        return;
                    default:
                        break;
                };
            }

            // learn from null constant
1973 1974 1975
            if (op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual)
            {
                BoundExpression operandComparedToNull = null;
1976

1977
                if (binary.Right.ConstantValue?.IsNull == true)
1978
                {
1979 1980 1981 1982 1983 1984
                    operandComparedToNull = binary.Left;
                }
                else if (binary.Left.ConstantValue?.IsNull == true)
                {
                    operandComparedToNull = binary.Right;
                }
1985

1986 1987 1988
                if (operandComparedToNull != null)
                {
                    operandComparedToNull = SkipReferenceConversions(operandComparedToNull);
1989

1990
                    // Set all nested conditional slots. For example in a?.b?.c we'll set a, b, and c.
1991 1992 1993
                    bool nonNullCase = op != BinaryOperatorKind.Equal; // true represents WhenTrue
                    splitAndLearnFromNonNullTest(operandComparedToNull, whenTrue: nonNullCase);

1994 1995
                    // `x == null` and `x != null` are pure null tests so update the null-state in the alternative branch too
                    LearnFromNullTest(operandComparedToNull, ref nonNullCase ? ref StateWhenFalse : ref StateWhenTrue);
1996 1997
                }
            }
1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023

            static BoundExpression skipImplicitNullableConversions(BoundExpression possiblyConversion)
            {
                while (possiblyConversion.Kind == BoundKind.Conversion &&
                    possiblyConversion is BoundConversion { ConversionKind: ConversionKind.ImplicitNullable, Operand: var operand })
                {
                    possiblyConversion = operand;
                }
                return possiblyConversion;
            }

            void splitAndLearnFromNonNullTest(BoundExpression operandComparedToNull, bool whenTrue)
            {
                var slotBuilder = ArrayBuilder<int>.GetInstance();
                GetSlotsToMarkAsNotNullable(operandComparedToNull, slotBuilder);
                if (slotBuilder.Count != 0)
                {
                    Split();
                    ref LocalState stateToUpdate = ref whenTrue ? ref this.StateWhenTrue : ref this.StateWhenFalse;
                    MarkSlotsAsNotNull(slotBuilder, ref stateToUpdate);
                }
                slotBuilder.Free();
            }

            static bool isNonNullConstant(BoundExpression expr)
                => skipImplicitNullableConversions(expr).ConstantValue?.IsNull == false;
2024
        }
2025

2026 2027 2028 2029 2030 2031 2032 2033 2034
        /// <summary>
        /// If we learn that the operand is non-null, we can infer that certain
        /// sub-expressions were also non-null.
        /// Get all nested conditional slots for those sub-expressions. For example in a?.b?.c we'll set a, b, and c.
        /// Only returns slots for tracked expressions.
        /// </summary>
        private void GetSlotsToMarkAsNotNullable(BoundExpression operand, ArrayBuilder<int> slotBuilder)
        {
            Debug.Assert(operand != null);
2035 2036 2037 2038 2039
            var previousConditionalAccessSlot = _lastConditionalAccessSlot;

            try
            {
                while (true)
2040
                {
2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060
                    // Due to the nature of binding, if there are conditional access they will be at the top of the bound tree,
                    // potentially with a conversion on top of it. We go through any conditional accesses, adding slots for the
                    // conditional receivers if they have them. If we ever get to a receiver that MakeSlot doesn't return a slot
                    // for, nothing underneath is trackable and we bail at that point. Example:
                    //
                    //     a?.GetB()?.C // a is a field, GetB is a method, and C is a property
                    //
                    // The top of the tree is the a?.GetB() conditional call. We'll ask for a slot for a, and we'll get one because
                    // fields have slots. The AccessExpression of the BoundConditionalAccess is another BoundConditionalAccess, this time
                    // with a receiver of the GetB() BoundCall. Attempting to get a slot for this receiver will fail, and we'll
                    // return an array with just the slot for a.
                    int slot;
                    switch (operand.Kind)
                    {
                        case BoundKind.Conversion:
                            // https://github.com/dotnet/roslyn/issues/33879 Detect when conversion has a nullable operand
                            operand = ((BoundConversion)operand).Operand;
                            continue;
                        case BoundKind.ConditionalAccess:
                            var conditional = (BoundConditionalAccess)operand;
2061

2062 2063
                            slot = MakeSlot(conditional.Receiver);
                            if (slot > 0)
2064
                            {
2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075
                                // We need to continue the walk regardless of whether the receiver should be updated.
                                var receiverType = conditional.Receiver.Type;
                                if (PossiblyNullableType(receiverType))
                                {
                                    slotBuilder.Add(slot);
                                }

                                if (receiverType.IsNullableType())
                                {
                                    slot = GetNullableOfTValueSlot(receiverType, slot, out _);
                                }
2076 2077
                            }

2078
                            if (slot > 0)
2079
                            {
2080 2081 2082 2083 2084 2085 2086
                                // When MakeSlot is called on the nested AccessExpression, it will recurse through receivers
                                // until it gets to the BoundConditionalReceiver associated with this node. In our override,
                                // we substitute this slot when we encounter a BoundConditionalReceiver, and reset the
                                // _lastConditionalAccess field.
                                _lastConditionalAccessSlot = slot;
                                operand = conditional.AccessExpression;
                                continue;
2087 2088
                            }

2089 2090 2091 2092 2093 2094
                            // If there's no slot for this receiver, there cannot be another slot for any of the remaining
                            // access expressions.
                            break;
                        default:
                            // Attempt to create a slot for the current thing. If there were any more conditional accesses,
                            // they would have been on top, so this is the last thing we need to specially handle.
2095

2096 2097 2098
                            // https://github.com/dotnet/roslyn/issues/33879 When we handle unconditional access survival (ie after
                            // c.D has been invoked, c must be nonnull or we've thrown a NullRef), revisit whether
                            // we need more special handling here
F
Fredric Silberberg 已提交
2099

2100 2101 2102 2103 2104
                            slot = MakeSlot(operand);
                            if (slot > 0 && PossiblyNullableType(operand.Type))
                            {
                                slotBuilder.Add(slot);
                            }
F
Fredric Silberberg 已提交
2105

2106 2107
                            break;
                    }
F
Fredric Silberberg 已提交
2108

2109
                    return;
2110
                }
2111 2112 2113 2114
            }
            finally
            {
                _lastConditionalAccessSlot = previousConditionalAccessSlot;
2115 2116
            }
        }
2117

2118
        private static bool PossiblyNullableType(TypeSymbol operandType) => operandType?.CanContainNull() == true;
2119

2120
        private static void MarkSlotsAsNotNull(ArrayBuilder<int> slots, ref LocalState stateToUpdate)
2121 2122 2123 2124 2125 2126 2127 2128
        {
            if (slots is null)
            {
                return;
            }

            foreach (int slot in slots)
            {
2129
                stateToUpdate[slot] = NullableFlowState.NotNull;
2130 2131 2132
            }
        }

2133 2134 2135 2136
        private void LearnFromNonNullTest(BoundExpression expression, ref LocalState state)
        {
            var slotBuilder = ArrayBuilder<int>.GetInstance();
            GetSlotsToMarkAsNotNullable(expression, slotBuilder);
2137
            MarkSlotsAsNotNull(slotBuilder, ref state);
2138 2139 2140
            slotBuilder.Free();
        }

2141
        private int LearnFromNullTest(BoundExpression expression, ref LocalState state)
2142
        {
2143 2144 2145
            var expressionWithoutConversion = RemoveConversion(expression, includeExplicitConversions: true).expression;
            var slot = MakeSlot(expressionWithoutConversion);
            if (slot > 0 && PossiblyNullableType(expressionWithoutConversion.Type))
2146
            {
2147
                SetStateAndTrackForFinally(ref state, slot, NullableFlowState.MaybeNull);
2148
            }
2149
            return slot;
2150 2151
        }

2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170
        private static BoundExpression SkipReferenceConversions(BoundExpression possiblyConversion)
        {
            while (possiblyConversion.Kind == BoundKind.Conversion)
            {
                var conversion = (BoundConversion)possiblyConversion;
                switch (conversion.ConversionKind)
                {
                    case ConversionKind.ImplicitReference:
                    case ConversionKind.ExplicitReference:
                        possiblyConversion = conversion.Operand;
                        break;
                    default:
                        return possiblyConversion;
                }
            }

            return possiblyConversion;
        }

C
Misc.  
Charles Stoner 已提交
2171 2172
        public override BoundNode VisitNullCoalescingAssignmentOperator(BoundNullCoalescingAssignmentOperator node)
        {
2173 2174 2175 2176 2177 2178
            BoundExpression leftOperand = node.LeftOperand;
            BoundExpression rightOperand = node.RightOperand;
            int leftSlot = MakeSlot(leftOperand);

            // The assignment to the left below needs the declared type from VisitLvalue, but the hidden
            // unnecessary check diagnostic needs the current adjusted type of the slot
2179
            TypeWithAnnotations targetType = VisitLvalueWithAnnotations(leftOperand);
2180
            var leftState = this.State.Clone();
2181 2182
            LearnFromNonNullTest(leftOperand, ref leftState);
            LearnFromNullTest(leftOperand, ref this.State);
2183
            TypeWithState rightResult = VisitOptionalImplicitConversion(rightOperand, targetType, UseLegacyWarnings(leftOperand), AssignmentKind.Assignment);
2184 2185
            TrackNullableStateForAssignment(rightOperand, targetType, leftSlot, rightResult, MakeSlot(rightOperand));
            Join(ref this.State, ref leftState);
2186
            TypeWithState resultType = GetNullCoalescingResultType(rightResult, targetType.Type);
2187
            ResultType = resultType;
2188
            return null;
C
Misc.  
Charles Stoner 已提交
2189 2190
        }

2191 2192 2193 2194
        public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node)
        {
            Debug.Assert(!IsConditionalState);

2195 2196 2197
            BoundExpression leftOperand = node.LeftOperand;
            BoundExpression rightOperand = node.RightOperand;

2198 2199
            TypeWithState leftResult = VisitRvalueWithState(leftOperand);
            TypeWithState rightResult;
2200

2201
            if (IsConstantNull(leftOperand))
2202
            {
2203
                rightResult = VisitRvalueWithState(rightOperand);
2204 2205
                // Should be able to use rightResult for the result of the operator but
                // binding may have generated a different result type in the case of errors.
2206
                ResultType = new TypeWithState(node.Type, rightResult.State);
2207
                return null;
2208 2209
            }

2210 2211
            var whenNotNull = this.State.Clone();
            LearnFromNonNullTest(leftOperand, ref whenNotNull);
2212
            LearnFromNullTest(leftOperand, ref this.State);
2213

2214
            bool leftIsConstant = leftOperand.ConstantValue != null;
2215 2216 2217 2218 2219
            if (leftIsConstant)
            {
                SetUnreachable();
            }

2220
            // https://github.com/dotnet/roslyn/issues/29955 For cases where the left operand determines
2221
            // the type, we should unwrap the right conversion and re-apply.
2222
            rightResult = VisitRvalueWithState(rightOperand);
2223
            Join(ref this.State, ref whenNotNull);
2224
            TypeSymbol resultType;
2225 2226
            var leftResultType = leftResult.Type;
            var rightResultType = rightResult.Type;
2227
            switch (node.OperatorResultKind)
2228
            {
2229 2230 2231 2232
                case BoundNullCoalescingOperatorResultKind.NoCommonType:
                    resultType = node.Type;
                    break;
                case BoundNullCoalescingOperatorResultKind.LeftType:
2233
                    resultType = getLeftResultType(leftResultType, rightResultType);
2234 2235
                    break;
                case BoundNullCoalescingOperatorResultKind.LeftUnwrappedType:
2236
                    resultType = getLeftResultType(leftResultType.StrippedType(), rightResultType);
2237 2238
                    break;
                case BoundNullCoalescingOperatorResultKind.RightType:
2239
                    resultType = getRightResultType(leftResultType, rightResultType);
2240 2241
                    break;
                case BoundNullCoalescingOperatorResultKind.LeftUnwrappedRightType:
2242
                    resultType = getRightResultType(leftResultType.StrippedType(), rightResultType);
2243 2244
                    break;
                case BoundNullCoalescingOperatorResultKind.RightDynamicType:
2245
                    resultType = rightResultType;
2246 2247 2248
                    break;
                default:
                    throw ExceptionUtilities.UnexpectedValue(node.OperatorResultKind);
2249 2250
            }

2251
            ResultType = GetNullCoalescingResultType(rightResult, resultType);
2252
            return null;
2253

2254 2255
            TypeSymbol getLeftResultType(TypeSymbol leftType, TypeSymbol rightType)
            {
2256
                Debug.Assert(!(rightType is null));
2257 2258 2259
                // If there was an identity conversion between the two operands (in short, if there
                // is no implicit conversion on the right operand), then check nullable conversions
                // in both directions since it's possible the right operand is the better result type.
2260
                if ((node.RightOperand as BoundConversion)?.ExplicitCastInCode != false &&
2261 2262 2263 2264
                    GenerateConversionForConditionalOperator(node.LeftOperand, leftType, rightType, reportMismatch: false).Exists)
                {
                    return rightType;
                }
2265

2266 2267 2268
                GenerateConversionForConditionalOperator(node.RightOperand, rightType, leftType, reportMismatch: true);
                return leftType;
            }
2269

2270 2271 2272 2273
            TypeSymbol getRightResultType(TypeSymbol leftType, TypeSymbol rightType)
            {
                GenerateConversionForConditionalOperator(node.LeftOperand, leftType, rightType, reportMismatch: true);
                return rightType;
2274
            }
2275 2276
        }

2277 2278 2279 2280 2281 2282
        /// <summary>
        /// Return top-level nullability for the expression. This method should be called on a limited
        /// set of expressions only. It should not be called on expressions tracked by flow analysis
        /// other than <see cref="BoundKind.ExpressionWithNullability"/> which is an expression
        /// specifically created in NullableWalker to represent the flow analysis state.
        /// </summary>
2283
        private static NullableAnnotation GetNullableAnnotation(BoundExpression expr)
2284 2285 2286 2287 2288
        {
            switch (expr.Kind)
            {
                case BoundKind.DefaultExpression:
                case BoundKind.Literal:
2289
                    return (expr.ConstantValue?.IsNull != false) ? NullableAnnotation.NotAnnotated : NullableAnnotation.Annotated;
2290
                case BoundKind.ExpressionWithNullability:
2291
                    return ((BoundExpressionWithNullability)expr).NullableAnnotation;
2292 2293
                case BoundKind.MethodGroup:
                case BoundKind.UnboundLambda:
2294
                    return NullableAnnotation.NotAnnotated;
2295 2296
                default:
                    Debug.Assert(false); // unexpected value
2297
                    return NullableAnnotation.Oblivious;
2298 2299 2300
            }
        }

2301
        private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResult, TypeSymbol resultType)
2302
        {
2303
            NullableFlowState resultState = rightResult.State;
2304
            return new TypeWithState(resultType, resultState);
2305 2306
        }

2307 2308 2309 2310
        public override BoundNode VisitConditionalAccess(BoundConditionalAccess node)
        {
            Debug.Assert(!IsConditionalState);

2311
            var receiver = node.Receiver;
2312
            var receiverType = VisitRvalueWithState(receiver);
2313 2314
            _currentConditionalReceiverVisitResult = _visitResult;
            var previousConditionalAccessSlot = _lastConditionalAccessSlot;
2315

2316
            var receiverState = this.State.Clone();
2317
            if (IsConstantNull(node.Receiver))
2318
            {
2319
                SetUnreachable();
2320
                _lastConditionalAccessSlot = -1;
2321 2322 2323
            }
            else
            {
2324 2325 2326 2327
                // In the when-null branch, the receiver is known to be maybe-null.
                // In the other branch, the receiver is known to be non-null.
                _lastConditionalAccessSlot = LearnFromNullTest(receiver, ref receiverState);
                LearnFromNonNullTest(receiver, ref this.State);
2328 2329
            }

2330
            var accessExpressionType = VisitRvalueWithState(node.AccessExpression);
A
Andy Gocke 已提交
2331
            Join(ref this.State, ref receiverState);
2332 2333 2334
            // Per LDM 2019-02-13 decision, the result of a conditional access might be null even if
            // both the receiver and right-hand-side are believed not to be null.
            NullableFlowState resultState = NullableFlowState.MaybeNull;
2335

2336 2337
            // https://github.com/dotnet/roslyn/issues/29956 Use flow analysis type rather than node.Type
            // so that nested nullability is inferred from flow analysis. See VisitConditionalOperator.
2338 2339
            TypeSymbol type = node.Type;

N
Neal Gafter 已提交
2340
            // If the result type does not allow annotations, then we produce a warning because
2341 2342
            // the result may be null.
            if (RequiresSafetyWarningWhenNullIntroduced(type))
2343
            {
2344
                ReportSafetyDiagnostic(ErrorCode.WRN_ConditionalAccessMayReturnNull, node.Syntax, node.Type);
2345 2346
            }

2347 2348
            _currentConditionalReceiverVisitResult = default;
            _lastConditionalAccessSlot = previousConditionalAccessSlot;
2349
            ResultType = new TypeWithState(type, resultState);
2350 2351 2352 2353 2354
            return null;
        }

        public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
        {
C
Charles Stoner 已提交
2355
            var isByRef = node.IsRef;
2356 2357 2358 2359

            VisitCondition(node.Condition);
            var consequenceState = this.StateWhenTrue;
            var alternativeState = this.StateWhenFalse;
2360 2361 2362

            BoundExpression consequence;
            BoundExpression alternative;
2363 2364
            Conversion consequenceConversion;
            Conversion alternativeConversion;
2365 2366
            TypeWithAnnotations consequenceResult;
            TypeWithAnnotations alternativeResult;
2367

2368 2369 2370
            bool isConstantTrue = IsConstantTrue(node.Condition);
            bool isConstantFalse = IsConstantFalse(node.Condition);
            if (isConstantTrue)
2371
            {
2372 2373
                (alternative, alternativeConversion, alternativeResult) = visitConditionalOperand(alternativeState, node.Alternative);
                (consequence, consequenceConversion, consequenceResult) = visitConditionalOperand(consequenceState, node.Consequence);
2374
            }
2375
            else if (isConstantFalse)
2376
            {
2377 2378
                (consequence, consequenceConversion, consequenceResult) = visitConditionalOperand(consequenceState, node.Consequence);
                (alternative, alternativeConversion, alternativeResult) = visitConditionalOperand(alternativeState, node.Alternative);
2379 2380
            }
            else
2381
            {
2382
                (consequence, consequenceConversion, consequenceResult) = visitConditionalOperand(consequenceState, node.Consequence);
2383
                Unsplit();
2384
                consequenceState = this.State;
2385
                (alternative, alternativeConversion, alternativeResult) = visitConditionalOperand(alternativeState, node.Alternative);
2386
                Unsplit();
A
Andy Gocke 已提交
2387
                Join(ref this.State, ref consequenceState);
2388 2389
            }

2390
            TypeSymbol resultType;
2391 2392
            if (node.HasErrors)
            {
2393
                resultType = null;
2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404
            }
            else
            {
                // Determine nested nullability using BestTypeInferrer.
                // For constant conditions, we could use the nested nullability of the particular
                // branch, but that requires using the nullability of the branch as it applies to the
                // target type. For instance, the result of the conditional in the following should
                // be `IEnumerable<object>` not `object[]`:
                //   object[] a = ...;
                //   IEnumerable<object?> b = ...;
                //   var c = true ? a : b;
2405 2406
                BoundExpression consequencePlaceholder = CreatePlaceholderIfNecessary(consequence, consequenceResult);
                BoundExpression alternativePlaceholder = CreatePlaceholderIfNecessary(alternative, alternativeResult);
2407
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
2408
                // https://github.com/dotnet/roslyn/issues/30432: InferBestTypeForConditionalOperator should use node.IsRef.
2409
                resultType = BestTypeInferrer.InferBestTypeForConditionalOperator(consequencePlaceholder, alternativePlaceholder, _conversions, out _, ref useSiteDiagnostics);
2410 2411
            }

2412
            TypeWithAnnotations visitResult;
2413 2414
            if ((object)resultType != null)
            {
2415 2416 2417
                var resultTypeWithAnnotations = TypeWithAnnotations.Create(resultType);
                TypeWithAnnotations convertedConsequenceResult = default;
                TypeWithAnnotations convertedAlternativeResult = default;
2418

2419 2420
                if (!isConstantFalse)
                {
2421
                    convertedConsequenceResult = convertResult(
2422 2423 2424 2425
                        node.Consequence,
                        consequence,
                        consequenceConversion,
                        resultTypeWithAnnotations,
2426
                        consequenceResult);
2427 2428 2429 2430
                }

                if (!isConstantTrue)
                {
2431
                    convertedAlternativeResult = convertResult(
2432 2433 2434 2435
                        node.Alternative,
                        alternative,
                        alternativeConversion,
                        resultTypeWithAnnotations,
2436
                        alternativeResult);
2437
                }
2438

N
Neal Gafter 已提交
2439
                if (!convertedAlternativeResult.HasType)
2440
                {
N
Neal Gafter 已提交
2441
                    Debug.Assert(convertedConsequenceResult.HasType);
2442
                    visitResult = convertedConsequenceResult;
2443
                }
N
Neal Gafter 已提交
2444
                else if (!convertedConsequenceResult.HasType)
2445
                {
N
Neal Gafter 已提交
2446
                    Debug.Assert(convertedAlternativeResult.HasType);
2447
                    visitResult = convertedAlternativeResult;
2448 2449 2450
                }
                else
                {
2451
                    visitResult = TypeWithAnnotations.Create(resultType,
2452
                        convertedConsequenceResult.NullableAnnotation.Join(convertedAlternativeResult.NullableAnnotation));
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468
                }
            }
            else
            {
                NullableAnnotation resultNullableAnnotation;

                if (isConstantTrue)
                {
                    resultNullableAnnotation = getNullableAnnotation(consequence, consequenceResult);
                }
                else if (isConstantFalse)
                {
                    resultNullableAnnotation = getNullableAnnotation(alternative, alternativeResult);
                }
                else
                {
2469
                    resultNullableAnnotation = getNullableAnnotation(consequence, consequenceResult).Join(getNullableAnnotation(alternative, alternativeResult));
2470 2471
                }

2472
                visitResult = TypeWithAnnotations.Create(node.Type.SetUnknownNullabilityForReferenceTypes(), resultNullableAnnotation);
2473
            }
N
Neal Gafter 已提交
2474
            LvalueResultType = visitResult;
2475

2476 2477
            return null;

2478
            NullableAnnotation getNullableAnnotation(BoundExpression expr, TypeWithAnnotations type)
2479
            {
N
Neal Gafter 已提交
2480
                if (type.HasType)
2481
                {
2482
                    return type.GetValueNullableAnnotation();
2483
                }
2484 2485
                if (expr.IsLiteralNullOrDefault())
                {
2486
                    return NullableAnnotation.Annotated;
2487
                }
2488
                return NullableAnnotation.Oblivious;
2489 2490
            }

2491
            (BoundExpression, Conversion, TypeWithAnnotations) visitConditionalOperand(LocalState state, BoundExpression operand)
2492
            {
2493
                Conversion conversion;
2494
                SetState(state);
2495
                TypeWithAnnotations resultWithAnnotation;
2496 2497
                if (isByRef)
                {
2498
                    resultWithAnnotation = VisitLvalueWithAnnotations(operand);
2499
                    conversion = Conversion.Identity;
2500 2501 2502
                }
                else
                {
2503
                    (operand, conversion) = RemoveConversion(operand, includeExplicitConversions: false);
2504
                    Visit(operand);
2505 2506
                    if (node.HasErrors)
                    {
N
Neal Gafter 已提交
2507
                        ResultType = ResultType.WithNotNullState();
2508 2509
                    }

N
Neal Gafter 已提交
2510
                    resultWithAnnotation = ResultType.ToTypeWithAnnotations();
2511
                }
2512 2513

                return (operand, conversion, resultWithAnnotation);
2514
            }
2515

2516
            TypeWithAnnotations convertResult(
2517 2518 2519
                BoundExpression node,
                BoundExpression operand,
                Conversion conversion,
2520 2521
                TypeWithAnnotations targetType,
                TypeWithAnnotations operandType)
2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532
            {
                return ApplyConversion(
                    node,
                    operand,
                    conversion,
                    targetType,
                    operandType.ToTypeWithState(),
                    checkConversion: true,
                    fromExplicitCast: false,
                    useLegacyWarnings: false,
                    AssignmentKind.Assignment,
2533
                    reportTopLevelWarnings: false).ToTypeWithAnnotations();
2534
            }
2535 2536
        }

2537
        /// <summary>
2538
        /// Placeholders are bound expressions with type and state.
2539 2540 2541
        /// But for typeless expressions (such as `null` or `(null, null)` we hold onto the original bound expression,
        /// as it will be useful for conversions from expression.
        /// </summary>
2542
        private static BoundExpression CreatePlaceholderIfNecessary(BoundExpression expr, TypeWithAnnotations type)
2543
        {
N
Neal Gafter 已提交
2544
            return !type.HasType ?
2545
                expr :
2546
                new BoundExpressionWithNullability(expr.Syntax, expr, type.NullableAnnotation, type.Type);
2547 2548
        }

2549 2550
        public override BoundNode VisitConditionalReceiver(BoundConditionalReceiver node)
        {
2551 2552 2553 2554 2555 2556 2557
            var rvalueType = _currentConditionalReceiverVisitResult.RValueType.Type;
            if (rvalueType?.IsNullableType() == true)
            {
                rvalueType = rvalueType.GetNullableUnderlyingType();
            }
            ResultType = new TypeWithState(rvalueType, NullableFlowState.NotNull);
            return null;
2558 2559 2560 2561
        }

        public override BoundNode VisitCall(BoundCall node)
        {
2562
            // Note: we analyze even omitted calls
2563
            TypeWithState receiverType = VisitCallReceiver(node);
2564 2565 2566
            ReinferMethodAndVisitArguments(node, receiverType);
            return null;
        }
2567

2568
        private void ReinferMethodAndVisitArguments(BoundCall node, TypeWithState receiverType)
2569
        {
2570
            // https://github.com/dotnet/roslyn/issues/29605 Can we handle some error cases?
2571
            // (Compare with CSharpOperationFactory.CreateBoundCallOperation.)
2572
            var method = node.Method;
2573 2574
            ImmutableArray<RefKind> refKindsOpt = node.ArgumentRefKindsOpt;
            (ImmutableArray<BoundExpression> arguments, ImmutableArray<Conversion> conversions) = RemoveArgumentConversions(node.Arguments, refKindsOpt);
N
Neal Gafter 已提交
2575
            if (!receiverType.HasNullType)
2576
            {
2577
                // Update method based on inferred receiver type.
2578
                method = (MethodSymbol)AsMemberOfType(receiverType.Type, method);
2579 2580
            }

2581
            method = VisitArguments(node, arguments, refKindsOpt, method.Parameters, node.ArgsToParamsOpt,
2582
                node.Expanded, node.InvokedAsExtensionMethod, conversions, method).method;
2583

2584 2585 2586
            if (method.MethodKind == MethodKind.LocalFunction)
            {
                var localFunc = (LocalFunctionSymbol)method.OriginalDefinition;
2587 2588 2589
                ReplayReadsAndWrites(localFunc, node.Syntax, writes: true);
            }

2590
            var type = method.ReturnTypeWithAnnotations;
2591
            LvalueResultType = type;
2592
        }
2593

2594
        private TypeWithState VisitCallReceiver(BoundCall node)
2595 2596
        {
            var receiverOpt = node.ReceiverOpt;
2597
            TypeWithState receiverType = default;
2598 2599 2600

            if (receiverOpt != null && node.Method.MethodKind != MethodKind.Constructor)
            {
2601
                receiverType = VisitRvalueWithState(receiverOpt);
2602

V
vsadov 已提交
2603
                // methods which are members of Nullable<T> (ex: ToString, GetHashCode) can be invoked on null receiver.
2604 2605 2606 2607 2608 2609
                // However, inherited methods (ex: GetType) are invoked on a boxed value (since base types are reference types)
                // and therefore in those cases nullable receivers should be checked for nullness.
                bool checkNullableValueType = false;

                var type = receiverType.Type;
                var method = node.Method;
2610
                if (!method.IsStatic &&
2611 2612 2613 2614 2615
                    type?.IsNullableType() == true &&
                    method.ContainingType.IsReferenceType)
                {
                    checkNullableValueType = true;
                }
2616 2617 2618 2619 2620 2621 2622 2623
                else if (method.OriginalDefinition == compilation.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_Value))
                {
                    // call to get_Value may not occur directly in source, but may be inserted as a result of premature lowering. 
                    // One example where we do it is foreach with nullables. 
                    // The reason is Dev10 compatibility (see: UnwrapCollectionExpressionIfNullable in ForEachLoopBinder.cs)
                    // Regardless of the reasons, we know that the method does not tolerate nulls.
                    checkNullableValueType = true;
                }
2624

2625 2626
                // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
                // after arguments have been visited, and only if the receiver has not changed.
2627
                CheckPossibleNullReceiver(receiverOpt, checkNullableValueType);
2628 2629 2630
            }

            return receiverType;
2631 2632
        }

2633 2634 2635 2636
        /// <summary>
        /// For each argument, figure out if its corresponding parameter is annotated with NotNullWhenFalse or
        /// EnsuresNotNull.
        /// </summary>
2637
        private static ImmutableArray<FlowAnalysisAnnotations> GetAnnotations(int numArguments,
2638 2639
            bool expanded, ImmutableArray<ParameterSymbol> parameters, ImmutableArray<int> argsToParamsOpt)
        {
2640
            ArrayBuilder<FlowAnalysisAnnotations> builder = null;
2641 2642 2643 2644

            for (int i = 0; i < numArguments; i++)
            {
                (ParameterSymbol parameter, _) = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded);
2645
                FlowAnalysisAnnotations annotations = parameter?.FlowAnalysisAnnotations ?? FlowAnalysisAnnotations.None;
2646

2647
                annotations = removeInapplicableAnnotations(parameter, annotations);
2648

2649
                if (annotations != FlowAnalysisAnnotations.None && builder == null)
2650
                {
2651 2652
                    builder = ArrayBuilder<FlowAnalysisAnnotations>.GetInstance(numArguments);
                    builder.AddMany(FlowAnalysisAnnotations.None, i);
2653 2654 2655 2656 2657 2658 2659 2660 2661
                }

                if (builder != null)
                {
                    builder.Add(annotations);
                }
            }

            return builder == null ? default : builder.ToImmutableAndFree();
2662

2663
            FlowAnalysisAnnotations removeInapplicableAnnotations(ParameterSymbol parameter, FlowAnalysisAnnotations annotations)
2664
            {
2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694
                // Ignore NotNullWhenTrue that is inapplicable
                annotations = removeInapplicableNotNullWhenSense(parameter, annotations, sense: true);

                // Ignore NotNullWhenFalse that is inapplicable
                annotations = removeInapplicableNotNullWhenSense(parameter, annotations, sense: false);

                const FlowAnalysisAnnotations both = FlowAnalysisAnnotations.AssertsTrue | FlowAnalysisAnnotations.AssertsFalse;
                if (parameter?.Type.SpecialType != SpecialType.System_Boolean)
                {
                    // AssertsTrue and AssertsFalse must be applied to a bool parameter
                    annotations &= ~both;
                }
                else if ((annotations & both) == both)
                {
                    // We'll ignore AssertsTrue and AssertsFalse if both set
                    annotations &= ~both;
                }

                return annotations;
            }

            FlowAnalysisAnnotations removeInapplicableNotNullWhenSense(ParameterSymbol parameter, FlowAnalysisAnnotations annotations, bool sense)
            {
                if (parameter is null)
                {
                    return annotations;
                }

                var whenSense = sense ? FlowAnalysisAnnotations.NotNullWhenTrue : FlowAnalysisAnnotations.NotNullWhenFalse;
                var whenNotSense = sense ? FlowAnalysisAnnotations.NotNullWhenFalse : FlowAnalysisAnnotations.NotNullWhenTrue;
2695 2696 2697 2698 2699 2700 2701 2702 2703

                // NotNullWhenSense (without NotNullWhenNotSense) must be applied on a bool-returning member
                if ((annotations & whenSense) != 0 &&
                    (annotations & whenNotSense) == 0 &&
                    parameter.ContainingSymbol.GetTypeOrReturnType().SpecialType != SpecialType.System_Boolean)
                {
                    annotations &= ~whenSense;
                }

2704
                // NotNullWhenSense must be applied to a reference type, a nullable value type, or an unconstrained generic type
2705
                if ((annotations & whenSense) != 0 && !parameter.Type.CanContainNull())
2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717
                {
                    annotations &= ~whenSense;
                }

                // NotNullWhenSense is inapplicable when argument corresponds to params parameter and we're in expanded form
                if ((annotations & whenSense) != 0 && expanded && ReferenceEquals(parameter, parameters.Last()))
                {
                    annotations &= ~whenSense;
                }

                return annotations;
            }
2718 2719
        }

2720
        // https://github.com/dotnet/roslyn/issues/29863 Record in the node whether type
2721 2722
        // arguments were implicit, to allow for cases where the syntax is not an
        // invocation (such as a synthesized call from a query interpretation).
2723
        private static bool HasImplicitTypeArguments(BoundExpression node)
2724
        {
2725 2726 2727
            var syntax = node.Syntax;
            if (syntax.Kind() != SyntaxKind.InvocationExpression)
            {
2728
                // Unexpected syntax kind.
2729 2730
                return false;
            }
2731
            var nameSyntax = Binder.GetNameSyntax(((InvocationExpressionSyntax)syntax).Expression, out var _);
2732
            if (nameSyntax == null)
2733
            {
2734 2735
                // Unexpected syntax kind.
                return false;
2736
            }
2737 2738
            nameSyntax = nameSyntax.GetUnqualifiedName();
            return nameSyntax.Kind() != SyntaxKind.GenericName;
2739 2740
        }

2741
        protected override void VisitArguments(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, MethodSymbol method)
2742
        {
2743 2744 2745
            // Callers should be using VisitArguments overload below.
            throw ExceptionUtilities.Unreachable;
        }
2746

2747
        private ImmutableArray<VisitResult> VisitArguments(
2748 2749 2750 2751 2752 2753
            BoundExpression node,
            ImmutableArray<BoundExpression> arguments,
            ImmutableArray<RefKind> refKindsOpt,
            MethodSymbol method,
            ImmutableArray<int> argsToParamsOpt,
            bool expanded)
2754
        {
2755 2756
            ImmutableArray<Conversion> conversions;
            (arguments, conversions) = RemoveArgumentConversions(arguments, refKindsOpt);
2757
            return VisitArguments(node, arguments, refKindsOpt, method is null ? default : method.Parameters, argsToParamsOpt, expanded, invokedAsExtensionMethod: false, conversions).results;
2758 2759
        }

2760
        private ImmutableArray<VisitResult> VisitArguments(
2761
            BoundExpression node,
2762 2763
            ImmutableArray<BoundExpression> arguments,
            ImmutableArray<RefKind> refKindsOpt,
2764
            PropertySymbol property,
2765 2766
            ImmutableArray<int> argsToParamsOpt,
            bool expanded)
2767
        {
2768 2769
            ImmutableArray<Conversion> conversions;
            (arguments, conversions) = RemoveArgumentConversions(arguments, refKindsOpt);
2770
            return VisitArguments(node, arguments, refKindsOpt, property is null ? default : property.Parameters, argsToParamsOpt, expanded, invokedAsExtensionMethod: false, conversions).results;
2771 2772
        }

2773
        /// <summary>
2774
        /// If you pass in a method symbol, its type arguments will be re-inferred and the re-inferred method will be returned.
2775
        /// </summary>
2776
        private (MethodSymbol method, ImmutableArray<VisitResult> results) VisitArguments(
2777 2778 2779 2780 2781
            BoundExpression node,
            ImmutableArray<BoundExpression> arguments,
            ImmutableArray<RefKind> refKindsOpt,
            ImmutableArray<ParameterSymbol> parameters,
            ImmutableArray<int> argsToParamsOpt,
2782
            bool expanded,
2783
            bool invokedAsExtensionMethod,
2784 2785
            ImmutableArray<Conversion> conversions,
            MethodSymbol method = null)
2786 2787
        {
            Debug.Assert(!arguments.IsDefault);
2788 2789 2790
            var savedState = this.State.Clone();

            // We do a first pass to work through the arguments without making any assumptions
2791
            ImmutableArray<VisitResult> results = VisitArgumentsEvaluate(arguments, refKindsOpt);
2792

2793
            if ((object)method != null && method.IsGenericMethod)
2794
            {
2795 2796 2797 2798 2799
                if (HasImplicitTypeArguments(node))
                {
                    method = InferMethodTypeArguments((BoundCall)node, method, GetArgumentsForMethodTypeInference(arguments, results));
                    parameters = method.Parameters;
                }
2800
                if (ConstraintsHelper.RequiresChecking(method))
2801 2802 2803 2804
                {
                    var syntax = node.Syntax;
                    CheckMethodConstraints((syntax as InvocationExpressionSyntax)?.Expression ?? syntax, method);
                }
2805 2806
            }

2807 2808
            if (!node.HasErrors && !parameters.IsDefault)
            {
2809
                VisitArgumentConversions(arguments, conversions, refKindsOpt, parameters, argsToParamsOpt, expanded, invokedAsExtensionMethod, results);
2810
            }
2811 2812 2813

            // We do a second pass through the arguments, ignoring any diagnostics produced, but honoring the annotations,
            // to get the proper result state.
2814
            ImmutableArray<FlowAnalysisAnnotations> annotations = GetAnnotations(arguments.Length, expanded, parameters, argsToParamsOpt);
2815 2816 2817 2818

            if (!annotations.IsDefault)
            {
                this.SetState(savedState);
2819 2820 2821 2822 2823

                bool saveDisableDiagnostics = _disableDiagnostics;
                _disableDiagnostics = true;
                if (!node.HasErrors && !parameters.IsDefault)
                {
2824 2825
                    // recompute out vars after state was reset
                    VisitArgumentConversions(arguments, conversions, refKindsOpt, parameters, argsToParamsOpt, expanded, invokedAsExtensionMethod, results);
2826
                }
2827
                VisitArgumentsEvaluateHonoringAnnotations(arguments, refKindsOpt, annotations);
2828 2829

                _disableDiagnostics = saveDisableDiagnostics;
2830 2831
            }

2832
            return (method, results);
2833 2834
        }

2835
        private ImmutableArray<VisitResult> VisitArgumentsEvaluate(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt)
2836 2837 2838 2839
        {
            Debug.Assert(!IsConditionalState);
            int n = arguments.Length;
            if (n == 0)
2840
            {
2841
                return ImmutableArray<VisitResult>.Empty;
2842
            }
2843
            var builder = ArrayBuilder<VisitResult>.GetInstance(n);
2844
            for (int i = 0; i < n; i++)
2845
            {
2846
                builder.Add(VisitArgumentEvaluate(arguments, refKindsOpt, i, preserveConditionalState: false));
2847 2848
            }

2849
            ResultType = _invalidType;
2850 2851 2852
            return builder.ToImmutableAndFree();
        }

2853
        private VisitResult VisitArgumentEvaluate(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, int i, bool preserveConditionalState)
2854
        {
2855
            Debug.Assert(!IsConditionalState);
2856 2857
            RefKind refKind = GetRefKind(refKindsOpt, i);
            var argument = arguments[i];
2858
            switch (refKind)
2859
            {
2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872
                case RefKind.Ref:
                    Visit(argument);
                    if (!preserveConditionalState)
                    {
                        Unsplit();
                    }
                    break;
                case RefKind.None:
                case RefKind.In:
                    if (preserveConditionalState)
                    {
                        Visit(argument);
                        // No Unsplit
N
Neal Gafter 已提交
2873
                        UseRvalueOnly(); // force use of flow result
2874 2875 2876
                    }
                    else
                    {
J
Cleanup  
Jared Parsons 已提交
2877
                        VisitRvalue(argument);
2878 2879 2880 2881 2882
                    }
                    break;
                case RefKind.Out:
                    // As far as we can tell, there is no scenario relevant to nullability analysis
                    // where splitting an L-value (for instance with a ref conditional) would affect the result.
2883
                    Visit(argument);
2884

2885
                    // We'll want to use the l-value type, rather than the result type, for method re-inference
N
Neal Gafter 已提交
2886
                    UseLvalueOnly();
2887
                    break;
2888
            }
2889 2890

            return _visitResult;
2891 2892 2893 2894
        }

        /// <summary>
        /// Visit all the arguments for the purpose of computing the exit state of the method,
2895
        /// given the annotations.
2896
        /// If there is any [NotNullWhenTrue/False] annotation, then we'll return in a conditional state for the invocation.
2897 2898 2899 2900
        /// </summary>
        private void VisitArgumentsEvaluateHonoringAnnotations(
            ImmutableArray<BoundExpression> arguments,
            ImmutableArray<RefKind> refKindsOpt,
2901
            ImmutableArray<FlowAnalysisAnnotations> annotations)
2902 2903 2904
        {
            Debug.Assert(!IsConditionalState);
            Debug.Assert(annotations.Length == arguments.Length);
2905
            Debug.Assert(_disableDiagnostics);
2906 2907 2908

            for (int i = 0; i < arguments.Length; i++)
            {
2909 2910 2911 2912
                FlowAnalysisAnnotations annotation = annotations[i];
                bool assertsTrue = (annotation & FlowAnalysisAnnotations.AssertsTrue) != 0;
                bool assertsFalse = (annotation & FlowAnalysisAnnotations.AssertsFalse) != 0;

2913
                if (this.IsConditionalState)
2914
                {
2915
                    // We could be in a conditional state because of a conditional annotation (like NotNullWhenFalse)
2916 2917
                    // Then WhenTrue/False states correspond to the invocation returning true/false

2918
                    // We'll first assume that we're in the unconditional state where the method returns true,
2919 2920 2921 2922 2923 2924
                    // then we'll repeat assuming the method returns false.

                    LocalState whenTrue = this.StateWhenTrue.Clone();
                    LocalState whenFalse = this.StateWhenFalse.Clone();

                    this.SetState(whenTrue);
2925
                    visitArgumentEvaluateAndUnsplit(i, assertsTrue, assertsFalse);
2926 2927 2928 2929
                    Debug.Assert(!IsConditionalState);
                    whenTrue = this.State; // LocalState may be a struct

                    this.SetState(whenFalse);
2930
                    visitArgumentEvaluateAndUnsplit(i, assertsTrue, assertsFalse);
2931 2932 2933 2934
                    Debug.Assert(!IsConditionalState);
                    whenFalse = this.State; // LocalState may be a struct

                    this.SetConditionalState(whenTrue, whenFalse);
2935 2936 2937
                }
                else
                {
2938
                    visitArgumentEvaluateAndUnsplit(i, assertsTrue, assertsFalse);
2939 2940 2941
                }

                var argument = arguments[i];
2942
                var argumentType = argument.Type;
2943
                if (!PossiblyNullableType(argumentType))
2944 2945 2946 2947
                {
                    continue;
                }

2948 2949
                bool notNullWhenTrue = (annotation & FlowAnalysisAnnotations.NotNullWhenTrue) != 0;
                bool notNullWhenFalse = (annotation & FlowAnalysisAnnotations.NotNullWhenFalse) != 0;
2950
                if (notNullWhenTrue || notNullWhenFalse)
2951
                {
2952 2953 2954 2955 2956 2957 2958 2959 2960
                    // The WhenTrue/False states correspond to the invocation returning true/false
                    bool wasPreviouslySplit = this.IsConditionalState;
                    Split();

                    var slotBuilder = ArrayBuilder<int>.GetInstance();
                    GetSlotsToMarkAsNotNullable(arguments[i], slotBuilder);

                    if (notNullWhenTrue)
                    {
2961
                        MarkSlotsAsNotNull(slotBuilder, ref StateWhenTrue);
2962 2963 2964
                    }
                    if (notNullWhenFalse)
                    {
2965
                        MarkSlotsAsNotNull(slotBuilder, ref StateWhenFalse);
2966 2967 2968
                        if (notNullWhenTrue && !wasPreviouslySplit) Unsplit();
                    }
                    slotBuilder.Free();
2969
                }
2970
            }
2971

2972
            ResultType = _invalidType;
2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997

            // Evaluate an argument, potentially producing a split state.
            // Then unsplit it based on [AssertsTrue] or [AssertsFalse] attributes, or default Unsplit otherwise.
            void visitArgumentEvaluateAndUnsplit(int argumentIndex, bool assertsTrue, bool assertsFalse)
            {
                Debug.Assert(!IsConditionalState);
                VisitArgumentEvaluate(arguments, refKindsOpt, argumentIndex, preserveConditionalState: true);

                if (!this.IsConditionalState)
                {
                    return;
                }
                else if (assertsTrue)
                {
                    this.SetState(this.StateWhenTrue);
                }
                else if (assertsFalse)
                {
                    this.SetState(this.StateWhenFalse);
                }
                else
                {
                    this.Unsplit();
                }
            }
2998 2999
        }

3000
        private void VisitArgumentConversions(
3001
            ImmutableArray<BoundExpression> arguments,
3002
            ImmutableArray<Conversion> conversions,
3003
            ImmutableArray<RefKind> refKindsOpt,
3004
            ImmutableArray<ParameterSymbol> parameters,
3005 3006
            ImmutableArray<int> argsToParamsOpt,
            bool expanded,
3007
            bool invokedAsExtensionMethod,
3008
            ImmutableArray<VisitResult> results)
3009
        {
3010
            for (int i = 0; i < arguments.Length; i++)
3011
            {
3012
                (ParameterSymbol parameter, TypeWithAnnotations parameterType) = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded);
3013
                if (parameter is null)
3014
                {
3015
                    continue;
3016
                }
3017
                VisitArgumentConversion(
3018
                    arguments[i],
3019
                    conversions.IsDefault ? Conversion.Identity : conversions[i],
3020 3021 3022
                    GetRefKind(refKindsOpt, i),
                    parameter,
                    parameterType,
3023 3024
                    results[i],
                    invokedAsExtensionMethod && i == 0);
3025 3026
            }
        }
3027

3028 3029 3030
        /// <summary>
        /// Report warnings for an argument corresponding to a specific parameter.
        /// </summary>
3031
        private void VisitArgumentConversion(
3032
            BoundExpression argument,
3033
            Conversion conversion,
3034 3035
            RefKind refKind,
            ParameterSymbol parameter,
3036
            TypeWithAnnotations parameterType,
3037
            VisitResult result,
3038
            bool extensionMethodThisArgument)
3039
        {
3040 3041 3042
            // Note: we allow for some variance in `in` and `out` cases. Unlike in binding, we're not
            // limited by CLR constraints.

3043
            var resultType = result.RValueType;
3044
            bool reported = false;
3045 3046 3047 3048 3049
            switch (refKind)
            {
                case RefKind.None:
                case RefKind.In:
                    {
3050
                        ApplyConversion(
3051 3052 3053 3054 3055
                            node: argument,
                            operandOpt: argument,
                            conversion: conversion,
                            targetTypeWithNullability: parameterType,
                            operandType: resultType,
3056 3057 3058
                            checkConversion: true,
                            fromExplicitCast: false,
                            useLegacyWarnings: false,
3059
                            assignmentKind: AssignmentKind.Argument,
3060 3061
                            target: parameter,
                            extensionMethodThisArgument: extensionMethodThisArgument);
3062 3063
                    }
                    break;
3064 3065 3066 3067
                case RefKind.Ref:
                    {
                        if (!argument.IsSuppressed)
                        {
3068 3069
                            var lvalueResultType = result.LValueType;
                            if (IsNullabilityMismatch(lvalueResultType, parameterType))
3070
                            {
3071 3072 3073 3074 3075 3076 3077
                                // declared types must match
                                ReportNullabilityMismatchInRefArgument(argument, argumentType: lvalueResultType, parameter, parameterType);
                            }
                            else
                            {
                                // types match, but state would let a null in
                                ReportNullableAssignmentIfNecessary(argument, parameterType, resultType, useLegacyWarnings: false);
3078 3079
                            }
                        }
3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090

                        // Check assignment from a fictional value from the parameter to the argument.
                        var parameterWithState = parameterType.ToTypeWithState();
                        if (argument.IsSuppressed)
                        {
                            parameterWithState = parameterWithState.WithNotNullState();
                        }

                        var parameterValue = new BoundParameter(argument.Syntax, parameter);
                        var lValueType = result.LValueType;
                        TrackNullableStateForAssignment(parameterValue, lValueType, MakeSlot(argument), parameterWithState);
3091
                    }
3092
                    break;
3093
                case RefKind.Out:
3094
                    {
3095
                        var parameterWithState = parameterType.ToTypeWithState();
3096
                        if (argument is BoundLocal local && local.DeclarationKind == BoundLocalDeclarationKind.WithInferredType)
3097
                        {
3098 3099
                            _variableTypes[local.LocalSymbol] = parameterType;
                        }
3100

3101
                        var lValueType = result.LValueType;
3102
                        // Check assignment from a fictional value from the parameter to the argument.
3103
                        var parameterValue = new BoundParameter(argument.Syntax, parameter);
3104

3105
                        if (!argument.IsSuppressed && !reported)
3106
                        {
3107
                            ReportNullableAssignmentIfNecessary(parameterValue, lValueType, parameterWithState, useLegacyWarnings: UseLegacyWarnings(argument));
3108

3109
                            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
3110
                            if (!_conversions.HasIdentityOrImplicitReferenceConversion(parameterType.Type, lValueType.Type, ref useSiteDiagnostics))
3111
                            {
3112
                                ReportNullabilityMismatchInArgument(argument, lValueType.Type, parameter, parameterType.Type, forOutput: true);
3113
                            }
3114
                        }
3115
                        else
3116
                        {
N
Neal Gafter 已提交
3117
                            parameterWithState = parameterWithState.WithNotNullState();
3118
                        }
3119

3120
                        // Set nullable state of argument to parameter type.
3121
                        TrackNullableStateForAssignment(parameterValue, lValueType, MakeSlot(argument), parameterWithState);
3122
                    }
3123
                    break;
3124 3125 3126 3127 3128
                default:
                    throw ExceptionUtilities.UnexpectedValue(refKind);
            }
        }

3129
        private static (ImmutableArray<BoundExpression> arguments, ImmutableArray<Conversion> conversions) RemoveArgumentConversions(
3130 3131
            ImmutableArray<BoundExpression> arguments,
            ImmutableArray<RefKind> refKindsOpt)
3132
        {
3133
            int n = arguments.Length;
3134
            var conversions = default(ImmutableArray<Conversion>);
3135
            if (n > 0)
3136
            {
3137
                var argumentsBuilder = ArrayBuilder<BoundExpression>.GetInstance(n);
3138
                var conversionsBuilder = ArrayBuilder<Conversion>.GetInstance(n);
3139 3140 3141 3142 3143
                bool includedConversion = false;
                for (int i = 0; i < n; i++)
                {
                    RefKind refKind = GetRefKind(refKindsOpt, i);
                    var argument = arguments[i];
3144
                    var conversion = Conversion.Identity;
3145 3146
                    if (refKind == RefKind.None)
                    {
3147 3148 3149
                        var before = argument;
                        (argument, conversion) = RemoveConversion(argument, includeExplicitConversions: false);
                        if (argument != before)
3150 3151 3152 3153 3154
                        {
                            includedConversion = true;
                        }
                    }
                    argumentsBuilder.Add(argument);
3155
                    conversionsBuilder.Add(conversion);
3156 3157 3158 3159
                }
                if (includedConversion)
                {
                    arguments = argumentsBuilder.ToImmutable();
3160
                    conversions = conversionsBuilder.ToImmutable();
3161 3162
                }
                argumentsBuilder.Free();
3163
                conversionsBuilder.Free();
3164
            }
3165
            return (arguments, conversions);
3166 3167
        }

3168 3169
        private VariableState GetVariableState()
        {
3170
            // https://github.com/dotnet/roslyn/issues/29617 To track nullability of captured variables inside and
3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183
            // outside a lambda, the lambda should be considered executed at the location the lambda
            // is converted to a delegate.
            return new VariableState(
                _variableSlot.ToImmutableDictionary(),
                ImmutableArray.Create(variableBySlot, start: 0, length: nextVariableSlot),
                _variableTypes.ToImmutableDictionary());
        }

        private UnboundLambda GetUnboundLambda(BoundLambda expr, VariableState variableState)
        {
            return expr.UnboundLambda.WithNullableState(_binder, variableState);
        }

3184
        private static (ParameterSymbol Parameter, TypeWithAnnotations Type) GetCorrespondingParameter(
3185 3186 3187 3188
            int argumentOrdinal,
            ImmutableArray<ParameterSymbol> parameters,
            ImmutableArray<int> argsToParamsOpt,
            bool expanded)
3189
        {
3190 3191
            if (parameters.IsDefault)
            {
3192
                return (default, default);
3193
            }
3194

3195
            int n = parameters.Length;
3196 3197 3198 3199
            ParameterSymbol parameter;

            if (argsToParamsOpt.IsDefault)
            {
3200
                if (argumentOrdinal < n)
3201
                {
3202
                    parameter = parameters[argumentOrdinal];
3203 3204 3205
                }
                else if (expanded)
                {
3206
                    parameter = parameters[n - 1];
3207 3208 3209 3210
                }
                else
                {
                    parameter = null;
3211 3212 3213 3214
                }
            }
            else
            {
3215 3216
                int parameterOrdinal = argsToParamsOpt[argumentOrdinal];

3217
                if (parameterOrdinal < n)
3218
                {
3219
                    parameter = parameters[parameterOrdinal];
3220 3221 3222 3223 3224 3225 3226 3227
                }
                else
                {
                    parameter = null;
                    expanded = false;
                }
            }

3228
            if (parameter is null)
3229
            {
3230
                Debug.Assert(!expanded);
3231
                return (default, default);
3232
            }
3233

3234
            var type = parameter.TypeWithAnnotations;
3235
            if (expanded && parameter.Ordinal == n - 1 && type.IsSZArray())
3236
            {
3237
                type = ((ArrayTypeSymbol)type.Type).ElementTypeWithAnnotations;
3238 3239 3240
            }

            return (parameter, type);
3241 3242
        }

3243
        private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol method, ImmutableArray<BoundExpression> arguments)
3244
        {
3245
            Debug.Assert(method.IsGenericMethod);
3246

3247
            // https://github.com/dotnet/roslyn/issues/27961 OverloadResolution.IsMemberApplicableInNormalForm and
3248 3249 3250 3251 3252 3253 3254
            // 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)
            {
                refKinds.AddRange(node.ArgumentRefKindsOpt);
            }
3255

3256
            // https://github.com/dotnet/roslyn/issues/27961 Do we really need OverloadResolution.GetEffectiveParameterTypes?
3257
            // Aren't we doing roughly the same calculations in GetCorrespondingParameter?
3258 3259
            OverloadResolution.GetEffectiveParameterTypes(
                definition,
3260
                arguments.Length,
3261 3262
                node.ArgsToParamsOpt,
                refKinds,
C
Charles Stoner 已提交
3263
                isMethodGroupConversion: false,
3264
                // https://github.com/dotnet/roslyn/issues/27961 `allowRefOmittedArguments` should be
3265 3266 3267 3268 3269
                // false for constructors and several other cases (see Binder use). Should we
                // capture the original value in the BoundCall?
                allowRefOmittedArguments: true,
                binder: _binder,
                expanded: node.Expanded,
3270
                parameterTypes: out ImmutableArray<TypeWithAnnotations> parameterTypes,
3271
                parameterRefKinds: out ImmutableArray<RefKind> parameterRefKinds);
3272

3273 3274 3275
            refKinds.Free();
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
            var result = MethodTypeInferrer.Infer(
3276 3277
                _binder,
                _conversions,
3278 3279 3280 3281 3282
                definition.TypeParameters,
                definition.ContainingType,
                parameterTypes,
                parameterRefKinds,
                arguments,
3283
                ref useSiteDiagnostics,
3284
                getTypeWithAnnotationOpt: s_getTypeWithAnnotations);
3285

3286
            if (!result.Success)
3287
            {
3288
                return method;
3289
            }
3290

3291
            return definition.Construct(result.InferredTypeArguments);
3292
        }
3293

3294 3295
        private readonly static Func<BoundExpression, TypeWithAnnotations> s_getTypeWithAnnotations =
            (expr) => TypeWithAnnotations.Create(expr.Type, GetNullableAnnotation(expr));
3296

3297
        private ImmutableArray<BoundExpression> GetArgumentsForMethodTypeInference(ImmutableArray<BoundExpression> arguments, ImmutableArray<VisitResult> argumentResults)
3298
        {
3299
            // https://github.com/dotnet/roslyn/issues/27961 MethodTypeInferrer.Infer relies
3300 3301
            // on the BoundExpressions for tuple element types and method groups.
            // By using a generic BoundValuePlaceholder, we're losing inference in those cases.
3302
            // https://github.com/dotnet/roslyn/issues/27961 Inference should be based on
3303 3304 3305 3306 3307
            // unconverted arguments. Consider cases such as `default`, lambdas, tuples.
            int n = arguments.Length;
            var builder = ArrayBuilder<BoundExpression>.GetInstance(n);
            for (int i = 0; i < n; i++)
            {
3308 3309
                var argumentResult = argumentResults[i].LValueType;
                if (!argumentResult.HasType)
N
Neal Gafter 已提交
3310
                    argumentResult = argumentResults[i].RValueType.ToTypeWithAnnotations();
3311
                builder.Add(getArgumentForMethodTypeInference(arguments[i], argumentResult));
3312 3313 3314
            }
            return builder.ToImmutableAndFree();

3315
            BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, TypeWithAnnotations argumentType)
3316
            {
3317 3318 3319 3320 3321 3322 3323
                if (argument.Kind == BoundKind.Lambda)
                {
                    // MethodTypeInferrer must infer nullability for lambdas based on the nullability
                    // from flow analysis rather than the declared nullability. To allow that, we need
                    // to re-bind lambdas in MethodTypeInferrer.
                    return GetUnboundLambda((BoundLambda)argument, GetVariableState());
                }
3324
                if (!argumentType.HasType)
3325 3326 3327 3328 3329 3330
                {
                    return argument;
                }
                if (argument is BoundLocal local && local.DeclarationKind == BoundLocalDeclarationKind.WithInferredType)
                {
                    // 'out var' doesn't contribute to inference
3331
                    return new BoundExpressionWithNullability(argument.Syntax, argument, NullableAnnotation.Oblivious, type: null);
3332
                }
3333
                return new BoundExpressionWithNullability(argument.Syntax, argument, argumentType.NullableAnnotation, argumentType.Type);
3334 3335
            }
        }
3336

3337 3338 3339
        private void CheckMethodConstraints(SyntaxNode syntax, MethodSymbol method)
        {
            var diagnosticsBuilder = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
3340
            var nullabilityBuilder = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
3341 3342 3343 3344
            ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
            ConstraintsHelper.CheckMethodConstraints(
                method,
                _conversions,
J
Jared Parsons 已提交
3345
                includeNullability: true,
3346 3347
                compilation,
                diagnosticsBuilder,
3348
                nullabilityBuilder,
3349
                ref useSiteDiagnosticsBuilder);
3350
            foreach (var pair in nullabilityBuilder)
3351 3352 3353 3354
            {
                Diagnostics.Add(pair.DiagnosticInfo, syntax.Location);
            }
            useSiteDiagnosticsBuilder?.Free();
3355
            nullabilityBuilder.Free();
3356 3357 3358
            diagnosticsBuilder.Free();
        }

3359 3360 3361 3362
        private void ReplayReadsAndWrites(LocalFunctionSymbol localFunc,
                                  SyntaxNode syntax,
                                  bool writes)
        {
3363
            // https://github.com/dotnet/roslyn/issues/27233 Support field initializers in local functions.
3364 3365
        }

3366 3367 3368 3369 3370 3371 3372 3373 3374 3375
        /// <summary>
        /// Returns the expression without the top-most conversion plus the conversion.
        /// If the expression is not a conversion, returns the original expression plus
        /// the Identity conversion. If `includeExplicitConversions` is true, implicit and
        /// explicit conversions are considered. If `includeExplicitConversions` is false
        /// only implicit conversions are considered and if the expression is an explicit
        /// conversion, the expression is returned as is, with the Identity conversion.
        /// (Currently, the only visit method that passes `includeExplicitConversions: true`
        /// is VisitConversion. All other callers are handling implicit conversions only.)
        /// </summary>
3376
        private static (BoundExpression expression, Conversion conversion) RemoveConversion(BoundExpression expr, bool includeExplicitConversions)
3377
        {
3378
            ConversionGroup group = null;
3379 3380 3381 3382 3383 3384 3385
            while (true)
            {
                if (expr.Kind != BoundKind.Conversion)
                {
                    break;
                }
                var conversion = (BoundConversion)expr;
3386
                if (group != conversion.ConversionGroupOpt && group != null)
3387
                {
3388
                    // E.g.: (C)(B)a
3389 3390
                    break;
                }
3391 3392 3393 3394 3395 3396
                group = conversion.ConversionGroupOpt;
                Debug.Assert(group != null || !conversion.ExplicitCastInCode); // Explicit conversions should include a group.
                if (!includeExplicitConversions && group?.IsExplicitConversion == true)
                {
                    return (expr, Conversion.Identity);
                }
3397
                expr = conversion.Operand;
3398 3399 3400 3401 3402 3403 3404 3405 3406
                if (group == null)
                {
                    // Ungrouped conversion should not be followed by another ungrouped
                    // conversion. Otherwise, the conversions should have been grouped.
                    Debug.Assert(expr.Kind != BoundKind.Conversion ||
                        ((BoundConversion)expr).ConversionGroupOpt != null ||
                        ((BoundConversion)expr).ConversionKind == ConversionKind.NoConversion);
                    return (expr, conversion.Conversion);
                }
3407
            }
3408
            return (expr, group?.Conversion ?? Conversion.Identity);
3409
        }
3410

3411 3412
        // See Binder.BindNullCoalescingOperator for initial binding.
        private Conversion GenerateConversionForConditionalOperator(BoundExpression sourceExpression, TypeSymbol sourceType, TypeSymbol destinationType, bool reportMismatch)
3413
        {
3414
            var conversion = GenerateConversion(_conversions, sourceExpression, sourceType, destinationType, fromExplicitCast: false, extensionMethodThisArgument: false);
3415
            bool canConvertNestedNullability = conversion.Exists;
3416
            if (!canConvertNestedNullability && reportMismatch && !sourceExpression.IsSuppressed)
3417
            {
3418
                ReportNullabilityMismatchInAssignment(sourceExpression.Syntax, GetTypeAsDiagnosticArgument(sourceType), destinationType);
3419
            }
3420 3421
            return conversion;
        }
3422

3423
        private static Conversion GenerateConversion(Conversions conversions, BoundExpression sourceExpression, TypeSymbol sourceType, TypeSymbol destinationType, bool fromExplicitCast, bool extensionMethodThisArgument)
3424
        {
3425
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
3426 3427 3428 3429 3430 3431 3432 3433 3434 3435
            bool useExpression = UseExpressionForConversion(sourceExpression);
            if (extensionMethodThisArgument)
            {
                return conversions.ClassifyImplicitExtensionMethodThisArgConversion(
                    useExpression ? sourceExpression : null,
                    sourceType,
                    destinationType,
                    ref useSiteDiagnostics);
            }
            return useExpression ?
3436 3437 3438 3439 3440 3441 3442
                (fromExplicitCast ?
                    conversions.ClassifyConversionFromExpression(sourceExpression, destinationType, ref useSiteDiagnostics, forCast: true) :
                    conversions.ClassifyImplicitConversionFromExpression(sourceExpression, destinationType, ref useSiteDiagnostics)) :
                (fromExplicitCast ?
                    conversions.ClassifyConversionFromType(sourceType, destinationType, ref useSiteDiagnostics, forCast: true) :
                    conversions.ClassifyImplicitConversionFromType(sourceType, destinationType, ref useSiteDiagnostics));
        }
3443

3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467
        /// <summary>
        /// Returns true if the expression should be used as the source when calculating
        /// a conversion from this expression, rather than using the type (with nullability)
        /// calculated by visiting this expression. Typically, that means expressions that
        /// do not have an explicit type but there are several other cases as well.
        /// (See expressions handled in ClassifyImplicitBuiltInConversionFromExpression.)
        /// </summary>
        private static bool UseExpressionForConversion(BoundExpression value)
        {
            if (value is null)
            {
                return false;
            }
            if (value.Type is null || value.Type.IsDynamic() || value.ConstantValue != null)
            {
                return true;
            }
            switch (value.Kind)
            {
                case BoundKind.InterpolatedString:
                    return true;
                default:
                    return false;
            }
3468 3469
        }

3470 3471 3472
        /// <summary>
        /// Adjust declared type based on inferred nullability at the point of reference.
        /// </summary>
3473
        private TypeWithState GetAdjustedResult(TypeWithAnnotations type, int slot)
3474 3475 3476 3477 3478
        {
            return GetAdjustedResult(type.ToTypeWithState(), slot);
        }

        private TypeWithState GetAdjustedResult(TypeWithState type, int slot)
3479
        {
3480
            if (slot > 0 && slot < this.State.Capacity)
3481
            {
3482 3483
                NullableFlowState state = this.State[slot];
                return new TypeWithState(type.Type, state);
3484
            }
3485

3486
            return type;
3487
        }
3488

3489
        private static Symbol AsMemberOfType(TypeSymbol type, Symbol symbol)
3490
        {
3491 3492 3493
            Debug.Assert((object)symbol != null);

            var containingType = type as NamedTypeSymbol;
N
Neal Gafter 已提交
3494
            if (containingType is null || containingType.IsErrorType() || symbol is ErrorMethodSymbol)
3495
            {
3496 3497
                return symbol;
            }
3498 3499 3500 3501 3502

            if (symbol.Kind == SymbolKind.Method)
            {
                if (((MethodSymbol)symbol).MethodKind == MethodKind.LocalFunction)
                {
3503
                    // https://github.com/dotnet/roslyn/issues/27233 Handle type substitution for local functions.
3504 3505
                    return symbol;
                }
3506
            }
3507 3508
            var symbolDef = symbol.OriginalDefinition;
            var symbolDefContainer = symbolDef.ContainingType;
3509 3510 3511 3512 3513
            if (symbolDefContainer.IsTupleType)
            {
                return AsMemberOfTupleType((TupleTypeSymbol)containingType, symbol);
            }
            if (symbolDefContainer.IsAnonymousType)
3514
            {
3515 3516
                int? memberIndex = symbol.Kind == SymbolKind.Property ? symbol.MemberIndexOpt : null;
                if (!memberIndex.HasValue)
3517
                {
3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535
                    return symbol;
                }
                return AnonymousTypeManager.GetAnonymousTypeProperty(containingType, memberIndex.GetValueOrDefault());
            }
            if (!symbolDefContainer.IsGenericType)
            {
                Debug.Assert(symbol.ContainingType.IsDefinition);
                return symbol;
            }
            if (symbolDefContainer.IsInterface)
            {
                if (tryAsMemberOfSingleType(containingType, out Symbol result))
                {
                    return result;
                }
                foreach (var @interface in containingType.AllInterfacesNoUseSiteDiagnostics)
                {
                    if (tryAsMemberOfSingleType(@interface, out result))
3536
                    {
3537
                        return result;
3538
                    }
3539 3540 3541 3542 3543 3544 3545
                }
            }
            else
            {
                while (true)
                {
                    if (tryAsMemberOfSingleType(containingType, out Symbol result))
3546
                    {
3547
                        return result;
3548
                    }
3549 3550
                    containingType = containingType.BaseTypeNoUseSiteDiagnostics;
                    if ((object)containingType == null)
3551
                    {
3552
                        break;
3553
                    }
3554
                }
3555 3556 3557 3558 3559 3560 3561
            }
            Debug.Assert(false); // If this assert fails, add an appropriate test.
            return symbol;

            bool tryAsMemberOfSingleType(NamedTypeSymbol singleType, out Symbol result)
            {
                if (!singleType.OriginalDefinition.Equals(symbolDefContainer, TypeCompareKind.AllIgnoreOptions))
3562
                {
3563 3564
                    result = null;
                    return false;
3565
                }
3566 3567 3568
                result = symbolDef.SymbolAsMember(singleType);
                if (result is MethodSymbol resultMethod && resultMethod.IsGenericMethod)
                {
3569
                    result = resultMethod.Construct(((MethodSymbol)symbol).TypeArgumentsWithAnnotations);
3570 3571
                }
                return true;
3572
            }
3573 3574
        }

3575 3576
        private static Symbol AsMemberOfTupleType(TupleTypeSymbol tupleType, Symbol symbol)
        {
3577
            if (symbol.ContainingType.Equals(tupleType))
3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595
            {
                return symbol;
            }
            switch (symbol.Kind)
            {
                case SymbolKind.Field:
                    {
                        var index = ((FieldSymbol)symbol).TupleElementIndex;
                        if (index >= 0)
                        {
                            return tupleType.TupleElements[index];
                        }
                        return tupleType.GetTupleMemberSymbolForUnderlyingMember(((TupleFieldSymbol)symbol).UnderlyingField);
                    }
                case SymbolKind.Property:
                    return tupleType.GetTupleMemberSymbolForUnderlyingMember(((TuplePropertySymbol)symbol).UnderlyingProperty);
                case SymbolKind.Event:
                    return tupleType.GetTupleMemberSymbolForUnderlyingMember(((TupleEventSymbol)symbol).UnderlyingEvent);
3596 3597
                case SymbolKind.Method:
                    return tupleType.GetTupleMemberSymbolForUnderlyingMember(((TupleMethodSymbol)symbol).UnderlyingMethod);
3598 3599 3600 3601 3602
                default:
                    throw ExceptionUtilities.UnexpectedValue(symbol.Kind);
            }
        }

3603 3604
        public override BoundNode VisitConversion(BoundConversion node)
        {
3605
            // https://github.com/dotnet/roslyn/issues/29959 Assert VisitConversion is only used for explicit conversions.
3606 3607 3608
            //Debug.Assert(node.ExplicitCastInCode);
            //Debug.Assert(node.ConversionGroupOpt != null);
            //Debug.Assert(!node.ConversionGroupOpt.ExplicitType.IsNull);
3609

3610
            TypeWithAnnotations explicitType = node.ConversionGroupOpt?.ExplicitType ?? default;
N
Neal Gafter 已提交
3611
            bool fromExplicitCast = explicitType.HasType;
3612
            TypeWithAnnotations targetType = fromExplicitCast ? explicitType : TypeWithAnnotations.Create(node.Type);
N
Neal Gafter 已提交
3613
            Debug.Assert(targetType.HasType);
3614 3615

            (BoundExpression operand, Conversion conversion) = RemoveConversion(node, includeExplicitConversions: true);
3616
            TypeWithState operandType = VisitRvalueWithState(operand);
3617
            ResultType = ApplyConversion(
3618 3619 3620 3621 3622
                node,
                operand,
                conversion,
                targetType,
                operandType,
3623
                checkConversion: true,
3624
                fromExplicitCast: fromExplicitCast,
3625
                useLegacyWarnings: fromExplicitCast && !RequiresSafetyWarningWhenNullIntroduced(explicitType.Type),
3626 3627
                AssignmentKind.Assignment,
                reportTopLevelWarnings: fromExplicitCast,
3628
                reportRemainingWarnings: true);
3629 3630 3631 3632 3633 3634 3635 3636 3637

            switch (node.ConversionKind)
            {
                case ConversionKind.ImplicitNullable:
                case ConversionKind.ExplicitNullable:
                    TrackNullableStateIfNullableConversion(node);
                    break;
            }

3638 3639
            return null;
        }
3640

3641 3642 3643 3644 3645 3646
        /// <summary>
        /// Visit an expression. If an explicit target type is provided, the expression is converted
        /// to that type. This method should be called whenever an expression may contain
        /// an implicit conversion, even if that conversion was omitted from the bound tree,
        /// so the conversion can be re-classified with nullability.
        /// </summary>
3647
        private TypeWithState VisitOptionalImplicitConversion(BoundExpression expr, TypeWithAnnotations targetTypeOpt, bool useLegacyWarnings, AssignmentKind assignmentKind)
3648
        {
N
Neal Gafter 已提交
3649
            if (!targetTypeOpt.HasType)
3650
            {
3651
                return VisitRvalueWithState(expr);
3652
            }
3653

3654
            (BoundExpression operand, Conversion conversion) = RemoveConversion(expr, includeExplicitConversions: false);
3655
            var operandType = VisitRvalueWithState(operand);
3656 3657 3658 3659 3660
            // If an explicit conversion was used in place of an implicit conversion, the explicit
            // conversion was created by initial binding after reporting "error CS0266:
            // Cannot implicitly convert type '...' to '...'. An explicit conversion exists ...".
            // Since an error was reported, we don't need to report nested warnings as well.
            bool reportNestedWarnings = !conversion.IsExplicit;
3661
            var resultType = ApplyConversion(
3662 3663 3664 3665 3666 3667 3668 3669
                expr,
                operand,
                conversion,
                targetTypeOpt,
                operandType,
                checkConversion: true,
                fromExplicitCast: false,
                useLegacyWarnings: useLegacyWarnings,
3670 3671
                assignmentKind,
                reportTopLevelWarnings: true,
3672
                reportRemainingWarnings: reportNestedWarnings);
3673 3674 3675 3676 3677 3678 3679 3680 3681 3682

            var conv = expr as BoundConversion;
            if (conv != null && conv.ConversionKind == ConversionKind.ImplicitNullable)
            {
                TrackNullableStateIfNullableConversion(conv);
            }

            return resultType;
        }

3683
        private static bool AreNullableAndUnderlyingTypes(TypeSymbol nullableTypeOpt, TypeSymbol underlyingTypeOpt, out TypeWithAnnotations underlyingTypeWithAnnotations)
3684 3685 3686 3687 3688
        {
            if (nullableTypeOpt?.IsNullableType() == true &&
                underlyingTypeOpt?.IsNullableType() == false)
            {
                var typeArg = nullableTypeOpt.GetNullableUnderlyingTypeWithAnnotations();
3689
                if (typeArg.Type.Equals(underlyingTypeOpt, TypeCompareKind.AllIgnoreOptions))
3690 3691 3692 3693 3694 3695 3696
                {
                    underlyingTypeWithAnnotations = typeArg;
                    return true;
                }
            }
            underlyingTypeWithAnnotations = default;
            return false;
3697 3698
        }

3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713
        public override BoundNode VisitTupleLiteral(BoundTupleLiteral node)
        {
            VisitTupleExpression(node);
            return null;
        }

        public override BoundNode VisitConvertedTupleLiteral(BoundConvertedTupleLiteral node)
        {
            VisitTupleExpression(node);
            return null;
        }

        private void VisitTupleExpression(BoundTupleExpression node)
        {
            var arguments = node.Arguments;
3714
            ImmutableArray<TypeWithState> elementTypes = arguments.SelectAsArray((a, w) => w.VisitRvalueWithState(a), this);
N
Neal Gafter 已提交
3715
            ImmutableArray<TypeWithAnnotations> elementTypesWithAnnotations = elementTypes.SelectAsArray(a => a.ToTypeWithAnnotations());
3716
            var tupleOpt = (TupleTypeSymbol)node.Type;
3717 3718
            if (tupleOpt is null)
            {
3719
                ResultType = default;
3720 3721 3722 3723 3724 3725
            }
            else
            {
                int slot = GetOrCreateObjectCreationPlaceholderSlot(node);
                if (slot > 0)
                {
3726
                    this.State[slot] = NullableFlowState.NotNull;
3727 3728
                    TrackNullableStateOfTupleElements(slot, tupleOpt, arguments, elementTypes, useRestField: false);
                }
3729

3730
                tupleOpt = tupleOpt.WithElementTypes(elementTypesWithAnnotations);
J
Jared Parsons 已提交
3731
                var locations = tupleOpt.TupleElements.SelectAsArray((element, location) => element.Locations.FirstOrDefault() ?? location, node.Syntax.Location);
3732
                tupleOpt.CheckConstraints(_conversions, includeNullability: true, node.Syntax, locations, compilation, diagnosticsOpt: null, nullabilityDiagnosticsOpt: Diagnostics);
3733
                ResultType = new TypeWithState(tupleOpt, NullableFlowState.NotNull);
3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745
            }
        }

        /// <summary>
        /// Set the nullability of tuple elements for tuples at the point of construction.
        /// If <paramref name="useRestField"/> is true, the tuple was constructed with an explicit
        /// 'new ValueTuple' call, in which case the 8-th element, if any, represents the 'Rest' field.
        /// </summary>
        private void TrackNullableStateOfTupleElements(
            int slot,
            TupleTypeSymbol tupleType,
            ImmutableArray<BoundExpression> values,
3746
            ImmutableArray<TypeWithState> types,
3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773
            bool useRestField)
        {
            Debug.Assert(values.Length == types.Length);
            Debug.Assert(values.Length == (useRestField ? Math.Min(tupleType.TupleElements.Length, TupleTypeSymbol.RestPosition) : tupleType.TupleElements.Length));

            if (slot > 0)
            {
                var tupleElements = tupleType.TupleElements;
                int n = values.Length;
                if (useRestField)
                {
                    n = Math.Min(n, TupleTypeSymbol.RestPosition - 1);
                }
                for (int i = 0; i < n; i++)
                {
                    trackState(values[i], tupleElements[i], types[i]);
                }
                if (useRestField && values.Length == TupleTypeSymbol.RestPosition)
                {
                    var restField = tupleType.GetMembers(TupleTypeSymbol.RestFieldName).FirstOrDefault() as FieldSymbol;
                    if ((object)restField != null)
                    {
                        trackState(values.Last(), restField, types.Last());
                    }
                }
            }

3774
            void trackState(BoundExpression value, FieldSymbol field, TypeWithState valueType) =>
3775
                TrackNullableStateForAssignment(value, field.TypeWithAnnotations, GetOrCreateSlot(field, slot), valueType, MakeSlot(value));
3776 3777
        }

3778
        private void TrackNullableStateOfNullableValue(int containingSlot, TypeSymbol containingType, BoundExpression value, TypeWithState valueType, int valueSlot)
3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798
        {
            Debug.Assert(containingType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T);
            Debug.Assert(containingSlot > 0);
            Debug.Assert(valueSlot > 0);

            int targetSlot = GetNullableOfTValueSlot(containingType, containingSlot, out Symbol symbol);
            Debug.Assert(targetSlot > 0);
            if (targetSlot > 0)
            {
                TrackNullableStateForAssignment(value, symbol.GetTypeOrReturnType(), targetSlot, valueType, valueSlot);
            }
        }

        private void TrackNullableStateIfNullableConversion(BoundConversion node)
        {
            Debug.Assert(node.ConversionKind == ConversionKind.ImplicitNullable || node.ConversionKind == ConversionKind.ExplicitNullable);

            var operand = node.Operand;
            var operandType = operand.Type;
            var convertedType = node.Type;
3799
            if (AreNullableAndUnderlyingTypes(convertedType, operandType, out TypeWithAnnotations underlyingType))
3800 3801
            {
                // Conversion of T to Nullable<T> is equivalent to new Nullable<T>(t).
V
vsadov 已提交
3802
                TrackNullableStateOfNullableValue(node, operand, convertedType, underlyingType);
3803 3804 3805
            }
        }

3806
        private void TrackNullableStateOfNullableValue(BoundExpression node, BoundExpression operand, TypeSymbol convertedType, TypeWithAnnotations underlyingType)
3807 3808 3809 3810 3811 3812 3813
        {
            int valueSlot = MakeSlot(operand);
            if (valueSlot > 0)
            {
                int containingSlot = GetOrCreateObjectCreationPlaceholderSlot(node);
                Debug.Assert(containingSlot > 0);
                TrackNullableStateOfNullableValue(containingSlot, convertedType, operand, underlyingType.ToTypeWithState(), valueSlot);
3814 3815 3816
            }
        }

C
Charles Stoner 已提交
3817 3818 3819
        public override BoundNode VisitTupleBinaryOperator(BoundTupleBinaryOperator node)
        {
            base.VisitTupleBinaryOperator(node);
3820
            SetNotNullResult(node);
C
Charles Stoner 已提交
3821 3822 3823
            return null;
        }

3824 3825
        private void ReportNullabilityMismatchWithTargetDelegate(SyntaxNode syntax, NamedTypeSymbol delegateType, MethodSymbol method)
        {
3826 3827
            Debug.Assert((object)method != null);
            Debug.Assert(method.MethodKind != MethodKind.LambdaMethod);
3828

3829 3830
            MethodSymbol invoke = delegateType?.DelegateInvokeMethod;
            if (invoke is null)
3831 3832 3833 3834
            {
                return;
            }

3835
            if (IsNullabilityMismatch(method.ReturnTypeWithAnnotations, invoke.ReturnTypeWithAnnotations, requireIdentity: false))
3836
            {
3837
                ReportSafetyDiagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, syntax,
3838
                    new FormattedSymbol(method, SymbolDisplayFormat.MinimallyQualifiedFormat),
3839 3840 3841 3842 3843 3844
                    delegateType);
            }

            int count = Math.Min(invoke.ParameterCount, method.ParameterCount);
            for (int i = 0; i < count; i++)
            {
3845 3846
                var invokeParameter = invoke.Parameters[i];
                var methodParameter = method.Parameters[i];
3847
                if (IsNullabilityMismatch(invokeParameter.TypeWithAnnotations, methodParameter.TypeWithAnnotations, requireIdentity: invokeParameter.RefKind != RefKind.None))
3848
                {
3849
                    ReportSafetyDiagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, syntax,
3850
                        new FormattedSymbol(methodParameter, SymbolDisplayFormat.ShortFormat),
3851
                        new FormattedSymbol(method, SymbolDisplayFormat.MinimallyQualifiedFormat),
3852 3853 3854 3855 3856
                        delegateType);
                }
            }
        }

3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876
        private void ReportNullabilityMismatchWithTargetDelegate(SyntaxNode syntax, NamedTypeSymbol delegateType, UnboundLambda unboundLambda)
        {
            if (!unboundLambda.HasExplicitlyTypedParameterList)
            {
                return;
            }

            MethodSymbol invoke = delegateType?.DelegateInvokeMethod;
            if (invoke is null)
            {
                return;
            }

            int count = Math.Min(invoke.ParameterCount, unboundLambda.ParameterCount);
            for (int i = 0; i < count; i++)
            {
                var invokeParameter = invoke.Parameters[i];
                // Parameter nullability is expected to match exactly. This corresponds to the behavior of initial binding.
                //    Action<string> x = (object o) => { }; // error CS1661: Cannot convert lambda expression to delegate type 'Action<string>' because the parameter types do not match the delegate parameter types
                //    Action<object> y = (object? o) => { }; // warning CS8622: Nullability of reference types in type of parameter 'o' of 'lambda expression' doesn't match the target delegate 'Action<object>'.
3877
                // https://github.com/dotnet/roslyn/issues/29959 Consider relaxing and allow implicit conversions of nullability.
3878
                // (Compare with method group conversions which pass `requireIdentity: false`.)
3879
                if (IsNullabilityMismatch(invokeParameter.TypeWithAnnotations, unboundLambda.ParameterTypeWithAnnotations(i), requireIdentity: true))
3880
                {
3881
                    // https://github.com/dotnet/roslyn/issues/29959 Consider using location of specific lambda parameter.
3882
                    ReportSafetyDiagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, syntax,
3883 3884 3885 3886 3887 3888 3889
                        unboundLambda.ParameterName(i),
                        unboundLambda.MessageID.Localize(),
                        delegateType);
                }
            }
        }

3890
        private bool IsNullabilityMismatch(TypeWithAnnotations source, TypeWithAnnotations destination, bool requireIdentity)
3891 3892 3893 3894 3895 3896 3897 3898 3899
        {
            if (!HasTopLevelNullabilityConversion(source, destination, requireIdentity))
            {
                return true;
            }
            if (requireIdentity)
            {
                return IsNullabilityMismatch(source, destination);
            }
3900 3901
            var sourceType = source.Type;
            var destinationType = destination.Type;
3902 3903 3904 3905
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
            return !_conversions.ClassifyImplicitConversionFromType(sourceType, destinationType, ref useSiteDiagnostics).Exists;
        }

3906
        private bool HasTopLevelNullabilityConversion(TypeWithAnnotations source, TypeWithAnnotations destination, bool requireIdentity)
3907 3908
        {
            return requireIdentity ?
3909 3910
                _conversions.HasTopLevelNullabilityIdentityConversion(source, destination) :
                _conversions.HasTopLevelNullabilityImplicitConversion(source, destination);
3911 3912
        }

3913 3914 3915 3916 3917 3918 3919 3920 3921
        /// <summary>
        /// Apply the conversion to the type of the operand and return the resulting type. (If the
        /// operand does not have an explicit type, the operand expression is used for the type.)
        /// If `checkConversion` is set, the incoming conversion is assumed to be from binding and will be
        /// re-calculated, this time considering nullability. (Note that the conversion calculation considers
        /// nested nullability only. The caller is responsible for checking the top-level nullability of
        /// the type returned by this method.) `canConvertNestedNullability` is set if the conversion
        /// considering nested nullability succeeded. `node` is used only for the location of diagnostics.
        /// </summary>
3922
        private TypeWithState ApplyConversion(
3923
            BoundExpression node,
3924 3925
            BoundExpression operandOpt,
            Conversion conversion,
3926
            TypeWithAnnotations targetTypeWithNullability,
3927
            TypeWithState operandType,
3928 3929
            bool checkConversion,
            bool fromExplicitCast,
3930 3931 3932 3933
            bool useLegacyWarnings,
            AssignmentKind assignmentKind,
            ParameterSymbol target = null,
            bool reportTopLevelWarnings = true,
3934
            bool reportRemainingWarnings = true,
3935
            bool extensionMethodThisArgument = false)
3936 3937
        {
            Debug.Assert(node != null);
3938
            Debug.Assert(operandOpt != null || !operandType.HasNullType);
N
Neal Gafter 已提交
3939
            Debug.Assert(targetTypeWithNullability.HasType);
3940
            Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
3941

3942
            NullableFlowState resultState = NullableFlowState.NotNull;
3943
            bool canConvertNestedNullability = true;
3944
            bool isSuppressed = false;
3945

3946
            if (operandOpt?.IsSuppressed == true)
3947 3948 3949
            {
                reportTopLevelWarnings = false;
                reportRemainingWarnings = false;
3950
                isSuppressed = true;
3951 3952
            }

3953
            TypeSymbol targetType = targetTypeWithNullability.Type;
3954
            switch (conversion.Kind)
3955
            {
3956
                case ConversionKind.MethodGroup:
3957
                    if (reportRemainingWarnings)
3958
                    {
3959
                        ReportNullabilityMismatchWithTargetDelegate(node.Syntax, targetType.GetDelegateType(), conversion.Method);
3960
                    }
3961
                    resultState = NullableFlowState.NotNull;
3962 3963
                    break;

3964
                case ConversionKind.AnonymousFunction:
3965 3966 3967 3968
                    if (operandOpt.Kind == BoundKind.Lambda)
                    {
                        var lambda = (BoundLambda)operandOpt;
                        var delegateType = targetType.GetDelegateType();
3969
                        var unboundLambda = lambda.UnboundLambda;
3970 3971
                        var variableState = GetVariableState();
                        Analyze(compilation, lambda, Diagnostics, delegateInvokeMethod: delegateType?.DelegateInvokeMethod, returnTypes: null, initialState: variableState);
3972
                        if (reportRemainingWarnings)
3973 3974 3975
                        {
                            ReportNullabilityMismatchWithTargetDelegate(node.Syntax, delegateType, unboundLambda);
                        }
3976 3977

                        return new TypeWithState(targetType, NullableFlowState.NotNull);
3978 3979 3980
                    }
                    break;

3981
                case ConversionKind.InterpolatedString:
3982
                    resultState = NullableFlowState.NotNull;
3983
                    break;
3984

3985 3986
                case ConversionKind.ExplicitUserDefined:
                case ConversionKind.ImplicitUserDefined:
3987
                    // cf. Binder.CreateUserDefinedConversion
3988
                    {
3989
                        if (!conversion.IsValid)
3990 3991 3992
                        {
                            break;
                        }
3993 3994

                        // operand -> conversion "from" type
3995
                        // May be distinct from method parameter type for Nullable<T>.
3996 3997 3998 3999
                        operandType = ApplyConversion(
                            node,
                            operandOpt,
                            conversion.UserDefinedFromConversion,
4000
                            TypeWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.FromType),
4001
                            operandType,
4002
                            checkConversion: true,
4003
                            fromExplicitCast: false,
4004
                            useLegacyWarnings,
4005 4006
                            assignmentKind,
                            target);
4007

4008
                        // Update method based on operandType: see https://github.com/dotnet/roslyn/issues/29605.
4009
                        // (see NullableReferenceTypesTests.ImplicitConversions_07).
4010 4011 4012 4013
                        var methodOpt = conversion.Method;
                        Debug.Assert((object)methodOpt != null);
                        Debug.Assert(methodOpt.ParameterCount == 1);
                        var parameter = methodOpt.Parameters[0];
4014
                        var parameterType = parameter.TypeWithAnnotations;
4015 4016 4017 4018 4019 4020 4021 4022
                        TypeWithState underlyingOperandType = default;
                        bool isLiftedConversion = false;
                        if (operandType.Type.IsNullableType() && !parameterType.IsNullableType())
                        {
                            var underlyingOperandTypeWithAnnotations = operandType.Type.GetNullableUnderlyingTypeWithAnnotations();
                            underlyingOperandType = underlyingOperandTypeWithAnnotations.ToTypeWithState();
                            isLiftedConversion = parameterType.Equals(underlyingOperandTypeWithAnnotations, TypeCompareKind.AllIgnoreOptions);
                        }
4023 4024

                        // conversion "from" type -> method parameter type
4025
                        NullableFlowState operandState = operandType.State;
4026
                        _ = ClassifyAndApplyConversion(operandOpt ?? node, parameterType, isLiftedConversion ? underlyingOperandType : operandType,
4027
                            useLegacyWarnings, AssignmentKind.Argument, target: parameter, reportWarnings: reportRemainingWarnings);
4028 4029

                        // method parameter type -> method return type
4030
                        var methodReturnType = methodOpt.ReturnTypeWithAnnotations;
4031 4032
                        if (isLiftedConversion)
                        {
4033
                            operandType = LiftedReturnType(methodReturnType, operandState);
4034
                            if (RequiresSafetyWarningWhenNullIntroduced(methodReturnType.Type) && operandState == NullableFlowState.MaybeNull)
4035 4036 4037
                            {
                                ReportNullableAssignmentIfNecessary(node, targetTypeWithNullability, operandType, useLegacyWarnings: useLegacyWarnings, assignmentKind, target, conversion: conversion);
                            }
4038 4039 4040
                        }
                        else
                        {
4041
                            operandType = methodReturnType.ToTypeWithState();
4042
                        }
4043 4044

                        // method return type -> conversion "to" type
4045
                        // May be distinct from method return type for Nullable<T>.
4046
                        operandType = ClassifyAndApplyConversion(operandOpt ?? node, TypeWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.ToType), operandType,
4047
                            useLegacyWarnings, assignmentKind, target, reportWarnings: reportRemainingWarnings);
4048 4049

                        // conversion "to" type -> final type
4050
                        // https://github.com/dotnet/roslyn/issues/29959 If the original conversion was
4051
                        // explicit, this conversion should not report nested nullability mismatches.
4052
                        // (see NullableReferenceTypesTests.ExplicitCast_UserDefined_02).
4053 4054
                        operandType = ClassifyAndApplyConversion(node, targetTypeWithNullability, operandType,
                            useLegacyWarnings, assignmentKind, target, reportWarnings: reportRemainingWarnings);
4055
                        return operandType;
4056
                    }
4057

4058 4059
                case ConversionKind.ExplicitDynamic:
                case ConversionKind.ImplicitDynamic:
4060
                case ConversionKind.Unboxing:
N
Neal Gafter 已提交
4061
                case ConversionKind.Boxing:
4062
                    resultState = operandType.State;
4063 4064
                    break;

N
Neal Gafter 已提交
4065
                case ConversionKind.ImplicitThrow:
4066
                    break;
4067

4068
                case ConversionKind.NoConversion:
4069
                case ConversionKind.DefaultOrNullLiteral:
4070 4071 4072 4073
                    checkConversion = false;
                    goto case ConversionKind.Identity;

                case ConversionKind.Identity:
4074 4075 4076 4077 4078 4079 4080 4081
                    // If the operand is an explicit conversion, and this identity conversion
                    // is converting to the same type including nullability, skip the conversion
                    // to avoid reporting redundant warnings. Also check useLegacyWarnings
                    // since that value was used when reporting warnings for the explicit cast.
                    if (useLegacyWarnings && operandOpt?.Kind == BoundKind.Conversion)
                    {
                        var operandConversion = (BoundConversion)operandOpt;
                        var explicitType = operandConversion.ConversionGroupOpt.ExplicitType;
4082
                        if (explicitType.HasType && explicitType.Equals(targetTypeWithNullability, TypeCompareKind.ConsiderEverything))
4083 4084 4085 4086
                        {
                            return operandType;
                        }
                    }
4087
                    if (operandType.Type?.IsTupleType == true)
4088 4089 4090
                    {
                        goto case ConversionKind.ImplicitTuple;
                    }
4091 4092
                    goto case ConversionKind.ImplicitReference;

4093
                case ConversionKind.ImplicitReference:
N
Neal Gafter 已提交
4094 4095 4096
                    if (reportTopLevelWarnings &&
                        operandOpt?.Kind == BoundKind.Literal &&
                        operandOpt.ConstantValue?.IsNull == true &&
4097 4098
                        !isSuppressed &&
                        RequiresSafetyWarningWhenNullIntroduced(targetType))
4099
                    {
4100 4101 4102
                        // For type parameters that cannot be annotated, the analysis must report those
                        // places where null values first sneak in, like `default`, `null`, and `GetFirstOrDefault`.
                        // This is one of those places.
N
Neal Gafter 已提交
4103
                        ReportSafetyDiagnostic(ErrorCode.WRN_NullLiteralMayIntroduceNullT, node.Syntax, targetType);
F
Fredric Silberberg 已提交
4104
                    }
4105
                    goto case ConversionKind.ExplicitReference;
4106

4107 4108 4109 4110 4111 4112 4113
                case ConversionKind.ExplicitReference:
                    // Inherit state from the operand.
                    if (checkConversion)
                    {
                        // https://github.com/dotnet/roslyn/issues/29959 Assert conversion is similar to original.
                        conversion = GenerateConversion(_conversions, operandOpt, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument);
                        canConvertNestedNullability = conversion.Exists;
4114
                    }
4115 4116

                    resultState = operandType.State;
4117
                    break;
4118

4119
                case ConversionKind.ImplicitNullable:
4120 4121
                    if (checkConversion)
                    {
4122
                        conversion = GenerateConversion(_conversions, operandOpt, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument);
4123 4124
                        canConvertNestedNullability = conversion.Exists;
                    }
4125 4126

                    resultState = operandType.State;
4127 4128
                    break;

4129
                case ConversionKind.ExplicitNullable:
4130
                    if (operandType.Type?.IsNullableType() == true && !targetType.IsNullableType())
4131 4132
                    {
                        // Explicit conversion of Nullable<T> to T is equivalent to Nullable<T>.Value.
4133
                        if (reportTopLevelWarnings && operandType.MayBeNull)
4134
                        {
4135
                            ReportSafetyDiagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, node.Syntax);
4136
                        }
4137

4138 4139 4140 4141 4142 4143 4144
                        // Mark the value as not nullable, regardless of whether it was known to be nullable,
                        // because the implied call to `.Value` will only succeed if not null.
                        if (operandOpt != null)
                        {
                            int slot = MakeSlot(operandOpt);
                            if (slot > 0)
                            {
4145
                                this.State[slot] = NullableFlowState.NotNull;
4146 4147 4148 4149 4150
                            }
                        }
                    }
                    goto case ConversionKind.ImplicitNullable;

4151 4152 4153 4154 4155 4156 4157
                case ConversionKind.ImplicitTupleLiteral:
                case ConversionKind.ImplicitTuple:
                case ConversionKind.ExplicitTupleLiteral:
                case ConversionKind.ExplicitTuple:
                    if (checkConversion)
                    {
                        // https://github.com/dotnet/roslyn/issues/29699: Report warnings for user-defined conversions on tuple elements.
4158
                        conversion = GenerateConversion(_conversions, operandOpt, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument);
4159 4160
                        canConvertNestedNullability = conversion.Exists;
                    }
4161
                    resultState = NullableFlowState.NotNull;
4162 4163
                    break;

4164 4165 4166 4167
                case ConversionKind.Deconstruction:
                    // Can reach here, with an error type, when the
                    // Deconstruct method is missing or inaccessible.
                    break;
4168

4169 4170 4171
                case ConversionKind.ExplicitEnumeration:
                    // Can reach here, with an error type.
                    break;
4172

4173
                default:
F
Fredric Silberberg 已提交
4174
                    Debug.Assert(targetType.IsValueType);
4175
                    break;
4176
            }
4177

4178 4179
            if (isSuppressed)
            {
4180 4181
                resultState = NullableFlowState.NotNull;
            }
4182
            else if (fromExplicitCast && targetTypeWithNullability.NullableAnnotation.IsAnnotated() && !targetType.IsNullableType())
4183
            {
N
Neal Gafter 已提交
4184
                // An explicit cast to a nullable reference type introduces nullability
4185
                resultState = NullableFlowState.MaybeNull;
4186 4187
            }

4188
            var resultType = new TypeWithState(targetType, resultState);
4189

4190
            if (operandType.Type?.IsErrorType() != true && !targetType.IsErrorType())
4191
            {
4192 4193
                // Need to report all warnings that apply since the warnings can be suppressed individually.
                if (reportTopLevelWarnings)
4194
                {
4195 4196 4197 4198 4199 4200 4201 4202 4203 4204
                    if (RequiresSafetyWarningWhenNullIntroduced(targetType) && conversion.IsImplicit && !conversion.IsDynamic)
                    {
                        // For type parameters that cannot be annotated, the analysis must report those
                        // places where null values first sneak in, like `default`, `null`, and `GetFirstOrDefault`,
                        // as a safety diagnostic.  But we do not warn when such values flow through implicit conversion.
                    }
                    else
                    {
                        ReportNullableAssignmentIfNecessary(node, targetTypeWithNullability, operandType, useLegacyWarnings, assignmentKind, target, conversion: conversion);
                    }
4205
                }
4206
                if (reportRemainingWarnings && !canConvertNestedNullability)
4207
                {
4208 4209
                    if (assignmentKind == AssignmentKind.Argument)
                    {
4210
                        ReportNullabilityMismatchInArgument(node, operandType.Type, target, targetType, forOutput: false);
4211 4212 4213
                    }
                    else
                    {
4214
                        ReportNullabilityMismatchInAssignment(node.Syntax, GetTypeAsDiagnosticArgument(operandType.Type), targetType);
4215
                    }
4216 4217 4218 4219
                }
            }

            return resultType;
4220 4221
        }

4222 4223 4224
        /// <summary>
        /// Return the return type for a lifted operator, given the nullability state of its operands.
        /// </summary>
4225
        private TypeWithState LiftedReturnType(TypeWithAnnotations returnType, NullableFlowState operandState)
4226
        {
4227
            bool typeNeedsLifting = returnType.Type.IsNonNullableValueType();
4228 4229
            TypeSymbol type = typeNeedsLifting
                ? compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(returnType))
4230
                : returnType.Type;
4231
            NullableFlowState state = returnType.ToTypeWithState().State.Join(operandState);
4232 4233 4234 4235 4236
            return new TypeWithState(type, state);
        }

        private TypeWithState ClassifyAndApplyConversion(
            BoundExpression node,
4237
            TypeWithAnnotations targetType,
4238 4239 4240 4241 4242
            TypeWithState operandType,
            bool useLegacyWarnings,
            AssignmentKind assignmentKind,
            ParameterSymbol target,
            bool reportWarnings)
4243
        {
4244
            Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
4245
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
4246
            var conversion = _conversions.ClassifyStandardConversion(null, operandType.Type, targetType.Type, ref useSiteDiagnostics);
4247
            if (reportWarnings && !conversion.Exists)
4248
            {
4249 4250
                if (assignmentKind == AssignmentKind.Argument)
                {
4251
                    ReportNullabilityMismatchInArgument(node, operandType.Type, target, targetType.Type, forOutput: false);
4252 4253 4254
                }
                else
                {
4255
                    ReportNullabilityMismatchInAssignment(node.Syntax, operandType.Type, targetType.Type);
4256
                }
4257
            }
4258

4259 4260 4261 4262 4263 4264 4265 4266 4267 4268
            return ApplyConversion(
                node,
                operandOpt: null,
                conversion,
                targetType,
                operandType,
                checkConversion: false,
                fromExplicitCast: false,
                useLegacyWarnings: useLegacyWarnings,
                assignmentKind,
4269 4270 4271
                target,
                reportTopLevelWarnings: reportWarnings,
                reportRemainingWarnings: reportWarnings);
4272 4273
        }

4274 4275 4276 4277 4278 4279 4280 4281 4282
        public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
        {
            if (node.MethodOpt?.MethodKind == MethodKind.LocalFunction)
            {
                var syntax = node.Syntax;
                var localFunc = (LocalFunctionSymbol)node.MethodOpt.OriginalDefinition;
                ReplayReadsAndWrites(localFunc, syntax, writes: false);
            }

4283
            base.VisitDelegateCreationExpression(node);
4284
            SetNotNullResult(node);
4285
            return null;
4286 4287 4288 4289 4290 4291 4292 4293 4294
        }

        public override BoundNode VisitMethodGroup(BoundMethodGroup node)
        {
            Debug.Assert(!IsConditionalState);

            BoundExpression receiverOpt = node.ReceiverOpt;
            if (receiverOpt != null)
            {
J
Cleanup  
Jared Parsons 已提交
4295
                VisitRvalue(receiverOpt);
4296 4297 4298 4299
                // https://github.com/dotnet/roslyn/issues/30563: Should not check receiver here.
                // That check should be handled when applying the method group conversion,
                // when we have a specific method, to avoid reporting null receiver warnings
                // for extension method delegates.
4300 4301 4302
                CheckPossibleNullReceiver(receiverOpt);
            }

4303
            SetNotNullResult(node);
4304 4305 4306 4307 4308
            return null;
        }

        public override BoundNode VisitLambda(BoundLambda node)
        {
4309
            SetNotNullResult(node);
4310
            return null;
4311 4312 4313 4314
        }

        public override BoundNode VisitUnboundLambda(UnboundLambda node)
        {
4315 4316 4317 4318
            // The presence of this node suggests an error was detected in an earlier phase.
            // Analyze the body to report any additional warnings.
            var lambda = node.BindForErrorRecovery();
            Analyze(compilation, lambda, Diagnostics, delegateInvokeMethod: null, returnTypes: null, initialState: GetVariableState());
4319
            SetNotNullResult(node);
4320 4321 4322
            return null;
        }

4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339
        public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
        {
            var body = node.Body;
            if (body != null)
            {
                Analyze(
                    compilation,
                    node.Symbol,
                    body,
                    Diagnostics,
                    useMethodSignatureReturnType: false,
                    useMethodSignatureParameterTypes: false,
                    methodSignatureOpt: null,
                    returnTypes: null,
                    initialState: GetVariableState(),
                    callbackOpt: _callbackOpt);
            }
4340
            ResultType = _invalidType;
4341 4342 4343
            return null;
        }

4344 4345
        public override BoundNode VisitThisReference(BoundThisReference node)
        {
4346
            VisitThisOrBaseReference(node);
4347 4348 4349
            return null;
        }

4350 4351
        private void VisitThisOrBaseReference(BoundExpression node)
        {
N
Neal Gafter 已提交
4352
            var rvalueResult = new TypeWithState(node.Type, NullableFlowState.NotNull);
4353
            var lvalueResult = TypeWithAnnotations.Create(node.Type, NullableAnnotation.NotAnnotated);
N
Neal Gafter 已提交
4354
            SetResult(rvalueResult, lvalueResult);
4355 4356
        }

4357 4358
        public override BoundNode VisitParameter(BoundParameter node)
        {
4359 4360 4361
            var parameter = node.ParameterSymbol;
            int slot = GetOrCreateSlot(parameter);
            var type = GetDeclaredParameterResult(parameter);
4362
            SetResult(GetAdjustedResult(type, slot), type);
4363 4364 4365 4366 4367 4368 4369
            return null;
        }

        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            Debug.Assert(!IsConditionalState);

4370
            var left = node.Left;
4371
            var right = node.Right;
4372
            Visit(left);
4373
            TypeWithAnnotations leftLValueType = LvalueResultType;
4374

4375
            if (left.Kind == BoundKind.EventAccess && ((BoundEventAccess)left).EventSymbol.IsWindowsRuntimeEvent)
4376 4377 4378 4379
            {
                // Event assignment is a call to an Add method. (Note that assignment
                // of non-field-like events uses BoundEventAssignmentOperator
                // rather than BoundAssignmentOperator.)
J
Cleanup  
Jared Parsons 已提交
4380
                VisitRvalue(right);
4381
                SetNotNullResult(node);
4382 4383
            }
            else
4384
            {
4385 4386 4387 4388 4389 4390 4391 4392 4393 4394
                TypeWithState rightType;
                if (!node.IsRef)
                {
                    rightType = VisitOptionalImplicitConversion(right, leftLValueType, UseLegacyWarnings(left), AssignmentKind.Assignment);
                }
                else
                {
                    rightType = VisitRefExpression(right, leftLValueType);
                }

4395
                TrackNullableStateForAssignment(right, leftLValueType, MakeSlot(left), rightType, MakeSlot(right));
4396
                SetResult(new TypeWithState(leftLValueType.Type, rightType.State), leftLValueType);
4397 4398 4399 4400 4401
            }

            return null;
        }

4402 4403 4404 4405 4406
        private static bool UseLegacyWarnings(BoundExpression expr)
        {
            switch (expr.Kind)
            {
                case BoundKind.Local:
4407
                    return !RequiresSafetyWarningWhenNullIntroduced(expr.Type);
4408 4409
                case BoundKind.Parameter:
                    RefKind kind = ((BoundParameter)expr).ParameterSymbol.RefKind;
4410
                    return kind == RefKind.None && !RequiresSafetyWarningWhenNullIntroduced(expr.Type);
4411 4412 4413 4414 4415
                default:
                    return false;
            }
        }

4416 4417
        public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node)
        {
4418 4419 4420 4421 4422 4423 4424 4425
            var left = node.Left;
            var right = node.Right;
            var variables = GetDeconstructionAssignmentVariables(left);

            if (node.HasErrors)
            {
                // In the case of errors, simply visit the right as an r-value to update
                // any nullability state even though deconstruction is skipped.
J
Cleanup  
Jared Parsons 已提交
4426
                VisitRvalue(right.Operand);
4427 4428 4429 4430 4431 4432 4433 4434
            }
            else
            {
                VisitDeconstructionArguments(variables, right.Conversion, right.Operand);
            }

            variables.FreeAll(v => v.NestedVariables);

4435 4436 4437
            // https://github.com/dotnet/roslyn/issues/33011: Result type should be inferred and the constraints should
            // be re-verified. Even though the standard tuple type has no constraints we support that scenario. Constraints_78
            // has a test for this case that should start failing when this is fixed.
4438
            SetNotNullResult(node);
4439 4440 4441
            return null;
        }

4442 4443 4444 4445 4446 4447 4448 4449
        private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> variables, Conversion conversion, BoundExpression right)
        {
            Debug.Assert(conversion.Kind == ConversionKind.Deconstruction);

            int n = variables.Count;

            if (!conversion.DeconstructionInfo.IsDefault)
            {
J
Cleanup  
Jared Parsons 已提交
4450
                VisitRvalue(right);
4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485

                var invocation = conversion.DeconstructionInfo.Invocation as BoundCall;
                var deconstructMethod = invocation?.Method;

                if ((object)deconstructMethod != null)
                {
                    if (!invocation.InvokedAsExtensionMethod)
                    {
                        CheckPossibleNullReceiver(right);
                    }
                    else
                    {
                        // https://github.com/dotnet/roslyn/issues/33006: Check nullability of `this` argument.
                    }

                    // https://github.com/dotnet/roslyn/issues/33006: Update `Deconstruct` method
                    // based on inferred receiver type, and check constraints.

                    var parameters = deconstructMethod.Parameters;
                    int offset = invocation.InvokedAsExtensionMethod ? 1 : 0;
                    Debug.Assert(parameters.Length - offset == n);

                    for (int i = 0; i < n; i++)
                    {
                        var variable = variables[i];
                        var underlyingConversion = conversion.UnderlyingConversions[i];
                        var nestedVariables = variable.NestedVariables;
                        if (nestedVariables != null)
                        {
                            // https://github.com/dotnet/roslyn/issues/33005: Not handling deconstructing argument of Deconstruct.
                            //VisitDeconstructionArguments(nestedVariables, underlyingConversion, arg);
                        }
                        else
                        {
                            var parameter = parameters[i + offset];
4486
                            VisitArgumentConversion(
4487
                                variable.Expression, underlyingConversion, parameter.RefKind, parameter, parameter.TypeWithAnnotations,
4488
                                new VisitResult(variable.Type.ToTypeWithState(), variable.Type), extensionMethodThisArgument: false);
4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500
                        }
                    }
                }
            }
            else
            {
                var rightParts = GetDeconstructionRightParts(right);
                Debug.Assert(rightParts.Length == n);

                for (int i = 0; i < n; i++)
                {
                    var variable = variables[i];
C
Charles Stoner 已提交
4501
                    var underlyingConversion = conversion.UnderlyingConversions[i];
4502 4503 4504 4505
                    var rightPart = rightParts[i];
                    var nestedVariables = variable.NestedVariables;
                    if (nestedVariables != null)
                    {
C
Charles Stoner 已提交
4506
                        VisitDeconstructionArguments(nestedVariables, underlyingConversion, rightPart);
4507 4508 4509 4510
                    }
                    else
                    {
                        var targetType = variable.Type;
4511 4512
                        TypeWithState operandType;
                        TypeWithState valueType;
C
Charles Stoner 已提交
4513 4514 4515 4516 4517 4518 4519 4520 4521
                        int valueSlot;
                        if (underlyingConversion.IsIdentity)
                        {
                            operandType = default;
                            valueType = VisitOptionalImplicitConversion(rightPart, targetType, useLegacyWarnings: true, AssignmentKind.Assignment);
                            valueSlot = MakeSlot(rightPart);
                        }
                        else
                        {
4522
                            operandType = VisitRvalueWithState(rightPart);
C
Charles Stoner 已提交
4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537
                            valueType = ApplyConversion(
                                rightPart,
                                rightPart,
                                underlyingConversion,
                                targetType,
                                operandType,
                                checkConversion: true,
                                fromExplicitCast: false,
                                useLegacyWarnings: true,
                                AssignmentKind.Assignment,
                                reportTopLevelWarnings: true,
                                reportRemainingWarnings: true);
                            valueSlot = -1;
                        }

4538
                        int targetSlot = MakeSlot(variable.Expression);
C
Charles Stoner 已提交
4539 4540 4541 4542 4543 4544
                        TrackNullableStateForAssignment(rightPart, targetType, targetSlot, valueType, valueSlot);

                        // Conversion of T to Nullable<T> is equivalent to new Nullable<T>(t).
                        // (Should this check be moved to VisitOptionalImplicitConversion or TrackNullableStateForAssignment?)
                        if (targetSlot > 0 &&
                            underlyingConversion.Kind == ConversionKind.ImplicitNullable &&
4545
                            AreNullableAndUnderlyingTypes(targetType.Type, operandType.Type, out TypeWithAnnotations underlyingType))
C
Charles Stoner 已提交
4546 4547 4548 4549
                        {
                            valueSlot = MakeSlot(rightPart);
                            if (valueSlot > 0)
                            {
4550 4551
                                var valueBeforeNullableWrapping = new TypeWithState(underlyingType.Type, NullableFlowState.NotNull);
                                TrackNullableStateOfNullableValue(targetSlot, targetType.Type, rightPart, valueBeforeNullableWrapping, valueSlot);
C
Charles Stoner 已提交
4552 4553
                            }
                        }
4554 4555 4556 4557 4558 4559 4560 4561
                    }
                }
            }
        }

        private readonly struct DeconstructionVariable
        {
            internal readonly BoundExpression Expression;
4562
            internal readonly TypeWithAnnotations Type;
4563 4564
            internal readonly ArrayBuilder<DeconstructionVariable> NestedVariables;

4565
            internal DeconstructionVariable(BoundExpression expression, TypeWithAnnotations type)
4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597
            {
                Expression = expression;
                Type = type;
                NestedVariables = null;
            }

            internal DeconstructionVariable(ArrayBuilder<DeconstructionVariable> nestedVariables)
            {
                Expression = null;
                Type = default;
                NestedVariables = nestedVariables;
            }
        }

        private ArrayBuilder<DeconstructionVariable> GetDeconstructionAssignmentVariables(BoundTupleExpression tuple)
        {
            var arguments = tuple.Arguments;
            var builder = ArrayBuilder<DeconstructionVariable>.GetInstance(arguments.Length);
            foreach (var argument in arguments)
            {
                builder.Add(getDeconstructionAssignmentVariable(argument));
            }
            return builder;

            DeconstructionVariable getDeconstructionAssignmentVariable(BoundExpression expr)
            {
                switch (expr.Kind)
                {
                    case BoundKind.TupleLiteral:
                    case BoundKind.ConvertedTupleLiteral:
                        return new DeconstructionVariable(GetDeconstructionAssignmentVariables((BoundTupleExpression)expr));
                    default:
4598
                        Visit(expr);
4599
                        return new DeconstructionVariable(expr, LvalueResultType);
4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632
                }
            }
        }

        /// <summary>
        /// Return the sub-expressions for the righthand side of a deconstruction
        /// assignment. cf. LocalRewriter.GetRightParts.
        /// </summary>
        private ImmutableArray<BoundExpression> GetDeconstructionRightParts(BoundExpression expr)
        {
            switch (expr.Kind)
            {
                case BoundKind.TupleLiteral:
                case BoundKind.ConvertedTupleLiteral:
                    return ((BoundTupleExpression)expr).Arguments;
                case BoundKind.Conversion:
                    {
                        var conv = (BoundConversion)expr;
                        switch (conv.ConversionKind)
                        {
                            case ConversionKind.Identity:
                            case ConversionKind.ImplicitTupleLiteral:
                                return GetDeconstructionRightParts(conv.Operand);
                        }
                    }
                    break;
            }

            if (expr.Type is TupleTypeSymbol tupleType)
            {
                // https://github.com/dotnet/roslyn/issues/33011: Should include conversion.UnderlyingConversions[i].
                // For instance, Boxing conversions (see Deconstruction_ImplicitBoxingConversion_02) and
                // ImplicitNullable conversions (see Deconstruction_ImplicitNullableConversion_02).
J
Cleanup  
Jared Parsons 已提交
4633
                VisitRvalue(expr);
4634 4635 4636 4637 4638 4639 4640
                var fields = tupleType.TupleElements;
                return fields.SelectAsArray((f, e) => (BoundExpression)new BoundFieldAccess(e.Syntax, e, f, constantValueOpt: null), expr);
            }

            throw ExceptionUtilities.Unreachable;
        }

4641 4642 4643 4644
        public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
        {
            Debug.Assert(!IsConditionalState);

4645
            var operandType = VisitRvalueWithState(node.Operand);
4646
            var operandLvalue = LvalueResultType;
4647 4648
            bool setResult = false;

4649 4650
            if (this.State.Reachable)
            {
4651
                // https://github.com/dotnet/roslyn/issues/29961 Update increment method based on operand type.
4652
                MethodSymbol incrementOperator = (node.OperatorKind.IsUserDefined() && (object)node.MethodOpt != null && node.MethodOpt.ParameterCount == 1) ? node.MethodOpt : null;
4653
                TypeWithAnnotations targetTypeOfOperandConversion;
4654 4655
                AssignmentKind assignmentKind = AssignmentKind.Assignment;
                ParameterSymbol target = null;
4656

4657
                // https://github.com/dotnet/roslyn/issues/29961 Update conversion method based on operand type.
4658 4659
                if (node.OperandConversion.IsUserDefined && (object)node.OperandConversion.Method != null && node.OperandConversion.Method.ParameterCount == 1)
                {
4660
                    targetTypeOfOperandConversion = node.OperandConversion.Method.ReturnTypeWithAnnotations;
4661 4662 4663
                }
                else if ((object)incrementOperator != null)
                {
4664
                    targetTypeOfOperandConversion = incrementOperator.Parameters[0].TypeWithAnnotations;
4665 4666
                    assignmentKind = AssignmentKind.Argument;
                    target = incrementOperator.Parameters[0];
4667 4668 4669 4670
                }
                else
                {
                    // Either a built-in increment, or an error case.
4671
                    targetTypeOfOperandConversion = default;
4672 4673
                }

4674
                TypeWithState resultOfOperandConversionType;
4675

N
Neal Gafter 已提交
4676
                if (targetTypeOfOperandConversion.HasType)
4677
                {
4678
                    // https://github.com/dotnet/roslyn/issues/29961 Should something special be done for targetTypeOfOperandConversion for lifted case?
4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690
                    resultOfOperandConversionType = ApplyConversion(
                        node.Operand,
                        node.Operand,
                        node.OperandConversion,
                        targetTypeOfOperandConversion,
                        operandType,
                        checkConversion: true,
                        fromExplicitCast: false,
                        useLegacyWarnings: false,
                        assignmentKind,
                        target,
                        reportTopLevelWarnings: true,
4691
                        reportRemainingWarnings: true);
4692 4693 4694
                }
                else
                {
4695
                    resultOfOperandConversionType = operandType;
4696 4697
                }

4698
                TypeWithState resultOfIncrementType;
N
Neal Gafter 已提交
4699
                if (incrementOperator is null)
4700
                {
4701
                    resultOfIncrementType = resultOfOperandConversionType;
4702
                }
4703
                else
4704
                {
4705
                    resultOfIncrementType = incrementOperator.ReturnTypeWithAnnotations.ToTypeWithState();
4706 4707
                }

N
Neal Gafter 已提交
4708
                var operandTypeWithAnnotations = operandType.ToTypeWithAnnotations();
4709 4710 4711 4712
                resultOfIncrementType = ApplyConversion(
                    node,
                    node,
                    node.ResultConversion,
4713
                    operandTypeWithAnnotations,
4714 4715 4716 4717 4718
                    resultOfIncrementType,
                    checkConversion: true,
                    fromExplicitCast: false,
                    useLegacyWarnings: false,
                    AssignmentKind.Assignment);
4719

4720
                // https://github.com/dotnet/roslyn/issues/29961 Check node.Type.IsErrorType() instead?
4721
                if (!node.HasErrors)
4722
                {
4723
                    var op = node.OperatorKind.Operator();
4724
                    ResultType = (op == UnaryOperatorKind.PrefixIncrement || op == UnaryOperatorKind.PrefixDecrement) ? resultOfIncrementType : operandType;
4725 4726
                    setResult = true;

4727
                    TrackNullableStateForAssignment(node, targetType: operandLvalue, targetSlot: MakeSlot(node.Operand), valueType: resultOfIncrementType);
4728 4729
                }
            }
4730 4731

            if (!setResult)
4732
            {
4733
                SetNotNullResult(node);
4734 4735 4736 4737 4738 4739 4740
            }

            return null;
        }

        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
4741
            Visit(node.Left);
4742
            TypeWithAnnotations leftLValueType = LvalueResultType;
4743
            TypeWithState leftResultType = ResultType;
4744 4745 4746

            Debug.Assert(!IsConditionalState);

4747
            TypeWithState leftOnRightType = GetAdjustedResult(leftResultType, MakeSlot(node.Left));
4748

4749 4750 4751 4752 4753 4754 4755 4756
            // https://github.com/dotnet/roslyn/issues/29962 Update operator based on inferred argument types.
            if ((object)node.Operator.LeftType != null)
            {
                // https://github.com/dotnet/roslyn/issues/29962 Ignoring top-level nullability of operator left parameter.
                leftOnRightType = ApplyConversion(
                    node.Left,
                    node.Left,
                    node.LeftConversion,
4757
                    TypeWithAnnotations.Create(node.Operator.LeftType),
4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769
                    leftOnRightType,
                    checkConversion: true,
                    fromExplicitCast: false,
                    useLegacyWarnings: false,
                    AssignmentKind.Assignment,
                    reportTopLevelWarnings: false,
                    reportRemainingWarnings: true);
            }
            else
            {
                leftOnRightType = default;
            }
4770

4771
            TypeWithState resultType;
4772 4773 4774 4775
            TypeWithState rightType = VisitRvalueWithState(node.Right);
            if ((object)node.Operator.ReturnType != null)
            {
                if (node.Operator.Kind.IsUserDefined() && (object)node.Operator.Method != null && node.Operator.Method.ParameterCount == 2)
4776
                {
4777 4778
                    ReportArgumentWarnings(node.Left, leftOnRightType, node.Operator.Method.Parameters[0]);
                    ReportArgumentWarnings(node.Right, rightType, node.Operator.Method.Parameters[1]);
4779 4780
                }

4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795
                resultType = InferResultNullability(node.Operator.Kind, node.Operator.Method, node.Operator.ReturnType, leftOnRightType, rightType);
                resultType = ApplyConversion(
                    node,
                    node,
                    node.FinalConversion,
                    leftLValueType,
                    resultType,
                    checkConversion: true,
                    fromExplicitCast: false,
                    useLegacyWarnings: false,
                    AssignmentKind.Assignment);
            }
            else
            {
                resultType = new TypeWithState(node.Type, NullableFlowState.NotNull);
4796 4797
            }

4798 4799
            TrackNullableStateForAssignment(node, leftLValueType, MakeSlot(node.Left), resultType);
            ResultType = resultType;
4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810
            return null;
        }

        public override BoundNode VisitFixedLocalCollectionInitializer(BoundFixedLocalCollectionInitializer node)
        {
            var initializer = node.Expression;
            if (initializer.Kind == BoundKind.AddressOfOperator)
            {
                initializer = ((BoundAddressOfOperator)initializer).Operand;
            }

J
Cleanup  
Jared Parsons 已提交
4811
            VisitRvalue(initializer);
4812
            SetNotNullResult(node);
4813 4814 4815 4816 4817
            return null;
        }

        public override BoundNode VisitAddressOfOperator(BoundAddressOfOperator node)
        {
4818
            SetNotNullResult(node);
4819 4820 4821
            return null;
        }

4822
        private void ReportArgumentWarnings(BoundExpression argument, TypeWithState argumentType, ParameterSymbol parameter)
4823
        {
4824
            var paramType = parameter.TypeWithAnnotations;
4825
            ReportNullableAssignmentIfNecessary(argument, paramType, argumentType, useLegacyWarnings: false, AssignmentKind.Argument, target: parameter);
4826

4827
            if (!argumentType.HasNullType && IsNullabilityMismatch(paramType.Type, argumentType.Type))
4828
            {
4829
                ReportNullabilityMismatchInArgument(argument, argumentType.Type, parameter, paramType.Type, forOutput: false);
4830 4831 4832
            }
        }

4833
        private void ReportNullabilityMismatchInRefArgument(BoundExpression argument, TypeWithAnnotations argumentType, ParameterSymbol parameter, TypeWithAnnotations parameterType)
4834 4835 4836 4837 4838 4839 4840
        {
            ReportSafetyDiagnostic(ErrorCode.WRN_NullabilityMismatchInArgument,
                argument.Syntax, argumentType, parameterType,
                new FormattedSymbol(parameter, SymbolDisplayFormat.ShortFormat),
                new FormattedSymbol(parameter.ContainingSymbol, SymbolDisplayFormat.MinimallyQualifiedFormat));
        }

4841 4842 4843 4844
        /// <summary>
        /// Report warning passing argument where nested nullability does not match
        /// parameter (e.g.: calling `void F(object[] o)` with `F(new[] { maybeNull })`).
        /// </summary>
4845
        private void ReportNullabilityMismatchInArgument(BoundExpression argument, TypeSymbol argumentType, ParameterSymbol parameter, TypeSymbol parameterType, bool forOutput)
4846
        {
4847 4848
            ReportSafetyDiagnostic(forOutput ? ErrorCode.WRN_NullabilityMismatchInArgumentForOutput : ErrorCode.WRN_NullabilityMismatchInArgument,
                argument.Syntax, argumentType, parameterType,
4849 4850 4851 4852
                new FormattedSymbol(parameter, SymbolDisplayFormat.ShortFormat),
                new FormattedSymbol(parameter.ContainingSymbol, SymbolDisplayFormat.MinimallyQualifiedFormat));
        }

4853
        private TypeWithAnnotations GetDeclaredLocalResult(LocalSymbol local)
4854
        {
4855
            return _variableTypes.TryGetValue(local, out TypeWithAnnotations type) ?
4856
                type :
4857
                local.TypeWithAnnotations;
4858 4859
        }

4860
        private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter)
4861
        {
4862
            return _variableTypes.TryGetValue(parameter, out TypeWithAnnotations type) ?
4863
                type :
4864
                parameter.TypeWithAnnotations;
4865 4866
        }

4867
        public override BoundNode VisitBaseReference(BoundBaseReference node)
4868
        {
4869 4870
            VisitThisOrBaseReference(node);
            return null;
4871 4872 4873 4874
        }

        public override BoundNode VisitFieldAccess(BoundFieldAccess node)
        {
4875
            VisitMemberAccess(node, node.ReceiverOpt, node.FieldSymbol);
4876
            return null;
4877 4878 4879 4880
        }

        public override BoundNode VisitPropertyAccess(BoundPropertyAccess node)
        {
4881
            VisitMemberAccess(node, node.ReceiverOpt, node.PropertySymbol);
4882
            return null;
4883 4884 4885
        }

        public override BoundNode VisitIndexerAccess(BoundIndexerAccess node)
4886 4887 4888 4889 4890 4891
        {
            VisitIndexerAccess(node, out _);
            return null;
        }

        private void VisitIndexerAccess(BoundIndexerAccess node, out PropertySymbol indexer)
4892
        {
4893
            var receiverOpt = node.ReceiverOpt;
J
Cleanup  
Jared Parsons 已提交
4894
            VisitRvalue(receiverOpt);
4895 4896
            // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
            // after indices have been visited, and only if the receiver has not changed.
4897 4898
            CheckPossibleNullReceiver(receiverOpt);

4899
            // https://github.com/dotnet/roslyn/issues/29964 Update indexer based on inferred receiver type.
4900 4901
            indexer = node.Indexer;

4902
            VisitArguments(node, node.Arguments, node.ArgumentRefKindsOpt, node.Indexer, node.ArgsToParamsOpt, node.Expanded);
4903

4904
            LvalueResultType = node.Indexer.TypeWithAnnotations;
4905 4906 4907 4908
        }

        public override BoundNode VisitEventAccess(BoundEventAccess node)
        {
4909
            VisitMemberAccess(node, node.ReceiverOpt, node.EventSymbol);
4910 4911
            return null;
        }
4912

4913
        private void VisitMemberAccess(BoundExpression node, BoundExpression receiverOpt, Symbol member)
4914
        {
4915
            Debug.Assert(!IsConditionalState);
4916

4917
            var receiverType = (receiverOpt != null) ? VisitRvalueWithState(receiverOpt) : default;
4918

4919 4920 4921 4922 4923 4924 4925 4926 4927 4928
            SpecialMember? nullableOfTMember = null;
            if (!member.IsStatic)
            {
                member = AsMemberOfType(receiverType.Type, member);
                nullableOfTMember = GetNullableOfTMember(member);
                // https://github.com/dotnet/roslyn/issues/30598: For l-values, mark receiver as not null
                // after RHS has been visited, and only if the receiver has not changed.
                bool skipReceiverNullCheck = nullableOfTMember != SpecialMember.System_Nullable_T_get_Value;
                CheckPossibleNullReceiver(receiverOpt, checkNullableValueType: !skipReceiverNullCheck);
            }
4929

4930 4931
            var type = member.GetTypeOrReturnType();
            var resultType = type.ToTypeWithState();
4932

4933 4934 4935 4936 4937 4938
            // We are supposed to track information for the node. Use whatever we managed to
            // accumulate so far.
            if (PossiblyNullableType(resultType.Type))
            {
                int slot = MakeMemberSlot(receiverOpt, member);
                if (slot > 0 && slot < this.State.Capacity)
4939
                {
4940 4941
                    var state = this.State[slot];
                    resultType = new TypeWithState(resultType.Type, state);
4942
                }
4943
            }
4944

4945
            Debug.Assert(!IsConditionalState);
4946
            if (nullableOfTMember == SpecialMember.System_Nullable_T_get_HasValue && !(receiverOpt is null))
4947
            {
4948
                int containingSlot = MakeSlot(receiverOpt);
4949
                if (containingSlot > 0)
4950
                {
4951 4952
                    Split();
                    this.StateWhenTrue[containingSlot] = NullableFlowState.NotNull;
4953 4954
                }
            }
4955 4956

            SetResult(resultType, type);
4957 4958
        }

4959
        private SpecialMember? GetNullableOfTMember(Symbol member)
4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978
        {
            if (member.Kind == SymbolKind.Property)
            {
                var getMethod = ((PropertySymbol)member.OriginalDefinition).GetMethod;
                if ((object)getMethod != null && getMethod.ContainingType.SpecialType == SpecialType.System_Nullable_T)
                {
                    if (getMethod == compilation.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_Value))
                    {
                        return SpecialMember.System_Nullable_T_get_Value;
                    }
                    if (getMethod == compilation.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_HasValue))
                    {
                        return SpecialMember.System_Nullable_T_get_HasValue;
                    }
                }
            }
            return null;
        }

4979
        private int GetNullableOfTValueSlot(TypeSymbol containingType, int containingSlot, out Symbol valueProperty)
4980 4981
        {
            Debug.Assert(containingType.IsNullableType());
4982
            Debug.Assert(TypeSymbol.Equals(GetSlotType(containingSlot), containingType, TypeCompareKind.ConsiderEverything2));
4983 4984

            var getValue = (MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_Value);
4985 4986
            valueProperty = getValue?.AsMember((NamedTypeSymbol)containingType)?.AssociatedSymbol;
            return (valueProperty is null) ? -1 : GetOrCreateSlot(valueProperty, containingSlot);
4987 4988
        }

4989 4990 4991
        protected override void VisitForEachExpression(BoundForEachStatement node)
        {
            var expr = node.Expression;
J
Cleanup  
Jared Parsons 已提交
4992
            VisitRvalue(expr);
4993 4994 4995
            CheckPossibleNullReceiver(expr);
        }

4996 4997 4998
        public override void VisitForEachIterationVariables(BoundForEachStatement node)
        {
            // declare and assign all iteration variables
4999
            TypeWithAnnotations sourceType = node.EnumeratorInfoOpt?.ElementTypeWithAnnotations ?? default;
5000
            TypeWithState sourceState = sourceType.ToTypeWithState();
5001 5002
            foreach (var iterationVariable in node.IterationVariables)
            {
5003
                var state = NullableFlowState.NotNull;
5004
                if (!sourceState.HasNullType)
5005
                {
5006
                    TypeWithAnnotations destinationType = iterationVariable.TypeWithAnnotations;
5007 5008

                    if (iterationVariable.IsRef)
5009
                    {
5010 5011 5012 5013 5014 5015 5016
                        // foreach (ref DestinationType variable in collection)
                        if (IsNullabilityMismatch(sourceType, destinationType))
                        {
                            var foreachSyntax = (ForEachStatementSyntax)node.Syntax;
                            ReportNullabilityMismatchInAssignment(foreachSyntax.Type, sourceType, destinationType);
                        }
                        state = sourceState.State;
5017
                    }
5018 5019 5020 5021 5022 5023 5024
                    else
                    {
                        // foreach (DestinationType variable in collection)
                        // foreach (var variable in collection)
                        // foreach (var (..., ...) in collection)
                        // and asynchronous variants
                        HashSet<DiagnosticInfo> useSiteDiagnostics = null;
5025
                        Conversion conversion = _conversions.ClassifyImplicitConversionFromType(sourceType.Type, destinationType.Type, ref useSiteDiagnostics);
5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037
                        TypeWithState result = ApplyConversion(
                            node.IterationVariableType,
                            operandOpt: null,
                            conversion,
                            destinationType,
                            sourceState,
                            checkConversion: false,
                            fromExplicitCast: false,
                            useLegacyWarnings: false,
                            AssignmentKind.Assignment,
                            reportTopLevelWarnings: false,
                            reportRemainingWarnings: false);
5038
                        if (destinationType.Type.IsReferenceType && destinationType.NullableAnnotation.IsNotAnnotated() && result.MayBeNull)
5039 5040 5041
                        {
                            ReportNonSafetyDiagnostic(node.IterationVariableType.Syntax);
                        }
5042

5043 5044
                        state = result.State;
                    }
5045 5046 5047 5048 5049
                }

                int slot = GetOrCreateSlot(iterationVariable);
                if (slot > 0)
                {
5050
                    this.State[slot] = state;
5051
                }
5052 5053 5054
            }
        }

5055 5056 5057
        public override BoundNode VisitFromEndIndexExpression(BoundFromEndIndexExpression node)
        {
            var result = base.VisitFromEndIndexExpression(node);
5058
            SetNotNullResult(node);
5059 5060 5061
            return result;
        }

5062 5063
        public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMember node)
        {
5064 5065
            // Should be handled by VisitObjectCreationExpression.
            throw ExceptionUtilities.Unreachable;
5066 5067 5068 5069
        }

        public override BoundNode VisitDynamicObjectInitializerMember(BoundDynamicObjectInitializerMember node)
        {
5070
            SetNotNullResult(node);
5071 5072 5073 5074 5075 5076
            return null;
        }

        public override BoundNode VisitBadExpression(BoundBadExpression node)
        {
            var result = base.VisitBadExpression(node);
5077
            var type = TypeWithAnnotations.Create(node.Type);
N
Neal Gafter 已提交
5078
            LvalueResultType = type;
5079 5080 5081 5082 5083 5084
            return result;
        }

        public override BoundNode VisitTypeExpression(BoundTypeExpression node)
        {
            var result = base.VisitTypeExpression(node);
5085
            SetNotNullResult(node);
5086 5087 5088 5089 5090
            return result;
        }

        public override BoundNode VisitTypeOrValueExpression(BoundTypeOrValueExpression node)
        {
5091
            // These should not appear after initial binding except in error cases.
5092
            var result = base.VisitTypeOrValueExpression(node);
5093
            SetNotNullResult(node);
5094 5095 5096 5097 5098 5099 5100
            return result;
        }

        public override BoundNode VisitUnaryOperator(BoundUnaryOperator node)
        {
            Debug.Assert(!IsConditionalState);

5101 5102 5103
            _ = base.VisitUnaryOperator(node);
            var argumentResult = ResultType;
            TypeWithState resultType;
5104

5105
            if (node.OperatorKind.IsUserDefined() && (object)node.MethodOpt != null && node.MethodOpt.ParameterCount == 1)
5106
            {
5107 5108
                // Update method based on inferred operand type: see https://github.com/dotnet/roslyn/issues/29605.
                ReportArgumentWarnings(node.Operand, argumentResult, node.MethodOpt.Parameters[0]);
5109 5110
                if (node.OperatorKind.IsLifted())
                {
5111
                    resultType = LiftedReturnType(node.MethodOpt.ReturnTypeWithAnnotations, argumentResult.State);
5112
                }
5113
                else
5114
                {
5115
                    resultType = node.MethodOpt.ReturnTypeWithAnnotations.ToTypeWithState();
5116 5117
                }
            }
5118 5119 5120 5121
            else
            {
                resultType = new TypeWithState(node.Type, node.OperatorKind.IsLifted() ? argumentResult.State : NullableFlowState.NotNull);
            }
5122

5123
            ResultType = resultType;
5124
            return null;
5125 5126 5127 5128 5129
        }

        public override BoundNode VisitPointerIndirectionOperator(BoundPointerIndirectionOperator node)
        {
            var result = base.VisitPointerIndirectionOperator(node);
5130
            var type = TypeWithAnnotations.Create(node.Type);
N
Neal Gafter 已提交
5131
            LvalueResultType = type;
5132 5133 5134 5135 5136 5137
            return result;
        }

        public override BoundNode VisitPointerElementAccess(BoundPointerElementAccess node)
        {
            var result = base.VisitPointerElementAccess(node);
5138
            var type = TypeWithAnnotations.Create(node.Type);
N
Neal Gafter 已提交
5139
            LvalueResultType = type;
5140 5141 5142 5143 5144
            return result;
        }

        public override BoundNode VisitRefTypeOperator(BoundRefTypeOperator node)
        {
J
Cleanup  
Jared Parsons 已提交
5145
            VisitRvalue(node.Operand);
5146
            SetNotNullResult(node);
5147
            return null;
5148 5149 5150 5151 5152
        }

        public override BoundNode VisitMakeRefOperator(BoundMakeRefOperator node)
        {
            var result = base.VisitMakeRefOperator(node);
5153
            SetNotNullResult(node);
5154 5155 5156 5157 5158 5159
            return result;
        }

        public override BoundNode VisitRefValueOperator(BoundRefValueOperator node)
        {
            var result = base.VisitRefValueOperator(node);
5160
            var type = TypeWithAnnotations.Create(node.Type, node.NullableAnnotation);
N
Neal Gafter 已提交
5161
            LvalueResultType = type;
5162 5163 5164
            return result;
        }

5165
        private TypeWithState InferResultNullability(BoundUserDefinedConditionalLogicalOperator node)
5166
        {
5167 5168
            if (node.OperatorKind.IsLifted())
            {
5169
                // https://github.com/dotnet/roslyn/issues/33879 Conversions: Lifted operator
5170 5171
                // Should this use the updated flow type and state?  How should it compute nullability?
                return new TypeWithState(node.Type, NullableFlowState.NotNull);
5172
            }
5173

5174
            // Update method based on inferred operand types: see https://github.com/dotnet/roslyn/issues/29605.
5175 5176
            if ((object)node.LogicalOperator != null && node.LogicalOperator.ParameterCount == 2)
            {
5177
                return node.LogicalOperator.ReturnTypeWithAnnotations.ToTypeWithState();
5178 5179 5180
            }
            else
            {
5181
                return default;
5182 5183 5184 5185 5186 5187
            }
        }

        protected override void AfterLeftChildOfBinaryLogicalOperatorHasBeenVisited(BoundExpression node, BoundExpression right, bool isAnd, bool isBool, ref LocalState leftTrue, ref LocalState leftFalse)
        {
            Debug.Assert(!IsConditionalState);
5188 5189 5190 5191 5192 5193 5194
            TypeWithState leftType = ResultType;
            // https://github.com/dotnet/roslyn/issues/29605 Update operator methods based on inferred operand types.
            MethodSymbol logicalOperator = null;
            MethodSymbol trueFalseOperator = null;
            BoundExpression left = null;

            switch (node.Kind)
5195
            {
5196 5197 5198 5199 5200 5201 5202 5203 5204 5205
                case BoundKind.BinaryOperator:
                    Debug.Assert(!((BoundBinaryOperator)node).OperatorKind.IsUserDefined());
                    break;
                case BoundKind.UserDefinedConditionalLogicalOperator:
                    var binary = (BoundUserDefinedConditionalLogicalOperator)node;
                    if (binary.LogicalOperator != null && binary.LogicalOperator.ParameterCount == 2)
                    {
                        logicalOperator = binary.LogicalOperator;
                        left = binary.Left;
                        trueFalseOperator = isAnd ? binary.FalseOperator : binary.TrueOperator;
5206

5207
                        if ((object)trueFalseOperator != null && trueFalseOperator.ParameterCount != 1)
5208
                        {
5209
                            trueFalseOperator = null;
5210
                        }
5211 5212 5213 5214 5215
                    }
                    break;
                default:
                    throw ExceptionUtilities.UnexpectedValue(node.Kind);
            }
5216

5217
            Debug.Assert(trueFalseOperator is null || ((object)logicalOperator != null && left != null));
5218

5219 5220 5221 5222
            if ((object)trueFalseOperator != null)
            {
                ReportArgumentWarnings(left, leftType, trueFalseOperator.Parameters[0]);
            }
5223

5224 5225 5226 5227
            if ((object)logicalOperator != null)
            {
                ReportArgumentWarnings(left, leftType, logicalOperator.Parameters[0]);
            }
5228

5229 5230
            Visit(right);
            TypeWithState rightType = ResultType;
5231

5232
            ResultType = InferResultNullabilityOfBinaryLogicalOperator(node, leftType, rightType);
5233

5234 5235 5236
            if ((object)logicalOperator != null)
            {
                ReportArgumentWarnings(right, rightType, logicalOperator.Parameters[1]);
5237
            }
5238 5239

            AfterRightChildOfBinaryLogicalOperatorHasBeenVisited(node, right, isAnd, isBool, ref leftTrue, ref leftFalse);
5240 5241
        }

5242
        private TypeWithState InferResultNullabilityOfBinaryLogicalOperator(BoundExpression node, TypeWithState leftType, TypeWithState rightType)
5243 5244 5245 5246
        {
            switch (node.Kind)
            {
                case BoundKind.BinaryOperator:
5247
                    return InferResultNullability((BoundBinaryOperator)node, leftType, rightType);
5248 5249 5250
                case BoundKind.UserDefinedConditionalLogicalOperator:
                    return InferResultNullability((BoundUserDefinedConditionalLogicalOperator)node);
                default:
5251
                    throw ExceptionUtilities.UnexpectedValue(node.Kind);
5252 5253 5254 5255 5256 5257
            }
        }

        public override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
        {
            var result = base.VisitAwaitExpression(node);
5258
            CheckPossibleNullReceiver(node.Expression);
N
Neal Gafter 已提交
5259
            if (node.Type.IsValueType || node.HasErrors || node.AwaitableInfo.GetResult is null)
5260
            {
5261
                SetNotNullResult(node);
5262 5263 5264
            }
            else
            {
5265
                // Update method based on inferred receiver type: see https://github.com/dotnet/roslyn/issues/29605.
5266
                ResultType = node.AwaitableInfo.GetResult.ReturnTypeWithAnnotations.ToTypeWithState();
5267
            }
5268

5269 5270 5271 5272 5273 5274
            return result;
        }

        public override BoundNode VisitTypeOfOperator(BoundTypeOfOperator node)
        {
            var result = base.VisitTypeOfOperator(node);
5275
            ResultType = new TypeWithState(node.Type, NullableFlowState.NotNull);
5276 5277 5278 5279 5280 5281
            return result;
        }

        public override BoundNode VisitMethodInfo(BoundMethodInfo node)
        {
            var result = base.VisitMethodInfo(node);
5282
            SetNotNullResult(node);
5283 5284 5285 5286 5287 5288
            return result;
        }

        public override BoundNode VisitFieldInfo(BoundFieldInfo node)
        {
            var result = base.VisitFieldInfo(node);
5289
            SetNotNullResult(node);
5290 5291 5292 5293 5294
            return result;
        }

        public override BoundNode VisitDefaultExpression(BoundDefaultExpression node)
        {
5295 5296
            Debug.Assert(!this.IsConditionalState);

5297
            var result = base.VisitDefaultExpression(node);
5298
            TypeSymbol type = node.Type;
5299 5300 5301 5302 5303
            if (EmptyStructTypeCache.IsTrackableStructType(type))
            {
                int slot = GetOrCreateObjectCreationPlaceholderSlot(node);
                if (slot > 0)
                {
5304
                    this.State[slot] = NullableFlowState.NotNull;
C
Charles Stoner 已提交
5305
                    InheritNullableStateOfTrackableStruct(type, slot, valueSlot: -1, isDefaultValue: true);
5306 5307
                }
            }
5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320

            // https://github.com/dotnet/roslyn/issues/33344: this fails to produce an updated tuple type for a default expression
            // (should produce nullable element types for those elements that are of reference types)
            ResultType = TypeWithState.ForType(type);

            if (ResultType.State == NullableFlowState.MaybeNull && RequiresSafetyWarningWhenNullIntroduced(ResultType.Type) && !node.IsSuppressed)
            {
                // For type parameters that cannot be annotated, the analysis must report those
                // places where null values first sneak in, like `default`, `null`, and `GetFirstOrDefault`.
                // This is one of those places.
                ReportSafetyDiagnostic(ErrorCode.WRN_DefaultExpressionMayIntroduceNullT, node.Syntax, GetTypeAsDiagnosticArgument(ResultType.Type));
            }

5321 5322 5323 5324 5325
            return result;
        }

        public override BoundNode VisitIsOperator(BoundIsOperator node)
        {
5326 5327 5328
            Debug.Assert(!this.IsConditionalState);

            var operand = node.Operand;
5329 5330
            var result = base.VisitIsOperator(node);
            Debug.Assert(node.Type.SpecialType == SpecialType.System_Boolean);
5331

5332
            if (operand.Type?.IsValueType == false)
5333
            {
5334 5335 5336 5337 5338
                var slotBuilder = ArrayBuilder<int>.GetInstance();
                GetSlotsToMarkAsNotNullable(operand, slotBuilder);
                if (slotBuilder.Count > 0)
                {
                    Split();
5339
                    MarkSlotsAsNotNull(slotBuilder, ref StateWhenTrue);
5340 5341
                }
                slotBuilder.Free();
5342 5343
            }

5344
            SetNotNullResult(node);
5345 5346 5347 5348 5349
            return result;
        }

        public override BoundNode VisitAsOperator(BoundAsOperator node)
        {
5350 5351 5352
            var argumentType = VisitRvalueWithState(node.Operand);
            NullableFlowState resultState = NullableFlowState.NotNull;
            var type = node.Type;
5353

5354
            if (type.CanContainNull())
5355
            {
5356
                switch (node.Conversion.Kind)
5357
                {
5358 5359 5360 5361
                    case ConversionKind.Identity:
                    case ConversionKind.ImplicitReference:
                    case ConversionKind.Boxing:
                    case ConversionKind.ImplicitNullable:
N
Neal Gafter 已提交
5362
                        resultState = argumentType.State;
5363
                        break;
5364

5365 5366 5367 5368 5369 5370 5371
                    default:
                        resultState = NullableFlowState.MaybeNull;
                        if (RequiresSafetyWarningWhenNullIntroduced(type))
                        {
                            ReportSafetyDiagnostic(ErrorCode.WRN_AsOperatorMayReturnNull, node.Syntax, type);
                        }
                        break;
5372 5373 5374
                }
            }

5375
            ResultType = new TypeWithState(type, resultState);
5376
            return null;
5377 5378 5379 5380 5381
        }

        public override BoundNode VisitSizeOfOperator(BoundSizeOfOperator node)
        {
            var result = base.VisitSizeOfOperator(node);
5382
            SetNotNullResult(node);
5383 5384 5385 5386 5387 5388 5389
            return result;
        }

        public override BoundNode VisitArgList(BoundArgList node)
        {
            var result = base.VisitArgList(node);
            Debug.Assert(node.Type.SpecialType == SpecialType.System_RuntimeArgumentHandle);
5390
            SetNotNullResult(node);
5391 5392 5393 5394 5395
            return result;
        }

        public override BoundNode VisitArgListOperator(BoundArgListOperator node)
        {
5396
            VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt);
N
Neal Gafter 已提交
5397
            Debug.Assert(node.Type is null);
5398
            SetNotNullResult(node);
5399
            return null;
5400 5401 5402 5403 5404 5405 5406
        }

        public override BoundNode VisitLiteral(BoundLiteral node)
        {
            var result = base.VisitLiteral(node);

            Debug.Assert(!IsConditionalState);
5407
            ResultType = new TypeWithState(node.Type, node.Type?.CanContainNull() != false && node.ConstantValue?.IsNull == true ? NullableFlowState.MaybeNull : NullableFlowState.NotNull);
5408 5409 5410 5411 5412 5413 5414 5415

            return result;
        }

        public override BoundNode VisitPreviousSubmissionReference(BoundPreviousSubmissionReference node)
        {
            var result = base.VisitPreviousSubmissionReference(node);
            Debug.Assert(node.WasCompilerGenerated);
5416
            SetNotNullResult(node);
5417 5418 5419 5420 5421 5422 5423
            return result;
        }

        public override BoundNode VisitHostObjectMemberReference(BoundHostObjectMemberReference node)
        {
            var result = base.VisitHostObjectMemberReference(node);
            Debug.Assert(node.WasCompilerGenerated);
5424
            SetNotNullResult(node);
5425 5426 5427 5428 5429 5430
            return result;
        }

        public override BoundNode VisitPseudoVariable(BoundPseudoVariable node)
        {
            var result = base.VisitPseudoVariable(node);
5431
            SetNotNullResult(node);
5432 5433 5434
            return result;
        }

5435 5436 5437
        public override BoundNode VisitRangeExpression(BoundRangeExpression node)
        {
            var result = base.VisitRangeExpression(node);
5438
            SetNotNullResult(node);
5439 5440 5441
            return result;
        }

5442 5443 5444
        public override BoundNode VisitRangeVariable(BoundRangeVariable node)
        {
            var result = base.VisitRangeVariable(node);
5445
            SetNotNullResult(node); // https://github.com/dotnet/roslyn/issues/29863 Need to review this
5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457
            return result;
        }

        public override BoundNode VisitLabel(BoundLabel node)
        {
            var result = base.VisitLabel(node);
            SetUnknownResultNullability();
            return result;
        }

        public override BoundNode VisitDynamicMemberAccess(BoundDynamicMemberAccess node)
        {
5458
            var receiver = node.Receiver;
J
Cleanup  
Jared Parsons 已提交
5459
            VisitRvalue(receiver);
5460
            CheckPossibleNullReceiver(receiver);
5461 5462

            Debug.Assert(node.Type.IsDynamic());
5463
            var result = TypeWithAnnotations.Create(node.Type);
N
Neal Gafter 已提交
5464
            LvalueResultType = result;
5465
            return null;
5466 5467 5468 5469
        }

        public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node)
        {
J
Cleanup  
Jared Parsons 已提交
5470
            VisitRvalue(node.Expression);
5471
            VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt);
5472
            Debug.Assert(node.Type.IsDynamic());
5473
            Debug.Assert(node.Type.IsReferenceType);
5474
            var result = TypeWithAnnotations.Create(node.Type, NullableAnnotation.Oblivious);
N
Neal Gafter 已提交
5475
            LvalueResultType = result;
5476
            return null;
5477 5478 5479 5480
        }

        public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOperator node)
        {
J
Cleanup  
Jared Parsons 已提交
5481
            VisitRvalue(node.ReceiverOpt);
5482 5483
            Debug.Assert(!IsConditionalState);
            var receiverOpt = node.ReceiverOpt;
5484
            var @event = node.Event;
5485
            if (!@event.IsStatic)
5486
            {
5487
                @event = (EventSymbol)AsMemberOfType(ResultType.Type, @event);
5488 5489
                // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
                // after arguments have been visited, and only if the receiver has not changed.
5490 5491
                CheckPossibleNullReceiver(receiverOpt);
            }
J
Cleanup  
Jared Parsons 已提交
5492
            VisitRvalue(node.Argument);
5493
            // https://github.com/dotnet/roslyn/issues/31018: Check for delegate mismatch.
5494
            SetNotNullResult(node); // https://github.com/dotnet/roslyn/issues/29969 Review whether this is the correct result
5495
            return null;
5496 5497 5498 5499
        }

        public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node)
        {
5500
            Debug.Assert(!IsConditionalState);
5501
            var arguments = node.Arguments;
5502 5503
            var argumentResults = VisitArgumentsEvaluate(arguments, node.ArgumentRefKindsOpt);
            VisitObjectOrDynamicObjectCreation(node, arguments, argumentResults, node.InitializerExpressionOpt);
5504
            return null;
5505 5506 5507 5508
        }

        public override BoundNode VisitObjectInitializerExpression(BoundObjectInitializerExpression node)
        {
5509
            // Only reachable from bad expression. Otherwise handled in VisitObjectCreationExpression().
5510
            SetNotNullResult(node);
5511
            return null;
5512 5513 5514 5515
        }

        public override BoundNode VisitCollectionInitializerExpression(BoundCollectionInitializerExpression node)
        {
5516
            // Only reachable from bad expression. Otherwise handled in VisitObjectCreationExpression().
5517
            SetNotNullResult(node);
5518
            return null;
5519 5520 5521 5522
        }

        public override BoundNode VisitDynamicCollectionElementInitializer(BoundDynamicCollectionElementInitializer node)
        {
5523
            // Only reachable from bad expression. Otherwise handled in VisitObjectCreationExpression().
5524
            SetNotNullResult(node);
5525
            return null;
5526 5527 5528 5529 5530
        }

        public override BoundNode VisitImplicitReceiver(BoundImplicitReceiver node)
        {
            var result = base.VisitImplicitReceiver(node);
5531
            SetNotNullResult(node);
5532 5533 5534 5535 5536
            return result;
        }

        public override BoundNode VisitAnonymousPropertyDeclaration(BoundAnonymousPropertyDeclaration node)
        {
5537
            throw ExceptionUtilities.Unreachable;
5538 5539 5540 5541 5542
        }

        public override BoundNode VisitNoPiaObjectCreationExpression(BoundNoPiaObjectCreationExpression node)
        {
            var result = base.VisitNoPiaObjectCreationExpression(node);
5543
            ResultType = new TypeWithState(node.Type, NullableFlowState.NotNull);
5544 5545 5546 5547 5548
            return result;
        }

        public override BoundNode VisitNewT(BoundNewT node)
        {
5549 5550
            VisitObjectOrDynamicObjectCreation(node, ImmutableArray<BoundExpression>.Empty, ImmutableArray<VisitResult>.Empty, node.InitializerExpressionOpt);
            return null;
5551 5552 5553 5554 5555
        }

        public override BoundNode VisitArrayInitialization(BoundArrayInitialization node)
        {
            var result = base.VisitArrayInitialization(node);
5556
            SetNotNullResult(node);
5557 5558 5559 5560 5561
            return result;
        }

        private void SetUnknownResultNullability()
        {
5562
            ResultType = default;
5563 5564 5565 5566 5567
        }

        public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node)
        {
            var result = base.VisitStackAllocArrayCreation(node);
N
Neal Gafter 已提交
5568
            Debug.Assert(node.Type is null || node.Type.IsPointerType() || node.Type.IsRefLikeType);
5569
            SetNotNullResult(node);
5570 5571 5572 5573 5574
            return result;
        }

        public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node)
        {
5575
            var receiver = node.ReceiverOpt;
J
Cleanup  
Jared Parsons 已提交
5576
            VisitRvalue(receiver);
5577 5578
            // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
            // after indices have been visited, and only if the receiver has not changed.
5579
            CheckPossibleNullReceiver(receiver);
5580
            VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt);
5581
            Debug.Assert(node.Type.IsDynamic());
5582
            var result = TypeWithAnnotations.Create(node.Type, NullableAnnotation.Oblivious);
5583
            LvalueResultType = result;
5584
            return null;
5585 5586
        }

5587
        private void CheckPossibleNullReceiver(BoundExpression receiverOpt, bool checkNullableValueType = false)
5588
        {
5589
            Debug.Assert(!this.IsConditionalState);
5590
            if (receiverOpt != null && this.State.Reachable)
5591
            {
N
Neal Gafter 已提交
5592 5593
                var resultTypeSymbol = ResultType.Type;
                if (resultTypeSymbol is null)
5594 5595 5596
                {
                    return;
                }
N
Neal Gafter 已提交
5597
#if DEBUG
N
Neal Gafter 已提交
5598
                Debug.Assert(receiverOpt.Type is null || AreCloseEnough(receiverOpt.Type, resultTypeSymbol));
N
Neal Gafter 已提交
5599
#endif
5600
                if (ResultType.MayBeNull)
5601
                {
N
Neal Gafter 已提交
5602 5603
                    bool isValueType = resultTypeSymbol.IsValueType;
                    if (isValueType && (!checkNullableValueType || !resultTypeSymbol.IsNullableTypeOrTypeParameter() || resultTypeSymbol.GetNullableUnderlyingType().IsErrorType()))
5604 5605 5606
                    {
                        return;
                    }
5607

5608
                    ReportSafetyDiagnostic(isValueType ? ErrorCode.WRN_NullableValueTypeMayBeNull : ErrorCode.WRN_NullReferenceReceiver, receiverOpt.Syntax);
5609
                }
5610

5611
                LearnFromNonNullTest(receiverOpt, ref this.State);
5612 5613 5614
            }
        }

5615
        private static bool IsNullabilityMismatch(TypeWithAnnotations type1, TypeWithAnnotations type2)
5616
        {
5617
            // Note, when we are paying attention to nullability, we ignore insignificant differences and oblivious mismatch.
5618
            // See TypeCompareKind.UnknownNullableModifierMatchesAny and TypeCompareKind.IgnoreInsignificantNullableModifiersDifference
5619
            return type1.Equals(type2, TypeCompareKind.AllIgnoreOptions) &&
5620
                !type1.Equals(type2, TypeCompareKind.AllIgnoreOptions & ~TypeCompareKind.IgnoreNullableModifiersForReferenceTypes);
5621 5622 5623 5624
        }

        private static bool IsNullabilityMismatch(TypeSymbol type1, TypeSymbol type2)
        {
5625
            // Note, when we are paying attention to nullability, we ignore insignificant differences and oblivious mismatch.
5626
            // See TypeCompareKind.UnknownNullableModifierMatchesAny and TypeCompareKind.IgnoreInsignificantNullableModifiersDifference
5627
            return type1.Equals(type2, TypeCompareKind.AllIgnoreOptions) &&
5628
                !type1.Equals(type2, TypeCompareKind.AllIgnoreOptions & ~TypeCompareKind.IgnoreNullableModifiersForReferenceTypes);
5629 5630 5631 5632 5633
        }

        public override BoundNode VisitQueryClause(BoundQueryClause node)
        {
            var result = base.VisitQueryClause(node);
5634
            SetNotNullResult(node); // https://github.com/dotnet/roslyn/issues/29863 Implement nullability analysis in LINQ queries
5635 5636 5637 5638 5639 5640
            return result;
        }

        public override BoundNode VisitNameOfOperator(BoundNameOfOperator node)
        {
            var result = base.VisitNameOfOperator(node);
5641
            ResultType = new TypeWithState(node.Type, NullableFlowState.NotNull);
5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654
            return result;
        }

        public override BoundNode VisitNamespaceExpression(BoundNamespaceExpression node)
        {
            var result = base.VisitNamespaceExpression(node);
            SetUnknownResultNullability();
            return result;
        }

        public override BoundNode VisitInterpolatedString(BoundInterpolatedString node)
        {
            var result = base.VisitInterpolatedString(node);
5655
            ResultType = new TypeWithState(node.Type, NullableFlowState.NotNull);
5656 5657 5658 5659 5660 5661 5662 5663 5664 5665
            return result;
        }

        public override BoundNode VisitStringInsert(BoundStringInsert node)
        {
            var result = base.VisitStringInsert(node);
            SetUnknownResultNullability();
            return result;
        }

5666 5667 5668
        public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node)
        {
            var result = base.VisitConvertedStackAllocExpression(node);
5669
            SetNotNullResult(node);
5670 5671 5672 5673 5674
            return result;
        }

        public override BoundNode VisitDiscardExpression(BoundDiscardExpression node)
        {
5675
            var result = TypeWithAnnotations.Create(node.Type);
5676 5677
            var rValueType = new TypeWithState(node.Type, NullableFlowState.MaybeNull);
            SetResult(rValueType, result);
5678 5679 5680
            return null;
        }

5681 5682
        public override BoundNode VisitThrowExpression(BoundThrowExpression node)
        {
5683
            VisitThrow(node.Expression);
5684
            ResultType = default;
5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697
            return null;
        }

        public override BoundNode VisitThrowStatement(BoundThrowStatement node)
        {
            VisitThrow(node.ExpressionOpt);
            return null;
        }

        private void VisitThrow(BoundExpression expr)
        {
            if (expr != null)
            {
5698
                var result = VisitRvalueWithState(expr);
5699 5700 5701 5702
                // Cases:
                // null
                // null!
                // Other (typed) expression, including suppressed ones
5703
                if (result.MayBeNull)
5704
                {
5705
                    ReportSafetyDiagnostic(ErrorCode.WRN_PossibleNull, expr.Syntax);
5706 5707 5708
                }
            }
            SetUnreachable();
5709 5710
        }

5711 5712 5713 5714 5715 5716 5717
        public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node)
        {
            BoundExpression expr = node.Expression;
            if (expr == null)
            {
                return null;
            }
5718
            var method = (MethodSymbol)_symbol;
5719
            TypeWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None,
5720
                method.ReturnType, errorLocationNode: null, diagnostics: null).elementType;
5721

5722
            _ = VisitOptionalImplicitConversion(expr, elementType, useLegacyWarnings: false, AssignmentKind.Return);
5723 5724 5725
            return null;
        }

5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741
        protected override void VisitCatchBlock(BoundCatchBlock node, ref LocalState finallyState)
        {
            if (node.Locals.Length > 0)
            {
                LocalSymbol local = node.Locals[0];
                if (local.DeclarationKind == LocalDeclarationKind.CatchVariable)
                {
                    int slot = GetOrCreateSlot(local);
                    if (slot > 0)
                        this.State[slot] = NullableFlowState.NotNull;
                }
            }

            base.VisitCatchBlock(node, ref finallyState);
        }

J
Julien Couvreur 已提交
5742 5743 5744 5745 5746 5747 5748 5749
        public override BoundNode VisitLockStatement(BoundLockStatement node)
        {
            VisitRvalue(node.Argument);
            CheckPossibleNullReceiver(node.Argument);
            VisitStatement(node.Body);
            return null;
        }

5750 5751 5752 5753 5754 5755 5756 5757 5758
        public override BoundNode VisitAttribute(BoundAttribute node)
        {
            VisitArguments(node, node.ConstructorArguments, ImmutableArray<RefKind>.Empty, node.Constructor, argsToParamsOpt: node.ConstructorArgumentsToParamsOpt, expanded: node.ConstructorExpanded);
            foreach (var assignment in node.NamedArguments)
            {
                Visit(assignment);
            }

            SetNotNullResult(node);
C
Chris Sienkiewicz 已提交
5759
            return null;
5760 5761
        }

5762 5763
        protected override string Dump(LocalState state)
        {
5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790
            if (!state.Reachable)
                return "unreachable";

            var pooledBuilder = PooledStringBuilder.GetInstance();
            var builder = pooledBuilder.Builder;
            for (int i = 0; i < state.Capacity; i++)
            {
                if (nameForSlot(i) is string name)
                {
                    builder.Append(name);
                    builder.Append(state[i] == NullableFlowState.MaybeNull ? "?" : "!");
                }
            }

            return pooledBuilder.ToStringAndFree();

            string nameForSlot(int slot)
            {
                if (slot < 0)
                    return null;
                VariableIdentifier id = this.variableBySlot[slot];
                var name = id.Symbol?.Name;
                if (name == null)
                    return null;
                return nameForSlot(id.ContainingSlot) is string containingSlotName
                    ? containingSlotName + "." + name : name;
            }
5791 5792
        }

A
Andy Gocke 已提交
5793
        protected override void Meet(ref LocalState self, ref LocalState other)
5794
        {
5795 5796 5797 5798 5799 5800 5801 5802 5803
            if (!self.Reachable)
                return;

            if (!other.Reachable)
            {
                self = other.Clone();
                return;
            }

5804 5805 5806 5807 5808 5809 5810 5811
            if (self.Capacity != other.Capacity)
            {
                Normalize(ref self);
                Normalize(ref other);
            }

            for (int slot = 1; slot < self.Capacity; slot++)
            {
5812 5813
                NullableFlowState selfState = self[slot];
                NullableFlowState otherState = other[slot];
5814
                NullableFlowState union = selfState.Meet(otherState);
5815

5816
                if (selfState != union)
5817 5818 5819 5820 5821 5822
                {
                    self[slot] = union;
                }
            }
        }

A
Andy Gocke 已提交
5823
        protected override bool Join(ref LocalState self, ref LocalState other)
5824
        {
5825 5826
            if (!other.Reachable)
                return false;
5827

5828
            if (!self.Reachable)
5829
            {
5830
                self = other.Clone();
5831 5832
                return true;
            }
5833 5834 5835 5836

            bool result = false;

            if (self.Capacity != other.Capacity)
5837
            {
5838 5839
                Normalize(ref self);
                Normalize(ref other);
5840 5841
            }

5842 5843 5844
            for (int slot = 1; slot < self.Capacity; slot++)
            {
                NullableFlowState selfAnnotation = self[slot];
5845
                NullableFlowState intersection = selfAnnotation.Join(other[slot]);
5846 5847 5848 5849 5850 5851 5852 5853 5854
                if (selfAnnotation != intersection)
                {
                    self[slot] = intersection;
                    result = true;
                }
            }

            return result;
        }
5855

5856
        [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
5857
#if REFERENCE_STATE
A
Andy Gocke 已提交
5858
        internal class LocalState : ILocalState
5859
#else
A
Andy Gocke 已提交
5860
        internal struct LocalState : ILocalState
5861 5862
#endif
        {
5863
            private ArrayBuilder<NullableFlowState> _state;
5864
            public bool Reachable { get; }
5865

5866
            internal LocalState(bool reachable, ArrayBuilder<NullableFlowState> state)
5867
            {
5868 5869
                this.Reachable = reachable;
                this._state = state;
5870 5871
            }

5872
            internal int Capacity => _state?.Count ?? 0;
5873 5874 5875

            internal void EnsureCapacity(int capacity)
            {
5876 5877 5878 5879 5880
                if (!Reachable)
                {
                    return;
                }

5881 5882
                if (_state == null)
                {
5883
                    _state = new ArrayBuilder<NullableFlowState>(capacity);
5884 5885 5886 5887 5888 5889
                }

                if (_state.Count < capacity)
                {
                    _state.Count = capacity;
                }
5890 5891
            }

5892
            internal NullableFlowState this[int slot]
5893 5894 5895
            {
                get
                {
5896
                    if (slot < Capacity && this.Reachable)
5897 5898 5899 5900
                    {
                        return _state[slot];
                    }

5901
                    return NullableFlowState.NotNull;
5902 5903 5904
                }
                set
                {
5905 5906 5907 5908 5909 5910 5911
                    if (this.Reachable)
                    {
                        // All variables are be considered not null in unreachable code.
                        // Moreover, no states should be modified in unreachable code, as there is only one unreachable state.
                        EnsureCapacity(slot + 1);
                        _state[slot] = value;
                    }
5912 5913 5914 5915 5916 5917 5918 5919 5920
                }
            }

            /// <summary>
            /// Produce a duplicate of this flow analysis state.
            /// </summary>
            /// <returns></returns>
            public LocalState Clone()
            {
5921
                ArrayBuilder<NullableFlowState> clone;
5922

5923 5924 5925 5926 5927
                if (_state == null)
                {
                    clone = null;
                }
                else
5928
                {
5929
                    clone = new ArrayBuilder<NullableFlowState>(_state.Count);
5930 5931
                    clone.Count = 0;
                    clone.AddRange(_state);
5932
                }
5933

5934
                return new LocalState(Reachable, clone);
5935
            }
5936

5937
            internal string GetDebuggerDisplay()
5938 5939 5940 5941 5942 5943
            {
                var pooledBuilder = PooledStringBuilder.GetInstance();
                var builder = pooledBuilder.Builder;
                builder.Append(" ");
                for (int i = this.Capacity - 1; i >= 0; i--)
                {
5944 5945 5946 5947
                    string append;

                    switch (_state[i])
                    {
5948 5949
                        case NullableFlowState.NotNull:
                            append = "!";
5950
                            break;
5951 5952
                        case NullableFlowState.MaybeNull:
                            append = "?";
5953 5954 5955 5956 5957 5958
                            break;
                        default:
                            throw ExceptionUtilities.UnexpectedValue(_state[i]);
                    }

                    builder.Append(append);
5959 5960 5961 5962
                }

                return pooledBuilder.ToStringAndFree();
            }
5963 5964 5965
        }
    }
}