Пример #1
0
 private void CheckUninitializedVariableUse(BoundVariableRef x)
 {
     if (x.MaybeUninitialized && !x.Access.IsQuiet)
     {
         _diagnostics.Add(_routine, x.PhpSyntax, ErrorCode.WRN_UninitializedVariableUse, x.Name.NameValue.ToString());
     }
 }
Пример #2
0
 /// <summary>
 /// Ensures that the variable is of the given type(s) in the positive branch or not of this type in the negative
 /// branch. If the current branch is unfeasible, assigns an appropriate boolean to the
 /// <see cref="BoundExpression.ConstantValue"/> of <paramref name="checkExpr"/>.
 /// </summary>
 /// <param name="varRef">The reference to the variable whose types to check.</param>
 /// <param name="targetType">The target type of the variable.</param>
 /// <param name="branch">The branch to check - <see cref="ConditionBranch.ToTrue"/> is understood as the positive
 /// branch if <paramref name="isPositiveCheck"/> is true.</param>
 /// <param name="flowState">The flow state of the branch.</param>
 /// <param name="skipPositiveIfAnyType">Whether to skip a mask with <see cref="TypeRefMask.IsAnyType"/> in the
 /// positive branch (in the negative branch, it is always skipped).</param>
 /// <param name="checkExpr">The expression to have its <see cref="BoundExpression.ConstantValue"/> potentially
 /// updated.</param>
 /// <param name="isPositiveCheck">Whether the expression returns true when the type check succeeds. For example,
 /// in the case of != it would be false.</param>
 public static void HandleTypeCheckingExpression(
     BoundVariableRef varRef,
     TypeRefMask targetType,
     ConditionBranch branch,
     FlowState flowState,
     bool skipPositiveIfAnyType = false,
     BoundExpression checkExpr  = null,
     bool isPositiveCheck       = true)
 {
     HandleTypeCheckingExpression(varRef, (var) => targetType, branch, flowState, skipPositiveIfAnyType, checkExpr, isPositiveCheck);
 }
        public override VoidStruct VisitVariableRef(BoundVariableRef x)
        {
            if (x.PhpSyntax?.Span.Contains(_position) == true)
            {
                var symbolOpt = x is ILocalReferenceOperation loc ? loc.Local : null;

                _result = new SymbolStat(_tctx, x.PhpSyntax.Span, x, symbolOpt);
            }

            //
            return(base.VisitVariableRef(x));
        }
Пример #4
0
        /// <summary>
        /// Ensures that the variable is of the given type(s) in the positive branch or not of this type in the negative
        /// branch. If the current branch is unfeasible, assigns an appropriate boolean to the
        /// <see cref="BoundExpression.ConstantValue"/> of <paramref name="checkExpr"/>.
        /// </summary>
        /// <param name="varRef">The reference to the variable whose types to check.</param>
        /// <param name="targetTypeCallback">The callback that receives the current type mask of the variable and returns
        /// the target one.</param>
        /// <param name="branch">The branch to check - <see cref="ConditionBranch.ToTrue"/> is understood as the positive
        /// branch if <paramref name="isPositiveCheck"/> is true.</param>
        /// <param name="flowState">The flow state of the branch.</param>
        /// <param name="skipPositiveIfAnyType">Whether to skip a mask with <see cref="TypeRefMask.IsAnyType"/> in the
        /// positive branch (in the negative branch, it is always skipped).</param>
        /// <param name="checkExpr">The expression to have its <see cref="BoundExpression.ConstantValue"/> potentially
        /// updated.</param>
        /// <param name="isPositiveCheck">Whether the expression returns true when the type check succeeds. For example,
        /// in the case of != it would be false.</param>
        public static void HandleTypeCheckingExpression(
            BoundVariableRef varRef,
            Func <TypeRefMask, TypeRefMask> targetTypeCallback,
            ConditionBranch branch,
            FlowState flowState,
            bool skipPositiveIfAnyType = false,
            BoundExpression checkExpr  = null,
            bool isPositiveCheck       = true)
        {
            if (!TryGetVariableHandle(varRef.Variable, flowState, out VariableHandle handle))
            {
                return;
            }

            var currentType = flowState.GetLocalType(handle);
            var targetType  = targetTypeCallback(currentType);

            // Model negative type checks (such as $x != null) by inverting branches for the core checking function
            var branchHlp = isPositiveCheck ? branch : branch.NegativeBranch();

            bool isFeasible = HandleTypeChecking(currentType, targetType, branchHlp, flowState, handle, skipPositiveIfAnyType);

            // If the constant value was not meant to be updated, skip its computation
            if (checkExpr == null)
            {
                return;
            }

            if (!currentType.IsRef)
            {
                // If the true branch proves to be unfeasible, the function always returns false and vice versa
                var resultConstVal = isFeasible ? default(Optional <object>) : new Optional <object>(!branch.TargetValue().Value);

                // Each branch can clean only the constant value it produced during its analysis (in order not to lose result
                // of the other branch): true branch can produce false value and vice versa
                if (!resultConstVal.EqualsOptional(checkExpr.ConstantValue) &&
                    (resultConstVal.HasValue ||
                     checkExpr.ConstantValue.Value is false && branch == ConditionBranch.ToTrue ||
                     checkExpr.ConstantValue.Value is true && branch == ConditionBranch.ToFalse))
                {
                    checkExpr.ConstantValue = resultConstVal;
                }
            }
            else
            {
                // We cannot reason about the result of the check if the variable can be modified by reference
                checkExpr.ConstantValue = default(Optional <object>);
            }
        }
