/** * This first pass parses code and looks for the subset * of issues that can be found without having to have * an understanding of the entire code base. * * @param CodeBase $code_base * The CodeBase represents state across the entire * code base. This is a mutable object which is * populated as we parse files * * @param string $file_path * The full path to a file we'd like to parse * * @return Context */ public static function parseFile(CodeBase $code_base, string $file_path) : Context { $context = (new Context())->withFile($file_path); // Convert the file to an Abstract Syntax Tree // before passing it on to the recursive version // of this method $node = \ast\parse_file($file_path, Config::get()->ast_version); if (Config::get()->dump_ast) { echo $file_path . "\n" . str_repeat("¯", strlen($file_path)) . "\n"; Debug::printNode($node); return $context; } if (empty($node)) { Issue::emit(Issue::EmptyFile, $file_path, 0, $file_path); return $context; } return self::parseNodeInContext($code_base, $context, $node); }
/** * This first pass parses code and looks for the subset * of issues that can be found without having to have * an understanding of the entire code base. * * @param CodeBase $code_base * The CodeBase represents state across the entire * code base. This is a mutable object which is * populated as we parse files * * @param string $file_path * The full path to a file we'd like to parse * * @return Context */ public static function parseFile(CodeBase $code_base, string $file_path) : Context { $context = (new Context())->withFile($file_path); // Convert the file to an Abstract Syntax Tree // before passing it on to the recursive version // of this method try { $node = \ast\parse_file(Config::projectPath($file_path), Config::get()->ast_version); } catch (\ParseError $parse_error) { Issue::maybeEmit($code_base, $context, Issue::SyntaxError, $parse_error->getLine(), $parse_error->getMessage()); return $context; } if (Config::get()->dump_ast) { echo $file_path . "\n" . str_repeat("¯", strlen($file_path)) . "\n"; Debug::printNode($node); return $context; } if (empty($node)) { Issue::maybeEmit($code_base, $context, Issue::EmptyFile, 0, $file_path); return $context; } return self::parseNodeInContext($code_base, $context, $node); }
/** * Accepts a visitor that differentiates on the flag value * of the AST node. * * @suppress PhanUnreferencedMethod */ public function acceptAnyFlagVisitor(FlagVisitor $visitor) { switch ($this->node->flags) { case \ast\flags\ASSIGN_ADD: return $visitor->visitAssignAdd($this->node); case \ast\flags\ASSIGN_BITWISE_AND: return $visitor->visitAssignBitwiseAnd($this->node); case \ast\flags\ASSIGN_BITWISE_OR: return $visitor->visitAssignBitwiseOr($this->node); case \ast\flags\ASSIGN_BITWISE_XOR: return $visitor->visitAssignBitwiseXor($this->node); case \ast\flags\ASSIGN_CONCAT: return $visitor->visitAssignConcat($this->node); case \ast\flags\ASSIGN_DIV: return $visitor->visitAssignDiv($this->node); case \ast\flags\ASSIGN_MOD: return $visitor->visitAssignMod($this->node); case \ast\flags\ASSIGN_MUL: return $visitor->visitAssignMul($this->node); case \ast\flags\ASSIGN_POW: return $visitor->visitAssignPow($this->node); case \ast\flags\ASSIGN_SHIFT_LEFT: return $visitor->visitAssignShiftLeft($this->node); case \ast\flags\ASSIGN_SHIFT_RIGHT: return $visitor->visitAssignShiftRight($this->node); case \ast\flags\ASSIGN_SUB: return $visitor->visitAssignSub($this->node); case \ast\flags\BINARY_ADD: return $visitor->visitBinaryAdd($this->node); case \ast\flags\BINARY_BITWISE_AND: return $visitor->visitBinaryBitwiseAnd($this->node); case \ast\flags\BINARY_BITWISE_OR: return $visitor->visitBinaryBitwiseOr($this->node); case \ast\flags\BINARY_BITWISE_XOR: return $visitor->visitBinaryBitwiseXor($this->node); case \ast\flags\BINARY_BOOL_XOR: return $visitor->visitBinaryBoolXor($this->node); case \ast\flags\BINARY_CONCAT: return $visitor->visitBinaryConcat($this->node); case \ast\flags\BINARY_DIV: return $visitor->visitBinaryDiv($this->node); case \ast\flags\BINARY_IS_EQUAL: return $visitor->visitBinaryIsEqual($this->node); case \ast\flags\BINARY_IS_IDENTICAL: return $visitor->visitBinaryIsIdentical($this->node); case \ast\flags\BINARY_IS_NOT_EQUAL: return $visitor->visitBinaryIsNotEqual($this->node); case \ast\flags\BINARY_IS_NOT_IDENTICAL: return $visitor->visitBinaryIsNotIdentical($this->node); case \ast\flags\BINARY_IS_SMALLER: return $visitor->visitBinaryIsSmaller($this->node); case \ast\flags\BINARY_IS_SMALLER_OR_EQUAL: return $visitor->visitBinaryIsSmallerOrEqual($this->node); case \ast\flags\BINARY_MOD: return $visitor->visitBinaryMod($this->node); case \ast\flags\BINARY_MUL: return $visitor->visitBinaryMul($this->node); case \ast\flags\BINARY_POW: return $visitor->visitBinaryPow($this->node); case \ast\flags\BINARY_SHIFT_LEFT: return $visitor->visitBinaryShiftLeft($this->node); case \ast\flags\BINARY_SHIFT_RIGHT: return $visitor->visitBinaryShiftRight($this->node); case \ast\flags\BINARY_SPACESHIP: return $visitor->visitBinarySpaceship($this->node); case \ast\flags\BINARY_SUB: return $visitor->visitBinarySub($this->node); case \ast\flags\CLASS_ABSTRACT: return $visitor->visitClassAbstract($this->node); case \ast\flags\CLASS_FINAL: return $visitor->visitClassFinal($this->node); case \ast\flags\CLASS_INTERFACE: return $visitor->visitClassInterface($this->node); case \ast\flags\CLASS_TRAIT: return $visitor->visitClassTrait($this->node); case \ast\flags\MODIFIER_ABSTRACT: return $visitor->visitModifierAbstract($this->node); case \ast\flags\MODIFIER_FINAL: return $visitor->visitModifierFinal($this->node); case \ast\flags\MODIFIER_PRIVATE: return $visitor->visitModifierPrivate($this->node); case \ast\flags\MODIFIER_PROTECTED: return $visitor->visitModifierProtected($this->node); case \ast\flags\MODIFIER_PUBLIC: return $visitor->visitModifierPublic($this->node); case \ast\flags\MODIFIER_STATIC: return $visitor->visitModifierStatic($this->node); case \ast\flags\NAME_FQ: return $visitor->visitNameFq($this->node); case \ast\flags\NAME_NOT_FQ: return $visitor->visitNameNotFq($this->node); case \ast\flags\NAME_RELATIVE: return $visitor->visitNameRelative($this->node); case \ast\flags\PARAM_REF: return $visitor->visitParamRef($this->node); case \ast\flags\PARAM_VARIADIC: return $visitor->visitParamVariadic($this->node); case \ast\flags\RETURNS_REF: return $visitor->visitReturnsRef($this->node); case \ast\flags\TYPE_ARRAY: return $visitor->visitUnionTypeArray($this->node); case \ast\flags\TYPE_BOOL: return $visitor->visitUnionTypeBool($this->node); case \ast\flags\TYPE_CALLABLE: return $visitor->visitUnionTypeCallable($this->node); case \ast\flags\TYPE_DOUBLE: return $visitor->visitUnionTypeDouble($this->node); case \ast\flags\TYPE_LONG: return $visitor->visitUnionTypeLong($this->node); case \ast\flags\TYPE_NULL: return $visitor->visitUnionTypeNull($this->node); case \ast\flags\TYPE_OBJECT: return $visitor->visitUnionTypeObject($this->node); case \ast\flags\TYPE_STRING: return $visitor->visitUnionTypeString($this->node); case \ast\flags\UNARY_BITWISE_NOT: return $visitor->visitUnaryBitwiseNot($this->node); case \ast\flags\UNARY_BOOL_NOT: return $visitor->visitUnaryBoolNot($this->node); case \ast\flags\BINARY_BOOL_AND: return $visitor->visitBinaryBoolAnd($this->node); case \ast\flags\BINARY_BOOL_OR: return $visitor->visitBinaryBoolOr($this->node); case \ast\flags\BINARY_IS_GREATER: return $visitor->visitBinaryIsGreater($this->node); case \ast\flags\BINARY_IS_GREATER_OR_EQUAL: return $visitor->visitBinaryIsGreaterOrEqual($this->node); case \ast\flags\CLASS_ANONYMOUS: return $visitor->visitClassAnonymous($this->node); case \ast\flags\EXEC_EVAL: return $visitor->visitExecEval($this->node); case \ast\flags\EXEC_INCLUDE: return $visitor->visitExecInclude($this->node); case \ast\flags\EXEC_INCLUDE_ONCE: return $visitor->visitExecIncludeOnce($this->node); case \ast\flags\EXEC_REQUIRE: return $visitor->visitExecRequire($this->node); case \ast\flags\EXEC_REQUIRE_ONCE: return $visitor->visitExecRequireOnce($this->node); case \ast\flags\MAGIC_CLASS: return $visitor->visitMagicClass($this->node); case \ast\flags\MAGIC_DIR: return $visitor->visitMagicDir($this->node); case \ast\flags\MAGIC_FILE: return $visitor->visitMagicFile($this->node); case \ast\flags\MAGIC_FUNCTION: return $visitor->visitMagicFunction($this->node); case \ast\flags\MAGIC_LINE: return $visitor->visitMagicLine($this->node); case \ast\flags\MAGIC_METHOD: return $visitor->visitMagicMethod($this->node); case \ast\flags\MAGIC_NAMESPACE: return $visitor->visitMagicNamespace($this->node); case \ast\flags\MAGIC_TRAIT: return $visitor->visitMagicTrait($this->node); case \ast\flags\UNARY_MINUS: return $visitor->visitUnaryMinus($this->node); case \ast\flags\UNARY_PLUS: return $visitor->visitUnaryPlus($this->node); case \ast\flags\UNARY_SILENCE: return $visitor->visitUnarySilence($this->node); case \ast\flags\USE_CONST: return $visitor->visitUseConst($this->node); case \ast\flags\USE_FUNCTION: return $visitor->visitUseFunction($this->node); case \ast\flags\USE_NORMAL: return $visitor->visitUseNormal($this->node); default: assert(false, "All flags must match. Found " . Debug::astFlagDescription($this->node->flags ?? 0)); break; } }
/** * Visit a node with kind `\ast\AST_TYPE` * * @param Node $node * A node of the type indicated by the method name that we'd * like to figure out the type that it produces. * * @return UnionType * The set of types that are possibly produced by the * given node */ public function visitType(Node $node) : UnionType { switch ($node->flags) { case \ast\flags\TYPE_ARRAY: return ArrayType::instance()->asUnionType(); case \ast\flags\TYPE_BOOL: return BoolType::instance()->asUnionType(); case \ast\flags\TYPE_CALLABLE: return CallableType::instance()->asUnionType(); case \ast\flags\TYPE_DOUBLE: return FloatType::instance()->asUnionType(); case \ast\flags\TYPE_LONG: return IntType::instance()->asUnionType(); case \ast\flags\TYPE_NULL: return NullType::instance()->asUnionType(); case \ast\flags\TYPE_OBJECT: return ObjectType::instance()->asUnionType(); case \ast\flags\TYPE_STRING: return StringType::instance()->asUnionType(); default: assert(false, "All flags must match. Found " . Debug::astFlagDescription($node->flags ?? 0)); break; } }
/** * Default visitor for node kinds that do not have * an overriding method * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visit(Node $node) : Context { assert(false, "Unknown left side of assignment in {$this->context} with node type " . Debug::nodeName($node)); return $this->visitVar($node); }
/** * This first pass parses code and looks for the subset * of issues that can be found without having to have * an understanding of the entire code base. * * @param CodeBase $code_base * The CodeBase represents state across the entire * code base. This is a mutable object which is * populated as we parse files * * @param string $file_path * The full path to a file we'd like to parse */ public function parseFile(CodeBase $code_base, string $file_path) : Context { // Convert the file to an Abstract Syntax Tree // before passing it on to the recursive version // of this method $node = \ast\parse_file($file_path, Config::get()->ast_version); $context = (new Context())->withFile($file_path); if (Config::get()->dump_ast) { echo $file_path . "\n" . str_repeat("¯", strlen($file_path)) . "\n"; Debug::printNode($node); return $context; } if (empty($node)) { Log::err(Log::EUNDEF, "Empty or missing file {$file_path}", $file_path, 0); return $context; } return $this->parseNodeInContext($code_base, $context, $node); }