Beispiel #1
0
        /// <summary>
        /// Bind the pattern switch section, producing subsumption diagnostics.
        /// </summary>
        /// <param name="boundSwitchExpression"/>
        /// <param name="node"/>
        /// <param name="originalBinder"/>
        /// <param name="defaultLabel">If a default label is found in this section, assigned that label</param>
        /// <param name="someValueMatched">If a constant label is found that matches the constant input, assigned that label</param>
        /// <param name="subsumption">A helper class that uses a decision tree to produce subsumption diagnostics.</param>
        /// <param name="diagnostics"></param>
        /// <returns></returns>
        private BoundPatternSwitchSection BindPatternSwitchSection(
            BoundExpression boundSwitchExpression,
            SwitchSectionSyntax node,
            Binder originalBinder,
            ref BoundPatternSwitchLabel defaultLabel,
            ref bool someValueMatched,
            SubsumptionDiagnosticBuilder subsumption,
            DiagnosticBag diagnostics)
        {
            // Bind match section labels
            var boundLabelsBuilder = ArrayBuilder <BoundPatternSwitchLabel> .GetInstance();

            var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section.

            Debug.Assert(sectionBinder != null);
            var labelsByNode = LabelsByNode;

            foreach (var labelSyntax in node.Labels)
            {
                LabelSymbol             label      = labelsByNode[labelSyntax];
                BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, boundSwitchExpression, labelSyntax, label, ref defaultLabel, diagnostics);
                bool valueMatched; // true if we find an unconditional constant label that matches the input constant's value
                bool isReachable = subsumption.AddLabel(boundLabel, diagnostics, out valueMatched);
                boundLabel        = boundLabel.Update(boundLabel.Label, boundLabel.Pattern, boundLabel.Guard, isReachable && !someValueMatched);
                someValueMatched |= valueMatched;
                boundLabelsBuilder.Add(boundLabel);
            }

            // Bind switch section statements
            var boundStatementsBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            foreach (var statement in node.Statements)
            {
                boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics));
            }

            return(new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree()));
        }
        /// <summary>
        /// Bind the pattern switch section, producing subsumption diagnostics.
        /// </summary>
        /// <param name="boundSwitchExpression"/>
        /// <param name="node"/>
        /// <param name="originalBinder"/>
        /// <param name="defaultLabel">If a default label is found in this section, assigned that label</param>
        /// <param name="someValueMatched">If a constant label is found that matches the constant input, assigned that label</param>
        /// <param name="subsumption">A helper class that uses a decision tree to produce subsumption diagnostics.</param>
        /// <param name="diagnostics"></param>
        /// <returns></returns>
        private BoundPatternSwitchSection BindPatternSwitchSection(
            BoundExpression boundSwitchExpression,
            SwitchSectionSyntax node,
            Binder originalBinder,
            ref BoundPatternSwitchLabel defaultLabel,
            ref bool someValueMatched,
            SubsumptionDiagnosticBuilder subsumption,
            DiagnosticBag diagnostics)
        {
            // Bind match section labels
            var boundLabelsBuilder = ArrayBuilder<BoundPatternSwitchLabel>.GetInstance();
            var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section.
            Debug.Assert(sectionBinder != null);
            var labelsByNode = LabelsByNode;

            foreach (var labelSyntax in node.Labels)
            {
                LabelSymbol label = labelsByNode[labelSyntax];
                BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, boundSwitchExpression, labelSyntax, label, ref defaultLabel, diagnostics);
                bool valueMatched; // true if we find an unconditional constant label that matches the input constant's value
                bool isReachable = subsumption.AddLabel(boundLabel, diagnostics, out valueMatched);
                boundLabel = boundLabel.Update(boundLabel.Label, boundLabel.Pattern, boundLabel.Guard, isReachable && !someValueMatched);
                someValueMatched |= valueMatched;
                boundLabelsBuilder.Add(boundLabel);
            }

            // Bind switch section statements
            var boundStatementsBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            foreach (var statement in node.Statements)
            {
                boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics));
            }

            return new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree());
        }
        /// <summary>
        /// Bind the pattern switch section, producing subsumption diagnostics.
        /// </summary>
        /// <param name="node"/>
        /// <param name="originalBinder"/>
        /// <param name="defaultLabel">If a default label is found in this section, assigned that label</param>
        /// <param name="someCaseMatches">If a case is found that would always match the input, set to true</param>
        /// <param name="subsumption">A helper class that uses a decision tree to produce subsumption diagnostics.</param>
        /// <param name="diagnostics"></param>
        /// <returns></returns>
        private BoundPatternSwitchSection BindPatternSwitchSection(
            SwitchSectionSyntax node,
            Binder originalBinder,
            ref BoundPatternSwitchLabel defaultLabel,
            ref bool someCaseMatches,
            SubsumptionDiagnosticBuilder subsumption,
            DiagnosticBag diagnostics)
        {
            // Bind match section labels
            var boundLabelsBuilder = ArrayBuilder <BoundPatternSwitchLabel> .GetInstance();

            var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section.

            Debug.Assert(sectionBinder != null);
            var labelsByNode = LabelsByNode;

            bool?inputMatchesType(TypeSymbol patternType)
            {
                // use-site diagnostics will have been reported previously.
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;

                return(ExpressionOfTypeMatchesPatternType(Conversions, SwitchGoverningType, patternType, ref useSiteDiagnostics, out _, SwitchGoverningExpression.ConstantValue, true));
            }

            foreach (var labelSyntax in node.Labels)
            {
                LabelSymbol             label      = labelsByNode[labelSyntax];
                BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, labelSyntax, label, ref defaultLabel, diagnostics);
                bool isNotSubsumed        = subsumption.AddLabel(boundLabel, diagnostics);
                bool guardAlwaysSatisfied = boundLabel.Guard == null || boundLabel.Guard.ConstantValue == ConstantValue.True;

                // patternMatches is true if the input expression is unconditionally matched by the pattern, false if it never matches, null otherwise.
                // While subsumption would produce an error for an unreachable pattern based on the input's type, this is used for reachability (warnings),
                // and takes the input value into account.
                bool?patternMatches;
                if (labelSyntax.Kind() == SyntaxKind.DefaultSwitchLabel)
                {
                    patternMatches = null;
                }
                else if (boundLabel.Pattern.Kind == BoundKind.WildcardPattern)
                {
                    // wildcard pattern matches anything
                    patternMatches = true;
                }
                else if (boundLabel.Pattern is BoundDeclarationPattern d)
                {
                    // `var x` matches anything
                    // `Type x` matches anything of a subtype of `Type` except null
                    patternMatches = d.IsVar ? true : inputMatchesType(d.DeclaredType.Type);
                }
                else if (boundLabel.Pattern is BoundConstantPattern p)
                {
                    // `case 2` matches the input `2`
                    patternMatches = SwitchGoverningExpression.ConstantValue?.Equals(p.ConstantValue);
                }
                else
                {
                    patternMatches = null;
                }

                bool labelIsReachable = isNotSubsumed && !someCaseMatches && patternMatches != false;
                boundLabel = boundLabel.Update(boundLabel.Label, boundLabel.Pattern, boundLabel.Guard, labelIsReachable);
                boundLabelsBuilder.Add(boundLabel);

                // labelWouldMatch is true if we find an unconditional (no `when` clause restriction) label that matches the input expression
                bool labelWouldMatch = guardAlwaysSatisfied && patternMatches == true;
                someCaseMatches |= labelWouldMatch;
            }

            // Bind switch section statements
            var boundStatementsBuilder = ArrayBuilder <BoundStatement> .GetInstance();

            foreach (var statement in node.Statements)
            {
                boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics));
            }

            return(new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree()));
        }