Пример #5
0
 internal CatchBlock Update(BoundTypeRef typeRef, BoundVariableRef variable, List <BoundStatement> statements, Edge nextEdge)
 {
     if (typeRef == _typeRef && variable == _variable && statements == Statements && nextEdge == NextEdge)
     {
         return(this);
     }
     else
     {
         return(new CatchBlock(typeRef, variable, statements)
         {
             NextEdge = nextEdge
         }
                .WithLocalPropertiesFrom(this));
     }
 }
        public override object VisitAssign(BoundAssignEx x)
        {
            // A = A <binOp> <right>
            if (x.Target is BoundVariableRef trg &&
                MatchExprSkipCopy(x.Value, out BoundBinaryEx binOp, isCopied: out _) &&
                binOp.Left is BoundVariableRef valLeft &&
                trg.Variable == valLeft.Variable)
            {
                var newTrg =
                    new BoundVariableRef(trg.Name)
                    .WithAccess(trg.Access.WithRead())
                    .WithSyntax(trg.PhpSyntax);

                // A = A +/- 1; => ++A; / --A;
                if ((binOp.Operation == Ast.Operations.Add || binOp.Operation == Ast.Operations.Sub) &&
                    binOp.Right.ConstantValue.IsInteger(out long rightVal) && rightVal == 1)
                {
                    TransformationCount++;
                    return(new BoundIncDecEx(newTrg, binOp.Operation == Ast.Operations.Add, false).WithAccess(x));
                }

                // A = A & B => A &= B; // &, |, ^, <<, >>, +, -, *, /, %, **, .
                switch (binOp.Operation)
                {
                case Ast.Operations.BitAnd:
                case Ast.Operations.BitOr:
                case Ast.Operations.BitXor:
                case Ast.Operations.ShiftLeft:
                case Ast.Operations.ShiftRight:
                case Ast.Operations.Add:
                case Ast.Operations.Sub:
                case Ast.Operations.Mul:
                case Ast.Operations.Div:
                case Ast.Operations.Mod:
                case Ast.Operations.Pow:
                case Ast.Operations.Concat:
                    TransformationCount++;
                    var compoundOp = AstUtils.BinaryToCompoundOp(binOp.Operation);
                    return(new BoundCompoundAssignEx(newTrg, binOp.Right, compoundOp).WithAccess(x));
                }
            }

            return(base.VisitAssign(x));
        }
Пример #7
0
        public override void VisitVariableRef(BoundVariableRef x)
        {
            ISymbol symbolOpt = null;

            try
            {
                // may throw NotImplementedException
                symbolOpt = (ISymbol)
                            (x.Variable as IVariable)?.Variable ??
                            (x.Variable as IParameterInitializer)?.Parameter;
            }
            catch (NotImplementedException)
            {
                // ignore
            }

            _result.Add(new SymbolStat(_tctx, x.PhpSyntax.Span, x, symbolOpt));

            //
            base.VisitVariableRef(x);
        }
