Пример #1
0
        /// <summary>
        /// Checks if expression is an explicit function parameter null-check and, if so, valuates it
        /// based on the assumption that parameter is non-null.
        /// </summary>
        /// <param name="valContext">Valuation context</param>
        /// <param name="expression">Expression to valuate</param>
        /// <param name="state">Valuation state</param>
        /// <returns>Expression valuation</returns>
        public bool?Valuate(NullCheckContext valContext, ExpressionSyntax expression, NullCheckState state)
        {
            if (state.filterParams != null && !state.filterParams.Any())
            {
                return(null);
            }

            bool IsNullLiteral(ExpressionSyntax expr) =>
            expr.IsKind(SyntaxKind.NullLiteralExpression);

            bool IsParameterRef(NullCheckContext lContext, ExpressionSyntax lExpression, NullCheckState lState)
            {
                var param = ParameterRefValidator.Validate(lContext, lExpression, lState);

                if (param != null)
                {
                    lState.affectedParams.Add(param);
                    return(true);
                }
                return(false);
            }

            if (!expression.IsKind(SyntaxKind.EqualsExpression) &&
                !expression.IsKind(SyntaxKind.NotEqualsExpression))
            {
                return(null); // not <smth> ==(!=) <smth>
            }
            // param != null === true
            bool valuation  = expression.IsKind(SyntaxKind.NotEqualsExpression);
            var  binaryExpr = (BinaryExpressionSyntax)expression;

            if (IsParameterRef(valContext, binaryExpr.Left, state) &&
                IsNullLiteral(binaryExpr.Right) ||
                IsParameterRef(valContext, binaryExpr.Right, state) &&
                IsNullLiteral(binaryExpr.Left))
            {
                // ok, param ==(!=) null or vice-versa
                return(valuation);
            }
            return(null);
        }
Пример #2
0
        /// <summary>
        /// Checks if expression is a call to a method that has
        /// a null-check of reference-typed function parameters (contained in a filtering set,
        /// if it is provided) as its body and stores the parameters' symbols into state.
        /// Otherwise (or if method's body cannot be found) clears the state and returns <c>null</c>.
        /// </summary>
        /// <param name="valContext">Valuation context</param>
        /// <param name="expression">Expression to check</param>
        /// <param name="state">State to mutate</param>
        /// <returns>If the expression is a call to a method that has a null-check of
        /// reference-typed function parameters, the result is valuation of the method (<c>param != null</c>),
        /// and <c>null</c> otherwise or if method's body cannot be accessed.</returns>
        public bool?Valuate(NullCheckContext valContext, ExpressionSyntax expression, NullCheckState state)
        {
            if (!state.HasFilters)
            {
                state.Clear();
                return(null); // everything is filtered out. no point in continuing.
            }

            if (!(expression is InvocationExpressionSyntax invocation))
            {
                return(null); // expression is not an invocation
            }
            var definition = GetFunctionDefinition(valContext, invocation);

            if (definition == null || definition.SyntaxTree != valContext.context.SemanticModel.SyntaxTree)
            {
                // does not return bool or could not find body in this syntax tree
                return(null);
            }

            // create (callee argument, callee parameter) pairs
            var argZipParam = invocation.ArgumentList.Arguments.Zip(
                from param in definition.ParameterList.Parameters
                select valContext.context.SemanticModel.GetDeclaredSymbol(param),
                (arg, param) => new { arg, param }
                );

            // select only callee arguments that are passed through from caller parameters
            var passthruArgsAndParams = from pair in argZipParam
                                        select new
            {
                arg = ParameterRefValidator.Validate(valContext, pair.arg.Expression, state),
                pair.param
            };

            passthruArgsAndParams = from pair in passthruArgsAndParams where pair.arg != null select pair;

            // select the callee parameters for filtering
            var passthruParams = from pair in passthruArgsAndParams select pair.param;

            ExpressionSyntax bodyExpr;

            if (definition.Body != null)
            {
                // supports only body consisting of one return statement.
                bodyExpr = (definition.Body.Statements.FirstOrDefault() as ReturnStatementSyntax)?.Expression;
                if (bodyExpr == null)
                {
                    return(null);
                }
            }
            else
            {
                bodyExpr = definition.ExpressionBody.Expression;
            }
            // find null check of passed-through caller parameters inside the callee
            var localState = new NullCheckState(new HashSet <IParameterSymbol>(passthruParams));
            var valuation  = valContext.valuationProvider.Valuate(valContext, bodyExpr, localState);

            // for each found checked callee parameter find corresponding caller parameter,
            // as correct filtering is already done
            // (this also clears the state if valuation failed)
            state.affectedParams =
                (from el in (from localSymbol in localState.affectedParams
                             select passthruArgsAndParams.First(
                                 pair => SymbolEqualityComparer.Default.Equals(pair.param, localSymbol)
                                 ).arg)
                 select el).ToList();
            return(valuation);
        }