Skip to content
1 change: 0 additions & 1 deletion examples/TemplateChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ private function checkMethod(MethodIdentifier $method_id, PhpParser\Node $stmt,
$method_id->fq_class_name,
new CodeLocation($this, $stmt),
null,
null,
[],
new ClassLikeNameOptions(true),
) === false
Expand Down
91 changes: 75 additions & 16 deletions src/Psalm/Codebase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use LanguageServerProtocol\Range;
use LanguageServerProtocol\SignatureInformation;
use LanguageServerProtocol\TextEdit;
use PHP_CodeSniffer\Reports\Code;
use PhpParser;
use PhpParser\Node\Arg;
use Psalm\CodeLocation\Raw;
Expand All @@ -31,6 +32,7 @@
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Codebase\Analyzer;
use Psalm\Internal\Codebase\ClassLikes;
use Psalm\Internal\Codebase\CodeUseGraph;
use Psalm\Internal\Codebase\Functions;
use Psalm\Internal\Codebase\InternalCallMapHandler;
use Psalm\Internal\Codebase\Methods;
Expand Down Expand Up @@ -173,6 +175,8 @@

public Populator $populator;

public ?CodeUseGraph $code_use_graph = null;

public ?TaintFlowGraph $taint_flow_graph = null;

public bool $server_mode = false;
Expand Down Expand Up @@ -337,6 +341,69 @@
$this->loadAnalyzer();
}

/**
* @param lowercase-string $fq_class_name_lc
*/
public function addReferenceToClass(
string $fq_class_name_lc,
?CodeLocation $location = null,
?Context $context = null,
): void {
if ($this->code_use_graph === null) {
return;
}
$class = $this->code_use_graph->getNodeForClass($fq_class_name_lc);

$this->code_use_graph->addReferenceToNode(
$class,
$context,

Check failure on line 359 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:359:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 359 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:359:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 359 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:359:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 359 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:359:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)
$location,
);
}

/**
* @param lowercase-string $fq_class_name_lc
* @param lowercase-string $property_name_lc
*/
public function addReferenceToProperty(
string $fq_class_name_lc,
string $property_name_lc,
?CodeLocation $location = null,
?Context $context = null,
): void {
if ($this->code_use_graph === null) {
return;
}
$class = $this->code_use_graph->getNodeForProperty($fq_class_name_lc, $property_name_lc);

$this->code_use_graph->addReferenceToNode(
$class,
$context,

Check failure on line 381 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:381:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 381 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:381:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 381 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:381:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 381 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:381:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)
$location,
);
}

/**
* @param lowercase-string $function_id
*/
public function addReferenceToFunctionLike(
string $function_id,
?CodeLocation $location = null,
?Context $context = null,
): void {
if ($this->code_use_graph === null) {
return;
}
$function = $this->code_use_graph->getNodeForFunctionLike($function_id);

$this->code_use_graph->addReferenceToNode(
$function,
$context,

Check failure on line 401 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:401:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 401 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:401:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 401 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:401:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)

Check failure on line 401 in src/Psalm/Codebase.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Codebase.php:401:13: PossiblyNullArgument: Argument 2 of Psalm\Internal\Codebase\CodeUseGraph::addReferenceToNode cannot be null, possibly null value provided (see https://psalm.dev/078)
$location,
);
}


/**
* Used to register a taint, or to fetch the ID of an already registered taint by its alias.
*
Expand Down Expand Up @@ -741,14 +808,12 @@
public function classOrInterfaceExists(
string $fq_class_name,
?CodeLocation $code_location = null,
?string $calling_fq_class_name = null,
?string $calling_method_id = null,
?Context $context = null,
): bool {
return $this->classlikes->classOrInterfaceExists(
$fq_class_name,
$code_location,
$calling_fq_class_name,
$calling_method_id,
$context,
);
}

Expand All @@ -761,14 +826,12 @@
public function classOrInterfaceOrEnumExists(
string $fq_class_name,
?CodeLocation $code_location = null,
?string $calling_fq_class_name = null,
?string $calling_method_id = null,
?Context $context = null,
): bool {
return $this->classlikes->classOrInterfaceOrEnumExists(
$fq_class_name,
$code_location,
$calling_fq_class_name,
$calling_method_id,
$context,
);
}

Expand All @@ -787,14 +850,12 @@
public function classExists(
string $fq_class_name,
?CodeLocation $code_location = null,
?string $calling_fq_class_name = null,
?string $calling_method_id = null,
?Context $context = null,
): bool {
return $this->classlikes->classExists(
$fq_class_name,
$code_location,
$calling_fq_class_name,
$calling_method_id,
$context,
);
}

Expand Down Expand Up @@ -826,14 +887,12 @@
public function interfaceExists(
string $fq_interface_name,
?CodeLocation $code_location = null,
?string $calling_fq_class_name = null,
?string $calling_method_id = null,
?Context $context = null,
): bool {
return $this->classlikes->interfaceExists(
$fq_interface_name,
$code_location,
$calling_fq_class_name,
$calling_method_id,
$context,
);
}

Expand Down
3 changes: 3 additions & 0 deletions src/Psalm/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ final class Context

public bool $strict_types = false;

/**
* @var lowercase-string|null
*/
public ?string $calling_function_id = null;

