Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
D
drupal
Manage
Activity
Members
Labels
Plan
Wiki
Custom issue tracker
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Model registry
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
project
drupal
Merge requests
!10111
Closes
#3486376
Code
Review changes
Check out branch
Download
Patches
Plain diff
Open
Closes
#3486376
issue/drupal-3486376:3486376-introduce-a-return-type-will-be-added
into
11.x
Overview
0
Commits
40
Pipelines
18
Changes
4
Open
mondrake
requested to merge
issue/drupal-3486376:3486376-introduce-a-return-type-will-be-added
into
11.x
5 months ago
Overview
0
Commits
40
Pipelines
18
Changes
4
Expand
Closes
#3486376
0
0
Merge request reports
Compare
11.x
version 17
c9077e0a
1 month ago
version 16
97104cd6
2 months ago
version 15
63652800
3 months ago
version 14
fc0da349
3 months ago
version 13
7c14e65c
3 months ago
version 12
3f4b0025
5 months ago
version 11
b6be2fca
5 months ago
version 10
7deb531d
5 months ago
version 9
51068a5b
5 months ago
version 8
123a97ed
5 months ago
version 7
b95d8065
5 months ago
version 6
27b7488d
5 months ago
version 5
6db0de28
5 months ago
version 4
9bb73cbe
5 months ago
version 3
f448228d
5 months ago
version 2
71f67bed
5 months ago
version 1
38ee2679
5 months ago
11.x (HEAD)
and
latest version
latest version
e92437a9
40 commits,
1 month ago
version 17
c9077e0a
19 commits,
1 month ago
version 16
97104cd6
18 commits,
2 months ago
version 15
63652800
19 commits,
3 months ago
version 14
fc0da349
18 commits,
3 months ago
version 13
7c14e65c
16 commits,
3 months ago
version 12
3f4b0025
14 commits,
5 months ago
version 11
b6be2fca
13 commits,
5 months ago
version 10
7deb531d
12 commits,
5 months ago
version 9
51068a5b
11 commits,
5 months ago
version 8
123a97ed
10 commits,
5 months ago
version 7
b95d8065
9 commits,
5 months ago
version 6
27b7488d
8 commits,
5 months ago
version 5
6db0de28
6 commits,
5 months ago
version 4
9bb73cbe
5 commits,
5 months ago
version 3
f448228d
4 commits,
5 months ago
version 2
71f67bed
3 commits,
5 months ago
version 1
38ee2679
2 commits,
5 months ago
4 files
+
491
−
0
Inline
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
Files
4
Search (e.g. *.vue) (Ctrl+P)
core/tests/PHPStan/Rules/ReturnTypeWillBeAdded.php
0 → 100644
+
224
−
0
Options
<?php
declare
(
strict_types
=
1
);
namespace
Drupal\PHPStan\Rules
;
use
PhpParser\Node
;
use
PHPStan\Analyser\Scope
;
use
PHPStan\Node\InClassMethodNode
;
use
PHPStan\PhpDoc\ResolvedPhpDocBlock
;
use
PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode
;
use
PHPStan\Rules\Rule
;
use
PHPStan\Rules\RuleErrorBuilder
;
use
PHPStan\Type\FileTypeMapper
;
use
PHPStan\Type\VerbosityLevel
;
use
PHPStan\Type\ParserNodeTypeToPHPStanType
;
/**
* Check methods for future return type addition.
*
* @implements Rule<\PHPStan\Node\InClassMethodNode>
*
* @internal
*/
final
class
ReturnTypeWillBeAdded
implements
Rule
{
public
function
__construct
(
private
readonly
FileTypeMapper
$fileTypeMapper
,
)
{
}
/**
* {@inheritdoc}
*/
public
function
getNodeType
():
string
{
return
InClassMethodNode
::
class
;
}
/**
* {@inheritdoc}
*/
public
function
processNode
(
Node
$node
,
Scope
$scope
):
array
{
$method
=
$node
->
getMethodReflection
();
$methodName
=
$method
->
getName
();
// Skip magic methods.
if
(
$node
->
getOriginalNode
()
->
isMagic
())
{
return
[];
}
// Check that there are no duplicate '@return-type-will-be-added'
// annotations on the method.
$resolvedPhpDoc
=
$this
->
fileTypeMapper
->
getResolvedPhpDoc
(
$scope
->
getFile
(),
$scope
->
isInClass
()
?
$scope
->
getClassReflection
()
->
getName
()
:
NULL
,
$scope
->
isInTrait
()
?
$scope
->
getTraitReflection
()
->
getName
()
:
NULL
,
$methodName
,
$method
->
getDocComment
()
??
''
,
);
try
{
$returnTypeWillBeAddedTag
=
$this
->
getReturnTypeWillBeAddedTag
(
$resolvedPhpDoc
);
}
catch
(
\LogicException
$e
)
{
$message
=
sprintf
(
"%s::%s() %s"
,
$method
->
getDeclaringClass
()
->
getName
(),
$method
->
getName
(),
$e
->
getMessage
(),
);
return
[
RuleErrorBuilder
::
message
(
$message
)
->
line
(
$node
->
getStartLine
())
->
identifier
(
'drupal.returnTypeWillBeAdded'
)
->
build
(),
];
}
// Determine the native return type of the method.
$methodNativeParserReturnType
=
$node
->
getOriginalNode
()
->
getReturnType
();
// @phpstan-ignore phpstanApi.method
$methodNativeReturnType
=
$methodNativeParserReturnType
?
ParserNodeTypeToPHPStanType
::
resolve
(
$methodNativeParserReturnType
,
$scope
->
getClassReflection
())
:
NULL
;
// Determine the PhpDoc return type of the method.
$methodPhpDocReturnTag
=
$resolvedPhpDoc
->
getReturnTag
();
$methodPhpDocReturnType
=
$methodPhpDocReturnTag
?
$methodPhpDocReturnTag
->
getType
()
:
NULL
;
// Annotation @return-type-will-be-added exists, but method already has a
// native return type.
if
(
$returnTypeWillBeAddedTag
&&
$methodNativeReturnType
)
{
$message
=
sprintf
(
"%s::%s() specifies a '@return-type-will-be-added' annotation, but already has a '%s' native return type."
,
$method
->
getDeclaringClass
()
->
getName
(),
$method
->
getName
(),
$methodNativeReturnType
->
describe
(
VerbosityLevel
::
value
()),
);
return
[
RuleErrorBuilder
::
message
(
$message
)
->
line
(
$node
->
getStartLine
())
->
identifier
(
'drupal.returnTypeWillBeAdded'
)
->
build
(),
];
}
// Annotation @return-type-will-be-added exists, but no @return.
if
(
$returnTypeWillBeAddedTag
&&
$methodPhpDocReturnType
===
NULL
)
{
$message
=
sprintf
(
"%s::%s() specifies a '@return-type-will-be-added' annotation, but a '@return' annotation is missing."
,
$method
->
getDeclaringClass
()
->
getName
(),
$method
->
getName
(),
);
return
[
RuleErrorBuilder
::
message
(
$message
)
->
line
(
$node
->
getStartLine
())
->
identifier
(
'drupal.returnTypeWillBeAdded'
)
->
build
(),
];
}
// If method is declared in current class, we are done. We only continue
// checks for methods prototyped elsewhere.
$prototypeClass
=
$method
->
getPrototype
()
->
getDeclaringClass
();
if
(
$prototypeClass
==
$method
->
getDeclaringClass
())
{
return
[];
}
$prototypeMethod
=
$prototypeClass
->
getMethod
(
$methodName
,
$scope
);
$prototypeTrait
=
method_exists
(
$prototypeMethod
,
'getDeclaringTrait'
)
?
$prototypeMethod
->
getDeclaringTrait
()
:
NULL
;
// Get PHPDoc for the prototype.
$resolvedPrototypePhpDoc
=
$this
->
fileTypeMapper
->
getResolvedPhpDoc
(
$prototypeClass
->
getFileName
(),
$prototypeClass
->
getName
(),
$prototypeTrait
?
$prototypeTrait
->
getName
()
:
NULL
,
$methodName
,
$prototypeMethod
->
getDocComment
()
??
''
,
);
// Check for '@return-type-will-be-added' annotation on the prototype.
try
{
$prototypeReturnTypeWillBeAddedTag
=
$this
->
getReturnTypeWillBeAddedTag
(
$resolvedPrototypePhpDoc
);
}
catch
(
\LogicException
)
{
$prototypeReturnTypeWillBeAddedTag
=
NULL
;
// Keep going, duplicates were reported on the prototype parsing already.
}
// No annotation in prototype, return.
if
(
$prototypeReturnTypeWillBeAddedTag
==
NULL
)
{
return
[];
}
// Determine the PhpDoc return type of the method.
$prototypeMethodPhpDocReturnTag
=
$resolvedPrototypePhpDoc
->
getReturnTag
();
$prototypeMethodPhpDocReturnType
=
$prototypeMethodPhpDocReturnTag
?
$prototypeMethodPhpDocReturnTag
->
getType
()
:
NULL
;
// Annotation in prototype, method has no native return type.
if
(
$prototypeReturnTypeWillBeAddedTag
&&
$methodNativeReturnType
===
NULL
&&
$prototypeMethodPhpDocReturnType
)
{
$message
=
sprintf
(
"%s::%s() will add '%s' as a native return type declaration %s. Add the return type to the implementation now."
,
$prototypeTrait
?
$prototypeTrait
->
getName
()
:
$prototypeClass
->
getName
(),
$methodName
,
$prototypeMethodPhpDocReturnType
->
describe
(
VerbosityLevel
::
value
()),
(
string
)
$prototypeReturnTypeWillBeAddedTag
->
value
?:
'in the future'
,
);
return
[
RuleErrorBuilder
::
message
(
$message
)
->
line
(
$node
->
getStartLine
())
->
identifier
(
'drupal.returnTypeWillBeAdded'
)
->
build
(),
];
}
// Annotation in prototype, method has a diverging native return type.
if
(
$prototypeReturnTypeWillBeAddedTag
&&
$methodNativeReturnType
&&
$prototypeMethodPhpDocReturnType
&&
!
$prototypeMethodPhpDocReturnType
->
isSuperTypeOf
(
$methodNativeReturnType
)
->
yes
()
)
{
$message
=
sprintf
(
"Declaration of %s::%s(): %s must be compatible with %s::%s(): %s that will be added as a native return type declaration %s. Change the return type to the implementation now."
,
$method
->
getDeclaringClass
()
->
getName
(),
$methodName
,
$methodNativeReturnType
->
describe
(
VerbosityLevel
::
value
()),
$prototypeTrait
?
$prototypeTrait
->
getName
()
:
$prototypeClass
->
getName
(),
$methodName
,
$prototypeMethodPhpDocReturnType
->
describe
(
VerbosityLevel
::
value
()),
(
string
)
$prototypeReturnTypeWillBeAddedTag
->
value
?:
'in the future'
,
);
return
[
RuleErrorBuilder
::
message
(
$message
)
->
line
(
$node
->
getStartLine
())
->
identifier
(
'drupal.returnTypeWillBeAdded'
)
->
build
(),
];
}
return
[];
}
/**
* Returns the @return-type-will-be-added tag if existing.
*
* @param \PHPStan\PhpDoc\ResolvedPhpDocBlock $resolvedPhpDoc
* The resolved PHPDoc block.
*
* @return \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode|null
* A PHPDoc tag node containing the @return-type-will-be-added annotation.
*
* @throws \LogicException
* If multiple @return-type-will-be-added annotations are found in a single
* PHPDoc block.
*/
private
function
getReturnTypeWillBeAddedTag
(
ResolvedPhpDocBlock
$resolvedPhpDoc
):
?PhpDocTagNode
{
$tags
=
[];
foreach
(
$resolvedPhpDoc
->
getPhpDocNodes
()
as
$node
)
{
$tags
=
array_merge
(
$tags
,
$node
->
getTagsByName
(
'@return-type-will-be-added'
));
}
if
((
$tagsCount
=
count
(
$tags
))
>
1
)
{
throw
new
\LogicException
(
"'@return-type-will-be-added' annotation should only be defined once. Found
{
$tagsCount
}
instances."
);
}
return
$tags
[
0
]
??
NULL
;
}
}
Loading