private void CheckUninitializedVariableUse(BoundVariableRef x) { if (x.MaybeUninitialized && !x.Access.IsQuiet) { _diagnostics.Add(_routine, x.PhpSyntax, ErrorCode.WRN_UninitializedVariableUse, x.Name.NameValue.ToString()); } }
/// <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)); }
/// <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>); } }
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)); }
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); }
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);
public override void VisitVariableRef(BoundVariableRef x) { CheckUninitializedVariableUse(x); base.VisitVariableRef(x); }
private CatchBlock(IBoundTypeRef typeRef, BoundVariableRef variable, List <BoundStatement> statements) : base(statements) { _typeRef = typeRef; _variable = variable; }
public CatchBlock(IBoundTypeRef typeRef, BoundVariableRef variable) : this(typeRef, variable, new List <BoundStatement>()) { }
public override object VisitVariableRef(BoundVariableRef x) { return(x.Update((BoundVariableName)Accept(x.Name))); }
public override T VisitVariableRef(BoundVariableRef x) { CheckUninitializedVariableUse(x); return base.VisitVariableRef(x); }
public CatchBlock(BoundTypeRef typeRef, BoundVariableRef variable) { _typeRef = typeRef; _variable = variable; }
/// <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); } }
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); } }