mirror of
https://github.com/danog/psalm.git
synced 2024-11-29 20:28:59 +01:00
Break FileScanner out from FileChecker
This commit is contained in:
parent
f4a9306eee
commit
e05a7c00cc
@ -54,7 +54,8 @@
|
||||
<xs:element name="extension" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="filetypeHandler" type="xs:string" />
|
||||
<xs:attribute name="scanner" type="xs:string" />
|
||||
<xs:attribute name="checker" type="xs:string" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
|
@ -16,47 +16,10 @@ class TemplateChecker extends Psalm\Checker\FileChecker
|
||||
{
|
||||
const VIEW_CLASS = 'Your\\View\\Class';
|
||||
|
||||
public function scan()
|
||||
{
|
||||
$stmts = $this->getStatements();
|
||||
|
||||
if (empty($stmts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$first_stmt = $stmts[0];
|
||||
|
||||
if (($first_stmt instanceof PhpParser\Node\Stmt\Nop) && ($doc_comment = $first_stmt->getDocComment())) {
|
||||
$comment_block = CommentChecker::parseDocComment(trim($doc_comment->getText()));
|
||||
|
||||
if (isset($comment_block['specials']['variablesfrom'])) {
|
||||
$variables_from = trim($comment_block['specials']['variablesfrom'][0]);
|
||||
|
||||
$first_line_regex = '/([A-Za-z\\\0-9]+::[a-z_A-Z]+)(\s+weak)?/';
|
||||
|
||||
$matches = [];
|
||||
|
||||
if (!preg_match($first_line_regex, $variables_from, $matches)) {
|
||||
throw new \InvalidArgumentException('Could not interpret doc comment correctly');
|
||||
}
|
||||
|
||||
/** @psalm-suppress MixedArgument */
|
||||
list($fq_class_name, $method_name) = explode('::', $matches[1]);
|
||||
|
||||
$this->project_checker->queueClassLikeForScanning($fq_class_name, $this->file_path, true);
|
||||
}
|
||||
}
|
||||
|
||||
$this->project_checker->queueClassLikeForScanning(self::VIEW_CLASS, $this->file_path);
|
||||
|
||||
parent::scan();
|
||||
}
|
||||
|
||||
public function analyze(Context $context = null, $update_docblocks = false)
|
||||
{
|
||||
$this->project_checker->enableCache();
|
||||
|
||||
$stmts = $this->getStatements();
|
||||
$stmts = $this->project_checker->getStatementsForFile($this->file_path);
|
||||
|
||||
if (empty($stmts)) {
|
||||
return;
|
||||
@ -102,7 +65,7 @@ class TemplateChecker extends Psalm\Checker\FileChecker
|
||||
]);
|
||||
}
|
||||
|
||||
$this->checkWithViewClass($this_params);
|
||||
$this->checkWithViewClass($this_params, $stmts);
|
||||
|
||||
$this->project_checker->disableCache();
|
||||
}
|
||||
@ -158,13 +121,12 @@ class TemplateChecker extends Psalm\Checker\FileChecker
|
||||
|
||||
/**
|
||||
* @param Context $context
|
||||
* @param array<PhpParser\Node\Stmt> $stmts
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function checkWithViewClass(Context $context)
|
||||
protected function checkWithViewClass(Context $context, array $stmts)
|
||||
{
|
||||
$stmts = $this->getStatements();
|
||||
|
||||
$pseudo_method_stmts = [];
|
||||
|
||||
foreach ($stmts as $stmt) {
|
||||
|
56
examples/TemplateScanner.php
Normal file
56
examples/TemplateScanner.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace Psalm\Examples\Template;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm;
|
||||
use Psalm\Checker\CommentChecker;
|
||||
use Psalm\Checker\ProjectChecker;
|
||||
use Psalm\Storage\FileStorage;
|
||||
|
||||
class TemplateScanner extends Psalm\Scanner\FileScanner
|
||||
{
|
||||
const VIEW_CLASS = 'Your\\View\\Class';
|
||||
|
||||
/**
|
||||
* @param array<mixed, PhpParser\Node> $stmts
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function scan(ProjectChecker $project_checker, array $stmts, FileStorage $file_storage)
|
||||
{
|
||||
if (empty($stmts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$first_stmt = $stmts[0];
|
||||
|
||||
if (($first_stmt instanceof PhpParser\Node\Stmt\Nop) && ($doc_comment = $first_stmt->getDocComment())) {
|
||||
$comment_block = CommentChecker::parseDocComment(trim($doc_comment->getText()));
|
||||
|
||||
if (isset($comment_block['specials']['variablesfrom'])) {
|
||||
$variables_from = trim($comment_block['specials']['variablesfrom'][0]);
|
||||
|
||||
$first_line_regex = '/([A-Za-z\\\0-9]+::[a-z_A-Z]+)(\s+weak)?/';
|
||||
|
||||
$matches = [];
|
||||
|
||||
if (!preg_match($first_line_regex, $variables_from, $matches)) {
|
||||
throw new \InvalidArgumentException('Could not interpret doc comment correctly');
|
||||
}
|
||||
|
||||
/** @psalm-suppress MixedArgument */
|
||||
list($fq_class_name, $method_name) = explode('::', $matches[1]);
|
||||
|
||||
$project_checker->queueClassLikeForScanning(
|
||||
$fq_class_name,
|
||||
$this->file_path,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$project_checker->queueClassLikeForScanning(self::VIEW_CLASS, $this->file_path);
|
||||
|
||||
parent::scan($project_checker, $stmts, $file_storage);
|
||||
}
|
||||
}
|
@ -444,15 +444,17 @@ class ClassChecker extends ClassLikeChecker
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset(self::$trait_checkers[strtolower($fq_trait_name)])) {
|
||||
throw new \UnexpectedValueException(
|
||||
'Expecting trait statements to exist for ' . $fq_trait_name
|
||||
);
|
||||
}
|
||||
$trait_file_checker = $project_checker->getFileCheckerForClassLike($fq_trait_name);
|
||||
$trait_node = $project_checker->getTraitNode($fq_trait_name);
|
||||
$trait_aliases = $project_checker->getTraitAliases($fq_trait_name);
|
||||
$trait_checker = new TraitChecker(
|
||||
$trait_node,
|
||||
$trait_file_checker,
|
||||
$fq_trait_name,
|
||||
$trait_aliases
|
||||
);
|
||||
|
||||
$trait_checker = self::$trait_checkers[strtolower($fq_trait_name)];
|
||||
|
||||
foreach ($trait_checker->class->stmts as $trait_stmt) {
|
||||
foreach ($trait_node->stmts as $trait_stmt) {
|
||||
if ($trait_stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||
$trait_method_checker = $this->analyzeClassMethod(
|
||||
$trait_stmt,
|
||||
@ -664,15 +666,9 @@ class ClassChecker extends ClassLikeChecker
|
||||
$this->source->getAliases()
|
||||
);
|
||||
|
||||
if (!isset(self::$trait_checkers[strtolower($fq_trait_name)])) {
|
||||
throw new \UnexpectedValueException(
|
||||
'Expecting trait statements to exist for ' . $fq_trait_name
|
||||
);
|
||||
}
|
||||
$trait_node = $project_checker->getTraitNode($fq_trait_name);
|
||||
|
||||
$trait_checker = self::$trait_checkers[strtolower($fq_trait_name)];
|
||||
|
||||
foreach ($trait_checker->class->stmts as $trait_stmt) {
|
||||
foreach ($trait_node->stmts as $trait_stmt) {
|
||||
if ($trait_stmt instanceof PhpParser\Node\Stmt\Property) {
|
||||
$this->checkForMissingPropertyType($project_checker, $trait_stmt);
|
||||
}
|
||||
|
@ -91,13 +91,6 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
*/
|
||||
protected static $property_map;
|
||||
|
||||
/**
|
||||
* A lookup table of cached TraitCheckers
|
||||
*
|
||||
* @var array<string, TraitChecker>
|
||||
*/
|
||||
public static $trait_checkers;
|
||||
|
||||
/**
|
||||
* A lookup table of cached ClassCheckers
|
||||
*
|
||||
@ -173,8 +166,6 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod &&
|
||||
strtolower($stmt->name) === strtolower($method_name)
|
||||
) {
|
||||
$project_checker = $this->getFileChecker()->project_checker;
|
||||
|
||||
$method_id = $this->fq_class_name . '::' . $stmt->name;
|
||||
|
||||
if ($project_checker->canCache() && isset($project_checker->method_checkers[$method_id])) {
|
||||
@ -195,13 +186,15 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
$this->source->getAliases()
|
||||
);
|
||||
|
||||
if (!isset(self::$trait_checkers[strtolower($fq_trait_name)])) {
|
||||
throw new \UnexpectedValueException(
|
||||
'Expecting trait statements to exist for ' . $fq_trait_name
|
||||
);
|
||||
}
|
||||
|
||||
$trait_checker = self::$trait_checkers[strtolower($fq_trait_name)];
|
||||
$trait_file_checker = $project_checker->getFileCheckerForClassLike($fq_trait_name);
|
||||
$trait_node = $project_checker->getTraitNode($fq_trait_name);
|
||||
$trait_aliases = $project_checker->getTraitAliases($fq_trait_name);
|
||||
$trait_checker = new TraitChecker(
|
||||
$trait_node,
|
||||
$trait_file_checker,
|
||||
$fq_trait_name,
|
||||
$trait_aliases
|
||||
);
|
||||
|
||||
foreach ($trait_checker->class->stmts as $trait_stmt) {
|
||||
if ($trait_stmt instanceof PhpParser\Node\Stmt\ClassMethod &&
|
||||
@ -1113,9 +1106,6 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
||||
public static function clearCache()
|
||||
{
|
||||
self::$file_classes = [];
|
||||
|
||||
self::$trait_checkers = [];
|
||||
|
||||
self::$class_checkers = [];
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use Psalm\ClassLikeDocblockComment;
|
||||
use Psalm\Exception\DocblockParseException;
|
||||
use Psalm\Exception\IncorrectDocblockException;
|
||||
use Psalm\Exception\TypeParseTreeException;
|
||||
use Psalm\FileSource;
|
||||
use Psalm\FunctionDocblockComment;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Type;
|
||||
use Psalm\VarDocblockComment;
|
||||
|
||||
@ -17,7 +17,6 @@ class CommentChecker
|
||||
|
||||
/**
|
||||
* @param string $comment
|
||||
* @param StatementsSource $source
|
||||
* @param Aliases $aliases
|
||||
* @param array<string, string>|null $template_types
|
||||
* @param int|null $var_line_number
|
||||
@ -30,7 +29,7 @@ class CommentChecker
|
||||
*/
|
||||
public static function getTypeFromComment(
|
||||
$comment,
|
||||
StatementsSource $source,
|
||||
FileSource $source,
|
||||
Aliases $aliases,
|
||||
array $template_types = null,
|
||||
$var_line_number = null,
|
||||
|
@ -2,7 +2,6 @@
|
||||
namespace Psalm\Checker;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\Config;
|
||||
use Psalm\Context;
|
||||
use Psalm\FileManipulation\FileManipulationBuffer;
|
||||
use Psalm\IssueBuffer;
|
||||
@ -74,36 +73,15 @@ class FileChecker extends SourceChecker implements StatementsSource
|
||||
public $project_checker;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @param string $file_path
|
||||
* @param string $file_name
|
||||
* @param ProjectChecker $project_checker
|
||||
*/
|
||||
public $will_analyze;
|
||||
|
||||
/**
|
||||
* @param string $file_path
|
||||
* @param ProjectChecker $project_checker
|
||||
* @param bool $will_analyze
|
||||
*/
|
||||
public function __construct(
|
||||
$file_path,
|
||||
ProjectChecker $project_checker,
|
||||
$will_analyze = true
|
||||
) {
|
||||
$this->file_path = $file_path;
|
||||
$this->file_name = Config::getInstance()->shortenFileName($this->file_path);
|
||||
$this->project_checker = $project_checker;
|
||||
$this->will_analyze = $will_analyze;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function scan()
|
||||
public function __construct(ProjectChecker $project_checker, $file_path, $file_name)
|
||||
{
|
||||
$stmts = $this->getStatements();
|
||||
|
||||
$traverser = new PhpParser\NodeTraverser();
|
||||
$traverser->addVisitor(new \Psalm\Visitor\DependencyFinderVisitor($this->project_checker, $this));
|
||||
$traverser->traverse($stmts);
|
||||
$this->file_path = $file_path;
|
||||
$this->file_name = $file_name;
|
||||
$this->project_checker = $project_checker;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,9 +109,9 @@ class FileChecker extends SourceChecker implements StatementsSource
|
||||
|
||||
$this->context->is_global = true;
|
||||
|
||||
$config = Config::getInstance();
|
||||
$config = $this->project_checker->getConfig();
|
||||
|
||||
$stmts = $this->getStatements();
|
||||
$stmts = $this->project_checker->getStatementsForFile($this->file_path);
|
||||
|
||||
$statements_checker = new StatementsChecker($this);
|
||||
|
||||
@ -171,8 +149,6 @@ class FileChecker extends SourceChecker implements StatementsSource
|
||||
$class_checker->analyze(null, $this->context);
|
||||
}
|
||||
|
||||
$config = Config::getInstance();
|
||||
|
||||
foreach ($this->function_checkers as $function_checker) {
|
||||
$function_context = new Context($this->context->self);
|
||||
$function_context->collect_references = $this->project_checker->collect_references;
|
||||
@ -193,7 +169,7 @@ class FileChecker extends SourceChecker implements StatementsSource
|
||||
$return_type_location = $function_storage->return_type_location;
|
||||
|
||||
$function_checker->verifyReturnType(
|
||||
$statements_checker->getFileChecker()->project_checker,
|
||||
$this->project_checker,
|
||||
$return_type,
|
||||
null,
|
||||
$return_type_location
|
||||
@ -356,14 +332,6 @@ class FileChecker extends SourceChecker implements StatementsSource
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, \PhpParser\Node\Stmt>
|
||||
*/
|
||||
public function getStatements()
|
||||
{
|
||||
return $this->project_checker->getStatementsForFile($this->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string
|
||||
*/
|
||||
@ -397,6 +365,8 @@ class FileChecker extends SourceChecker implements StatementsSource
|
||||
IssueBuffer::clearCache();
|
||||
FileManipulationBuffer::clearCache();
|
||||
FunctionLikeChecker::clearCache();
|
||||
\Psalm\Provider\ClassLikeStorageProvider::deleteAll();
|
||||
\Psalm\Provider\FileStorageProvider::deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Psalm\Checker;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\Aliases;
|
||||
use Psalm\Config;
|
||||
use Psalm\Context;
|
||||
use Psalm\FileManipulation\FileManipulationBuffer;
|
||||
@ -19,6 +21,7 @@ use Psalm\Provider\FileReferenceProvider;
|
||||
use Psalm\Provider\FileStorageProvider;
|
||||
use Psalm\Provider\ParserCacheProvider;
|
||||
use Psalm\Provider\StatementsProvider;
|
||||
use Psalm\Scanner\FileScanner;
|
||||
use Psalm\Storage\ClassLikeStorage;
|
||||
use Psalm\Storage\FileStorage;
|
||||
use Psalm\Type;
|
||||
@ -270,6 +273,16 @@ class ProjectChecker
|
||||
*/
|
||||
public $only_replace_php_types_with_non_docblock_types = false;
|
||||
|
||||
/**
|
||||
* @var array<string, PhpParser\Node\Stmt\Trait_>
|
||||
*/
|
||||
private $trait_nodes = [];
|
||||
|
||||
/**
|
||||
* @var array<string, Aliases>
|
||||
*/
|
||||
private $trait_aliases = [];
|
||||
|
||||
const TYPE_CONSOLE = 'console';
|
||||
const TYPE_JSON = 'json';
|
||||
const TYPE_EMACS = 'emacs';
|
||||
@ -951,7 +964,7 @@ class ProjectChecker
|
||||
* @psalm-suppress UnusedParam
|
||||
*/
|
||||
function ($i, $file_path) use ($filetype_handlers) {
|
||||
$file_checker = $this->getFile($file_path, $filetype_handlers, true);
|
||||
$file_checker = $this->getFile($file_path, $filetype_handlers);
|
||||
|
||||
if ($this->debug_output) {
|
||||
echo 'Analyzing ' . $file_checker->getFilePath() . PHP_EOL;
|
||||
@ -1467,15 +1480,17 @@ class ProjectChecker
|
||||
*
|
||||
* @return FileChecker
|
||||
*/
|
||||
private function getFile($file_path, array $filetype_handlers, $will_analyze = false)
|
||||
private function getFile($file_path, array $filetype_handlers)
|
||||
{
|
||||
$extension = (string)pathinfo($file_path)['extension'];
|
||||
|
||||
$file_name = $this->config->shortenFileName($file_path);
|
||||
|
||||
if (isset($filetype_handlers[$extension])) {
|
||||
/** @var FileChecker */
|
||||
$file_checker = new $filetype_handlers[$extension]($file_path, $this);
|
||||
$file_checker = new $filetype_handlers[$extension]($this, $file_path, $file_name);
|
||||
} else {
|
||||
$file_checker = new FileChecker($file_path, $this, $will_analyze);
|
||||
$file_checker = new FileChecker($this, $file_path, $file_name);
|
||||
}
|
||||
|
||||
if ($this->debug_output) {
|
||||
@ -1490,7 +1505,7 @@ class ProjectChecker
|
||||
* @param array $filetype_handlers
|
||||
* @param bool $will_analyze
|
||||
*
|
||||
* @return FileChecker
|
||||
* @return FileScanner
|
||||
*/
|
||||
private function scanFile($file_path, array $filetype_handlers, $will_analyze = false)
|
||||
{
|
||||
@ -1498,11 +1513,13 @@ class ProjectChecker
|
||||
$file_name_parts = explode('.', array_pop($path_parts));
|
||||
$extension = count($file_name_parts) > 1 ? array_pop($file_name_parts) : null;
|
||||
|
||||
$file_name = $this->config->shortenFileName($file_path);
|
||||
|
||||
if (isset($filetype_handlers[$extension])) {
|
||||
/** @var FileChecker */
|
||||
$file_checker = new $filetype_handlers[$extension]($file_path, $this);
|
||||
/** @var FileScanner */
|
||||
$file_scanner = new $filetype_handlers[$extension]($file_path, $file_name, $will_analyze);
|
||||
} else {
|
||||
$file_checker = new FileChecker($file_path, $this, $will_analyze);
|
||||
$file_scanner = new FileScanner($file_path, $file_name, $will_analyze);
|
||||
}
|
||||
|
||||
if (isset($this->scanned_files[$file_path])) {
|
||||
@ -1521,9 +1538,13 @@ class ProjectChecker
|
||||
|
||||
$this->scanned_files[$file_path] = true;
|
||||
|
||||
$file_checker->scan();
|
||||
$file_scanner->scan(
|
||||
$this,
|
||||
$this->getStatementsForFile($file_path),
|
||||
$this->file_storage_provider->create($file_path)
|
||||
);
|
||||
|
||||
return $file_checker;
|
||||
return $file_scanner;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1664,7 +1685,7 @@ class ProjectChecker
|
||||
$file_checker = $this->getFileCheckerForClassLike($appearing_fq_class_name);
|
||||
}
|
||||
|
||||
$stmts = $file_checker->getStatements();
|
||||
$stmts = $this->getStatementsForFile($file_checker->getFilePath());
|
||||
|
||||
$file_checker->populateCheckers($stmts);
|
||||
|
||||
@ -1681,7 +1702,7 @@ class ProjectChecker
|
||||
*
|
||||
* @return FileChecker
|
||||
*/
|
||||
private function getFileCheckerForClassLike($fq_class_name)
|
||||
public function getFileCheckerForClassLike($fq_class_name)
|
||||
{
|
||||
$fq_class_name_lc = strtolower($fq_class_name);
|
||||
|
||||
@ -1700,7 +1721,9 @@ class ProjectChecker
|
||||
return $this->file_checkers[$file_path];
|
||||
}
|
||||
|
||||
$file_checker = new FileChecker($file_path, $this, true);
|
||||
$file_name = $this->config->shortenFileName($file_path);
|
||||
|
||||
$file_checker = new FileChecker($this, $file_path, $file_name);
|
||||
|
||||
if ($this->cache) {
|
||||
$this->file_checkers[$file_path] = $file_checker;
|
||||
@ -2059,4 +2082,52 @@ class ProjectChecker
|
||||
{
|
||||
return $this->issues_to_fix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fq_trait_name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addTraitNode($fq_trait_name, PhpParser\Node\Stmt\Trait_ $node, Aliases $aliases)
|
||||
{
|
||||
$fq_trait_name_lc = strtolower($fq_trait_name);
|
||||
$this->trait_nodes[$fq_trait_name_lc] = $node;
|
||||
$this->trait_aliases[$fq_trait_name_lc] = $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fq_trait_name
|
||||
*
|
||||
* @return PhpParser\Node\Stmt\Trait_
|
||||
*/
|
||||
public function getTraitNode($fq_trait_name)
|
||||
{
|
||||
$fq_trait_name_lc = strtolower($fq_trait_name);
|
||||
|
||||
if (isset($this->trait_nodes[$fq_trait_name_lc])) {
|
||||
return $this->trait_nodes[$fq_trait_name_lc];
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException(
|
||||
'Expecting trait statements to exist for ' . $fq_trait_name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fq_trait_name
|
||||
*
|
||||
* @return Aliases
|
||||
*/
|
||||
public function getTraitAliases($fq_trait_name)
|
||||
{
|
||||
$fq_trait_name_lc = strtolower($fq_trait_name);
|
||||
|
||||
if (isset($this->trait_aliases[$fq_trait_name_lc])) {
|
||||
return $this->trait_aliases[$fq_trait_name_lc];
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException(
|
||||
'Expecting trait aliases to exist for ' . $fq_trait_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -449,7 +449,7 @@ class BinaryOpChecker
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StatementsSource $statements_source
|
||||
* @param StatementsSource|null $statements_source
|
||||
* @param PhpParser\Node\Expr $left
|
||||
* @param PhpParser\Node\Expr $right
|
||||
* @param PhpParser\Node $parent
|
||||
@ -458,20 +458,24 @@ class BinaryOpChecker
|
||||
* @return void
|
||||
*/
|
||||
public static function analyzeNonDivArithmenticOp(
|
||||
StatementsSource $statements_source,
|
||||
$statements_source,
|
||||
PhpParser\Node\Expr $left,
|
||||
PhpParser\Node\Expr $right,
|
||||
PhpParser\Node $parent,
|
||||
Type\Union &$result_type = null,
|
||||
Context $context = null
|
||||
) {
|
||||
$project_checker = $statements_source->getFileChecker()->project_checker;
|
||||
$project_checker = $statements_source
|
||||
? $statements_source->getFileChecker()->project_checker
|
||||
: null;
|
||||
|
||||
$left_type = isset($left->inferredType) ? $left->inferredType : null;
|
||||
$right_type = isset($right->inferredType) ? $right->inferredType : null;
|
||||
$config = Config::getInstance();
|
||||
|
||||
if ($project_checker->infer_types_from_usage
|
||||
if ($project_checker
|
||||
&& $project_checker->infer_types_from_usage
|
||||
&& $statements_source
|
||||
&& $context
|
||||
&& $left_type
|
||||
&& $right_type
|
||||
@ -491,7 +495,7 @@ class BinaryOpChecker
|
||||
|
||||
if ($left_type && $right_type) {
|
||||
if ($left_type->isNullable()) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new PossiblyNullOperand(
|
||||
'Left operand cannot be nullable, got ' . $left_type,
|
||||
new CodeLocation($statements_source, $left)
|
||||
@ -501,7 +505,7 @@ class BinaryOpChecker
|
||||
// fall through
|
||||
}
|
||||
} elseif ($left_type->isNull()) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new NullOperand(
|
||||
'Left operand cannot be null',
|
||||
new CodeLocation($statements_source, $left)
|
||||
@ -515,7 +519,7 @@ class BinaryOpChecker
|
||||
}
|
||||
|
||||
if ($right_type->isNullable()) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new PossiblyNullOperand(
|
||||
'Right operand cannot be nullable, got ' . $right_type,
|
||||
new CodeLocation($statements_source, $right)
|
||||
@ -525,7 +529,7 @@ class BinaryOpChecker
|
||||
// fall through
|
||||
}
|
||||
} elseif ($right_type->isNull()) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new NullOperand(
|
||||
'Right operand cannot be null',
|
||||
new CodeLocation($statements_source, $right)
|
||||
@ -547,7 +551,7 @@ class BinaryOpChecker
|
||||
|
||||
if ($left_type_part instanceof TMixed || $right_type_part instanceof TMixed) {
|
||||
if ($left_type_part instanceof TMixed) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new MixedOperand(
|
||||
'Left operand cannot be mixed',
|
||||
new CodeLocation($statements_source, $left)
|
||||
@ -557,7 +561,7 @@ class BinaryOpChecker
|
||||
// fall through
|
||||
}
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new MixedOperand(
|
||||
'Right operand cannot be mixed',
|
||||
new CodeLocation($statements_source, $right)
|
||||
@ -582,7 +586,7 @@ class BinaryOpChecker
|
||||
|| (!$left_type_part instanceof TArray && !$left_type_part instanceof ObjectLike)
|
||||
) {
|
||||
if (!$left_type_part instanceof TArray && !$left_type_part instanceof ObjectLike) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new InvalidOperand(
|
||||
'Cannot add an array to a non-array ' . $left_type_part,
|
||||
new CodeLocation($statements_source, $left)
|
||||
@ -592,7 +596,7 @@ class BinaryOpChecker
|
||||
// fall through
|
||||
}
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new InvalidOperand(
|
||||
'Cannot add an array to a non-array ' . $right_type_part,
|
||||
new CodeLocation($statements_source, $right)
|
||||
@ -662,7 +666,7 @@ class BinaryOpChecker
|
||||
($left_type_part instanceof TInt && $right_type_part instanceof TFloat)
|
||||
) {
|
||||
if ($config->strict_binary_operands) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new InvalidOperand(
|
||||
'Cannot add ints to floats',
|
||||
new CodeLocation($statements_source, $parent)
|
||||
@ -684,7 +688,7 @@ class BinaryOpChecker
|
||||
|
||||
if ($left_type_part->isNumericType() && $right_type_part->isNumericType()) {
|
||||
if ($config->strict_binary_operands) {
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new InvalidOperand(
|
||||
'Cannot add numeric types together, please cast explicitly',
|
||||
new CodeLocation($statements_source, $parent)
|
||||
@ -706,7 +710,7 @@ class BinaryOpChecker
|
||||
|
||||
$non_numeric_type = $left_type_part->isNumericType() ? $right_type_part : $left_type_part;
|
||||
|
||||
if (IssueBuffer::accepts(
|
||||
if ($statements_source && IssueBuffer::accepts(
|
||||
new InvalidOperand(
|
||||
'Cannot add a numeric type to a non-numeric type ' . $non_numeric_type,
|
||||
new CodeLocation($statements_source, $parent)
|
||||
|
@ -2,7 +2,6 @@
|
||||
namespace Psalm\Checker\Statements\Expression;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\Checker\FileChecker;
|
||||
use Psalm\Checker\Statements\ExpressionChecker;
|
||||
use Psalm\Checker\StatementsChecker;
|
||||
use Psalm\CodeLocation;
|
||||
@ -71,12 +70,12 @@ class IncludeChecker
|
||||
|
||||
if ($current_file_checker->project_checker->fileExists($path_to_file)) {
|
||||
if (is_subclass_of($current_file_checker, 'Psalm\\Checker\\FileChecker')) {
|
||||
$include_file_checker = new FileChecker(
|
||||
$path_to_file,
|
||||
$current_file_checker->project_checker,
|
||||
false
|
||||
$project_checker = $statements_checker->getFileChecker()->project_checker;
|
||||
|
||||
$statements_checker->analyze(
|
||||
$project_checker->getStatementsForFile($path_to_file),
|
||||
$context
|
||||
);
|
||||
$statements_checker->analyze($include_file_checker->getStatements(), $context);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -584,10 +584,6 @@ class StatementsChecker extends SourceChecker implements StatementsSource
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$statements_source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Plus ||
|
||||
$stmt instanceof PhpParser\Node\Expr\BinaryOp\Minus ||
|
||||
$stmt instanceof PhpParser\Node\Expr\BinaryOp\Mod ||
|
||||
|
@ -2,24 +2,55 @@
|
||||
namespace Psalm\Checker;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\TraitSource;
|
||||
use Psalm\Aliases;
|
||||
use Psalm\StatementsSource;
|
||||
|
||||
class TraitChecker extends ClassLikeChecker
|
||||
{
|
||||
/**
|
||||
* @param PhpParser\Node\Stmt\Trait_ $class
|
||||
* @param TraitSource $trait_source
|
||||
* @param string $fq_class_name
|
||||
* @var Aliases
|
||||
*/
|
||||
public function __construct(PhpParser\Node\Stmt\Trait_ $class, TraitSource $trait_source, $fq_class_name)
|
||||
{
|
||||
$this->source = $trait_source;
|
||||
$this->file_checker = $trait_source->getFileChecker();
|
||||
private $aliases;
|
||||
|
||||
/**
|
||||
* @param string $fq_class_name
|
||||
*/
|
||||
public function __construct(
|
||||
PhpParser\Node\Stmt\Trait_ $class,
|
||||
StatementsSource $source,
|
||||
$fq_class_name,
|
||||
Aliases $aliases
|
||||
) {
|
||||
$this->source = $source;
|
||||
$this->file_checker = $source->getFileChecker();
|
||||
$this->class = $class;
|
||||
$this->fq_class_name = $fq_class_name;
|
||||
$this->storage = $this->file_checker->project_checker->classlike_storage_provider->get($fq_class_name);
|
||||
$this->aliases = $aliases;
|
||||
}
|
||||
|
||||
self::$trait_checkers[strtolower($fq_class_name)] = $this;
|
||||
/**
|
||||
* @return ?string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->aliases->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Aliases
|
||||
*/
|
||||
public function getAliases()
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getAliasedClassesFlipped()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,15 +67,13 @@ class CodeLocation
|
||||
const FUNCTION_PARAM_VAR = 5;
|
||||
|
||||
/**
|
||||
* @param StatementsSource $statements_source
|
||||
* @param \PhpParser\Node $stmt
|
||||
* @param bool $single_line
|
||||
* @param ?int $regex_type
|
||||
* @param ?CodeLocation $previous_location
|
||||
* @param ?string $selected_text
|
||||
*/
|
||||
public function __construct(
|
||||
StatementsSource $statements_source,
|
||||
FileSource $file_source,
|
||||
\PhpParser\Node $stmt,
|
||||
CodeLocation $previous_location = null,
|
||||
$single_line = false,
|
||||
@ -84,8 +82,8 @@ class CodeLocation
|
||||
) {
|
||||
$this->file_start = (int)$stmt->getAttribute('startFilePos');
|
||||
$this->file_end = (int)$stmt->getAttribute('endFilePos');
|
||||
$this->file_path = $statements_source->getCheckedFilePath();
|
||||
$this->file_name = $statements_source->getCheckedFileName();
|
||||
$this->file_path = $file_source->getCheckedFilePath();
|
||||
$this->file_name = $file_source->getCheckedFileName();
|
||||
$this->single_line = $single_line;
|
||||
$this->regex_type = $regex_type;
|
||||
$this->previous_location = $previous_location;
|
||||
|
@ -2,11 +2,11 @@
|
||||
namespace Psalm;
|
||||
|
||||
use Psalm\Checker\ClassLikeChecker;
|
||||
use Psalm\Checker\FileChecker;
|
||||
use Psalm\Checker\ProjectChecker;
|
||||
use Psalm\Config\IssueHandler;
|
||||
use Psalm\Config\ProjectFileFilter;
|
||||
use Psalm\Exception\ConfigException;
|
||||
use Psalm\Scanner\FileScanner;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class Config
|
||||
@ -513,9 +513,13 @@ class Config
|
||||
public function initializePlugins(ProjectChecker $project_checker)
|
||||
{
|
||||
foreach ($this->filetype_handlers as &$path) {
|
||||
$project_checker->file_storage_provider->create($path);
|
||||
$plugin_file_checker = new FileChecker($path, $project_checker);
|
||||
$plugin_file_checker->scan();
|
||||
$storage = $project_checker->file_storage_provider->create($path);
|
||||
$plugin_file_scanner = new FileScanner($path, $this->shortenFileName($path), false);
|
||||
$plugin_file_scanner->scan(
|
||||
$project_checker,
|
||||
$project_checker->getStatementsForFile($path),
|
||||
$storage
|
||||
);
|
||||
|
||||
$declared_classes = ClassLikeChecker::getClassesForFile($project_checker, $path);
|
||||
|
||||
@ -651,23 +655,28 @@ class Config
|
||||
{
|
||||
$project_checker->register_global_functions = true;
|
||||
|
||||
$generic_stubs = realpath(__DIR__ . '/Stubs/CoreGenericFunctions.php');
|
||||
$generic_stubs_path = realpath(__DIR__ . '/Stubs/CoreGenericFunctions.php');
|
||||
|
||||
if ($generic_stubs) {
|
||||
$generic_stub_checker = new FileChecker(
|
||||
$generic_stubs,
|
||||
$project_checker
|
||||
);
|
||||
$project_checker->file_storage_provider->create($generic_stubs);
|
||||
$generic_stub_checker->scan();
|
||||
} else {
|
||||
if (!$generic_stubs_path) {
|
||||
throw new \UnexpectedValueException('Cannot locate core generic stubs');
|
||||
}
|
||||
|
||||
foreach ($this->stub_files as $stub_file) {
|
||||
$stub_checker = new FileChecker($stub_file, $project_checker);
|
||||
$project_checker->file_storage_provider->create($stub_file);
|
||||
$stub_checker->scan();
|
||||
$file_storage = $project_checker->file_storage_provider->create($generic_stubs_path);
|
||||
$file_to_scan = new FileScanner($generic_stubs_path, $this->shortenFileName($generic_stubs_path), false);
|
||||
$file_to_scan->scan(
|
||||
$project_checker,
|
||||
$project_checker->getStatementsForFile($generic_stubs_path),
|
||||
$file_storage
|
||||
);
|
||||
|
||||
foreach ($this->stub_files as $stub_file_path) {
|
||||
$file_storage = $project_checker->file_storage_provider->create($stub_file_path);
|
||||
$file_to_scan = new FileScanner($stub_file_path, $this->shortenFileName($stub_file_path), false);
|
||||
$file_to_scan->scan(
|
||||
$project_checker,
|
||||
$project_checker->getStatementsForFile($stub_file_path),
|
||||
$file_storage
|
||||
);
|
||||
}
|
||||
|
||||
$project_checker->register_global_functions = false;
|
||||
@ -771,9 +780,13 @@ class Config
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_checker = new FileChecker($file_path, $project_checker);
|
||||
$project_checker->file_storage_provider->create($file_path);
|
||||
$file_checker->scan();
|
||||
$file_storage = $project_checker->file_storage_provider->create($file_path);
|
||||
$file_to_scan = new FileScanner($file_path, $this->shortenFileName($file_path), false);
|
||||
$file_to_scan->scan(
|
||||
$project_checker,
|
||||
$project_checker->getStatementsForFile($file_path),
|
||||
$file_storage
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
25
src/Psalm/FileSource.php
Normal file
25
src/Psalm/FileSource.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace Psalm;
|
||||
|
||||
interface FileSource
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFileName();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFilePath();
|
||||
}
|
@ -2,9 +2,9 @@
|
||||
namespace Psalm;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\Checker\FileChecker;
|
||||
use Psalm\Checker\StatementsChecker;
|
||||
use Psalm\FileManipulation\FileManipulation;
|
||||
use Psalm\Scanner\FileScanner;
|
||||
use Psalm\Storage\ClassLikeStorage;
|
||||
|
||||
abstract class Plugin
|
||||
@ -63,7 +63,7 @@ abstract class Plugin
|
||||
public function visitClassLike(
|
||||
PhpParser\Node\Stmt\ClassLike $stmt,
|
||||
ClassLikeStorage $storage,
|
||||
FileChecker $file_checker,
|
||||
FileScanner $file_checker,
|
||||
Aliases $aliases,
|
||||
array &$file_replacements = []
|
||||
) {
|
||||
|
@ -65,7 +65,7 @@ class ClassLikeStorageProvider
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAll()
|
||||
public static function deleteAll()
|
||||
{
|
||||
self::$storage = [];
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ class FileStorageProvider
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAll()
|
||||
public static function deleteAll()
|
||||
{
|
||||
self::$storage = [];
|
||||
}
|
||||
|
83
src/Psalm/Scanner/FileScanner.php
Normal file
83
src/Psalm/Scanner/FileScanner.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace Psalm\Scanner;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Psalm\Checker\ProjectChecker;
|
||||
use Psalm\FileSource;
|
||||
use Psalm\Storage\FileStorage;
|
||||
use Psalm\Visitor\DependencyFinderVisitor;
|
||||
|
||||
class FileScanner implements FileSource
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $file_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $file_name;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $will_analyze;
|
||||
|
||||
/**
|
||||
* @param string $file_path
|
||||
* @param string $file_name
|
||||
* @param bool $will_analyze
|
||||
*/
|
||||
public function __construct($file_path, $file_name, $will_analyze)
|
||||
{
|
||||
$this->file_path = $file_path;
|
||||
$this->file_name = $file_name;
|
||||
$this->will_analyze = $will_analyze;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed, PhpParser\Node> $stmts
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function scan(ProjectChecker $project_checker, array $stmts, FileStorage $file_storage)
|
||||
{
|
||||
$traverser = new NodeTraverser();
|
||||
$traverser->addVisitor(new DependencyFinderVisitor($project_checker, $file_storage, $this));
|
||||
$traverser->traverse($stmts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath()
|
||||
{
|
||||
return $this->file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName()
|
||||
{
|
||||
return $this->file_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFilePath()
|
||||
{
|
||||
return $this->file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFileName()
|
||||
{
|
||||
return $this->file_name;
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ namespace Psalm;
|
||||
|
||||
use Psalm\Checker\FileChecker;
|
||||
|
||||
interface StatementsSource
|
||||
interface StatementsSource extends FileSource
|
||||
{
|
||||
/**
|
||||
* @return ?string
|
||||
@ -40,26 +40,6 @@ interface StatementsSource
|
||||
*/
|
||||
public function getParentFQCLN();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFileName();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFilePath();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -1,151 +0,0 @@
|
||||
<?php
|
||||
namespace Psalm;
|
||||
|
||||
use Psalm\Checker\FileChecker;
|
||||
|
||||
class TraitSource implements StatementsSource
|
||||
{
|
||||
/** @var Aliases */
|
||||
private $aliases;
|
||||
|
||||
/** @var FileChecker */
|
||||
private $file_checker;
|
||||
|
||||
public function __construct(FileChecker $file_checker, Aliases $aliases)
|
||||
{
|
||||
$this->aliases = $aliases;
|
||||
$this->file_checker = $file_checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->aliases->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Aliases
|
||||
*/
|
||||
public function getAliases()
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getAliasedClassesFlipped()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFQCLN()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClassName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FileChecker
|
||||
*/
|
||||
public function getFileChecker()
|
||||
{
|
||||
return $this->file_checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getParentFQCLN()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName()
|
||||
{
|
||||
return $this->file_checker->getFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath()
|
||||
{
|
||||
return $this->file_checker->getFilePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFileName()
|
||||
{
|
||||
return $this->file_checker->getCheckedFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedFilePath()
|
||||
{
|
||||
return $this->file_checker->getCheckedFilePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isStatic()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FileChecker
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->file_checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of suppressed issues
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function getSuppressedIssues()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $new_issues
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addSuppressedIssues(array $new_issues)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $new_issues
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeSuppressedIssues(array $new_issues)
|
||||
{
|
||||
}
|
||||
}
|
@ -6,14 +6,12 @@ use Psalm\Aliases;
|
||||
use Psalm\Checker\ClassChecker;
|
||||
use Psalm\Checker\ClassLikeChecker;
|
||||
use Psalm\Checker\CommentChecker;
|
||||
use Psalm\Checker\FileChecker;
|
||||
use Psalm\Checker\FunctionChecker;
|
||||
use Psalm\Checker\FunctionLikeChecker;
|
||||
use Psalm\Checker\MethodChecker;
|
||||
use Psalm\Checker\ProjectChecker;
|
||||
use Psalm\Checker\Statements\Expression\IncludeChecker;
|
||||
use Psalm\Checker\StatementsChecker;
|
||||
use Psalm\Checker\TraitChecker;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Config;
|
||||
use Psalm\Exception\DocblockParseException;
|
||||
@ -26,76 +24,72 @@ use Psalm\Issue\InvalidDocblock;
|
||||
use Psalm\Issue\MisplacedRequiredParam;
|
||||
use Psalm\Issue\MissingDocblockType;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Scanner\FileScanner;
|
||||
use Psalm\Storage\ClassLikeStorage;
|
||||
use Psalm\Storage\FileStorage;
|
||||
use Psalm\Storage\FunctionLikeStorage;
|
||||
use Psalm\Storage\MethodStorage;
|
||||
use Psalm\Storage\PropertyStorage;
|
||||
use Psalm\TraitSource;
|
||||
use Psalm\Type;
|
||||
|
||||
class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements PhpParser\NodeVisitor
|
||||
{
|
||||
/** @var Aliases */
|
||||
protected $aliases;
|
||||
private $aliases;
|
||||
|
||||
/** @var Aliases */
|
||||
protected $file_aliases;
|
||||
private $file_aliases;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fq_classlike_names = [];
|
||||
private $fq_classlike_names = [];
|
||||
|
||||
/** @var ProjectChecker */
|
||||
protected $project_checker;
|
||||
private $project_checker;
|
||||
|
||||
/** @var FileChecker */
|
||||
protected $file_checker;
|
||||
/** @var FileScanner */
|
||||
private $file_scanner;
|
||||
|
||||
/** @var string */
|
||||
protected $file_path;
|
||||
private $file_path;
|
||||
|
||||
/** @var bool */
|
||||
protected $scan_deep;
|
||||
private $scan_deep;
|
||||
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
private $config;
|
||||
|
||||
/** @var bool */
|
||||
protected $queue_strings_as_possible_type = false;
|
||||
private $queue_strings_as_possible_type = false;
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected $class_template_types = [];
|
||||
private $class_template_types = [];
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected $function_template_types = [];
|
||||
private $function_template_types = [];
|
||||
|
||||
/** @var FunctionLikeStorage[] */
|
||||
protected $functionlike_storages = [];
|
||||
private $functionlike_storages = [];
|
||||
|
||||
/** @var FileStorage */
|
||||
protected $file_storage;
|
||||
private $file_storage;
|
||||
|
||||
/** @var ClassLikeStorage[] */
|
||||
protected $classlike_storages = [];
|
||||
private $classlike_storages = [];
|
||||
|
||||
/** @var \Psalm\Plugin[] */
|
||||
protected $plugins;
|
||||
private $plugins;
|
||||
|
||||
/**
|
||||
* @param ProjectChecker $project_checker
|
||||
* @param FileChecker $file_checker
|
||||
*/
|
||||
public function __construct(ProjectChecker $project_checker, FileChecker $file_checker)
|
||||
public function __construct(ProjectChecker $project_checker, FileStorage $file_storage, FileScanner $file_scanner)
|
||||
{
|
||||
$this->project_checker = $project_checker;
|
||||
$this->file_checker = $file_checker;
|
||||
$this->file_path = $file_checker->getFilePath();
|
||||
$this->scan_deep = $file_checker->will_analyze;
|
||||
$this->file_scanner = $file_scanner;
|
||||
$this->file_path = $file_scanner->getFilePath();
|
||||
$this->scan_deep = $file_scanner->will_analyze;
|
||||
$this->config = Config::getInstance();
|
||||
$this->aliases = $this->file_aliases = new Aliases();
|
||||
$this->file_storage = $project_checker->file_storage_provider->get($this->file_path);
|
||||
$this->file_storage = $file_storage;
|
||||
$this->plugins = $this->config->getPlugins();
|
||||
}
|
||||
|
||||
@ -170,7 +164,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
|
||||
$storage = $this->project_checker->classlike_storage_provider->create($fq_classlike_name);
|
||||
|
||||
$storage->location = new CodeLocation($this->file_checker, $node, null, true);
|
||||
$storage->location = new CodeLocation($this->file_scanner, $node, null, true);
|
||||
$storage->user_defined = true;
|
||||
|
||||
$doc_comment = $node->getDocComment();
|
||||
@ -188,7 +182,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
$e->getMessage() . ' in docblock for ' . implode('.', $this->fq_classlike_names),
|
||||
new CodeLocation($this->file_checker, $node, null, true)
|
||||
new CodeLocation($this->file_scanner, $node, null, true)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -266,12 +260,24 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
} elseif ($node instanceof PhpParser\Node\Stmt\Trait_) {
|
||||
$storage->is_trait = true;
|
||||
$this->project_checker->addFullyQualifiedTraitName($fq_classlike_name, $this->file_path);
|
||||
ClassLikeChecker::$trait_checkers[$fq_classlike_name_lc] = new TraitChecker(
|
||||
$this->project_checker->addTraitNode(
|
||||
$fq_classlike_name,
|
||||
$node,
|
||||
new TraitSource($this->file_checker, $this->aliases),
|
||||
$fq_classlike_name
|
||||
$this->aliases
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($node->stmts as $node_stmt) {
|
||||
if ($node_stmt instanceof PhpParser\Node\Stmt\ClassConst) {
|
||||
$this->visitClassConstDeclaration($node_stmt, $storage);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($node->stmts as $node_stmt) {
|
||||
if ($node_stmt instanceof PhpParser\Node\Stmt\Property) {
|
||||
$this->visitPropertyDeclaration($node_stmt, $this->config, $storage);
|
||||
}
|
||||
}
|
||||
} elseif (($node instanceof PhpParser\Node\Expr\New_
|
||||
|| $node instanceof PhpParser\Node\Expr\Instanceof_
|
||||
|| $node instanceof PhpParser\Node\Expr\StaticPropertyFetch
|
||||
@ -379,10 +385,6 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
$this->project_checker->queueClassLikeForScanning($trait_fqcln, $this->file_path, $this->scan_deep);
|
||||
$storage->used_traits[strtolower($trait_fqcln)] = $trait_fqcln;
|
||||
}
|
||||
} elseif ($node instanceof PhpParser\Node\Stmt\Property) {
|
||||
$this->visitPropertyDeclaration($node, $this->config);
|
||||
} elseif ($node instanceof PhpParser\Node\Stmt\ClassConst) {
|
||||
$this->visitClassConstDeclaration($node);
|
||||
} elseif ($node instanceof PhpParser\Node\Expr\Include_) {
|
||||
$this->visitInclude($node);
|
||||
} elseif ($node instanceof PhpParser\Node\Scalar\String_ && $this->queue_strings_as_possible_type) {
|
||||
@ -399,7 +401,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
try {
|
||||
$var_comment = CommentChecker::getTypeFromComment(
|
||||
(string)$doc_comment,
|
||||
$this->file_checker,
|
||||
$this->file_scanner,
|
||||
$this->aliases,
|
||||
null,
|
||||
null
|
||||
@ -483,14 +485,11 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
$plugin->visitClassLike(
|
||||
$node,
|
||||
$classlike_storage,
|
||||
$this->file_checker,
|
||||
$this->file_scanner,
|
||||
$this->aliases,
|
||||
$file_manipulations
|
||||
);
|
||||
}
|
||||
|
||||
if ($file_manipulations) {
|
||||
}
|
||||
}
|
||||
} elseif ($node instanceof PhpParser\Node\Stmt\Function_
|
||||
|| $node instanceof PhpParser\Node\Stmt\ClassMethod
|
||||
@ -609,7 +608,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
$storage->cased_name = $stmt->name;
|
||||
}
|
||||
|
||||
$storage->location = new CodeLocation($this->file_checker, $stmt, null, true);
|
||||
$storage->location = new CodeLocation($this->file_scanner, $stmt, null, true);
|
||||
|
||||
$required_param_count = 0;
|
||||
$i = 0;
|
||||
@ -625,7 +624,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
if (IssueBuffer::accepts(
|
||||
new DuplicateParam(
|
||||
'Duplicate param $' . $param->name . ' in docblock for ' . $cased_function_id,
|
||||
new CodeLocation($this->file_checker, $param, null, true)
|
||||
new CodeLocation($this->file_scanner, $param, null, true)
|
||||
)
|
||||
)) {
|
||||
continue;
|
||||
@ -644,7 +643,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
new MisplacedRequiredParam(
|
||||
'Required param $' . $param->name . ' should come before any optional params in ' .
|
||||
$cased_function_id,
|
||||
new CodeLocation($this->file_checker, $param, null, true)
|
||||
new CodeLocation($this->file_scanner, $param, null, true)
|
||||
)
|
||||
)) {
|
||||
// fall through
|
||||
@ -736,7 +735,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
|
||||
$storage->return_type = Type::parseString($return_type_string, true);
|
||||
$storage->return_type_location = new CodeLocation(
|
||||
$this->file_checker,
|
||||
$this->file_scanner,
|
||||
$stmt,
|
||||
null,
|
||||
false,
|
||||
@ -770,7 +769,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
if (IssueBuffer::accepts(
|
||||
new MissingDocblockType(
|
||||
$e->getMessage() . ' in docblock for ' . $cased_function_id,
|
||||
new CodeLocation($this->file_checker, $stmt, null, true)
|
||||
new CodeLocation($this->file_scanner, $stmt, null, true)
|
||||
)
|
||||
)) {
|
||||
// fall through
|
||||
@ -781,7 +780,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
$e->getMessage() . ' in docblock for ' . $cased_function_id,
|
||||
new CodeLocation($this->file_checker, $stmt, null, true)
|
||||
new CodeLocation($this->file_scanner, $stmt, null, true)
|
||||
)
|
||||
)) {
|
||||
// fall through
|
||||
@ -859,7 +858,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
|
||||
if (!$storage->return_type_location) {
|
||||
$storage->return_type_location = new CodeLocation(
|
||||
$this->file_checker,
|
||||
$this->file_scanner,
|
||||
$stmt,
|
||||
null,
|
||||
false,
|
||||
@ -899,7 +898,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
$e->getMessage() . ' in docblock for ' . $cased_function_id,
|
||||
new CodeLocation($this->file_checker, $stmt, null, true)
|
||||
new CodeLocation($this->file_scanner, $stmt, null, true)
|
||||
)
|
||||
)) {
|
||||
// fall through
|
||||
@ -998,9 +997,9 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
$param->name,
|
||||
$param->byRef,
|
||||
$param_type,
|
||||
new CodeLocation($this->file_checker, $param, null, false, CodeLocation::FUNCTION_PARAM_VAR),
|
||||
new CodeLocation($this->file_scanner, $param, null, false, CodeLocation::FUNCTION_PARAM_VAR),
|
||||
$param_typehint
|
||||
? new CodeLocation($this->file_checker, $param, null, false, CodeLocation::FUNCTION_PARAM_TYPE)
|
||||
? new CodeLocation($this->file_scanner, $param, null, false, CodeLocation::FUNCTION_PARAM_TYPE)
|
||||
: null,
|
||||
$is_optional,
|
||||
$is_nullable,
|
||||
@ -1053,7 +1052,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
}
|
||||
|
||||
$code_location = new CodeLocation(
|
||||
$this->file_checker,
|
||||
$this->file_scanner,
|
||||
$function,
|
||||
null,
|
||||
true,
|
||||
@ -1134,7 +1133,8 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
*/
|
||||
private function visitPropertyDeclaration(
|
||||
PhpParser\Node\Stmt\Property $stmt,
|
||||
Config $config
|
||||
Config $config,
|
||||
ClassLikeStorage $storage
|
||||
) {
|
||||
if (!$this->fq_classlike_names) {
|
||||
throw new \LogicException('$this->fq_classlike_names should not be empty');
|
||||
@ -1148,10 +1148,12 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
throw new \UnexpectedValueException('$this->classlike_storages should not be empty');
|
||||
}
|
||||
|
||||
$storage = $this->classlike_storages[count($this->classlike_storages) - 1];
|
||||
|
||||
$property_is_initialized = false;
|
||||
|
||||
$existing_constants = $storage->protected_class_constants
|
||||
+ $storage->private_class_constants
|
||||
+ $storage->public_class_constants;
|
||||
|
||||
if ($comment && $comment->getText() && ($config->use_docblock_types || $config->use_docblock_property_types)) {
|
||||
if (preg_match('/[ \t\*]+@psalm-suppress[ \t]+PropertyNotSetInConstructor/', (string)$comment)) {
|
||||
$property_is_initialized = true;
|
||||
@ -1161,7 +1163,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
$property_type_line_number = $comment->getLine();
|
||||
$var_comment = CommentChecker::getTypeFromComment(
|
||||
$comment->getText(),
|
||||
$this->file_checker,
|
||||
$this->file_scanner,
|
||||
$this->aliases,
|
||||
$this->function_template_types + $this->class_template_types,
|
||||
$property_type_line_number
|
||||
@ -1170,7 +1172,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
if (IssueBuffer::accepts(
|
||||
new MissingDocblockType(
|
||||
$e->getMessage(),
|
||||
new CodeLocation($this->file_checker, $stmt, null, true)
|
||||
new CodeLocation($this->file_scanner, $stmt, null, true)
|
||||
)
|
||||
)) {
|
||||
// fall through
|
||||
@ -1179,7 +1181,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidDocblock(
|
||||
$e->getMessage(),
|
||||
new CodeLocation($this->file_checker, $stmt, null, true)
|
||||
new CodeLocation($this->file_scanner, $stmt, null, true)
|
||||
)
|
||||
)) {
|
||||
// fall through
|
||||
@ -1200,14 +1202,14 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
|
||||
if (!$property_group_type) {
|
||||
if ($property->default) {
|
||||
$default_type = StatementsChecker::getSimpleType($property->default);
|
||||
$default_type = StatementsChecker::getSimpleType($property->default, null, $existing_constants);
|
||||
}
|
||||
|
||||
$property_type = false;
|
||||
} else {
|
||||
if ($var_comment && $var_comment->line_number) {
|
||||
$property_type_location = new CodeLocation(
|
||||
$this->file_checker,
|
||||
$this->file_scanner,
|
||||
$stmt,
|
||||
null,
|
||||
false,
|
||||
@ -1223,7 +1225,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
$property_storage = $storage->properties[$property->name] = new PropertyStorage();
|
||||
$property_storage->is_static = (bool)$stmt->isStatic();
|
||||
$property_storage->type = $property_type;
|
||||
$property_storage->location = new CodeLocation($this->file_checker, $property);
|
||||
$property_storage->location = new CodeLocation($this->file_scanner, $property);
|
||||
$property_storage->type_location = $property_type_location;
|
||||
$property_storage->has_default = $property->default ? true : false;
|
||||
$property_storage->suggested_type = $property_group_type ? null : $default_type;
|
||||
@ -1259,14 +1261,12 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function visitClassConstDeclaration(PhpParser\Node\Stmt\ClassConst $stmt)
|
||||
private function visitClassConstDeclaration(PhpParser\Node\Stmt\ClassConst $stmt, ClassLikeStorage $storage)
|
||||
{
|
||||
if (!$this->classlike_storages) {
|
||||
throw new \LogicException('$this->classlike_storages should not be empty');
|
||||
}
|
||||
|
||||
$storage = $this->classlike_storages[count($this->classlike_storages) - 1];
|
||||
|
||||
$existing_constants = $storage->protected_class_constants
|
||||
+ $storage->private_class_constants
|
||||
+ $storage->public_class_constants;
|
||||
@ -1274,7 +1274,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
foreach ($stmt->consts as $const) {
|
||||
$const_type = StatementsChecker::getSimpleType(
|
||||
$const->value,
|
||||
$this->file_checker,
|
||||
null,
|
||||
$existing_constants
|
||||
) ?: Type::getMixed();
|
||||
|
||||
|
@ -403,6 +403,38 @@ class ConfigTest extends TestCase
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psalm\Exception\CodeException
|
||||
* @expectedExceptionMessage UndefinedFunction - /src/somefile.php:2 - Function barBar does not exist
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNoStubFunction()
|
||||
{
|
||||
$this->project_checker = $this->getProjectCheckerWithConfig(
|
||||
TestConfig::loadFromXML(
|
||||
'psalm.xml',
|
||||
dirname(__DIR__),
|
||||
'<?xml version="1.0"?>
|
||||
<psalm>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
</projectFiles>
|
||||
</psalm>'
|
||||
)
|
||||
);
|
||||
|
||||
$file_path = getcwd() . '/src/somefile.php';
|
||||
|
||||
$this->addFile(
|
||||
$file_path,
|
||||
'<?php
|
||||
echo barBar("hello");'
|
||||
);
|
||||
|
||||
$this->analyzeFile($file_path, new Context());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
@ -22,8 +22,10 @@ class IncludeTest extends TestCase
|
||||
|
||||
$this->project_checker->scanFiles();
|
||||
|
||||
foreach ($files_to_check as $filename) {
|
||||
$file_checker = new FileChecker($filename, $this->project_checker);
|
||||
$config = $this->project_checker->getConfig();
|
||||
|
||||
foreach ($files_to_check as $file_path) {
|
||||
$file_checker = new FileChecker($this->project_checker, $file_path, $config->shortenFileName($file_path));
|
||||
$file_checker->analyze();
|
||||
}
|
||||
}
|
||||
@ -49,8 +51,10 @@ class IncludeTest extends TestCase
|
||||
$this->expectException('\Psalm\Exception\CodeException');
|
||||
$this->expectExceptionMessageRegexp('/\b' . preg_quote($error_message, '/') . '\b/');
|
||||
|
||||
foreach ($files_to_check as $filename) {
|
||||
$file_checker = new FileChecker($filename, $this->project_checker);
|
||||
$config = $this->project_checker->getConfig();
|
||||
|
||||
foreach ($files_to_check as $file_path) {
|
||||
$file_checker = new FileChecker($this->project_checker, $file_path, $config->shortenFileName($file_path));
|
||||
$file_checker->analyze();
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class MethodMutationTest extends TestCase
|
||||
}'
|
||||
);
|
||||
|
||||
new FileChecker('somefile.php', $this->project_checker);
|
||||
new FileChecker($this->project_checker, 'somefile.php', 'somefile.php');
|
||||
$this->project_checker->scanFiles();
|
||||
$method_context = new Context();
|
||||
$this->project_checker->getMethodMutations('FooController::barBar', $method_context);
|
||||
@ -126,7 +126,7 @@ class MethodMutationTest extends TestCase
|
||||
}'
|
||||
);
|
||||
|
||||
new FileChecker('somefile.php', $this->project_checker);
|
||||
new FileChecker($this->project_checker, 'somefile.php', 'somefile.php');
|
||||
$this->project_checker->scanFiles();
|
||||
$method_context = new Context();
|
||||
$this->project_checker->getMethodMutations('FooController::__construct', $method_context);
|
||||
@ -162,7 +162,7 @@ class MethodMutationTest extends TestCase
|
||||
}'
|
||||
);
|
||||
|
||||
new FileChecker('somefile.php', $this->project_checker);
|
||||
new FileChecker($this->project_checker, 'somefile.php', 'somefile.php');
|
||||
$this->project_checker->scanFiles();
|
||||
$method_context = new Context();
|
||||
$this->project_checker->getMethodMutations('FooController::__construct', $method_context);
|
||||
|
@ -28,6 +28,9 @@ class Php56Test extends TestCase
|
||||
const ONE_THIRD = self::ONE / self::THREE;
|
||||
const SENTENCE = "The value of THREE is " . self::THREE;
|
||||
|
||||
/** @var int */
|
||||
public $four = self::ONE + self::THREE;
|
||||
|
||||
/**
|
||||
* @param int $a
|
||||
* @return int
|
||||
@ -42,7 +45,8 @@ class Php56Test extends TestCase
|
||||
$c3 = C::THREE;
|
||||
$c1_3rd = C::ONE_THIRD;
|
||||
$c_sentence = C::SENTENCE;
|
||||
$cf = (new C)->f();',
|
||||
$cf = (new C)->f();
|
||||
$c4 = (new C)->four',
|
||||
'assertions' => [
|
||||
'$c1' => 'int',
|
||||
'$c2' => 'int',
|
||||
@ -50,6 +54,7 @@ class Php56Test extends TestCase
|
||||
'$c1_3rd' => 'float|int',
|
||||
'$c_sentence' => 'string',
|
||||
'$cf' => 'int',
|
||||
'$c4' => 'int',
|
||||
],
|
||||
],
|
||||
'constFeatures' => [
|
||||
|
@ -49,17 +49,6 @@ class TestCase extends BaseTestCase
|
||||
$this->project_checker->infer_types_from_usage = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
if ($this->project_checker) {
|
||||
$this->project_checker->classlike_storage_provider->deleteAll();
|
||||
$this->project_checker->file_storage_provider->deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file_path
|
||||
* @param string $contents
|
||||
@ -80,7 +69,8 @@ class TestCase extends BaseTestCase
|
||||
*/
|
||||
public function analyzeFile($file_path, Context $context)
|
||||
{
|
||||
$file_checker = new FileChecker($file_path, $this->project_checker);
|
||||
$config = $this->project_checker->getConfig();
|
||||
$file_checker = new FileChecker($this->project_checker, $file_path, $config->shortenFileName($file_path));
|
||||
$this->project_checker->registerAnalyzableFile($file_path);
|
||||
$this->project_checker->scanFiles();
|
||||
$file_checker->analyze($context);
|
||||
|
@ -28,7 +28,7 @@ class TypeReconciliationTest extends TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->file_checker = new FileChecker('somefile.php', $this->project_checker);
|
||||
$this->file_checker = new FileChecker($this->project_checker, 'somefile.php', 'somefile.php');
|
||||
$this->file_checker->context = new Context();
|
||||
$this->statements_checker = new StatementsChecker($this->file_checker);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user