/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $access = \cast($node, 'xp.compiler.ast.MemberAccessNode'); // Verify type // * var: Might have method // * primitive, array, map, int: Definitely don't have fields $type = $scope->typeOf($access->target); if ($type->isVariable()) { return ['T203', 'Member access (var).' . $access->name . '() verification deferred until runtime']; } else { if (!$type->isClass()) { return ['T305', 'Using member access on unsupported type ' . $type->compoundName()]; } } // Verify target method exists $target = new \xp\compiler\types\TypeInstance($scope->resolveType($type)); if ($target->hasField($access->name)) { $member = $target->getField($access->name); } else { if ($target->hasProperty($access->name)) { $member = $target->getProperty($access->name); } else { return ['T404', 'No such field $' . $access->name . ' in ' . $target->name()]; } } // Verify visibility if (!($member->modifiers & MODIFIER_PUBLIC)) { $enclosing = $scope->resolveType($scope->declarations[0]->name); if ($member->modifiers & MODIFIER_PRIVATE && !$enclosing->equals($target) || $member->modifiers & MODIFIER_PROTECTED && !($enclosing->equals($target) || $enclosing->isSubclassOf($target))) { return ['T403', sprintf('Accessing %s %s::$%s from %s', implode(' ', \lang\reflect\Modifiers::namesOf($member->modifiers)), $target->name(), $member->name, $enclosing->name())]; } } }
/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $access = \cast($node, 'xp.compiler.ast.ArrayAccessNode'); $type = $scope->typeOf($access->target); $result = TypeName::$VAR; $message = null; if ($type->isArray()) { $result = $type->arrayComponentType(); } else { if ($type->isMap()) { $result = $type->mapComponentType(); } else { if ($type->isClass()) { $ptr = new TypeInstance($scope->resolveType($type)); if ($ptr->hasIndexer()) { $result = $ptr->getIndexer()->type; } else { $message = ['T305', 'Type ' . $ptr->name() . ' does not support offset access']; } } else { if ($type->isVariable()) { $message = ['T203', 'Array access (var)' . $access->hashCode() . ' verification deferred until runtime']; } else { if ('string' === $type->name) { $result = $type; } else { $message = ['T305', 'Using array-access on unsupported type ' . $type->toString()]; } } } } } $scope->setType($access, $result); return $message; }
/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $arm = cast($node, 'xp.compiler.ast.ArmNode'); foreach ($arm->initializations as $i => $init) { $type = $scope->resolveType($scope->typeOf($init), false); if (!$type->isSubclassOf(self::$closeable)) { return ['A403', 'Type ' . $type->name() . ' for assignment #' . ($i + 1) . ' in ARM block is not closeable']; } } }
/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $call = \cast($node, 'xp.compiler.ast.MethodCallNode'); // Verify type // * var: Might have method // * primitive, array, map, int: Definitely don't have methods $type = $scope->typeOf($call->target); if ($type->isVariable()) { return ['T203', 'Member call (var).' . $call->name . '() verification deferred until runtime']; } else { if (!$type->isClass()) { return ['T305', 'Using member calls on unsupported type ' . $type->compoundName()]; } } return $this->verifyMethod($type, $call->name, $scope); }