Пример #8
0
        public override VoidStruct VisitVariableRef(BoundVariableRef x)
        {
            ISymbol symbolOpt = null;

            try
            {
                // may throw NotImplementedException
                symbolOpt = (ISymbol)
                            (x.Variable as IVariableDeclaratorOperation)?.Symbol ??
                            (x.Variable as IParameterInitializerOperation)?.Parameter;
            }
            catch (NotImplementedException)
            {
                // ignore
            }

            _result.Add(new SymbolStat(_tctx, x.PhpSyntax.Span, x, symbolOpt));

            //
            base.VisitVariableRef(x);

            return(default);
Пример #9
0
 public override void VisitVariableRef(BoundVariableRef x)
 {
     CheckUninitializedVariableUse(x);
     base.VisitVariableRef(x);
 }
Пример #10
0
 private CatchBlock(IBoundTypeRef typeRef, BoundVariableRef variable, List <BoundStatement> statements)
     : base(statements)
 {
     _typeRef  = typeRef;
     _variable = variable;
 }
Пример #11
0
 public CatchBlock(IBoundTypeRef typeRef, BoundVariableRef variable)
     : this(typeRef, variable, new List <BoundStatement>())
 {
 }
Пример #12
0
 public override object VisitVariableRef(BoundVariableRef x)
 {
     return(x.Update((BoundVariableName)Accept(x.Name)));
 }
Пример #13
0
        public override T VisitVariableRef(BoundVariableRef x)
        {
            CheckUninitializedVariableUse(x);

            return base.VisitVariableRef(x);
        }
Пример #14
0
 public CatchBlock(BoundTypeRef typeRef, BoundVariableRef variable)
 {
     _typeRef  = typeRef;
     _variable = variable;
 }
Пример #15
0
        /// <summary>
        /// Processes functions such as is_int, is_bool etc. Returns whether the function was one of these.
        /// </summary>
        private static bool HandleTypeCheckingFunctions(
            BoundGlobalFunctionCall call,
            string name,
            BoundVariableRef arg,
            ExpressionAnalysis analysis,
            ConditionBranch branch)
        {
            var typeCtx   = analysis.TypeCtx;
            var flowState = analysis.State;

            switch (name)
            {
            case "is_int":
            case "is_integer":
            case "is_long":
                HandleTypeCheckingExpression(arg, typeCtx.GetLongTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_bool":
                HandleTypeCheckingExpression(arg, typeCtx.GetBooleanTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_float":
            case "is_double":
            case "is_real":
                HandleTypeCheckingExpression(arg, typeCtx.GetDoubleTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_string":
                var stringMask = typeCtx.GetStringTypeMask() | typeCtx.GetWritableStringTypeMask();
                HandleTypeCheckingExpression(arg, stringMask, branch, flowState, checkExpr: call);
                return(true);

            case "is_resource":
                HandleTypeCheckingExpression(arg, typeCtx.GetResourceTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_null":
                HandleTypeCheckingExpression(arg, typeCtx.GetNullTypeMask(), branch, flowState, checkExpr: call);
                return(true);

            case "is_array":
                HandleTypeCheckingExpression(
                    arg,
                    currentType => typeCtx.GetArraysFromMask(currentType),
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            case "is_object":
                // Keep IncludesSubclasses flag in the true branch and clear it in the false branch
                HandleTypeCheckingExpression(
                    arg,
                    currentType => typeCtx.GetObjectsFromMask(currentType).WithIncludesSubclasses,
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            // TODO
            //case "is_scalar":
            //    return;

            case "is_numeric":
                HandleTypeCheckingExpression(
                    arg,
                    currentType =>
                {
                    // Specify numeric types if they are present
                    var targetType = typeCtx.IsLong(currentType) ? typeCtx.GetLongTypeMask() : 0;
                    targetType    |= typeCtx.IsDouble(currentType) ? typeCtx.GetDoubleTypeMask() : 0;

                    if (branch == ConditionBranch.ToTrue)
                    {
                        // Also string types can make is_numeric return true, but not anything else
                        targetType |= typeCtx.IsReadonlyString(currentType) ? typeCtx.GetStringTypeMask() : 0;
                        targetType |= typeCtx.IsWritableString(currentType) ? typeCtx.GetWritableStringTypeMask() : 0;

                        return(targetType);
                    }
                    else
                    {
                        // For number, is_numeric always returns true -> remove numeric types from false branch
                        return(targetType);
                    }
                },
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            case "is_callable":
                HandleTypeCheckingExpression(
                    arg,
                    currentType =>
                {
                    // Closure is specified in both branches
                    TypeRefMask targetType = 0;
                    AddTypeIfInContext(typeCtx, type => type.IsLambda, false, ref targetType);

                    if (branch == ConditionBranch.ToTrue)
                    {
                        // Also string types, arrays and objects can make is_callable return true, but not anything else
                        targetType |= typeCtx.IsReadonlyString(currentType) ? typeCtx.GetStringTypeMask() : 0;
                        targetType |= typeCtx.IsWritableString(currentType) ? typeCtx.GetWritableStringTypeMask() : 0;
                        targetType |= typeCtx.GetArraysFromMask(currentType);
                        targetType |= typeCtx.GetObjectsFromMask(currentType);

                        return(targetType);
                    }
                    else
                    {
                        // For closure, is_callable always returns true -> remove the closure type from false branch,
                        // don't remove IncludeSubclasses flag
                        return(targetType);
                    }
                },
                    branch,
                    flowState,
                    skipPositiveIfAnyType: true,
                    checkExpr: call);
                return(true);

            // TODO
            //case "is_iterable":
            //    return;

            default:
                return(false);
            }
        }
Пример #16
0
 private static bool CanBeTypeCheckingFunction(BoundGlobalFunctionCall call, string name, out BoundVariableRef arg)
 {
     if (name.StartsWith("is_") && call.ArgumentsInSourceOrder.Length == 1 &&
         call.ArgumentsInSourceOrder[0].Value is BoundVariableRef onlyArg)
     {
         arg = onlyArg;
         return(true);
     }
     else
     {
         arg = null;
         return(false);
     }
 }