/**
Expand Down
5 changes: 2 additions & 3 deletions src/Psalm/Internal/Analyzer/AttributesAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public static function analyze(
$attribute_name = (string) $attribute->name;
$attribute_name_location = new CodeLocation($source, $attribute->name);

$attribute_class_storage = $codebase->classlikes->classExists($fq_attribute_name)
$attribute_class_storage = $codebase->classlikes->classExists($fq_attribute_name, null, $context)
? $codebase->classlike_storage_provider->get($fq_attribute_name)
: null;

Expand Down Expand Up @@ -139,8 +139,7 @@ private static function analyzeAttributeConstruction(
$source,
$fq_attribute_name,
$attribute_name_location,
null,
null,
$context,
$suppressed_issues,
new ClassLikeNameOptions(
false,
Expand Down
31 changes: 17 additions & 14 deletions src/Psalm/Internal/Analyzer/ClassAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -1412,7 +1412,11 @@ private function analyzeTraitUse(
$aliases,
);

if (!$codebase->classlikes->hasFullyQualifiedTraitName($fq_trait_name, $trait_location)) {
if (!$codebase->classlikes->hasFullyQualifiedTraitName(
$fq_trait_name,
$trait_location,
$class_context,
)) {
IssueBuffer::maybeAdd(
new UndefinedTrait(
'Trait ' . $fq_trait_name . ' does not exist',
Expand Down Expand Up @@ -1490,7 +1494,7 @@ private function analyzeTraitUse(
if ($storage->allowed_mutations < $trait_storage->allowed_mutations) {
IssueBuffer::maybeAdd(
new MutableDependency(
$storage->name . ' is marked '.Mutations::TO_ATTRIBUTE_CLASS[
$storage->name . ' is marked '.Mutations::TO_ATTRIBUTE_CLASSLIKE[
$storage->allowed_mutations
].' but ' . $fq_trait_name . ' is not',
new CodeLocation($previous_trait_analyzer ?? $this, $trait_name),
Expand Down Expand Up @@ -1862,7 +1866,7 @@ private function analyzeClassMethod(
$class_context->self,
$analyzed_method_id,
$actual_method_id,
$method_context->has_returned,
$method_context,
);
}

Expand Down Expand Up @@ -1918,8 +1922,9 @@ public static function analyzeClassMethodReturnType(
string $fq_classlike_name,
MethodIdentifier $analyzed_method_id,
MethodIdentifier $actual_method_id,
bool $did_explicitly_return,
?Context $context,
): void {
$did_explicitly_return = (bool) $context?->has_returned;
$secondary_return_type_location = null;

$actual_method_storage = $codebase->methods->getStorage($actual_method_id);
Expand Down Expand Up @@ -1984,7 +1989,7 @@ public static function analyzeClassMethodReturnType(
foreach ($overridden_method_ids as $interface_method_id) {
$interface_class = $interface_method_id->fq_class_name;

if (!$codebase->classlikes->interfaceExists($interface_class)) {
if (!$codebase->classlikes->interfaceExists($interface_class, null, $context)) {
continue;
}

Expand Down Expand Up @@ -2061,7 +2066,7 @@ private function checkImplementedInterfaces(
$codebase->analyzer->addNodeReference(
$this->getFilePath(),
$interface_name,
$codebase->classlikes->interfaceExists($fq_interface_name)
$codebase->classlikes->interfaceExists($fq_interface_name, null, $class_context)
? $fq_interface_name
: '*'
. ($interface_name instanceof PhpParser\Node\Name\FullyQualified
Expand All @@ -2076,8 +2081,7 @@ private function checkImplementedInterfaces(
$this,
$fq_interface_name,
$interface_location,
null,
null,
$class_context,
$this->getSuppressedIssues(),
) === false) {
return false;
Expand Down Expand Up @@ -2212,7 +2216,7 @@ private function checkImplementedInterfaces(
) {
IssueBuffer::maybeAdd(
new ImmutableDependency(
$fq_interface_name . ' is marked with @'.Mutations::TO_ATTRIBUTE_CLASS[
$fq_interface_name . ' is marked with @'.Mutations::TO_ATTRIBUTE_CLASSLIKE[
$interface_storage->allowed_mutations
].', but '
. $fq_class_name . ' is not',
Expand Down Expand Up @@ -2370,8 +2374,7 @@ private function checkParentClass(
$this->getSource(),
$parent_fq_class_name,
$parent_reference_location,
null,
null,
$class_context,
$storage->suppressed_issues + $this->getSuppressedIssues(),
) === false) {
return;
Expand Down Expand Up @@ -2460,7 +2463,7 @@ private function checkParentClass(
) {
IssueBuffer::maybeAdd(
new ImmutableDependency(
$parent_fq_class_name . ' is marked with @'.Mutations::TO_ATTRIBUTE_CLASS[
$parent_fq_class_name . ' is marked with @'.Mutations::TO_ATTRIBUTE_CLASSLIKE[
$parent_class_storage->allowed_mutations
].', but '
. $fq_class_name . ' is not',
Expand All @@ -2473,7 +2476,7 @@ private function checkParentClass(
) {
IssueBuffer::maybeAdd(
new MutableDependency(
$fq_class_name . ' is marked with @'.Mutations::TO_ATTRIBUTE_CLASS[
$fq_class_name . ' is marked with @'.Mutations::TO_ATTRIBUTE_CLASSLIKE[
$storage->allowed_mutations
].', but parent class '
. $parent_fq_class_name . ' is not',
Expand All @@ -2488,7 +2491,7 @@ private function checkParentClass(
$codebase->analyzer->addNodeReference(
$this->getFilePath(),
$extended_class,
$codebase->classlikes->classExists($parent_fq_class_name)
$codebase->classlikes->classExists($parent_fq_class_name, null, $class_context)
? $parent_fq_class_name
: '*'
. ($extended_class instanceof PhpParser\Node\Name\FullyQualified
Expand Down
14 changes: 5 additions & 9 deletions src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,7 @@ public static function checkFullyQualifiedClassLikeName(
StatementsSource $statements_source,
string $fq_class_name,
CodeLocation $code_location,
?string $calling_fq_class_name,
?string $calling_method_id,
?Context $context,
array $suppressed_issues,
?ClassLikeNameOptions $options = null,
bool $check_classes = true,
Expand Down Expand Up @@ -265,22 +264,19 @@ public static function checkFullyQualifiedClassLikeName(
$class_exists = $codebase->classlikes->classExists(
$fq_class_name,
!$options->inferred ? $code_location : null,
$calling_fq_class_name,
$calling_method_id,
$context,
);

$interface_exists = $codebase->classlikes->interfaceExists(
$fq_class_name,
!$options->inferred ? $code_location : null,
$calling_fq_class_name,
$calling_method_id,
$context,
);

$enum_exists = $codebase->classlikes->enumExists(
$fq_class_name,
!$options->inferred ? $code_location : null,
$calling_fq_class_name,
$calling_method_id,
$context,
);

if (!$class_exists
Expand All @@ -290,7 +286,7 @@ public static function checkFullyQualifiedClassLikeName(
if (!$check_classes) {
return null;
}
if (!$options->allow_trait || !$codebase->classlikes->traitExists($fq_class_name, $code_location)) {
if (!$options->allow_trait || !$codebase->classlikes->traitExists($fq_class_name, !$options->inferred ? $code_location : null, $context)) {
if ($options->from_docblock) {
if (IssueBuffer::accepts(
new UndefinedDocblockClass(
Expand Down
3 changes: 1 addition & 2 deletions src/Psalm/Internal/Analyzer/FileAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,7 @@ public function analyze(
$this->getSource(),
$fq_source_classlike,
$location,
null,
null,
$file_context,
$this->suppressed_issues,
new ClassLikeNameOptions(
true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ public static function checkReturnType(
false,
false,
false,
$context->calling_method_id,
$context,
);

return null;
Expand Down
Loading
Loading