Skip to content
Permalink
Browse files

Add support for unused method & property automated removal

  • Loading branch information...
muglug committed Apr 17, 2019
1 parent 01f0052 commit 80e28d6a4ae42ac9f215ab7019e61a1d61463e9a
@@ -139,6 +139,10 @@ public static function analyzeInstance(
$codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
}
if ($stmt->name instanceof PhpParser\Node\Identifier) {
$codebase->analyzer->addMixedMemberName('$' . $stmt->name->name);
}
if (IssueBuffer::accepts(
new MixedPropertyAssignment(
$lhs_var_id . ' of type mixed cannot be assigned to',
@@ -467,6 +467,10 @@ private static function analyzeAtomicCall(
$has_mixed_method_call = true;
if ($stmt->name instanceof PhpParser\Node\Identifier) {
$codebase->analyzer->addMixedMemberName(strtolower($stmt->name->name));
}
if ($context->check_methods) {
if (IssueBuffer::accepts(
new MixedMethodCall(
@@ -260,6 +260,10 @@ public static function analyze(
|| $lhs_type_part instanceof Type\Atomic\TTemplateParam
|| $lhs_type_part instanceof Type\Atomic\TClassString
) {
if ($stmt->name instanceof PhpParser\Node\Identifier) {
$codebase->analyzer->addMixedMemberName(strtolower($stmt->name->name));
}
if (IssueBuffer::accepts(
new MixedMethodCall(
'Cannot call method on an unknown class',
@@ -235,6 +235,10 @@ public static function analyzeInstance(
$codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
}
if ($stmt->name instanceof PhpParser\Node\Identifier) {
$codebase->analyzer->addMixedMemberName('$' . $stmt->name->name);
}
if (IssueBuffer::accepts(
new MixedPropertyFetch(
'Cannot fetch property on mixed var ' . $stmt_var_id,
@@ -38,6 +38,7 @@
* file_references_to_class_members: array<string, array<string,bool>>,
* file_references_to_missing_class_members: array<string, array<string,bool>>,
* mixed_counts: array<string, array{0: int, 1: int}>,
* mixed_member_names: array<string, bool>,
* method_references_to_class_members: array<string, array<string,bool>>,
* method_references_to_missing_class_members: array<string, array<string,bool>>,
* analyzed_methods: array<string, array<string, int>>,
@@ -85,6 +86,13 @@ class Analyzer
*/
private $mixed_counts = [];
/**
* Used to store member names of mixed property/method access
*
* @var array<string, bool>
*/
private $mixed_member_names = [];
/**
* @var bool
*/
@@ -260,6 +268,7 @@ function () {
=> $file_reference_provider->getAllFileReferencesToMissingClassMembers(),
'method_references_to_missing_class_members'
=> $file_reference_provider->getAllMethodReferencesToMissingClassMembers(),
'mixed_member_names' => $analyzer->getMixedMemberNames(),
'mixed_counts' => $analyzer->getMixedCounts(),
'analyzed_methods' => $analyzer->getAnalyzedMethods(),
'file_maps' => $analyzer->getFileMaps(),
@@ -306,6 +315,9 @@ function () {
$codebase->file_reference_provider->addMethodReferencesToMissingClassMembers(
$pool_data['method_references_to_missing_class_members']
);
$this->addMixedMemberNames(
$pool_data['mixed_member_names']
);
$codebase->file_reference_provider->addClassLocations(
$pool_data['class_locations']
);
@@ -676,6 +688,36 @@ public function shiftFileOffsets(array $diff_map)
}
}
/**
* @return array<string, bool>
*/
public function getMixedMemberNames() : array
{
return $this->mixed_member_names;
}
/**
* @return void
*/
public function addMixedMemberName(string $member_id)
{
$this->mixed_member_names[$member_id] = true;
}
public function hasMixedMemberName(string $member_id) : bool
{
return isset($this->mixed_member_names[$member_id]);
}
/**
* @var array<string, bool> $names
* @return void
*/
public function addMixedMemberNames(array $names)
{
$this->mixed_member_names += $names;
}
/**
* @param string $file_path
*
@@ -7,6 +7,7 @@
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
use Psalm\CodeLocation;
use Psalm\Config;
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
use Psalm\Issue\PossiblyUnusedMethod;
use Psalm\Issue\PossiblyUnusedParam;
use Psalm\Issue\PossiblyUnusedProperty;
@@ -759,6 +760,9 @@ public function setConstantType(
*/
private function checkMethodReferences(ClassLikeStorage $classlike_storage, Methods $methods)
{
$project_analyzer = \Psalm\Internal\Analyzer\ProjectAnalyzer::getInstance();
$codebase = $project_analyzer->getCodebase();
foreach ($classlike_storage->appearing_method_ids as $method_name => $appearing_method_id) {
list($appearing_fq_classlike_name) = explode('::', $appearing_method_id);
@@ -830,24 +834,50 @@ private function checkMethodReferences(ClassLikeStorage $classlike_storage, Meth
}
if (!$has_parent_references) {
if (IssueBuffer::accepts(
new PossiblyUnusedMethod(
'Cannot find public calls to method ' . $method_id,
$method_storage->location,
$method_id
),
$issue = new PossiblyUnusedMethod(
'Cannot find public calls to method ' . $method_id,
$method_storage->location,
$method_id
);
if ($codebase->alter_code) {
if ($method_storage->stmt_location
&& isset($project_analyzer->getIssuesToFix()['PossiblyUnusedMethod'])
&& !$codebase->analyzer->hasMixedMemberName(strtolower($method_name))
&& !IssueBuffer::isSuppressed($issue, $method_storage->suppressed_issues)
) {
FileManipulationBuffer::addForCodeLocation(
$method_storage->stmt_location,
''
);
}
} elseif (IssueBuffer::accepts(
$issue,
$method_storage->suppressed_issues
)) {
// fall through
}
}
} elseif (!isset($classlike_storage->declaring_method_ids['__call'])) {
if (IssueBuffer::accepts(
new UnusedMethod(
'Method ' . $method_id . ' is never used',
$method_location,
$method_id
),
$issue = new UnusedMethod(
'Method ' . $method_id . ' is never used',
$method_location,
$method_id
);
if ($codebase->alter_code) {
if ($method_storage->stmt_location
&& isset($project_analyzer->getIssuesToFix()['UnusedMethod'])
&& !$codebase->analyzer->hasMixedMemberName(strtolower($method_name))
&& !IssueBuffer::isSuppressed($issue, $method_storage->suppressed_issues)
) {
FileManipulationBuffer::addForCodeLocation(
$method_storage->stmt_location,
''
);
}
} elseif (IssueBuffer::accepts(
$issue,
$method_storage->suppressed_issues
)) {
// fall through
@@ -899,21 +929,48 @@ private function checkMethodReferences(ClassLikeStorage $classlike_storage, Meth
$property_id = $classlike_storage->name . '::$' . $property_name;
if ($property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC) {
if (IssueBuffer::accepts(
new PossiblyUnusedProperty(
'Cannot find uses of public property ' . $property_id,
$property_storage->location
),
$issue = new PossiblyUnusedProperty(
'Cannot find uses of public property ' . $property_id,
$property_storage->location
);
if ($codebase->alter_code) {
if ($property_storage->stmt_location
&& isset($project_analyzer->getIssuesToFix()['PossiblyUnusedProperty'])
&& !$codebase->analyzer->hasMixedMemberName('$' . $property_name)
&& !IssueBuffer::isSuppressed($issue, $classlike_storage->suppressed_issues)
) {
FileManipulationBuffer::addForCodeLocation(
$property_storage->stmt_location,
''
);
}
} elseif (IssueBuffer::accepts(
$issue,
$classlike_storage->suppressed_issues
)) {
// fall through
}
} elseif (!isset($classlike_storage->declaring_method_ids['__get'])) {
if (IssueBuffer::accepts(
new UnusedProperty(
'Property ' . $property_id . ' is never used',
$property_storage->location
)
$issue = new UnusedProperty(
'Property ' . $property_id . ' is never used',
$property_storage->location
);
if ($codebase->alter_code) {
if ($property_storage->stmt_location
&& isset($project_analyzer->getIssuesToFix()['UnusedProperty'])
&& !$codebase->analyzer->hasMixedMemberName('$' . $property_name)
&& !IssueBuffer::isSuppressed($issue, $classlike_storage->suppressed_issues)
) {
FileManipulationBuffer::addForCodeLocation(
$property_storage->stmt_location,
''
);
}
} elseif (IssueBuffer::accepts(
$issue,
$classlike_storage->suppressed_issues
)) {
// fall through
}
@@ -24,6 +24,25 @@ public static function add($file_path, array $file_manipulations)
: $file_manipulations;
}
/**
* @return void
*/
public static function addForCodeLocation(\Psalm\CodeLocation $code_location, string $replacement_text)
{
$bounds = $code_location->getSnippetBounds();
self::add(
$code_location->file_path,
[
new FileManipulation(
$bounds[0],
$bounds[1],
$replacement_text
)
]
);
}
/**
* @param string $file_path
*
@@ -1451,6 +1451,8 @@ private function registerFunctionLike(PhpParser\Node\FunctionLike $stmt, $fake_m
$storage->location = new CodeLocation($this->file_scanner, $stmt, null, true);
}
$storage->stmt_location = new CodeLocation($this->file_scanner, $stmt);
$required_param_count = 0;
$i = 0;
$has_optional_param = false;
@@ -2535,6 +2537,7 @@ private function visitPropertyDeclaration(
$property_storage->signature_type_location = $signature_type_location;
$property_storage->type_location = $signature_type_location;
$property_storage->location = new CodeLocation($this->file_scanner, $property->name);
$property_storage->stmt_location = new CodeLocation($this->file_scanner, $stmt);
$property_storage->has_default = $property->default ? true : false;
$property_storage->deprecated = $var_comment ? $var_comment->deprecated : false;
$property_storage->internal = $var_comment ? $var_comment->internal : false;
@@ -51,61 +51,76 @@ class IssueBuffer
* @return bool
*/
public static function accepts(CodeIssue $e, array $suppressed_issues = [])
{
if (self::isSuppressed($e, $suppressed_issues)) {
return false;
}
return self::add($e);
}
/**
* @param CodeIssue $e
* @param array $suppressed_issues
*
* @return bool
*/
public static function isSuppressed(CodeIssue $e, array $suppressed_issues = []) : bool
{
$config = Config::getInstance();
$fqcn_parts = explode('\\', get_class($e));
$issue_type = array_pop($fqcn_parts);
if (in_array($issue_type, $suppressed_issues, true)) {
return false;
return true;
}
if (!$config->reportIssueInFile($issue_type, $e->getFilePath())) {
return false;
return true;
}
if ($e instanceof ClassIssue
&& $config->getReportingLevelForClass($issue_type, $e->fq_classlike_name) === Config::REPORT_SUPPRESS
) {
return false;
return true;
}
if ($e instanceof MethodIssue
&& $config->getReportingLevelForMethod($issue_type, $e->method_id) === Config::REPORT_SUPPRESS
) {
return false;
return true;
}
if ($e instanceof PropertyIssue
&& $config->getReportingLevelForProperty($issue_type, $e->property_id) === Config::REPORT_SUPPRESS
) {
return false;
return true;
}
$parent_issue_type = self::getParentIssueType($issue_type);
if ($parent_issue_type) {
if (in_array($parent_issue_type, $suppressed_issues, true)) {
return false;
return true;
}
if (!$config->reportIssueInFile($parent_issue_type, $e->getFilePath())) {
return false;
return true;
}
}
if ($e->getLocation()->getLineNumber() === -1) {
return false;
return true;
}
if (self::$recording_level > 0) {
self::$recorded_issues[self::$recording_level][] = $e;
return false;
return true;
}
return self::add($e);
return false;
}
/**

0 comments on commit 80e28d6

Please sign in to comment.
You can’t perform that action at this time.