Skip to content

Commit

Permalink
Add config to allow hoisting constants to the top of a file
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Jun 1, 2018
1 parent ccc0c3f commit 55fdef2
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 6 deletions.
1 change: 1 addition & 0 deletions config.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<xs:attribute name="allowStringToStandInForClass" type="xs:string" />
<xs:attribute name="usePhpDocMethodsWithoutMagicCall" type="xs:string" />
<xs:attribute name="memoizeMethodCallResults" type="xs:string" />
<xs:attribute name="hoistConstants" type="xs:string" />
</xs:complexType>

<xs:complexType name="ProjectFilesType">
Expand Down
28 changes: 28 additions & 0 deletions src/Psalm/Checker/StatementsChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,34 @@ public function analyze(
$project_checker = $this->getFileChecker()->project_checker;
$codebase = $project_checker->codebase;

if ($codebase->config->hoist_constants) {
foreach ($stmts as $stmt) {
if ($stmt instanceof PhpParser\Node\Stmt\Const_) {
foreach ($stmt->consts as $const) {
$this->setConstType(
$const->name->name,
self::getSimpleType($const->value, $this) ?: Type::getMixed(),
$context
);
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Expression
&& $stmt->expr instanceof PhpParser\Node\Expr\FuncCall
&& $stmt->expr->name instanceof PhpParser\Node\Name
&& $stmt->expr->name->parts === ['define']
&& isset($stmt->expr->args[1])
&& $stmt->expr->args[0]->value instanceof PhpParser\Node\Scalar\String_
) {
$const_name = $stmt->expr->args[0]->value->value;

$this->setConstType(
$const_name,
self::getSimpleType($stmt->expr->args[1]->value, $this) ?: Type::getMixed(),
$context
);
}
}
}

$original_context = null;

if ($loop_scope) {
Expand Down
10 changes: 10 additions & 0 deletions src/Psalm/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ class Config
*/
public $memoize_method_calls = false;

/**
* @var bool
*/
public $hoist_constants = false;

/**
* @var string[]
*/
Expand Down Expand Up @@ -516,6 +521,11 @@ public static function loadFromXML($base_dir, $file_contents)
$config->memoize_method_calls = $attribute_text === 'true' || $attribute_text === '1';
}

if (isset($config_xml['hoistConstants'])) {
$attribute_text = (string) $config_xml['hoistConstants'];
$config->hoist_constants = $attribute_text === 'true' || $attribute_text === '1';
}

if (isset($config_xml->projectFiles)) {
$config->project_files = ProjectFileFilter::loadFromXMLElement($config_xml->projectFiles, $base_dir, true);
}
Expand Down
46 changes: 40 additions & 6 deletions tests/IncludeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class IncludeTest extends TestCase
*
* @param array<int, string> $files_to_check
* @param array<string, string> $files
* @param bool $hide_external_errors
* @param bool $hoist_constants
*
* @return void
*/
public function testValidInclude(array $files, array $files_to_check, $hide_external_errors = true)
public function testValidInclude(array $files, array $files_to_check, $hoist_constants = false)
{
$codebase = $this->project_checker->getCodebase();

Expand All @@ -30,7 +30,7 @@ public function testValidInclude(array $files, array $files_to_check, $hide_exte
$codebase->scanFiles();

$config = $codebase->config;
$config->hide_external_errors = $hide_external_errors;
$config->hoist_constants = $hoist_constants;

foreach ($files_to_check as $file_path) {
$file_checker = new FileChecker($this->project_checker, $file_path, $config->shortenFileName($file_path));
Expand All @@ -44,15 +44,15 @@ public function testValidInclude(array $files, array $files_to_check, $hide_exte
* @param array<int, string> $files_to_check
* @param array<string, string> $files
* @param string $error_message
* @param bool $hide_external_errors
* @param bool $hoist_constants
*
* @return void
*/
public function testInvalidInclude(
array $files,
array $files_to_check,
$error_message,
$hide_external_errors = true
$hoist_constants = false
) {
$codebase = $this->project_checker->getCodebase();

Expand All @@ -71,7 +71,7 @@ public function testInvalidInclude(
$this->expectExceptionMessageRegexp('/\b' . preg_quote($error_message, '/') . '\b/');

$config = $codebase->config;
$config->hide_external_errors = $hide_external_errors;
$config->hoist_constants = $hoist_constants;

foreach ($files_to_check as $file_path) {
$file_checker = new FileChecker($this->project_checker, $file_path, $config->shortenFileName($file_path));
Expand Down Expand Up @@ -386,6 +386,23 @@ function bar(int $i) : bool { return (bool) rand(0, 1); }'
getcwd() . DIRECTORY_SEPARATOR . 'file1.php',
],
],
'hoistConstants' => [
'files' => [
getcwd() . DIRECTORY_SEPARATOR . 'file1.php' => '<?php
require_once("file2.php");',
getcwd() . DIRECTORY_SEPARATOR . 'file2.php' => '<?php
function bat() : void {
echo FOO . BAR;
}
define("FOO", 5);
const BAR = "BAR";',
],
'files_to_check' => [
getcwd() . DIRECTORY_SEPARATOR . 'file1.php',
],
'hoist_constants' => true,
],
];
}

Expand Down Expand Up @@ -510,6 +527,23 @@ public function fooFoo(): string {
],
'error_message' => 'InvalidReturnType',
],
'noHoistConstants' => [
'files' => [
getcwd() . DIRECTORY_SEPARATOR . 'file1.php' => '<?php
require_once("file2.php");',
getcwd() . DIRECTORY_SEPARATOR . 'file2.php' => '<?php
function bat() : void {
echo FOO . BAR;
}
define("FOO", 5);
const BAR = "BAR";',
],
'files_to_check' => [
getcwd() . DIRECTORY_SEPARATOR . 'file1.php',
],
'error_message' => 'UndefinedConstant',
],
];
}
}

0 comments on commit 55fdef2

Please sign in to comment.