Repository navigation
Fix six ways the result cache went stale #6357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b7cb686
Put the PHP version the analysis targets into the result cache metadata
ondrejmirtes 41e0a0a
Compare against an empty list when a file has no cached exported nodes
ondrejmirtes c20d79c
Record a dependency on a class named by a string
ondrejmirtes 8fc53e5
Guard the class an imported type alias comes from like every other tag
ondrejmirtes ee42a97
Let a rule declare that its error depends on a file, and watch includ…
ondrejmirtes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev
Previous commit
Let a rule declare that its error depends on a file, and watch includ…
…ed files The file named in a require() is not analysed, not scanned and not autoloadable, so no hash of it existed anywhere and neither direction of the check was noticed: deleting it kept the cached clean result, and creating it kept the cached error. It holds no symbol to reflect, and the dependency graph is built out of reflected symbols, so nothing about it could be recorded. Two things were missing, and both are about a path rather than a symbol. RuleErrorBuilder::fileDependency() lets a rule say that its error depends on a file, through the new FileDependenciesRuleError interface and the rule error classes generated for it. FileAnalyser records those paths as ordinary file dependencies, so any rule whose verdict comes from outside the analysed file - a path that must exist, a data file read during analysis - can be invalidated properly. RequireFileExistsRule declares every path the include could have resolved to, and creating any one of them clears the error. The other direction needs no error to hang on: when the file is there, the rule says nothing, and the include still has to be watched so that deleting it is noticed. DependencyResolver records the resolved path for every include, and NodeDependencies carries such paths next to the reflections. The path resolution both of them need - the working directory, the include path, the directory of the analysed file, with the trait case - moved into IncludedFilePathResolver. A watched path that does not exist is recorded with an empty hash, which no real hash can be, so the cache can hold it without reading it. restore() re-analyses the files naming it when it appears, keeps watching while it stays missing, and keeps the deletion behaviour it already had. e2e/result-cache-require-deleted covers both directions.
- Loading branch information
commit ee42a974a7e00162aa8388bd3407fbd6065f714f
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| return []; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| parameters: | ||
| level: 0 | ||
| tmpDir: tmp | ||
| paths: | ||
| - src |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace ResultCacheE2ERequireDeleted; | ||
|
|
||
| class Foo | ||
| { | ||
|
|
||
| public function load(): mixed | ||
| { | ||
| return require 'config/app.php'; | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| * | ||
| !.* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace PHPStan\File; | ||
|
|
||
| use PHPStan\Analyser\Scope; | ||
| use PHPStan\DependencyInjection\AutowiredParameter; | ||
| use PHPStan\DependencyInjection\AutowiredService; | ||
| use function array_merge; | ||
| use function array_values; | ||
| use function dirname; | ||
| use function explode; | ||
| use function get_include_path; | ||
| use const PATH_SEPARATOR; | ||
|
|
||
| /** | ||
| * The absolute paths an include/require of a given path could resolve to, in the order PHP would try | ||
| * them. `stream_resolve_include_path()` cannot be used: it resolves against the running script, and | ||
| * what matters is the analysed file. | ||
| */ | ||
| #[AutowiredService] | ||
| final class IncludedFilePathResolver | ||
| { | ||
|
|
||
| public function __construct( | ||
| #[AutowiredParameter] | ||
| private string $currentWorkingDirectory, | ||
| private FileHelper $fileHelper, | ||
| ) | ||
| { | ||
| } | ||
|
|
||
| /** | ||
| * @return list<string> | ||
| */ | ||
| public function resolve(string $path, Scope $scope): array | ||
| { | ||
| $directories = array_merge( | ||
| [$this->currentWorkingDirectory], | ||
| explode(PATH_SEPARATOR, get_include_path()), | ||
| [dirname($this->getScopeFile($scope))], | ||
| ); | ||
|
|
||
| $candidatePaths = []; | ||
| foreach ($directories as $directory) { | ||
| if ($directory === '') { | ||
| continue; | ||
| } | ||
|
|
||
| $candidatePath = (new FileHelper($directory))->absolutizePath($path); | ||
| $candidatePaths[$candidatePath] = $candidatePath; | ||
| } | ||
|
|
||
| return array_values($candidatePaths); | ||
| } | ||
|
|
||
| /** | ||
| * Both `__DIR__` and the "calling script's own directory" fallback of a relative include are | ||
| * resolved at compile time, so inside a trait they point at the file the trait is declared in - not | ||
| * at the file of the class that uses it, which is what Scope::getFile() returns in a trait context. | ||
| */ | ||
| private function getScopeFile(Scope $scope): string | ||
| { | ||
| if ($scope->isInTrait()) { | ||
| $traitFileName = $scope->getTraitReflection()->getFileName(); | ||
| if ($traitFileName !== null) { | ||
| return $this->fileHelper->normalizePath($traitFileName); | ||
| } | ||
| } | ||
|
|
||
| return $scope->getFile(); | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace PHPStan\Rules; | ||
|
|
||
| /** | ||
| * An error whose existence depends on files other than the one being analysed - a path named in the | ||
| * code that has to exist, a data file read at analysis time. The result cache watches the files | ||
| * listed here and re-analyses the file the error is reported in when one of them appears, changes or | ||
| * is deleted, which nothing else would do: the dependency graph is built from reflected symbols, and | ||
| * a path is not a symbol. | ||
| * | ||
| * Paths must be absolute and are watched whether or not they exist, so an error about a missing file | ||
| * is cleared by that file being created. | ||
| * | ||
| * @api | ||
| * @api-do-not-implement | ||
| */ | ||
| interface FileDependenciesRuleError extends RuleError | ||
| { | ||
|
|
||
| /** | ||
| * @return list<string> | ||
| */ | ||
| public function getFileDependencies(): array; | ||
|
|
||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discard this commit altogether ( Record which files hold an error from a rule built on collected data )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dropped from this PR. Reproduction moved to #6358 (the failing test on its own, no fix) and described in phpstan/phpstan#15162. This branch is also rebased on 2.2.x now that #6356 is in - the conflict was the two e2e blocks landing next to each other.