/** * 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) { $routine = \cast($node, 'xp.compiler.ast.RoutineNode'); $qname = $scope->declarations[0]->name->compoundName() . '::' . $routine->getName(); $empty = $routine->body === null; if ($scope->declarations[0] instanceof \xp\compiler\ast\InterfaceNode) { if (!$empty) { return ['R403', 'Interface methods may not have a body ' . $qname]; } else { if ($routine->modifiers !== MODIFIER_PUBLIC && $routine->modifiers !== 0) { return ['R401', 'Interface methods may only be public ' . $qname]; } } } else { if (Modifiers::isAbstract($routine->modifiers) && !$empty) { return ['R403', 'Abstract methods may not have a body ' . $qname]; } else { if (!Modifiers::isAbstract($routine->modifiers) && $empty) { return ['R401', 'Non-abstract methods must have a body ' . $qname]; } } if ($routine->extension && !Modifiers::isStatic($routine->modifiers)) { return ['E403', 'Extension methods must be static ' . $qname]; } } }
/** * Provide tests from a given package to the test suite. Handles recursion. * * @param lang.reflect.Package $package * @param unittest.TestSuite $suite * @param var[] $arguments * @return void */ private function provideFrom($package, $suite, $arguments) { foreach ($package->getClasses() as $class) { if ($class->isSubclassOf(TestCase::class) && !Modifiers::isAbstract($class->getModifiers())) { $suite->addTestClass($class, $arguments); } } if ($this->recursive) { foreach ($package->getPackages() as $package) { $this->provideFrom($package, $suite, $arguments); } } }
/** * Returns a list of all classes inside a given package * * @param lang.reflect.Package * @param bool recursive whether to include subpackages * @return lang.XPClass[] */ protected static function testClassesIn(Package $package, $recursive) { $r = array(); foreach ($package->getClasses() as $class) { if (!$class->isSubclassOf('unittest.TestCase') || Modifiers::isAbstract($class->getModifiers())) { continue; } $r[] = $class; } if ($recursive) { foreach ($package->getPackages() as $package) { $r = array_merge($r, self::testClassesIn($package, $recursive)); } } return $r; }
/** * Provide tests to test suite * * @param unittest.TestSuite $suite * @param var[] $arguments * @return void * @throws lang.IllegalArgumentException if no tests are found */ public function provideTo($suite, $arguments) { $it = new FilteredIOCollectionIterator(new FileCollection($this->loader->path), new ExtensionEqualsFilter(\xp::CLASS_FILE_EXT), true); $l = strlen($this->loader->path); $e = -strlen(\xp::CLASS_FILE_EXT); $empty = true; foreach ($it as $element) { $class = $this->loader->loadClass(strtr(substr($element->getUri(), $l, $e), DIRECTORY_SEPARATOR, '.')); if ($class->isSubclassOf(TestCase::class) && !Modifiers::isAbstract($class->getModifiers())) { $suite->addTestClass($class, $arguments); $empty = false; } } if ($empty) { throw new IllegalArgumentException('Cannot find any test cases in ' . $this->loader->toString()); } }
/** * Get all test cases * * @param var[] arguments * @return unittest.TestCase[] */ public function testCasesWith($arguments) { if (null === ($cl = $this->findLoaderFor($this->folder->getURI()))) { throw new IllegalArgumentException($this->folder->toString() . ' is not in class path'); } $l = strlen($cl->path); $e = -strlen(\xp::CLASS_FILE_EXT); $it = new FilteredIOCollectionIterator(new FileCollection($this->folder), new ExtensionEqualsFilter(\xp::CLASS_FILE_EXT), true); $cases = []; foreach ($it as $element) { $name = strtr(substr($element->getUri(), $l, $e), DIRECTORY_SEPARATOR, '.'); $class = XPClass::forName($name); if (!$class->isSubclassOf('unittest.TestCase') || Modifiers::isAbstract($class->getModifiers())) { continue; } $cases = array_merge($cases, $this->testCasesInClass($class, $arguments)); } if (empty($cases)) { throw new IllegalArgumentException('Cannot find any test cases in ' . $this->folder->toString()); } return $cases; }
public function is_abstract() { $this->assertTrue(\lang\reflect\Modifiers::isAbstract(self::$fixture->getModifiers())); }
public function abstractModifier() { $this->assertTrue(\lang\reflect\Modifiers::isAbstract(MODIFIER_ABSTRACT)); }
/** * Lists commands * * @return void */ protected function listCommands() { $commandsIn = function ($package) { $text = ''; foreach ($package->getClasses() as $class) { if ($class->isSubclassOf('util.cmd.Command') && !Modifiers::isAbstract($class->getModifiers())) { $text .= ' $ xpcli ' . $class->getSimpleName() . "\n"; } } return $text ?: ' (no commands)'; }; self::$err->writeLine('Named commands'); self::$err->writeLine(); if ($packages = Commands::allPackages()) { foreach (Commands::allPackages() as $package) { self::$err->writeLine('* ', $package); self::$err->writeLine($commandsIn($package)); } self::$err->writeLine(); } self::$err->writeLine('* Global package'); self::$err->writeLine($commandsIn(Package::forName(null))); }
/** * Asserts given modifiers do not contain abstract * * @param int $modifiers * @return void * @throws unittest.AssertionFailedError */ protected function assertNotAbstract($modifiers) { $this->assertFalse(Modifiers::isAbstract($modifiers), implode(' | ', Modifiers::namesOf($modifiers))); }
/** * Lists commands * * @return void */ protected function listCommands() { $commandsIn = function ($package) { $markdown = ''; foreach ($package->getClasses() as $class) { if ($class->isSubclassOf('util.cmd.Command') && !Modifiers::isAbstract($class->getModifiers())) { $markdown .= ' $ xp cmd ' . $class->getSimpleName() . "\n"; } } return $markdown ?: ' *(no commands)*'; }; $markdown = "# Named commands\n\n"; if ($packages = Commands::allPackages()) { foreach ($packages as $package) { $markdown .= '* In package **' . $package->getName() . "**\n\n" . $commandsIn($package); } $markdown .= "\n"; } $markdown .= "* In global package\n\n" . $commandsIn(Package::forName(null)); Help::render(self::$err, $markdown, []); }
/** * Emit an enum declaration * * Basic form: * <code> * public enum Day { MON, TUE, WED, THU, FRI, SAT, SUN } * </code> * * With values: * <code> * public enum Coin { penny(1), nickel(2), dime(10), quarter(25) } * </code> * * Abstract: * <code> * public abstract enum Operation { * plus { * public int evaluate(int $x, int $y) { return $x + $y; } * }, * minus { * public int evaluate(int $x, int $y) { return $x - $y; } * }; * * public abstract int evaluate(int $x, int $y); * } * </code> * * @param xp.compiler.emit.Buffer b * @param xp.compiler.ast.EnumNode declaration */ protected function emitEnum($b, $declaration) { $parent = $declaration->parent ?: new TypeName('lang.Enum'); $parentType = $this->resolveType($parent); $thisType = new TypeDeclaration(new ParseTree($this->scope[0]->package, [], $declaration), $parentType); $this->scope[0]->addResolved('self', $thisType); $this->scope[0]->addResolved('parent', $parentType); // FIXME: ??? $this->scope[0]->addResolved($declaration->name->name, $thisType); $this->scope[0]->imports[$declaration->name->name] = $declaration->name->name; $this->enter(new TypeDeclarationScope()); // Ensure parent class and interfaces are loaded $this->emitTypeName($b, 'class', $declaration); $b->append(' extends ' . $this->literal($parentType, true)); array_unshift($this->metadata, [[], []]); array_unshift($this->properties, ['get' => [], 'set' => []]); $abstract = Modifiers::isAbstract($declaration->modifiers); // Meta data $this->metadata[0]['class'] = $this->meta($declaration->comment, $declaration->annotations, []); // Generics if ($declaration->name->isGeneric()) { $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['self'] = $this->genericComponentAsMetadata($declaration->name); } if ($parent->isGeneric()) { $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['parent'] = $this->genericComponentAsMetadata($parent); } // Interfaces if ($declaration->implements) { $b->append(' implements '); $s = sizeof($declaration->implements) - 1; foreach ($declaration->implements as $i => $type) { if ($type->isGeneric()) { $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['implements'][$i] = $this->genericComponentAsMetadata($type); } $b->append($this->resolveType($type)->literal(true)); $i < $s && $b->append(', '); } } // Member declaration $b->append(' {'); // public static self[] values() { return parent::membersOf(__CLASS__) } $declaration->body[] = new MethodNode(['modifiers' => MODIFIER_PUBLIC | MODIFIER_STATIC, 'annotations' => null, 'name' => 'values', 'returns' => new TypeName('self[]'), 'parameters' => null, 'throws' => null, 'body' => [new ReturnNode(new StaticMethodCallNode(new TypeName('parent'), 'membersOf', [new StringNode($this->literal($thisType))]))], 'comment' => '(Generated)']); // Members foreach ((array) $declaration->body as $node) { $this->emitOne($b, $node); } $this->emitProperties($b, $this->properties[0]); // Initialization $b->append('static function __static() {'); foreach ($declaration->body as $i => $member) { if (!$member instanceof EnumMemberNode) { continue; } $b->append('self::$' . $member->name . '= '); if ($member->body) { if (!$abstract) { $this->error('E403', 'Only abstract enums can contain members with bodies (' . $member->name . ')'); // Continues so declaration is closed } $unique = new TypeName($declaration->name->name . '··' . $member->name); $decl = new ClassNode(0, null, $unique, $declaration->name, [], $member->body); $decl->synthetic = true; $ptr = new TypeDeclaration(new ParseTree(null, [], $decl), $thisType); $this->scope[0]->declarations[] = $decl; $b->append('new ' . $unique->name . '('); } else { $b->append('new self('); } if ($member->value) { $this->emitOne($b, $member->value); } else { $b->append($i); } $b->append(', \'' . $member->name . '\');'); } $b->append('}'); // Finish $b->append('}'); $this->leave(); $this->registerClass($b, $declaration, $thisType->name()); array_shift($this->properties); array_shift($this->metadata); // Register type info $this->types[0]->name = $thisType->name(); $this->types[0]->kind = Types::ENUM_KIND; $this->types[0]->literal = $this->declaration($declaration); $this->types[0]->parent = $parentType; $this->types[0]->modifiers = $declaration->modifiers; }