Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions docs/bootstrap-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ Regenerate: `php script/bootstrap-inventory.php`

| Metric | Count |
|--------|------:|
| PHP files on vm.php path | 195 |
| PHP files on vm.php path | 196 |
| Source constructs flagged (blockers) | 10 |
| Source constructs flagged (warnings) | 518 |
| Source constructs flagged (warnings) | 523 |

## Compiler CFG gaps (`lib/Compiler.php`)

Expand Down Expand Up @@ -197,9 +197,10 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
| `lib/JIT/SuperglobalInit.php` | 0 | 3 |
| `lib/JIT/ValueEchoHelper.php` | 0 | 1 |
| `lib/JIT/Variable.php` | 0 | 18 |
| `lib/Lint/IncrementDetector.php` | 0 | 4 |
| `lib/Lint/Issue.php` | 0 | 2 |
| `lib/Lint/LintCompiler.php` | 0 | 6 |
| `lib/Lint/Linter.php` | 0 | 4 |
| `lib/Lint/Linter.php` | 0 | 5 |
| `lib/Lint/UnsupportedRegistry.php` | 0 | 1 |
| `lib/Module.php` | 0 | 1 |
| `lib/ModuleAbstract.php` | 0 | 1 |
Expand Down Expand Up @@ -1318,6 +1319,14 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:
- new Variable (line 473)
- 15 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Lint/IncrementDetector.php`

**Warnings** (review for bootstrap subset):
- new ParserFactory (line 34)
- new NodeTraverser (line 56)
- new Issue (line 63)
- 2 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Lint/Issue.php`

**Warnings** (review for bootstrap subset):
Expand All @@ -1338,8 +1347,9 @@ These `LogicException` messages indicate CFG ops or expressions not yet lowered:

**Warnings** (review for bootstrap subset):
- new Runtime (line 24)
- new State (line 83)
- new LintCompiler (line 98)
- new IncrementDetector (line 44)
- new State (line 86)
- new LintCompiler (line 101)
- 9 class method(s) — PHPCfg Op\Stmt\ClassMethod not lowered in Compiler

### `lib/Lint/UnsupportedRegistry.php`
Expand Down
1 change: 1 addition & 0 deletions docs/unsupported-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Exit code `0` when the entry (and best-effort `include`/`require` targets with s
| `Expr_Yield`, `Expr_YieldFrom` | [#167](https://github.com/PurHur/php-compiler/issues/167) |
| `Expr_Closure` | [#72](https://github.com/PurHur/php-compiler/issues/72) |
| `Expr_ArrowFunction` | [#142](https://github.com/PurHur/php-compiler/issues/142) |
| `Expr_PreInc`, `Expr_PostInc`, `Expr_PreDec`, `Expr_PostDec` (`++`/`--`) | [#137](https://github.com/PurHur/php-compiler/issues/137) |
| `Expr_New` (non-trivial) | [#136](https://github.com/PurHur/php-compiler/issues/136) |
| Named arguments, traits, enums | [#168](https://github.com/PurHur/php-compiler/issues/168), [#169](https://github.com/PurHur/php-compiler/issues/169) |

Expand Down
74 changes: 74 additions & 0 deletions lib/Lint/IncrementDetector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\Lint;

use PhpParser\Node;
use PhpParser\Node\Expr\PostDec;
use PhpParser\Node\Expr\PostInc;
use PhpParser\Node\Expr\PreDec;
use PhpParser\Node\Expr\PreInc;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\ParserFactory;

/**
* php-cfg lowers ++/-- to assign+binary ops; lint still flags AST nodes until #137.
*/
final class IncrementDetector
{
/** @var array<class-string<Node>, string> */
public const NODE_TO_KIND = [
PreInc::class => 'Expr_PreInc',
PostInc::class => 'Expr_PostInc',
PreDec::class => 'Expr_PreDec',
PostDec::class => 'Expr_PostDec',
];

/**
* @return list<Issue>
*/
public function detect(string $code, string $filename): array
{
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
try {
$ast = $parser->parse($code);
} catch (\PhpParser\Error $e) {
return [];
}
if (!is_array($ast)) {
return [];
}

$visitor = new class extends NodeVisitorAbstract {
/** @var list<array{int, string}> */
public array $hits = [];

public function enterNode(Node $node)
{
if (isset(IncrementDetector::NODE_TO_KIND[get_class($node)])) {
$this->hits[] = [$node->getStartLine(), IncrementDetector::NODE_TO_KIND[get_class($node)]];
}
}
};

$traverser = new NodeTraverser();
$traverser->addVisitor($visitor);
$traverser->traverse($ast);

$issues = [];
foreach ($visitor->hits as [$line, $kind]) {
$tracking = UnsupportedRegistry::trackingIssueForKind($kind);
$issues[] = new Issue(
$filename,
$line,
$kind,
"Unsupported expression: {$kind}",
$tracking
);
}

return $issues;
}
}
5 changes: 4 additions & 1 deletion lib/Lint/Linter.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ public function lintFile(string $filename): array
*/
public function lintSource(string $code, string $filename): array
{
$issues = (new IncrementDetector())->detect($code, $filename);
$script = $this->parseForLint($code, $filename);
$issues = $this->lintScript($script);
foreach ($this->lintScript($script) as $issue) {
$issues[] = $issue;
}
$queue = [$filename => $script];
$seenFiles = [$filename => true];

Expand Down
4 changes: 4 additions & 0 deletions lib/Lint/UnsupportedRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ final class UnsupportedRegistry
'Expr_Closure' => 72,
'Stmt_Trait' => 168,
'Expr_NamedArgument' => 168,
'Expr_PreInc' => 137,
'Expr_PostInc' => 137,
'Expr_PreDec' => 137,
'Expr_PostDec' => 137,
];

public static function trackingIssueForKind(string $kind): ?int
Expand Down
16 changes: 16 additions & 0 deletions test/unit/LintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ public function testLintYieldReportsIssue167(): void
$this->assertStringNotContainsString('#114', $exit['stdout']);
}

public function testLintPreIncReportsIssue137(): void
{
$exit = $this->runLint(['-r', '++$i;']);
$this->assertSame(1, $exit['code']);
$this->assertStringContainsString('#137', $exit['stdout']);
$this->assertStringContainsString('Expr_PreInc', $exit['stdout']);
}

public function testLintPostIncReportsIssue137(): void
{
$exit = $this->runLint(['-r', '$i++;']);
$this->assertSame(1, $exit['code']);
$this->assertStringContainsString('#137', $exit['stdout']);
$this->assertStringContainsString('Expr_PostInc', $exit['stdout']);
}

public function testLintCleanScriptExitsZero(): void
{
$code = '<?php echo "ok";';
Expand Down
4 changes: 4 additions & 0 deletions test/unit/UnsupportedRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public static function kindToIssueProvider(): array
'yield from' => ['Expr_YieldFrom', 167],
'closure' => ['Expr_Closure', 72],
'arrow function' => ['Expr_ArrowFunction', 142],
'pre inc' => ['Expr_PreInc', 137],
'post inc' => ['Expr_PostInc', 137],
'pre dec' => ['Expr_PreDec', 137],
'post dec' => ['Expr_PostDec', 137],
];
}

Expand Down
Loading