From 2e9d73995c3e88406990c732469d4b4dfdad42c0 Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Mon, 24 Feb 2025 23:33:07 +0300 Subject: [PATCH 1/9] prototype: deprecated for new class calling --- src/linter/block_linter.go | 11 ++++++++++- src/linter/phpdoc_util.go | 2 +- src/linter/root.go | 11 ++++++++--- src/meta/metainfo.go | 13 +++++++++---- src/tests/checkers/deprecated_test.go | 10 +++++++--- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/linter/block_linter.go b/src/linter/block_linter.go index 3343caf69..35dcfc162 100644 --- a/src/linter/block_linter.go +++ b/src/linter/block_linter.go @@ -563,6 +563,15 @@ func (b *blockLinter) checkNew(e *ir.NewExpr) { if class.IsInterface() { b.report(e.Class, LevelError, "invalidNew", "Cannot instantiate interface %s", class.Name) } + + if class.IsDeprecated() { + if class.WithDeprecationNote() { + b.report(e, LevelNotice, "deprecated", "Try to create instance of the class %s (%s)", class.Name, class.DeprecationInfo) + } else { + b.report(e, LevelNotice, "deprecatedUntagged", "Try to create instance %s class that was marked as deprecated", class.Name) + } + } + b.walker.r.checker.CheckNameCase(e.Class, className, class.Name) } @@ -1298,7 +1307,7 @@ func (b *blockLinter) checkMethodCall(e *ir.MethodCallExpr) { } } - if call.info.Deprecated { + if call.info.IsDeprecated() { deprecation := call.info.DeprecationInfo if deprecation.WithDeprecationNote() { diff --git a/src/linter/phpdoc_util.go b/src/linter/phpdoc_util.go index 5d794927d..b8208cbcd 100644 --- a/src/linter/phpdoc_util.go +++ b/src/linter/phpdoc_util.go @@ -84,11 +84,11 @@ func (e *PHPDocErrors) pushLint(place PHPDocLocation, format string, args ...int type classPHPDocParseResult struct { properties meta.PropertiesMap methods meta.FunctionsMap + Deprecation meta.DeprecationInfo errs PHPDocErrors mixins []string packageName string internal bool - deprecated bool } func parseClassPHPDocMethod(classNode ir.Node, ctx *rootContext, result *classPHPDocParseResult, part *phpdoc.RawCommentPart) { diff --git a/src/linter/root.go b/src/linter/root.go index 0fb275f2c..b3a743bb1 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -201,9 +201,12 @@ func (d *rootWalker) EnterNode(n ir.Node) (res bool) { d.reportPHPDocErrors(doc.errs) d.handleClassDoc(doc, &cl) - if doc.deprecated { - d.Report(n, LevelNotice, "deprecated", "Has deprecated class %s", n.ClassName.Value) + // Handle attributes if any. + deprecation, ok := attributes.Deprecated(n.AttrGroups, d.ctx.st) + if ok { + doc.Deprecation.Append(deprecation) } + cl.DeprecationInfo = doc.Deprecation d.meta.Classes.Set(d.ctx.st.CurrentClass, cl) @@ -845,7 +848,9 @@ func (d *rootWalker) parseClassPHPDoc(class ir.Node, doc phpdoc.Comment) classPH case "package": parseClassPHPDocPackage(class, d.ctx.st, &result, part.(*phpdoc.PackageCommentPart)) case "deprecated": - result.deprecated = true + part := part.(*phpdoc.RawCommentPart) + result.Deprecation.Deprecated = true + result.Deprecation.Reason = part.ParamsText case "internal": result.internal = true } diff --git a/src/meta/metainfo.go b/src/meta/metainfo.go index 38f88c004..0d3388f55 100644 --- a/src/meta/metainfo.go +++ b/src/meta/metainfo.go @@ -178,15 +178,18 @@ type PropertyInfo struct { Typ types.Map AccessLevel AccessLevel Flags PropertyFlags + DeprecationInfo } func (info *PropertyInfo) IsFromAnnotation() bool { return info.Flags&PropFromAnnotation != 0 } +func (info *PropertyInfo) IsDeprecated() bool { return info.Deprecated } type ConstInfo struct { Pos ElementPosition Typ types.Map AccessLevel AccessLevel Value ConstValue + DeprecationInfo } type ClassFlags uint8 @@ -212,12 +215,14 @@ type ClassInfo struct { Mixins []string PackageInfo + DeprecationInfo } -func (info *ClassInfo) IsAbstract() bool { return info.Flags&ClassAbstract != 0 } -func (info *ClassInfo) IsFinal() bool { return info.Flags&ClassFinal != 0 } -func (info *ClassInfo) IsShape() bool { return info.Flags&ClassShape != 0 } -func (info *ClassInfo) IsInterface() bool { return info.Flags&ClassInterface != 0 } +func (info *ClassInfo) IsAbstract() bool { return info.Flags&ClassAbstract != 0 } +func (info *ClassInfo) IsFinal() bool { return info.Flags&ClassFinal != 0 } +func (info *ClassInfo) IsShape() bool { return info.Flags&ClassShape != 0 } +func (info *ClassInfo) IsInterface() bool { return info.Flags&ClassInterface != 0 } +func (info *ClassInfo) IsDeprecated() bool { return info.Deprecated } // TODO: rename it; it's not only class-related. type ClassParseState struct { diff --git a/src/tests/checkers/deprecated_test.go b/src/tests/checkers/deprecated_test.go index 336e78c18..f55f25472 100644 --- a/src/tests/checkers/deprecated_test.go +++ b/src/tests/checkers/deprecated_test.go @@ -83,14 +83,18 @@ declare(strict_types = "1"); class OldClass { /** - * @deprecated property + * @deprecated prp */ -public $prp; +public $deprecated; + +public $notDeprecated; } +$v = new OldClass(); +$v->prp; `) test.Expect = []string{ - "Has deprecated class OldClass", + "Try to create instance \\OldClass class that was marked as deprecated", "Has deprecated field in class OldClass", } test.RunAndMatch() From e81277d1ba85f0895b0e8d47f04a4a6369882a3d Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Tue, 25 Feb 2025 03:46:20 +0300 Subject: [PATCH 2/9] property call implementation --- src/linter/block_linter.go | 17 +++++++++-------- src/linter/block_utils.go | 8 +++++--- src/linter/report.go | 2 +- src/linter/root.go | 29 +++++++++++++++++++++-------- src/linter/root_checker.go | 2 +- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/linter/block_linter.go b/src/linter/block_linter.go index 35dcfc162..0fcdd74c9 100644 --- a/src/linter/block_linter.go +++ b/src/linter/block_linter.go @@ -251,13 +251,6 @@ func (b *blockLinter) checkClass(class *ir.ClassStmt) { case *ir.ClassMethodStmt: members = append(members, classMethod) b.walker.CheckParamNullability(value.Params) - case *ir.PropertyListStmt: - for _, element := range value.Doc.Parsed { - if element.Name() == "deprecated" { - b.report(stmt, LevelNotice, "deprecated", "Has deprecated field in class %s", class.ClassName.Value) - } - } - members = append(members, classOtherMember) default: members = append(members, classOtherMember) } @@ -566,7 +559,7 @@ func (b *blockLinter) checkNew(e *ir.NewExpr) { if class.IsDeprecated() { if class.WithDeprecationNote() { - b.report(e, LevelNotice, "deprecated", "Try to create instance of the class %s (%s)", class.Name, class.DeprecationInfo) + b.report(e, LevelNotice, "deprecated", "Try to create instance of the class %s that was marked as deprecated (%s)", class.Name, class.DeprecationInfo) } else { b.report(e, LevelNotice, "deprecatedUntagged", "Try to create instance %s class that was marked as deprecated", class.Name) } @@ -1414,6 +1407,14 @@ func (b *blockLinter) checkPropertyFetch(e *ir.PropertyFetchExpr) { if fetch.isFound && !fetch.isMagic && !canAccess(b.classParseState(), fetch.className, fetch.info.AccessLevel) { b.report(e.Property, LevelError, "accessLevel", "Cannot access %s property %s->%s", fetch.info.AccessLevel, fetch.className, fetch.propertyNode.Value) } + + if fetch.info.IsDeprecated() { + if fetch.info.WithDeprecationNote() { + b.report(e, LevelNotice, "deprecated", "Try to call property %s that was marked as deprecated (%s) in the class %s", fetch.propertyNode.Value, fetch.info.DeprecationInfo, fetch.className) + } else { + b.report(e, LevelNotice, "deprecatedUntagged", "Try to call property %s that was marked as deprecated in the class %s", fetch.propertyNode.Value, fetch.className) + } + } } func (b *blockLinter) checkStaticPropertyFetch(e *ir.StaticPropertyFetchExpr) { diff --git a/src/linter/block_utils.go b/src/linter/block_utils.go index d5580e6fc..eca069d5c 100644 --- a/src/linter/block_utils.go +++ b/src/linter/block_utils.go @@ -38,10 +38,12 @@ func findProperty(info *meta.Info, className, propName string) (res solver.FindP // Construct a dummy property from the magic method. p.ClassName = m.ClassName p.TraitName = m.TraitName + p.Info = meta.PropertyInfo{ - Pos: m.Info.Pos, - Typ: m.Info.Typ, - AccessLevel: m.Info.AccessLevel, + Pos: m.Info.Pos, + Typ: m.Info.Typ, + AccessLevel: m.Info.AccessLevel, + DeprecationInfo: m.Info.DeprecationInfo, } return p, true, true } diff --git a/src/linter/report.go b/src/linter/report.go index 26c247f7c..6cadf0801 100644 --- a/src/linter/report.go +++ b/src/linter/report.go @@ -578,7 +578,7 @@ g();`, { Name: "deprecatedUntagged", - Default: false, + Default: true, Quickfix: false, Comment: "Report usages of deprecated symbols if the `@deprecated` tag has no description (see `deprecated` check).", Before: `/** diff --git a/src/linter/root.go b/src/linter/root.go index b3a743bb1..ec41a3818 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -907,9 +907,9 @@ func (d *rootWalker) handleClassDoc(doc classPHPDocParseResult, cl *meta.ClassIn } } -func (d *rootWalker) parsePHPDocVar(doc phpdoc.Comment) (typesMap types.Map) { - for _, part := range doc.Parsed { - part, ok := part.(*phpdoc.TypeVarCommentPart) +func (d *rootWalker) parsePHPDocVar(doc phpdoc.Comment) (typesMap types.Map, deprecationInfo meta.DeprecationInfo) { + for _, partDoc := range doc.Parsed { + part, ok := partDoc.(*phpdoc.TypeVarCommentPart) if ok && part.Name() == "var" { converted := phpdoctypes.ToRealType(d.ctx.typeNormalizer.ClassFQNProvider(), d.config.KPHP, part.Type) moveShapesToContext(&d.ctx, converted.Shapes) @@ -917,9 +917,16 @@ func (d *rootWalker) parsePHPDocVar(doc phpdoc.Comment) (typesMap types.Map) { typesMap = types.NewMapWithNormalization(d.ctx.typeNormalizer, converted.Types) } + + rawPart, rawOk := partDoc.(*phpdoc.RawCommentPart) + + if rawOk && rawPart.Name() == "deprecated" { + deprecationInfo.Deprecated = true + deprecationInfo.Reason = rawPart.ParamsText + } } - return typesMap + return typesMap, deprecationInfo } func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { @@ -941,9 +948,14 @@ func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { } } - phpDocType := d.parsePHPDocVar(pl.Doc) + phpDocType, deprecationInfo := d.parsePHPDocVar(pl.Doc) typeHintType, _ := d.parseTypeHintNode(pl.Type) + deprecation, ok := attributes.Deprecated(pl.AttrGroups, d.ctx.st) + if ok { + deprecationInfo.Append(deprecation) + } + for _, pNode := range pl.Properties { prop := pNode.(*ir.PropertyStmt) @@ -964,9 +976,10 @@ func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { // TODO: handle duplicate property cl.Properties[nm] = meta.PropertyInfo{ - Pos: d.getElementPos(prop), - Typ: propTypes.Immutable(), - AccessLevel: accessLevel, + Pos: d.getElementPos(prop), + Typ: propTypes.Immutable(), + AccessLevel: accessLevel, + DeprecationInfo: deprecationInfo, } } diff --git a/src/linter/root_checker.go b/src/linter/root_checker.go index f7b12e0dd..cb109dd23 100644 --- a/src/linter/root_checker.go +++ b/src/linter/root_checker.go @@ -110,7 +110,7 @@ func (r *rootChecker) CheckPropertyList(pl *ir.PropertyListStmt) bool { r.walker.Report(pl, LevelNotice, "implicitModifiers", "Specify the access modifier for %s explicitly", target) } - docblockType := r.walker.parsePHPDocVar(pl.Doc) + docblockType, _ := r.walker.parsePHPDocVar(pl.Doc) r.CheckCommentMisspellings(pl, pl.Doc.Raw) r.CheckPHPDocVar(pl, pl.Doc, docblockType) From 4c60f6c04fbe217d959f9bf00e4f4bf263a0634f Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Tue, 25 Feb 2025 05:57:43 +0300 Subject: [PATCH 3/9] const call implementation --- src/linter/block_linter.go | 8 ++++++++ src/linter/root.go | 34 ++++++++++++++++++++++++++++++---- src/meta/metainfo.go | 2 ++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/linter/block_linter.go b/src/linter/block_linter.go index 0fcdd74c9..e2e0375dc 100644 --- a/src/linter/block_linter.go +++ b/src/linter/block_linter.go @@ -1456,6 +1456,14 @@ func (b *blockLinter) checkClassConstFetch(e *ir.ClassConstFetchExpr) { if fetch.isFound && !canAccess(b.classParseState(), fetch.implClassName, fetch.info.AccessLevel) { b.report(e.ConstantName, LevelError, "accessLevel", "Cannot access %s constant %s::%s", fetch.info.AccessLevel, fetch.implClassName, fetch.constName) } + + if fetch.info.IsDeprecated() { + if fetch.info.WithDeprecationNote() { + b.report(e, LevelNotice, "deprecated", "Try to call constant %s that was marked as deprecated (%s) from the class %s", fetch.constName, fetch.info.DeprecationInfo, fetch.className) + } else { + b.report(e, LevelNotice, "deprecatedUntagged", "Try to call constant %s that was marked as deprecated from the class %s", fetch.constName, fetch.className) + } + } } func (b *blockLinter) checkClassSpecialNameCase(n ir.Node, className string) { diff --git a/src/linter/root.go b/src/linter/root.go index ec41a3818..67c4ce54e 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -986,6 +986,24 @@ func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { return true } +func (d *rootWalker) parseConstPHPDoc(constList *ir.ClassConstListStmt, doc phpdoc.Comment) (deprecationInfo meta.DeprecationInfo) { + + if doc.Raw == "" { + return deprecationInfo + } + + for _, part := range doc.Parsed { + switch part.Name() { + case "deprecated": + part := part.(*phpdoc.RawCommentPart) + deprecationInfo.Deprecated = true + deprecationInfo.Reason = part.ParamsText + } + } + + return deprecationInfo +} + func (d *rootWalker) enterClassConstList(list *ir.ClassConstListStmt) bool { cl := d.getClass() accessLevel := meta.Public @@ -1002,6 +1020,13 @@ func (d *rootWalker) enterClassConstList(list *ir.ClassConstListStmt) bool { } } + deprecationInfo := d.parseConstPHPDoc(list, list.Doc) + + deprecation, ok := attributes.Deprecated(list.AttrGroups, d.ctx.st) + if ok { + deprecationInfo.Append(deprecation) + } + for _, cNode := range list.Consts { c := cNode.(*ir.ConstantStmt) @@ -1013,10 +1038,11 @@ func (d *rootWalker) enterClassConstList(list *ir.ClassConstListStmt) bool { // TODO: handle duplicate constant cl.Constants[nm] = meta.ConstInfo{ - Pos: d.getElementPos(c), - Typ: typ.Immutable(), - AccessLevel: accessLevel, - Value: value, + Pos: d.getElementPos(c), + Typ: typ.Immutable(), + AccessLevel: accessLevel, + Value: value, + DeprecationInfo: deprecationInfo, } } diff --git a/src/meta/metainfo.go b/src/meta/metainfo.go index 0d3388f55..ea5cf34c6 100644 --- a/src/meta/metainfo.go +++ b/src/meta/metainfo.go @@ -192,6 +192,8 @@ type ConstInfo struct { DeprecationInfo } +func (info *ConstInfo) IsDeprecated() bool { return info.Deprecated } + type ClassFlags uint8 const ( From 92c48036a82203eaaddd3206daff1e9a04e2d03d Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Wed, 26 Feb 2025 16:38:00 +0300 Subject: [PATCH 4/9] new tests and cache update --- src/linter/cache.go | 5 +- src/linter/cache_test.go | 4 +- src/linter/root.go | 3 +- src/tests/checkers/deprecated_test.go | 136 +++++++++++++++++- .../golden/testdata/flysystem/golden.txt | 18 +-- .../golden/testdata/inflector/golden.txt | 3 - 6 files changed, 148 insertions(+), 21 deletions(-) diff --git a/src/linter/cache.go b/src/linter/cache.go index bf66e43b8..89ccd3343 100644 --- a/src/linter/cache.go +++ b/src/linter/cache.go @@ -52,8 +52,9 @@ import ( // 53 - added DeprecationInfo for functions and methods and support for some attributes // 54 - forced cache version invalidation due to the #1165 // 55 - updated go version 1.16 -> 1.21 -// 56 - added isVariadic to meta.FuncInfo -const cacheVersion = 56 +// 56 - added isVariadic to meta.FuncInfo +// 57 - added DeprecationInfo for property and const +const cacheVersion = 57 var ( errWrongVersion = errors.New("Wrong cache version") diff --git a/src/linter/cache_test.go b/src/linter/cache_test.go index 8281dc48d..773966a40 100644 --- a/src/linter/cache_test.go +++ b/src/linter/cache_test.go @@ -149,7 +149,7 @@ main(); // // If cache encoding changes, there is a very high chance that // encoded data lengh will change as well. - wantLen := 5952 + wantLen := 6069 haveLen := buf.Len() if haveLen != wantLen { t.Errorf("cache len mismatch:\nhave: %d\nwant: %d", haveLen, wantLen) @@ -158,7 +158,7 @@ main(); // 2. Check cache "strings" hash. // // It catches new fields in cached types, field renames and encoding of additional named attributes. - wantStrings := "690e77c94ecdd7878de0bf6f6881d786cf1fafa4588f7905f54d700646c4952aad359008ae2dcddb1c7f29163ecee62355d525672090ac30257bc414f690006f" + wantStrings := "339e0a8348fcf8f5060b8ce3534f97be9af03fe462ca06eba27ab83488efc5e5d5813f4fb227535997f0c4a99253f57042876fdead6f058a923219c99593f3b6" haveStrings := collectCacheStrings(buf.String()) if haveStrings != wantStrings { t.Errorf("cache strings mismatch:\nhave: %q\nwant: %q", haveStrings, wantStrings) diff --git a/src/linter/root.go b/src/linter/root.go index 67c4ce54e..c16c5150a 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -993,8 +993,7 @@ func (d *rootWalker) parseConstPHPDoc(constList *ir.ClassConstListStmt, doc phpd } for _, part := range doc.Parsed { - switch part.Name() { - case "deprecated": + if part.Name() == "deprecated" { part := part.(*phpdoc.RawCommentPart) deprecationInfo.Deprecated = true deprecationInfo.Reason = part.ParamsText diff --git a/src/tests/checkers/deprecated_test.go b/src/tests/checkers/deprecated_test.go index f55f25472..7755ecb37 100644 --- a/src/tests/checkers/deprecated_test.go +++ b/src/tests/checkers/deprecated_test.go @@ -91,11 +91,141 @@ public $notDeprecated; } $v = new OldClass(); -$v->prp; +echo $v->deprecated; `) test.Expect = []string{ - "Try to create instance \\OldClass class that was marked as deprecated", - "Has deprecated field in class OldClass", + "Try to create instance of the class \\OldClass that was marked as deprecated", + "Try to call property deprecated that was marked as deprecated", + } + test.RunAndMatch() +} + +func TestDeprecatedClassPropertyConstants(t *testing.T) { + test := linttest.NewSuite(t) + test.AddFile(`a; + +echo SomeClass::OLD_ONE; + +echo SomeClass::OLD_TWO; +echo SomeClass::abc(); + +`) + test.Expect = []string{ + "Try to create instance of the class \\SomeClass that was marked as deprecated", + "Try to call property a that was marked as deprecated in the class \\SomeClass", + "Try to call constant OLD_ONE that was marked as deprecated", + "Try to call constant OLD_TWO that was marked as deprecated", + "Call to deprecated static method \\SomeClass::abc()", + } + test.RunAndMatch() +} + +func TestDeprecatedCallWithChainPropertyAndExplicitType(t *testing.T) { + test := linttest.NewSuite(t) + test.AddFile(`bar = new Bar(); +$a->bar->baz = new Baz(); + +echo $a->bar->baz->value; + +`) + test.Expect = []string{ + "Missing PHPDoc for \\Baz::getValue public", + "Try to call property value that was marked as deprecated", + } + test.RunAndMatch() +} + +// TODO: This test must fail after realisation CFG and DFG, because we can type inference correctly +func TestDeprecatedCallWithChainPropertyAndWithoutExplicitType(t *testing.T) { + test := linttest.NewSuite(t) + test.AddFile(`bar = new Bar(); +$a->bar->baz = new Baz(); + +echo $a->bar->baz->value; +`) + test.Expect = []string{ + "Missing PHPDoc for \\Baz::getValue public method", } test.RunAndMatch() } diff --git a/src/tests/golden/testdata/flysystem/golden.txt b/src/tests/golden/testdata/flysystem/golden.txt index bbd2f80e2..c9f0a517b 100644 --- a/src/tests/golden/testdata/flysystem/golden.txt +++ b/src/tests/golden/testdata/flysystem/golden.txt @@ -61,12 +61,9 @@ MAYBE missingPhpdoc: Missing PHPDoc for \League\Flysystem\Adapter\Polyfill\Str MAYBE missingPhpdoc: Missing PHPDoc for \League\Flysystem\Adapter\Polyfill\StreamedWritingTrait::update public method at testdata/flysystem/src/Adapter/Polyfill/StreamedWritingTrait.php:59 abstract public function update($pash, $contents, Config $config); ^^^^^^ -MAYBE deprecated: Has deprecated class Directory at testdata/flysystem/src/Directory.php:8 -class Directory extends Handler -^ -MAYBE deprecated: Has deprecated class File at testdata/flysystem/src/File.php:8 -class File extends Handler -^ +MAYBE deprecatedUntagged: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/File.php:140 + return new File($this->filesystem, $newpath); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/FileExistsException.php:21 public function __construct($path, $code = 0, BaseException $previous = null) ^^^^^^^^^^^^^ @@ -94,15 +91,18 @@ MAYBE typeHint: Specify the type for the parameter $config in PHPDoc, 'array' MAYBE typeHint: Specify the type for the parameter $config in PHPDoc, 'array' type hint too generic at testdata/flysystem/src/Filesystem.php:257 public function createDir($dirname, array $config = []) ^^^^^^^^^ +MAYBE deprecatedUntagged: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/Filesystem.php:368 + $handler = ($metadata && $metadata['type'] === 'file') ? new File($this, $path) : new Directory($this, $path); + ^^^^^^^^^^^^^^^^^^^^^^ +MAYBE deprecatedUntagged: Try to create instance \League\Flysystem\Directory class that was marked as deprecated at testdata/flysystem/src/Filesystem.php:368 + $handler = ($metadata && $metadata['type'] === 'file') ? new File($this, $path) : new Directory($this, $path); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/Filesystem.php:362 public function get($path, Handler $handler = null) ^^^^^^^ WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/FilesystemInterface.php:274 public function get($path, Handler $handler = null); ^^^^^^^ -MAYBE deprecated: Has deprecated class Handler at testdata/flysystem/src/Handler.php:10 -abstract class Handler -^ WARNING unused: Variable $e is unused (use $_ to ignore this inspection or specify --unused-var-regex flag) at testdata/flysystem/src/Handler.php:129 } catch (BadMethodCallException $e) { ^^ diff --git a/src/tests/golden/testdata/inflector/golden.txt b/src/tests/golden/testdata/inflector/golden.txt index c975a6be1..92cc75ead 100644 --- a/src/tests/golden/testdata/inflector/golden.txt +++ b/src/tests/golden/testdata/inflector/golden.txt @@ -1,6 +1,3 @@ -MAYBE deprecated: Has deprecated class Inflector at testdata/inflector/lib/Doctrine/Common/Inflector/Inflector.php:33 -final class Inflector -^ WARNING errorSilence: Don't use @, silencing errors is bad practice at testdata/inflector/lib/Doctrine/Common/Inflector/Inflector.php:54 @trigger_error(sprintf('The "%s" method is deprecated and will be dropped in Doctrine Inflector 3.0. Please update to the new Inflector API.', __METHOD__), E_USER_DEPRECATED); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From fddb5712bdd5ad2bb026e9583850ffe8bd0aa3f3 Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Tue, 18 Mar 2025 19:03:59 +0300 Subject: [PATCH 5/9] review fix --- src/linter/block_linter.go | 12 +++---- src/linter/phpdoc_util.go | 7 +++- src/linter/root.go | 35 +++++++++---------- src/linter/root_checker.go | 8 ++--- .../golden/testdata/flysystem/golden.txt | 8 ++--- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/linter/block_linter.go b/src/linter/block_linter.go index e2e0375dc..804e0e25c 100644 --- a/src/linter/block_linter.go +++ b/src/linter/block_linter.go @@ -561,7 +561,7 @@ func (b *blockLinter) checkNew(e *ir.NewExpr) { if class.WithDeprecationNote() { b.report(e, LevelNotice, "deprecated", "Try to create instance of the class %s that was marked as deprecated (%s)", class.Name, class.DeprecationInfo) } else { - b.report(e, LevelNotice, "deprecatedUntagged", "Try to create instance %s class that was marked as deprecated", class.Name) + b.report(e, LevelNotice, "deprecated", "Try to create instance %s class that was marked as deprecated", class.Name) } } @@ -982,7 +982,7 @@ func (b *blockLinter) checkDeprecatedFunctionCall(e *ir.FunctionCallExpr, call * return } - b.report(e.Function, LevelNotice, "deprecatedUntagged", "Call to deprecated function %s", utils.NameNodeToString(e.Function)) + b.report(e.Function, LevelNotice, "deprecated", "Call to deprecated function %s", utils.NameNodeToString(e.Function)) } func (b *blockLinter) checkFunctionAvailability(e *ir.FunctionCallExpr, call *funcCallInfo) { @@ -1307,7 +1307,7 @@ func (b *blockLinter) checkMethodCall(e *ir.MethodCallExpr) { b.report(e.Method, LevelNotice, "deprecated", "Call to deprecated method {%s}->%s() (%s)", call.methodCallerType, call.methodName, deprecation) } else { - b.report(e.Method, LevelNotice, "deprecatedUntagged", "Call to deprecated method {%s}->%s()", + b.report(e.Method, LevelNotice, "deprecated", "Call to deprecated method {%s}->%s()", call.methodCallerType, call.methodName) } } @@ -1351,7 +1351,7 @@ func (b *blockLinter) checkStaticCall(e *ir.StaticCallExpr) { b.report(e.Call, LevelNotice, "deprecated", "Call to deprecated static method %s::%s() (%s)", call.className, call.methodName, deprecation) } else { - b.report(e.Call, LevelNotice, "deprecatedUntagged", "Call to deprecated static method %s::%s()", + b.report(e.Call, LevelNotice, "deprecated", "Call to deprecated static method %s::%s()", call.className, call.methodName) } } @@ -1412,7 +1412,7 @@ func (b *blockLinter) checkPropertyFetch(e *ir.PropertyFetchExpr) { if fetch.info.WithDeprecationNote() { b.report(e, LevelNotice, "deprecated", "Try to call property %s that was marked as deprecated (%s) in the class %s", fetch.propertyNode.Value, fetch.info.DeprecationInfo, fetch.className) } else { - b.report(e, LevelNotice, "deprecatedUntagged", "Try to call property %s that was marked as deprecated in the class %s", fetch.propertyNode.Value, fetch.className) + b.report(e, LevelNotice, "deprecated", "Try to call property %s that was marked as deprecated in the class %s", fetch.propertyNode.Value, fetch.className) } } } @@ -1461,7 +1461,7 @@ func (b *blockLinter) checkClassConstFetch(e *ir.ClassConstFetchExpr) { if fetch.info.WithDeprecationNote() { b.report(e, LevelNotice, "deprecated", "Try to call constant %s that was marked as deprecated (%s) from the class %s", fetch.constName, fetch.info.DeprecationInfo, fetch.className) } else { - b.report(e, LevelNotice, "deprecatedUntagged", "Try to call constant %s that was marked as deprecated from the class %s", fetch.constName, fetch.className) + b.report(e, LevelNotice, "deprecated", "Try to call constant %s that was marked as deprecated from the class %s", fetch.constName, fetch.className) } } } diff --git a/src/linter/phpdoc_util.go b/src/linter/phpdoc_util.go index b8208cbcd..51beb21a5 100644 --- a/src/linter/phpdoc_util.go +++ b/src/linter/phpdoc_util.go @@ -84,13 +84,18 @@ func (e *PHPDocErrors) pushLint(place PHPDocLocation, format string, args ...int type classPHPDocParseResult struct { properties meta.PropertiesMap methods meta.FunctionsMap - Deprecation meta.DeprecationInfo + deprecation meta.DeprecationInfo errs PHPDocErrors mixins []string packageName string internal bool } +type variablePHPDocParseResult struct { + typesMap types.Map + deprecation meta.DeprecationInfo +} + func parseClassPHPDocMethod(classNode ir.Node, ctx *rootContext, result *classPHPDocParseResult, part *phpdoc.RawCommentPart) { // The syntax is: // @method [[static] return type] [name]([[type] [parameter]<, ...>]) [] diff --git a/src/linter/root.go b/src/linter/root.go index c16c5150a..809f95139 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -204,9 +204,9 @@ func (d *rootWalker) EnterNode(n ir.Node) (res bool) { // Handle attributes if any. deprecation, ok := attributes.Deprecated(n.AttrGroups, d.ctx.st) if ok { - doc.Deprecation.Append(deprecation) + doc.deprecation.Append(deprecation) } - cl.DeprecationInfo = doc.Deprecation + cl.DeprecationInfo = doc.deprecation d.meta.Classes.Set(d.ctx.st.CurrentClass, cl) @@ -849,8 +849,8 @@ func (d *rootWalker) parseClassPHPDoc(class ir.Node, doc phpdoc.Comment) classPH parseClassPHPDocPackage(class, d.ctx.st, &result, part.(*phpdoc.PackageCommentPart)) case "deprecated": part := part.(*phpdoc.RawCommentPart) - result.Deprecation.Deprecated = true - result.Deprecation.Reason = part.ParamsText + result.deprecation.Deprecated = true + result.deprecation.Reason = part.ParamsText case "internal": result.internal = true } @@ -907,26 +907,25 @@ func (d *rootWalker) handleClassDoc(doc classPHPDocParseResult, cl *meta.ClassIn } } -func (d *rootWalker) parsePHPDocVar(doc phpdoc.Comment) (typesMap types.Map, deprecationInfo meta.DeprecationInfo) { +func (d *rootWalker) parseVarPHPDoc(doc phpdoc.Comment) variablePHPDocParseResult { + var result variablePHPDocParseResult + for _, partDoc := range doc.Parsed { - part, ok := partDoc.(*phpdoc.TypeVarCommentPart) - if ok && part.Name() == "var" { + if part, ok := partDoc.(*phpdoc.TypeVarCommentPart); ok && part.Name() == "var" { converted := phpdoctypes.ToRealType(d.ctx.typeNormalizer.ClassFQNProvider(), d.config.KPHP, part.Type) moveShapesToContext(&d.ctx, converted.Shapes) d.handleClosuresFromDoc(converted.Closures) - typesMap = types.NewMapWithNormalization(d.ctx.typeNormalizer, converted.Types) + result.typesMap = types.NewMapWithNormalization(d.ctx.typeNormalizer, converted.Types) } - rawPart, rawOk := partDoc.(*phpdoc.RawCommentPart) - - if rawOk && rawPart.Name() == "deprecated" { - deprecationInfo.Deprecated = true - deprecationInfo.Reason = rawPart.ParamsText + if rawPart, ok := partDoc.(*phpdoc.RawCommentPart); ok && rawPart.Name() == "deprecated" { + result.deprecation.Deprecated = true + result.deprecation.Reason = rawPart.ParamsText } } - return typesMap, deprecationInfo + return result } func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { @@ -948,12 +947,12 @@ func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { } } - phpDocType, deprecationInfo := d.parsePHPDocVar(pl.Doc) + varPhpDocRes := d.parseVarPHPDoc(pl.Doc) typeHintType, _ := d.parseTypeHintNode(pl.Type) deprecation, ok := attributes.Deprecated(pl.AttrGroups, d.ctx.st) if ok { - deprecationInfo.Append(deprecation) + varPhpDocRes.deprecation.Append(deprecation) } for _, pNode := range pl.Properties { @@ -964,7 +963,7 @@ func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { // We need to clone the types, because otherwise, if several // properties are written in one definition, and null was // assigned to the first, then all properties become nullable. - propTypes := phpDocType.Clone().Append(typeHintType) + propTypes := varPhpDocRes.typesMap.Clone().Append(typeHintType) if prop.Expr != nil { propTypes = propTypes.Append(solver.ExprTypeLocal(d.scope(), d.ctx.st, prop.Expr)) @@ -979,7 +978,7 @@ func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { Pos: d.getElementPos(prop), Typ: propTypes.Immutable(), AccessLevel: accessLevel, - DeprecationInfo: deprecationInfo, + DeprecationInfo: varPhpDocRes.deprecation, } } diff --git a/src/linter/root_checker.go b/src/linter/root_checker.go index cb109dd23..c3c985228 100644 --- a/src/linter/root_checker.go +++ b/src/linter/root_checker.go @@ -110,13 +110,13 @@ func (r *rootChecker) CheckPropertyList(pl *ir.PropertyListStmt) bool { r.walker.Report(pl, LevelNotice, "implicitModifiers", "Specify the access modifier for %s explicitly", target) } - docblockType, _ := r.walker.parsePHPDocVar(pl.Doc) + varPhpDocRes := r.walker.parseVarPHPDoc(pl.Doc) r.CheckCommentMisspellings(pl, pl.Doc.Raw) - r.CheckPHPDocVar(pl, pl.Doc, docblockType) + r.CheckPHPDocVar(pl, pl.Doc, varPhpDocRes.typesMap) typeHintType, ok := r.walker.parseTypeHintNode(pl.Type) - if ok && !types.TypeHintHasMoreAccurateType(typeHintType, docblockType) { + if ok && !types.TypeHintHasMoreAccurateType(typeHintType, varPhpDocRes.typesMap) { r.walker.Report(pl, LevelNotice, "typeHint", "Specify the type for the property in PHPDoc, 'array' type hint too generic") } @@ -128,7 +128,7 @@ func (r *rootChecker) CheckPropertyList(pl *ir.PropertyListStmt) bool { // We need to clone the types, because otherwise, if several // properties are written in one definition, and null was // assigned to the first, then all properties become nullable. - propTypes := docblockType.Clone().Append(typeHintType) + propTypes := varPhpDocRes.typesMap.Clone().Append(typeHintType) r.CheckAssignNullToNotNullableProperty(prop, propTypes) } diff --git a/src/tests/golden/testdata/flysystem/golden.txt b/src/tests/golden/testdata/flysystem/golden.txt index c9f0a517b..62326f955 100644 --- a/src/tests/golden/testdata/flysystem/golden.txt +++ b/src/tests/golden/testdata/flysystem/golden.txt @@ -61,7 +61,7 @@ MAYBE missingPhpdoc: Missing PHPDoc for \League\Flysystem\Adapter\Polyfill\Str MAYBE missingPhpdoc: Missing PHPDoc for \League\Flysystem\Adapter\Polyfill\StreamedWritingTrait::update public method at testdata/flysystem/src/Adapter/Polyfill/StreamedWritingTrait.php:59 abstract public function update($pash, $contents, Config $config); ^^^^^^ -MAYBE deprecatedUntagged: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/File.php:140 +MAYBE deprecated: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/File.php:140 return new File($this->filesystem, $newpath); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/FileExistsException.php:21 @@ -91,10 +91,10 @@ MAYBE typeHint: Specify the type for the parameter $config in PHPDoc, 'array' MAYBE typeHint: Specify the type for the parameter $config in PHPDoc, 'array' type hint too generic at testdata/flysystem/src/Filesystem.php:257 public function createDir($dirname, array $config = []) ^^^^^^^^^ -MAYBE deprecatedUntagged: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/Filesystem.php:368 +MAYBE deprecated: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/Filesystem.php:368 $handler = ($metadata && $metadata['type'] === 'file') ? new File($this, $path) : new Directory($this, $path); ^^^^^^^^^^^^^^^^^^^^^^ -MAYBE deprecatedUntagged: Try to create instance \League\Flysystem\Directory class that was marked as deprecated at testdata/flysystem/src/Filesystem.php:368 +MAYBE deprecated: Try to create instance \League\Flysystem\Directory class that was marked as deprecated at testdata/flysystem/src/Filesystem.php:368 $handler = ($metadata && $metadata['type'] === 'file') ? new File($this, $path) : new Directory($this, $path); ^^^^^^^^^^^^^^^^^^^^^^^^^^^ WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/Filesystem.php:362 @@ -112,7 +112,7 @@ WARNING notExplicitNullableParam: parameter with null default value should be ex WARNING unused: Variable $e is unused (use $_ to ignore this inspection or specify --unused-var-regex flag) at testdata/flysystem/src/MountManager.php:275 } catch (PluginNotFoundException $e) { ^^ -MAYBE deprecatedUntagged: Call to deprecated method {\League\Flysystem\FilesystemInterface}->get() at testdata/flysystem/src/MountManager.php:646 +MAYBE deprecated: Call to deprecated method {\League\Flysystem\FilesystemInterface}->get() at testdata/flysystem/src/MountManager.php:646 return $this->getFilesystem($prefix)->get($path); ^^^ WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/MountManager.php:642 From 5a8545b3982112ab8f483e82bda7f0bfd8c10a6f Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Tue, 18 Mar 2025 19:04:44 +0300 Subject: [PATCH 6/9] doc update --- docs/checkers_doc.md | 31 +------------------------------ src/linter/report.go | 19 ------------------- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/docs/checkers_doc.md b/docs/checkers_doc.md index 587630d20..6b758d19d 100644 --- a/docs/checkers_doc.md +++ b/docs/checkers_doc.md @@ -4,7 +4,7 @@ | Total checks | Checks enabled by default | Disabled checks by default | Autofixable checks | | ------------ | ------------------------- | -------------------------- | ------------------ | -| 107 | 89 | 18 | 15 | +| 106 | 89 | 17 | 15 | ## Table of contents - Enabled by default @@ -102,7 +102,6 @@ - [`arrayAccess` checker](#arrayaccess-checker) - [`classMembersOrder` checker](#classmembersorder-checker) - [`complexity` checker](#complexity-checker) - - [`deprecatedUntagged` checker](#deprecateduntagged-checker) - [`errorSilence` checker](#errorsilence-checker) - [`getTypeMisUse` checker (autofixable)](#gettypemisuse-checker) - [`langDeprecated` checker](#langdeprecated-checker) @@ -2029,34 +2028,6 @@ function checkRights() {


-### `deprecatedUntagged` checker - -#### Description - -Report usages of deprecated symbols if the `@deprecated` tag has no description (see `deprecated` check). - -#### Non-compliant code: -```php -/** - * @deprecated - */ -function f() {} - -f(); -``` - -#### Compliant code: -```php -/** - * @deprecated - */ -function f() {} - -g(); -``` -


- - ### `errorSilence` checker #### Description diff --git a/src/linter/report.go b/src/linter/report.go index 6cadf0801..6483ccd88 100644 --- a/src/linter/report.go +++ b/src/linter/report.go @@ -573,25 +573,6 @@ f();`, */ function f() {} -g();`, - }, - - { - Name: "deprecatedUntagged", - Default: true, - Quickfix: false, - Comment: "Report usages of deprecated symbols if the `@deprecated` tag has no description (see `deprecated` check).", - Before: `/** - * @deprecated - */ -function f() {} - -f();`, - After: `/** - * @deprecated - */ -function f() {} - g();`, }, From cbf2a14f1a79c257e5420c857b14f74a1524a632 Mon Sep 17 00:00:00 2001 From: Danil Ovchinnikov Date: Fri, 21 Mar 2025 03:14:05 +0300 Subject: [PATCH 7/9] minor fix --- src/linter/cache.go | 74 ++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/linter/cache.go b/src/linter/cache.go index 89ccd3343..c16c91985 100644 --- a/src/linter/cache.go +++ b/src/linter/cache.go @@ -17,43 +17,43 @@ import ( // // Version log: // -// 27 - added Static field to meta.FuncInfo -// 28 - array type parsed as mixed[] -// 29 - updated type inference for ClassConstFetch -// 30 - resolve ClassConstFetch to a wrapped type string -// 31 - fixed plus operator type inference for arrays -// 32 - replaced Static:bool with Flags:uint8 in meta.FuncInfo -// 33 - support parsing of array and list -// 34 - support parsing of ?ClassName as "ClassName|null" -// 35 - added Flags:uint8 to meta.ClassInfo -// 36 - added FuncAbstract bit to FuncFlags -// added FuncFinal bit to FuncFlags -// added ClassFinal bit to ClassFlags -// FuncInfo now stores original function name -// ClassInfo now stores original class name -// 37 - added ClassShape bit to ClassFlags -// changed meta.scopeVar bool fields representation -// 38 - replaced TypesMap.immutable:bool with flags:uint8. -// added mapPrecise flag to mark precise type maps. -// 39 - added new field Value in ConstantInfo -// 40 - changed string const value storage (no quotes) -// 41 - const-folding affected const definition values -// 42 - bool-typed consts are now stored in meta info -// 43 - define'd const values stored in cache -// 44 - rename ConstantInfo => ConstInfo -// 45 - added Mixins field to meta.ClassInfo -// 46 - changed the way of inferring the return type of functions and methods -// 47 - forced cache version invalidation due to the #921 -// 48 - renamed meta.TypesMap to types.Map; this affects gob encoding -// 49 - for shape, names are now generated using the keys that make up this shape -// 50 - added Flags field for meta.PropertyInfo -// 51 - added anonymous classes -// 52 - renamed all PhpDoc and Phpdoc with PHPDoc -// 53 - added DeprecationInfo for functions and methods and support for some attributes -// 54 - forced cache version invalidation due to the #1165 -// 55 - updated go version 1.16 -> 1.21 -// 56 - added isVariadic to meta.FuncInfo -// 57 - added DeprecationInfo for property and const +// 27 - added Static field to meta.FuncInfo +// 28 - array type parsed as mixed[] +// 29 - updated type inference for ClassConstFetch +// 30 - resolve ClassConstFetch to a wrapped type string +// 31 - fixed plus operator type inference for arrays +// 32 - replaced Static:bool with Flags:uint8 in meta.FuncInfo +// 33 - support parsing of array and list +// 34 - support parsing of ?ClassName as "ClassName|null" +// 35 - added Flags:uint8 to meta.ClassInfo +// 36 - added FuncAbstract bit to FuncFlags +// added FuncFinal bit to FuncFlags +// added ClassFinal bit to ClassFlags +// FuncInfo now stores original function name +// ClassInfo now stores original class name +// 37 - added ClassShape bit to ClassFlags +// changed meta.scopeVar bool fields representation +// 38 - replaced TypesMap.immutable:bool with flags:uint8. +// added mapPrecise flag to mark precise type maps. +// 39 - added new field Value in ConstantInfo +// 40 - changed string const value storage (no quotes) +// 41 - const-folding affected const definition values +// 42 - bool-typed consts are now stored in meta info +// 43 - define'd const values stored in cache +// 44 - rename ConstantInfo => ConstInfo +// 45 - added Mixins field to meta.ClassInfo +// 46 - changed the way of inferring the return type of functions and methods +// 47 - forced cache version invalidation due to the #921 +// 48 - renamed meta.TypesMap to types.Map; this affects gob encoding +// 49 - for shape, names are now generated using the keys that make up this shape +// 50 - added Flags field for meta.PropertyInfo +// 51 - added anonymous classes +// 52 - renamed all PhpDoc and Phpdoc with PHPDoc +// 53 - added DeprecationInfo for functions and methods and support for some attributes +// 54 - forced cache version invalidation due to the #1165 +// 55 - updated go version 1.16 -> 1.21 +// 56 - added isVariadic to meta.FuncInfo +// 57 - added DeprecationInfo for property and const const cacheVersion = 57 var ( From c510c775fa525981fa3f800500b8dc67eb1080dc Mon Sep 17 00:00:00 2001 From: Danil Ovchinnikov Date: Fri, 21 Mar 2025 03:33:40 +0300 Subject: [PATCH 8/9] refactoring --- src/linter/root.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/linter/root.go b/src/linter/root.go index 809f95139..4b40ceae2 100644 --- a/src/linter/root.go +++ b/src/linter/root.go @@ -910,18 +910,23 @@ func (d *rootWalker) handleClassDoc(doc classPHPDocParseResult, cl *meta.ClassIn func (d *rootWalker) parseVarPHPDoc(doc phpdoc.Comment) variablePHPDocParseResult { var result variablePHPDocParseResult - for _, partDoc := range doc.Parsed { - if part, ok := partDoc.(*phpdoc.TypeVarCommentPart); ok && part.Name() == "var" { + if doc.Raw == "" { + return result + } + + for _, part := range doc.Parsed { + switch part.Name() { + case "var": + part := part.(*phpdoc.TypeVarCommentPart) converted := phpdoctypes.ToRealType(d.ctx.typeNormalizer.ClassFQNProvider(), d.config.KPHP, part.Type) moveShapesToContext(&d.ctx, converted.Shapes) d.handleClosuresFromDoc(converted.Closures) result.typesMap = types.NewMapWithNormalization(d.ctx.typeNormalizer, converted.Types) - } - - if rawPart, ok := partDoc.(*phpdoc.RawCommentPart); ok && rawPart.Name() == "deprecated" { + case "deprecated": + part := part.(*phpdoc.RawCommentPart) result.deprecation.Deprecated = true - result.deprecation.Reason = rawPart.ParamsText + result.deprecation.Reason = part.ParamsText } } @@ -985,8 +990,7 @@ func (d *rootWalker) enterPropertyList(pl *ir.PropertyListStmt) bool { return true } -func (d *rootWalker) parseConstPHPDoc(constList *ir.ClassConstListStmt, doc phpdoc.Comment) (deprecationInfo meta.DeprecationInfo) { - +func (d *rootWalker) parseConstPHPDoc(doc phpdoc.Comment) (deprecationInfo meta.DeprecationInfo) { if doc.Raw == "" { return deprecationInfo } @@ -1018,7 +1022,7 @@ func (d *rootWalker) enterClassConstList(list *ir.ClassConstListStmt) bool { } } - deprecationInfo := d.parseConstPHPDoc(list, list.Doc) + deprecationInfo := d.parseConstPHPDoc(list.Doc) deprecation, ok := attributes.Deprecated(list.AttrGroups, d.ctx.st) if ok { From ec22c9117b5c3bb8692a3c095162e45e88983a7e Mon Sep 17 00:00:00 2001 From: Richardas Kuchinskas Date: Fri, 18 Apr 2025 02:55:57 +0300 Subject: [PATCH 9/9] fixes after rebase --- src/linter/block_linter.go | 1 + src/tests/golden/testdata/flysystem/golden.txt | 15 +++------------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/linter/block_linter.go b/src/linter/block_linter.go index 1ac348778..ace77db1b 100644 --- a/src/linter/block_linter.go +++ b/src/linter/block_linter.go @@ -1532,6 +1532,7 @@ func (b *blockLinter) checkPropertyFetch(e *ir.PropertyFetchExpr) { } else { b.report(e, LevelNotice, "deprecated", "Try to call property %s that was marked as deprecated in the class %s", fetch.propertyNode.Value, fetch.className) } + } left, ok := b.walker.ctx.sc.GetVarType(e.Variable) if ok { diff --git a/src/tests/golden/testdata/flysystem/golden.txt b/src/tests/golden/testdata/flysystem/golden.txt index 4e70bb148..1e0d35101 100644 --- a/src/tests/golden/testdata/flysystem/golden.txt +++ b/src/tests/golden/testdata/flysystem/golden.txt @@ -400,21 +400,15 @@ MAYBE missingPhpdoc: Missing PHPDoc for \League\Flysystem\Adapter\Polyfill\Str MAYBE missingPhpdoc: Missing PHPDoc for \League\Flysystem\Adapter\Polyfill\StreamedWritingTrait::update public method at testdata/flysystem/src/Adapter/Polyfill/StreamedWritingTrait.php:59 abstract public function update($pash, $contents, Config $config); ^^^^^^ -MAYBE deprecated: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/File.php:140 - return new File($this->filesystem, $newpath); - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -MAYBE deprecated: Has deprecated class Directory at testdata/flysystem/src/Directory.php:8 -class Directory extends Handler -^ WARNING notNullSafetyFunctionArgumentPropertyFetch: potential null dereference when accessing property 'path' at testdata/flysystem/src/Directory.php:17 return $this->filesystem->deleteDir($this->path); ^^^^^^^^^^^ WARNING notNullSafetyFunctionArgumentPropertyFetch: potential null dereference when accessing property 'path' at testdata/flysystem/src/Directory.php:29 return $this->filesystem->listContents($this->path, $recursive); ^^^^^^^^^^^ -MAYBE deprecated: Has deprecated class File at testdata/flysystem/src/File.php:8 -class File extends Handler -^ +MAYBE deprecated: Try to create instance \League\Flysystem\File class that was marked as deprecated at testdata/flysystem/src/File.php:140 + return new File($this->filesystem, $newpath); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/FileExistsException.php:21 public function __construct($path, $code = 0, BaseException $previous = null) ^^^^^^^^^^^^^ @@ -454,9 +448,6 @@ WARNING notExplicitNullableParam: parameter with null default value should be ex WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/flysystem/src/FilesystemInterface.php:274 public function get($path, Handler $handler = null); ^^^^^^^ -MAYBE deprecated: Has deprecated class Handler at testdata/flysystem/src/Handler.php:10 -abstract class Handler -^ WARNING notNullSafetyFunctionArgumentPropertyFetch: potential null dereference when accessing property 'path' at testdata/flysystem/src/Handler.php:61 $metadata = $this->filesystem->getMetadata($this->path); ^^^^^^^^^^^