Skip to content

Commit

Permalink
Flag invalid declares
Browse files Browse the repository at this point in the history
  • Loading branch information
weirdan committed Sep 2, 2023
1 parent c059997 commit cd8fc46
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 8 deletions.
98 changes: 98 additions & 0 deletions src/Psalm/Internal/Analyzer/Statements/DeclareAnalyzer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

namespace Psalm\Internal\Analyzer\Statements;

use PhpParser;
use Psalm\CodeLocation;
use Psalm\Context;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Issue\UnrecognizedStatement;
use Psalm\IssueBuffer;

use function in_array;

/**
* @internal
*/
final class DeclareAnalyzer
{
public static function analyze(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Stmt\Declare_ $stmt,
Context $context
): void {
foreach ($stmt->declares as $declaration) {
$declaration_key = (string) $declaration->key;

if ($declaration_key === 'strict_types') {
self::analyzeStrictTypesDeclaration($statements_analyzer, $declaration, $context);
} elseif ($declaration_key === 'ticks') {
self::analyzeTicksDeclaration($statements_analyzer, $declaration);
} elseif ($declaration_key === 'encoding') {
self::analyzeEncodingDeclaration($statements_analyzer, $declaration);
} else {
IssueBuffer::maybeAdd(
new UnrecognizedStatement(
'Psalm does not understand the declare statement ' . $declaration->key,
new CodeLocation($statements_analyzer, $declaration),
),
$statements_analyzer->getSuppressedIssues(),
);
}
}
}

private static function analyzeStrictTypesDeclaration(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Stmt\DeclareDeclare $declaration,
Context $context
): void {
if (!$declaration->value instanceof PhpParser\Node\Scalar\LNumber
|| !in_array($declaration->value->value, [0, 1], true)
) {
IssueBuffer::maybeAdd(
new UnrecognizedStatement(
'strict_types declaration can only have 1 or 0 as a value',
new CodeLocation($statements_analyzer, $declaration),
),
$statements_analyzer->getSuppressedIssues(),
);

return;
}

if ($declaration->value->value === 1) {
$context->strict_types = true;
}
}

private static function analyzeTicksDeclaration(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Stmt\DeclareDeclare $declaration
): void {
if (!$declaration->value instanceof PhpParser\Node\Scalar\LNumber) {
IssueBuffer::maybeAdd(
new UnrecognizedStatement(
'ticks declaration should have integer as a value',
new CodeLocation($statements_analyzer, $declaration),
),
$statements_analyzer->getSuppressedIssues(),
);
}
}

private static function analyzeEncodingDeclaration(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Stmt\DeclareDeclare $declaration
): void {
if (!$declaration->value instanceof PhpParser\Node\Scalar\String_) {
IssueBuffer::maybeAdd(
new UnrecognizedStatement(
'encoding declaration should have string as a value',
new CodeLocation($statements_analyzer, $declaration),
),
$statements_analyzer->getSuppressedIssues(),
);
}
}
}
10 changes: 2 additions & 8 deletions src/Psalm/Internal/Analyzer/StatementsAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Psalm\Internal\Analyzer\Statements\Block\WhileAnalyzer;
use Psalm\Internal\Analyzer\Statements\BreakAnalyzer;
use Psalm\Internal\Analyzer\Statements\ContinueAnalyzer;
use Psalm\Internal\Analyzer\Statements\DeclareAnalyzer;
use Psalm\Internal\Analyzer\Statements\EchoAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\Assignment\InstancePropertyAssignmentAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
Expand Down Expand Up @@ -597,14 +598,7 @@ private static function analyzeStatement(
} elseif ($stmt instanceof PhpParser\Node\Stmt\Label) {
// do nothing
} elseif ($stmt instanceof PhpParser\Node\Stmt\Declare_) {
foreach ($stmt->declares as $declaration) {
if ((string) $declaration->key === 'strict_types'
&& $declaration->value instanceof PhpParser\Node\Scalar\LNumber
&& $declaration->value->value === 1
) {
$context->strict_types = true;
}
}
DeclareAnalyzer::analyze($statements_analyzer, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\HaltCompiler) {
$context->has_returned = true;
} else {
Expand Down
73 changes: 73 additions & 0 deletions tests/Internal/Analyzer/DeclareAnalyzerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace Psalm\Tests\Internal\Analyzer;

use Psalm\Tests\TestCase;
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;

final class DeclareAnalyzerTest extends TestCase
{
use ValidCodeAnalysisTestTrait;
use InvalidCodeAnalysisTestTrait;

public function providerValidCodeParse(): iterable
{
yield 'declareStrictTypes1' => [
'code' => <<<'PHP'
<?php declare(strict_types=1);
PHP,
];

yield 'declareStrictTypes0' => [
'code' => <<<'PHP'
<?php declare(strict_types=0);
PHP,
];

yield 'declareTicks' => [
'code' => <<<'PHP'
<?php declare(ticks=5);
PHP,
];

yield 'declareEncoding' => [
'code' => <<<'PHP'
<?php declare(encoding='ISO-8859-1');
PHP,
];
}

public function providerInvalidCodeParse(): iterable
{
yield 'declareUnknownDirective' => [
'code' => <<<'PHP'
<?php declare(whatever=123);
PHP,
'error_message' => 'UnrecognizedStatement',
];

yield 'declareUnknownValueForStrictTypes' => [
'code' => <<<'PHP'
<?php declare(strict_types='forty-two');
PHP,
'error_message' => 'UnrecognizedStatement',
];

yield 'declareInvalidValueForTicks' => [
'code' => <<<'PHP'
<?php declare(ticks='often');
PHP,
'error_message' => 'UnrecognizedStatement',
];

yield 'declareInvalidValueForEncoding' => [
'code' => <<<'PHP'
<?php declare(encoding=88591);
PHP,
'error_message' => 'UnrecognizedStatement',
];
}
}

0 comments on commit cd8fc46

Please sign in to comment.