mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Add extra issue for invalid clone and fix issue reporting;
This commit is contained in:
parent
4b283564ca
commit
e3a9cb98c3
@ -85,6 +85,7 @@
|
|||||||
<xs:element name="InvalidArrayAccess" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidArrayAccess" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidArrayAssignment" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidArrayAssignment" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidClass" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidClass" type="IssueHandlerType" minOccurs="0" />
|
||||||
|
<xs:element name="InvalidClone" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidParamDefault" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidParamDefault" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidDocblock" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidDocblock" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidFunctionCall" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidFunctionCall" type="IssueHandlerType" minOccurs="0" />
|
||||||
|
@ -472,7 +472,7 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
$global_context ? clone $global_context : null
|
$global_context ? clone $global_context : null
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!$config->excludeIssueInFile('InvalidReturnType', $source->getFileName())) {
|
if (!$config->excludeIssueInFile('InvalidReturnType', $source->getFilePath())) {
|
||||||
$return_type_location = null;
|
$return_type_location = null;
|
||||||
$secondary_return_type_location = null;
|
$secondary_return_type_location = null;
|
||||||
|
|
||||||
|
@ -307,7 +307,7 @@ class FileChecker extends SourceChecker implements StatementsSource
|
|||||||
$function_context = new Context($this->file_name, $this->context->self);
|
$function_context = new Context($this->file_name, $this->context->self);
|
||||||
$function_checker->analyze($function_context, $this->context);
|
$function_checker->analyze($function_context, $this->context);
|
||||||
|
|
||||||
if (!$config->excludeIssueInFile('InvalidReturnType', $this->file_name)) {
|
if (!$config->excludeIssueInFile('InvalidReturnType', $this->file_path)) {
|
||||||
/** @var string */
|
/** @var string */
|
||||||
$method_id = $function_checker->getMethodId();
|
$method_id = $function_checker->getMethodId();
|
||||||
|
|
||||||
|
@ -955,11 +955,11 @@ class CallChecker
|
|||||||
if ($function_params !== null) {
|
if ($function_params !== null) {
|
||||||
$by_ref = $argument_offset < count($function_params)
|
$by_ref = $argument_offset < count($function_params)
|
||||||
? $function_params[$argument_offset]->by_ref
|
? $function_params[$argument_offset]->by_ref
|
||||||
: $last_param->is_variadic && $last_param->by_ref;
|
: $last_param && $last_param->is_variadic && $last_param->by_ref;
|
||||||
|
|
||||||
$by_ref_type = null;
|
$by_ref_type = null;
|
||||||
|
|
||||||
if ($by_ref) {
|
if ($by_ref && $last_param) {
|
||||||
$by_ref_type = $argument_offset < count($function_params)
|
$by_ref_type = $argument_offset < count($function_params)
|
||||||
? clone $function_params[$argument_offset]->type
|
? clone $function_params[$argument_offset]->type
|
||||||
: clone $last_param->type;
|
: clone $last_param->type;
|
||||||
@ -992,11 +992,11 @@ class CallChecker
|
|||||||
if ($function_params !== null) {
|
if ($function_params !== null) {
|
||||||
$by_ref = $argument_offset < count($function_params)
|
$by_ref = $argument_offset < count($function_params)
|
||||||
? $function_params[$argument_offset]->by_ref
|
? $function_params[$argument_offset]->by_ref
|
||||||
: $last_param->is_variadic && $last_param->by_ref;
|
: $last_param && $last_param->is_variadic && $last_param->by_ref;
|
||||||
|
|
||||||
$by_ref_type = null;
|
$by_ref_type = null;
|
||||||
|
|
||||||
if ($by_ref) {
|
if ($by_ref && $last_param) {
|
||||||
$by_ref_type = $argument_offset < count($function_params)
|
$by_ref_type = $argument_offset < count($function_params)
|
||||||
? clone $function_params[$argument_offset]->type
|
? clone $function_params[$argument_offset]->type
|
||||||
: clone $last_param->type;
|
: clone $last_param->type;
|
||||||
|
@ -17,6 +17,7 @@ use Psalm\CodeLocation;
|
|||||||
use Psalm\Config;
|
use Psalm\Config;
|
||||||
use Psalm\Context;
|
use Psalm\Context;
|
||||||
use Psalm\Issue\ForbiddenCode;
|
use Psalm\Issue\ForbiddenCode;
|
||||||
|
use Psalm\Issue\InvalidClone;
|
||||||
use Psalm\Issue\InvalidOperand;
|
use Psalm\Issue\InvalidOperand;
|
||||||
use Psalm\Issue\InvalidScope;
|
use Psalm\Issue\InvalidScope;
|
||||||
use Psalm\Issue\InvalidStaticVariable;
|
use Psalm\Issue\InvalidStaticVariable;
|
||||||
@ -305,13 +306,7 @@ class ExpressionChecker
|
|||||||
|
|
||||||
$stmt->inferredType = Type::getNull();
|
$stmt->inferredType = Type::getNull();
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Expr\Clone_) {
|
} elseif ($stmt instanceof PhpParser\Node\Expr\Clone_) {
|
||||||
if (self::analyze($statements_checker, $stmt->expr, $context) === false) {
|
self::analyzeClone($statements_checker, $stmt, $context);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (property_exists($stmt->expr, 'inferredType')) {
|
|
||||||
$stmt->inferredType = $stmt->expr->inferredType;
|
|
||||||
}
|
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Expr\Instanceof_) {
|
} elseif ($stmt instanceof PhpParser\Node\Expr\Instanceof_) {
|
||||||
if (self::analyze($statements_checker, $stmt->expr, $context) === false) {
|
if (self::analyze($statements_checker, $stmt->expr, $context) === false) {
|
||||||
return false;
|
return false;
|
||||||
@ -1686,6 +1681,42 @@ class ExpressionChecker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StatementsChecker $statements_checker
|
||||||
|
* @param PhpParser\Node\Expr\Clone_ $stmt
|
||||||
|
* @param Context $context
|
||||||
|
* @return false|null
|
||||||
|
*/
|
||||||
|
protected static function analyzeClone(
|
||||||
|
StatementsChecker $statements_checker,
|
||||||
|
PhpParser\Node\Expr\Clone_ $stmt,
|
||||||
|
Context $context
|
||||||
|
) {
|
||||||
|
if (self::analyze($statements_checker, $stmt->expr, $context) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($stmt->expr->inferredType)) {
|
||||||
|
foreach ($stmt->expr->inferredType->types as $clone_type_part) {
|
||||||
|
if (!$clone_type_part instanceof TNamedObject && !$clone_type_part instanceof TObject) {
|
||||||
|
if (IssueBuffer::accepts(
|
||||||
|
new InvalidClone(
|
||||||
|
'Cannot clone ' . $clone_type_part,
|
||||||
|
new CodeLocation($statements_checker->getSource(), $stmt)
|
||||||
|
),
|
||||||
|
$statements_checker->getSuppressedIssues()
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->inferredType = $stmt->expr->inferredType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $fq_class_name
|
* @param string $fq_class_name
|
||||||
* @return boolean
|
* @return boolean
|
||||||
|
@ -186,7 +186,7 @@ class StatementsChecker extends SourceChecker implements StatementsSource
|
|||||||
|
|
||||||
$config = Config::getInstance();
|
$config = Config::getInstance();
|
||||||
|
|
||||||
if (!$config->excludeIssueInFile('InvalidReturnType', $this->getFileName())) {
|
if (!$config->excludeIssueInFile('InvalidReturnType', $this->getFilePath())) {
|
||||||
/** @var string */
|
/** @var string */
|
||||||
$method_id = $function_checkers[$stmt->name]->getMethodId();
|
$method_id = $function_checkers[$stmt->name]->getMethodId();
|
||||||
|
|
||||||
|
@ -422,24 +422,22 @@ class Config
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $issue_type
|
* @param string $issue_type
|
||||||
* @param string $file_name
|
* @param string $file_path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function excludeIssueInFile($issue_type, $file_name)
|
public function excludeIssueInFile($issue_type, $file_path)
|
||||||
{
|
{
|
||||||
if (!$this->totally_typed && in_array($issue_type, self::$MIXED_ISSUES)) {
|
if (!$this->totally_typed && in_array($issue_type, self::$MIXED_ISSUES)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$file_name = $this->shortenFileName($file_name);
|
|
||||||
|
|
||||||
if ($this->project_files && $this->hide_external_errors) {
|
if ($this->project_files && $this->hide_external_errors) {
|
||||||
if (!$this->isInProjectDirs($file_name)) {
|
if (!$this->isInProjectDirs($file_path)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->getReportingLevelForFile($issue_type, $file_name) === self::REPORT_SUPPRESS) {
|
if ($this->getReportingLevelForFile($issue_type, $file_path) === self::REPORT_SUPPRESS) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,18 +450,21 @@ class Config
|
|||||||
*/
|
*/
|
||||||
public function isInProjectDirs($file_path)
|
public function isInProjectDirs($file_path)
|
||||||
{
|
{
|
||||||
|
if ($file_path[0] !== '/') {
|
||||||
|
throw new \UnexpectedValueException('eesh');
|
||||||
|
}
|
||||||
return $this->project_files && $this->project_files->allows($file_path);
|
return $this->project_files && $this->project_files->allows($file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $issue_type
|
* @param string $issue_type
|
||||||
* @param string $file_name
|
* @param string $file_path
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getReportingLevelForFile($issue_type, $file_name)
|
public function getReportingLevelForFile($issue_type, $file_path)
|
||||||
{
|
{
|
||||||
if (isset($this->issue_handlers[$issue_type])) {
|
if (isset($this->issue_handlers[$issue_type])) {
|
||||||
return $this->issue_handlers[$issue_type]->getReportingLevelForFile($file_name);
|
return $this->issue_handlers[$issue_type]->getReportingLevelForFile($file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::REPORT_ERROR;
|
return self::REPORT_ERROR;
|
||||||
|
@ -13,6 +13,7 @@ class ErrorLevelFileFilter extends FileFilter
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SimpleXMLElement $e
|
* @param SimpleXMLElement $e
|
||||||
|
* @param Config $config
|
||||||
* @param bool $inclusive
|
* @param bool $inclusive
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
|
@ -55,13 +55,13 @@ class IssueHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $file_name
|
* @param string $file_path
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getReportingLevelForFile($file_name)
|
public function getReportingLevelForFile($file_path)
|
||||||
{
|
{
|
||||||
foreach ($this->custom_levels as $custom_level) {
|
foreach ($this->custom_levels as $custom_level) {
|
||||||
if ($custom_level->allows($file_name)) {
|
if ($custom_level->allows($file_path)) {
|
||||||
return $custom_level->getErrorLevel();
|
return $custom_level->getErrorLevel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
src/Psalm/Issue/InvalidClone.php
Normal file
6
src/Psalm/Issue/InvalidClone.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Psalm\Issue;
|
||||||
|
|
||||||
|
class InvalidClone extends CodeError
|
||||||
|
{
|
||||||
|
}
|
@ -41,7 +41,7 @@ class IssueBuffer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($config->excludeIssueInFile($issue_type, $e->getFileName())) {
|
if ($config->excludeIssueInFile($issue_type, $e->getFilePath())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user