From 560777b21a0ad1804d4c370833322b96e5170623 Mon Sep 17 00:00:00 2001 From: Junichi Yamamoto Date: Thu, 14 Dec 2023 15:06:23 +0900 Subject: [PATCH 1/4] Fix the Overriding Methods feature - Don't handle private methods as overridden/overriding methods - Handle also methods of Enum - Improve results of unit tests - Add/Fix unit tests --- .../php/editor/csl/OverridingMethodsImpl.java | 12 +++- .../php/editor/elements/IndexQueryImpl.java | 3 +- .../overriding/overridden/overridden_01.pass | 18 ----- .../overridden/testClass_01/testClass_01.pass | 17 +++++ .../testInterface_01/testInterface_01.pass | 6 ++ .../overriding/overriding/overriding_01.pass | 16 ----- .../overriding/testClass_01/testClass_01.pass | 15 ++++ .../testInterface_01/testInterface_01.pass | 15 ++++ .../{ => testClass_01}/ChildClass1.php | 0 .../{ => testClass_01}/ChildClass2.php | 7 ++ .../testClass_01.php} | 0 .../testInterface_01/ImplementingClass.php | 53 ++++++++++++++ .../testInterface_01/ImplementingEnum.php | 36 ++++++++++ .../testInterface_01/testInterface_01.php | 25 +++++++ .../{ => testClass_01}/ParentClass.php | 0 .../testClass_01.php} | 7 ++ .../overriding/testInterface_01/I.php | 25 +++++++ .../testInterface_01/testInterface_01.php | 71 +++++++++++++++++++ .../csl/OverridingMethodsImplTestBase.java | 36 ++++++++-- .../csl/OverridingMethodsOverriddenTest.java | 18 ++++- .../csl/OverridingMethodsOverridingTest.java | 18 ++++- 21 files changed, 349 insertions(+), 49 deletions(-) delete mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/overridden_01.pass create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testClass_01/testClass_01.pass create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testInterface_01/testInterface_01.pass delete mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/overriding_01.pass create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testClass_01/testClass_01.pass create mode 100644 php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testInterface_01/testInterface_01.pass rename php/php.editor/test/unit/data/testfiles/overriding/overridden/{ => testClass_01}/ChildClass1.php (100%) rename php/php.editor/test/unit/data/testfiles/overriding/overridden/{ => testClass_01}/ChildClass2.php (94%) rename php/php.editor/test/unit/data/testfiles/overriding/overridden/{overridden_01.php => testClass_01/testClass_01.php} (100%) create mode 100644 php/php.editor/test/unit/data/testfiles/overriding/overridden/testInterface_01/ImplementingClass.php create mode 100644 php/php.editor/test/unit/data/testfiles/overriding/overridden/testInterface_01/ImplementingEnum.php create mode 100644 php/php.editor/test/unit/data/testfiles/overriding/overridden/testInterface_01/testInterface_01.php rename php/php.editor/test/unit/data/testfiles/overriding/overriding/{ => testClass_01}/ParentClass.php (100%) rename php/php.editor/test/unit/data/testfiles/overriding/overriding/{overriding_01.php => testClass_01/testClass_01.php} (93%) create mode 100644 php/php.editor/test/unit/data/testfiles/overriding/overriding/testInterface_01/I.php create mode 100644 php/php.editor/test/unit/data/testfiles/overriding/overriding/testInterface_01/testInterface_01.php diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/csl/OverridingMethodsImpl.java b/php/php.editor/src/org/netbeans/modules/php/editor/csl/OverridingMethodsImpl.java index 00b6dfd17c27..24bc8854e5be 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/csl/OverridingMethodsImpl.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/csl/OverridingMethodsImpl.java @@ -45,6 +45,7 @@ import org.netbeans.modules.php.editor.model.MethodScope; import org.netbeans.modules.php.editor.model.ModelElement; import org.netbeans.modules.php.editor.model.Scope; +import org.netbeans.modules.php.editor.model.TraitScope; import org.netbeans.modules.php.editor.model.TypeScope; import org.openide.filesystems.FileObject; @@ -75,8 +76,13 @@ public Collection overrides(ParserResult info, El if (handle instanceof MethodScope) { MethodScope method = (MethodScope) handle; final ElementFilter methodNameFilter = ElementFilter.forName(NameKind.exact(method.getName())); - final Set overridenMethods = methodNameFilter.filter(getInheritedMethods(info, method)); + Set overridenMethods = methodNameFilter.filter(getInheritedMethods(info, method)); List retval = new ArrayList<>(); + Scope typeScope = method.getInScope(); + if (!(typeScope instanceof TraitScope)) { + ElementFilter notPrivateFilter = ElementFilter.forPrivateModifiers(false); + overridenMethods = notPrivateFilter.filter(overridenMethods); + } for (MethodElement methodElement : overridenMethods) { retval.add(MethodLocation.newInstance(methodElement)); } @@ -232,6 +238,10 @@ private Set getInheritedByMethods(final ParserResult info, final Scope inScope = method.getInScope(); assert inScope instanceof TypeScope; TypeScope typeScope = (TypeScope) inScope; + if (!(typeScope instanceof TraitScope) + && method.getPhpModifiers().isPrivate()) { + return Collections.emptySet(); + } final String signature = ((TypeScope) inScope).getIndexSignature(); if (signature != null && !signature.equals(classSignatureForInheritedByMethods)) { Index index = ElementQueryFactory.getIndexQuery(info); diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java b/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java index 961dc19c633b..fc97de86c744 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java @@ -1861,7 +1861,7 @@ public Set getDirectInheritedByTypes(final TypeElement typeElement) } } else if (typeElement.isInterface()) { final Collection result = results(PHPIndexer.FIELD_SUPER_IFACE, query, - new String[] {PHPIndexer.FIELD_SUPER_IFACE, InterfaceElementImpl.IDX_FIELD, ClassElementImpl.IDX_FIELD}); + new String[] {PHPIndexer.FIELD_SUPER_IFACE, InterfaceElementImpl.IDX_FIELD, ClassElementImpl.IDX_FIELD, EnumElementImpl.IDX_FIELD}); for (final IndexResult indexResult : result) { String[] values = indexResult.getValues(PHPIndexer.FIELD_SUPER_IFACE); for (String value : values) { @@ -1870,6 +1870,7 @@ public Set getDirectInheritedByTypes(final TypeElement typeElement) if (query.matchesName(PhpElementKind.IFACE, fqnForValue)) { directTypes.addAll(InterfaceElementImpl.fromSignature(NameKind.empty(), this, indexResult)); directTypes.addAll(ClassElementImpl.fromSignature(NameKind.empty(), this, indexResult)); + directTypes.addAll(EnumElementImpl.fromSignature(NameKind.empty(), this, indexResult)); } } } diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/overridden_01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/overridden_01.pass deleted file mode 100644 index 4924661cda21..000000000000 --- a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/overridden_01.pass +++ /dev/null @@ -1,18 +0,0 @@ -[Overridden] - -[Constant] -IMPLICIT_PUBLIC_PARENT_CONST (ChildClass1.php)(ChildClass2.php) -PROTECTED_PARENT_CONST (ChildClass1.php)(ChildClass2.php) -PUBLIC_PARENT_CONST (ChildClass1.php)(ChildClass2.php) -[Field] -$protectedParentClassField (ChildClass2.php) -$protectedStaticParentClassField (ChildClass2.php) -$publicParentClassField (ChildClass2.php) -$publicStaticParentClassField (ChildClass2.php) -[Method] -protectedParentClassMethod (ChildClass2.php) -protectedStaticParentClassMethod (ChildClass2.php) -publicParentClassMethod (ChildClass2.php) -publicStaticParentClassMethod (ChildClass2.php) -[Type] -ParentClass (ChildClass1.php)(ChildClass2.php) diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testClass_01/testClass_01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testClass_01/testClass_01.pass new file mode 100644 index 000000000000..6eb8f98c6c1a --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testClass_01/testClass_01.pass @@ -0,0 +1,17 @@ +[Overridden] +[Constant] +IMPLICIT_PUBLIC_PARENT_CONST : ParentClass (by ChildClass1)(by ChildClass2) +PROTECTED_PARENT_CONST : ParentClass (by ChildClass1)(by ChildClass2) +PUBLIC_PARENT_CONST : ParentClass (by ChildClass1)(by ChildClass2) +[Field] +$protectedParentClassField : ParentClass (by ChildClass2) +$protectedStaticParentClassField : ParentClass (by ChildClass2) +$publicParentClassField : ParentClass (by ChildClass2) +$publicStaticParentClassField : ParentClass (by ChildClass2) +[Method] +protectedParentClassMethod : ParentClass (by ChildClass2) +protectedStaticParentClassMethod : ParentClass (by ChildClass2) +publicParentClassMethod : ParentClass (by ChildClass2) +publicStaticParentClassMethod : ParentClass (by ChildClass2) +[Type] +ParentClass (by ChildClass1)(by ChildClass2) diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testInterface_01/testInterface_01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testInterface_01/testInterface_01.pass new file mode 100644 index 000000000000..ae2cfa3b6024 --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest/overriding/overridden/testInterface_01/testInterface_01.pass @@ -0,0 +1,6 @@ +[Overridden] +[Method] +interfaceMethod1 : I (by ExtendingClass)(by ImplementingClass)(by ImplementingEnum) +interfaceStaticMethod1 : I (by ExtendingClass)(by ImplementingClass)(by ImplementingEnum) +[Type] +I (by ExtendingClass)(by ImplementingClass)(by ImplementingEnum) diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/overriding_01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/overriding_01.pass deleted file mode 100644 index 02443f8dd6e4..000000000000 --- a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/overriding_01.pass +++ /dev/null @@ -1,16 +0,0 @@ -[Overring] - -[Constant] -IMPLICIT_PUBLIC_PARENT_CONST (ParentClass.php) -PROTECTED_PARENT_CONST (ParentClass.php) -PUBLIC_PARENT_CONST (ParentClass.php) -[Field] -$protectedParentClassField (ParentClass.php) -$protectedStaticParentClassField (ParentClass.php) -$publicParentClassField (ParentClass.php) -$publicStaticParentClassField (ParentClass.php) -[Method] -protectedParentClassMethod (ParentClass.php) -protectedStaticParentClassMethod (ParentClass.php) -publicParentClassMethod (ParentClass.php) -publicStaticParentClassMethod (ParentClass.php) diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testClass_01/testClass_01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testClass_01/testClass_01.pass new file mode 100644 index 000000000000..79dfcdbb373f --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testClass_01/testClass_01.pass @@ -0,0 +1,15 @@ +[Overrides] +[Constant] +IMPLICIT_PUBLIC_PARENT_CONST : ChildClass (from ParentClass) +PROTECTED_PARENT_CONST : ChildClass (from ParentClass) +PUBLIC_PARENT_CONST : ChildClass (from ParentClass) +[Field] +$protectedParentClassField : ChildClass (from ParentClass) +$protectedStaticParentClassField : ChildClass (from ParentClass) +$publicParentClassField : ChildClass (from ParentClass) +$publicStaticParentClassField : ChildClass (from ParentClass) +[Method] +protectedParentClassMethod : ChildClass (from ParentClass) +protectedStaticParentClassMethod : ChildClass (from ParentClass) +publicParentClassMethod : ChildClass (from ParentClass) +publicStaticParentClassMethod : ChildClass (from ParentClass) diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testInterface_01/testInterface_01.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testInterface_01/testInterface_01.pass new file mode 100644 index 000000000000..53ef9db1f303 --- /dev/null +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest/overriding/overriding/testInterface_01/testInterface_01.pass @@ -0,0 +1,15 @@ +[Overrides] +[Method] +interfaceMethod1 : ExtendingClass (from ImplementingClass) +interfaceMethod1 : ImplementingClass (from I) +interfaceMethod1 : ImplementingEnum (from I) +interfaceStaticMethod1 : ExtendingClass (from ImplementingClass) +interfaceStaticMethod1 : ImplementingClass (from I) +interfaceStaticMethod1 : ImplementingEnum (from I) + +[Overridden] +[Method] +interfaceMethod1 : ImplementingClass (by ExtendingClass) +interfaceStaticMethod1 : ImplementingClass (by ExtendingClass) +[Type] +ImplementingClass (by ExtendingClass) diff --git a/php/php.editor/test/unit/data/testfiles/overriding/overridden/ChildClass1.php b/php/php.editor/test/unit/data/testfiles/overriding/overridden/testClass_01/ChildClass1.php similarity index 100% rename from php/php.editor/test/unit/data/testfiles/overriding/overridden/ChildClass1.php rename to php/php.editor/test/unit/data/testfiles/overriding/overridden/testClass_01/ChildClass1.php diff --git a/php/php.editor/test/unit/data/testfiles/overriding/overridden/ChildClass2.php b/php/php.editor/test/unit/data/testfiles/overriding/overridden/testClass_01/ChildClass2.php similarity index 94% rename from php/php.editor/test/unit/data/testfiles/overriding/overridden/ChildClass2.php rename to php/php.editor/test/unit/data/testfiles/overriding/overridden/testClass_01/ChildClass2.php index 9c7f7522c301..f591798154eb 100644 --- a/php/php.editor/test/unit/data/testfiles/overriding/overridden/ChildClass2.php +++ b/php/php.editor/test/unit/data/testfiles/overriding/overridden/testClass_01/ChildClass2.php @@ -80,4 +80,11 @@ public static function publicStaticParentClassMethod(int $param): void { protected static function protectedStaticParentClassMethod(int $param1, string $param2): void { } + + // private + private function privateParentClassMethod(int $param): void { + } + + private static function privateStaticParentClassMethod(int $param): void { + } } diff --git a/php/php.editor/test/unit/data/testfiles/overriding/overridden/overridden_01.php b/php/php.editor/test/unit/data/testfiles/overriding/overridden/testClass_01/testClass_01.php similarity index 100% rename from php/php.editor/test/unit/data/testfiles/overriding/overridden/overridden_01.php rename to php/php.editor/test/unit/data/testfiles/overriding/overridden/testClass_01/testClass_01.php diff --git a/php/php.editor/test/unit/data/testfiles/overriding/overridden/testInterface_01/ImplementingClass.php b/php/php.editor/test/unit/data/testfiles/overriding/overridden/testInterface_01/ImplementingClass.php new file mode 100644 index 000000000000..17cec7cfa6d5 --- /dev/null +++ b/php/php.editor/test/unit/data/testfiles/overriding/overridden/testInterface_01/ImplementingClass.php @@ -0,0 +1,53 @@ +> overriding, boolean isOverriding) { + if (sb.length() > 0) { + sb.append("\n"); + } if (isOverriding) { - sb.append("[Overring]\n\n"); + sb.append("[Overrides]\n"); } else { - sb.append("[Overridden]\n\n"); + sb.append("[Overridden]\n"); } Map> overridingNames = new LinkedHashMap<>(); overridingNames.put("Constant", new ArrayList<>()); @@ -137,13 +142,30 @@ private void print(StringBuilder sb, final Map values = entry.getValue(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(elementHandle.getName()).append(" "); - List fileNames = new ArrayList<>(); + List types = new ArrayList<>(); for (DeclarationFinder.AlternativeLocation value : values) { - fileNames.add(value.getElement().getFileObject().getNameExt()); + switch (elementHandle.getKind()) { + case CLASS: + case INTERFACE: + types.add(value.getElement().getName()); + break; + default: + types.add(value.getElement().getIn()); + break; + } + } + switch (elementHandle.getKind()) { + case CLASS: + case INTERFACE: + // noop + break; + default: + stringBuilder.append(": ").append(elementHandle.getIn()).append(" "); + break; } - Collections.sort(fileNames); - for (String fileName : fileNames) { - stringBuilder.append("(").append(fileName).append(")"); + Collections.sort(types); + for (String fileName : types) { + stringBuilder.append("(").append((isOverriding ? "from " : "by ")).append(fileName).append(")"); } switch (elementHandle.getKind()) { case CONSTANT: diff --git a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest.java b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest.java index 1b9f5a65bedf..c850c26bda24 100644 --- a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest.java +++ b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverriddenTest.java @@ -33,8 +33,20 @@ public OverridingMethodsOverriddenTest(String testName) { super(testName); } - public void testOverridden_01() throws Exception { - performTest("overriding/overridden/overridden_01"); + public void testClass_01() throws Exception { + performTest(); + } + + public void testInterface_01() throws Exception { + performTest(); + } + + private void performTest() throws Exception { + performTest(getTestPath()); + } + + private String getTestPath() { + return "overriding/overridden/" + getName() + "/" + getName(); } @Override @@ -42,7 +54,7 @@ protected Map createClassPathsForTest() { return Collections.singletonMap( PhpSourcePath.SOURCE_CP, ClassPathSupport.createClassPath(new FileObject[] { - FileUtil.toFileObject(new File(getDataDir(), "/testfiles/overriding/overridden")) + FileUtil.toFileObject(new File(getDataDir(), "/testfiles/overriding/overridden/" + getName())) }) ); } diff --git a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest.java b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest.java index c623ff02c86c..dfc18b77886a 100644 --- a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest.java +++ b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/csl/OverridingMethodsOverridingTest.java @@ -33,8 +33,20 @@ public OverridingMethodsOverridingTest(String testName) { super(testName); } - public void testOverriding_01() throws Exception { - performTest("overriding/overriding/overriding_01"); + public void testClass_01() throws Exception { + performTest(); + } + + public void testInterface_01() throws Exception { + performTest(); + } + + private void performTest() throws Exception { + performTest(getTestPath()); + } + + private String getTestPath() { + return "overriding/overriding/" + getName() + "/" + getName(); } @Override @@ -42,7 +54,7 @@ protected Map createClassPathsForTest() { return Collections.singletonMap( PhpSourcePath.SOURCE_CP, ClassPathSupport.createClassPath(new FileObject[] { - FileUtil.toFileObject(new File(getDataDir(), "/testfiles/overriding/overriding")) + FileUtil.toFileObject(new File(getDataDir(), "/testfiles/overriding/overriding/" + getName())) }) ); } From 0a6df1e093dd2204be078cc8a717f67680d90e1c Mon Sep 17 00:00:00 2001 From: Junichi Yamamoto Date: Wed, 20 Dec 2023 07:37:12 +0900 Subject: [PATCH 2/4] PHP 8.3 Support: Marking overridden methods (#[\Override]) (Part 1) - https://github.com/apache/netbeans/issues/6701 - https://wiki.php.net/rfc/marking_overriden_methods - Add the `hasOverrideAttribute()` method to the `PhpVersion` - Add the `AddOverrideAttributeHint` - Add unit tests - Increment spec versions Note: If some trait methods have `#[\Override]`, we should also check wheter the parent class has them. However, attributes are not indexed at the moment. So, we should improve the indexer. (Currently, the following case is not supported.) e.g. ```php trait T { #[\Override] public function traitMethod(): void {} } class C { // Fatal error: C::traitMethod() has #[\Override] attribute, but no matching parent method exists use T; } ``` --- php/php.api.phpmodule/manifest.mf | 2 +- .../netbeans/modules/php/api/PhpVersion.java | 11 + php/php.editor/nbproject/project.properties | 2 +- php/php.editor/nbproject/project.xml | 2 +- .../modules/php/editor/CodeUtils.java | 5 + .../php/editor/elements/IndexQueryImpl.java | 7 +- .../modules/php/editor/resources/layer.xml | 1 + .../AddOverrideAttributeHint.java | 730 ++++++++++++++++++ .../AbstractClass.php | 32 + .../AbstractMethodsTrait.php | 25 + .../AddOverrideAttributeHint/Interface1.php | 22 + .../AddOverrideAttributeHint/Interface2.php | 27 + .../AddOverrideAttributeHint/ParentClass.php | 24 + .../testAbstractClass.php | 22 + ...stractClass.php.testAbstractClass_01.hints | 4 + ...actClass.php.testAbstractClass_Fix01.fixed | 23 + .../testAbstractTraitMethods_01.php | 37 + ...s_01.php.testAbstractTraitMethods_01.hints | 12 + ...1.php.testAbstractTraitMethods_Fix01.fixed | 38 + ...1.php.testAbstractTraitMethods_Fix02.fixed | 38 + ...1.php.testAbstractTraitMethods_Fix03.fixed | 38 + .../testAnonymousClass_01.php | 96 +++ ...usClass_01.php.testAnonymousClass_01.hints | 28 + ...lass_01.php.testAnonymousClass_Fix01.fixed | 97 +++ ...lass_01.php.testAnonymousClass_Fix02.fixed | 97 +++ ...lass_01.php.testAnonymousClass_Fix03.fixed | 97 +++ ...lass_01.php.testAnonymousClass_Fix04.fixed | 97 +++ ...lass_01.php.testAnonymousClass_Fix05.fixed | 97 +++ ...lass_01.php.testAnonymousClass_Fix06.fixed | 97 +++ ...lass_01.php.testAnonymousClass_Fix07.fixed | 97 +++ ...s_01.php.testAnonymousClass_PHP82_01.hints | 1 + .../testEnumImplementsInterface.php | 45 ++ ...e.php.testEnumImplementsInterface_01.hints | 16 + ...hp.testEnumImplementsInterface_Fix01.fixed | 46 ++ ...hp.testEnumImplementsInterface_Fix02.fixed | 46 ++ ...hp.testEnumImplementsInterface_Fix03.fixed | 46 ++ ...hp.testEnumImplementsInterface_Fix04.fixed | 46 ++ .../testExtendsAbstractClass.php | 26 + ...lass.php.testExtendsAbstractClass_01.hints | 8 + ...s.php.testExtendsAbstractClass_Fix01.fixed | 27 + ...s.php.testExtendsAbstractClass_Fix02.fixed | 27 + .../testExtendsClass.php | 55 ++ ...ExtendsClass.php.testExtendsClass_01.hints | 28 + ...endsClass.php.testExtendsClass_Fix01.fixed | 56 ++ ...endsClass.php.testExtendsClass_Fix02.fixed | 56 ++ ...endsClass.php.testExtendsClass_Fix03.fixed | 56 ++ ...endsClass.php.testExtendsClass_Fix04.fixed | 56 ++ ...endsClass.php.testExtendsClass_Fix05.fixed | 56 ++ ...endsClass.php.testExtendsClass_Fix06.fixed | 56 ++ ...endsClass.php.testExtendsClass_Fix07.fixed | 56 ++ ...sClass.php.testExtendsClass_PHP82_01.hints | 1 + .../testExtendsClassInNamespace_01.php | 75 ++ ....testExtendsClassInNamespace01_Fix01.fixed | 76 ++ ....testExtendsClassInNamespace01_Fix02.fixed | 76 ++ ....testExtendsClassInNamespace01_Fix03.fixed | 76 ++ ....testExtendsClassInNamespace01_Fix04.fixed | 76 ++ ....testExtendsClassInNamespace01_Fix05.fixed | 76 ++ ...1.php.testExtendsClassInNamespace_01.hints | 20 + .../testExtendsClassInNamespace_02.php | 76 ++ ....testExtendsClassInNamespace02_Fix01.fixed | 77 ++ ....testExtendsClassInNamespace02_Fix02.fixed | 77 ++ ...2.php.testExtendsClassInNamespace_02.hints | 8 + .../testImplementsInterface.php | 29 + ...rface.php.testImplementsInterface_01.hints | 8 + ...ce.php.testImplementsInterface_Fix01.fixed | 30 + ...ce.php.testImplementsInterface_Fix02.fixed | 28 + ...php.testImplementsInterface_PHP82_01.hints | 1 + .../testRemoveOverrideAbstractClass_01.php | 26 + ...p.testRemoveOverrideAbstractClass_01.hints | 4 + ...estRemoveOverrideAbstractClass_Fix01.fixed | 26 + .../testRemoveOverrideAnonymousClass_01.php | 79 ++ ....testRemoveOverrideAnonymousClass_01.hints | 4 + ...stRemoveOverrideAnonymousClass_Fix01.fixed | 78 ++ .../testRemoveOverrideAnonymousClass_02.php | 50 ++ ....testRemoveOverrideAnonymousClass_02.hints | 4 + ...stRemoveOverrideAnonymousClass_Fix02.fixed | 49 ++ .../testRemoveOverrideAnonymousClass_03.php | 50 ++ ....testRemoveOverrideAnonymousClass_03.hints | 4 + ...stRemoveOverrideAnonymousClass_Fix03.fixed | 49 ++ .../testRemoveOverrideEnum_01.php | 24 + ...num_01.php.testRemoveOverrideEnum_01.hints | 4 + ..._01.php.testRemoveOverrideEnum_Fix01.fixed | 23 + .../testRemoveOverrideEnum_02.php | 26 + ...num_02.php.testRemoveOverrideEnum_02.hints | 4 + ..._02.php.testRemoveOverrideEnum_Fix02.fixed | 26 + .../testRemoveOverrideInNamespace_01.php | 24 + ...php.testRemoveOverrideInNamespace_01.hints | 4 + ....testRemoveOverrideInNamespace_Fix01.fixed | 23 + .../testRemoveOverrideInNamespace_02.php | 24 + ...php.testRemoveOverrideInNamespace_02.hints | 1 + .../testRemoveOverrideInNamespace_03.php | 24 + ...php.testRemoveOverrideInNamespace_03.hints | 4 + ....testRemoveOverrideInNamespace_Fix03.fixed | 24 + .../testRemoveOverrideInNamespace_04.php | 25 + ...php.testRemoveOverrideInNamespace_04.hints | 4 + ....testRemoveOverrideInNamespace_Fix04.fixed | 24 + .../testRemoveOverrideInNamespace_05.php | 25 + ...php.testRemoveOverrideInNamespace_05.hints | 4 + ....testRemoveOverrideInNamespace_Fix05.fixed | 25 + .../testRemoveOverrideInNamespace_06.php | 25 + ...php.testRemoveOverrideInNamespace_06.hints | 4 + ....testRemoveOverrideInNamespace_Fix06.fixed | 25 + .../testRemoveOverrideInNamespace_07.php | 25 + ...php.testRemoveOverrideInNamespace_07.hints | 4 + ....testRemoveOverrideInNamespace_Fix07.fixed | 25 + .../testRemoveOverrideInNamespace_08.php | 27 + ...php.testRemoveOverrideInNamespace_08.hints | 4 + ....testRemoveOverrideInNamespace_Fix08.fixed | 26 + .../testRemoveOverrideInNamespace_09.php | 27 + ...php.testRemoveOverrideInNamespace_09.hints | 1 + .../testRemoveOverrideInterface_01.php | 24 + ...1.php.testRemoveOverrideInterface_01.hints | 4 + ...hp.testRemoveOverrideInterface_Fix01.fixed | 23 + .../testRemoveOverrideInterface_02.php | 25 + ...2.php.testRemoveOverrideInterface_02.hints | 4 + ...hp.testRemoveOverrideInterface_Fix02.fixed | 25 + .../testRemoveOverride_01.php | 26 + ...verride_01.php.testRemoveOverride_01.hints | 4 + ...ride_01.php.testRemoveOverride_Fix01.fixed | 25 + .../testRemoveOverride_02.php | 27 + ...verride_02.php.testRemoveOverride_02.hints | 4 + ...ride_02.php.testRemoveOverride_Fix02.fixed | 26 + .../testRemoveOverride_03.php | 27 + ...verride_03.php.testRemoveOverride_03.hints | 4 + ...ride_03.php.testRemoveOverride_Fix03.fixed | 26 + .../testRemoveOverride_04.php | 26 + ...verride_04.php.testRemoveOverride_04.hints | 4 + ...ride_04.php.testRemoveOverride_Fix04.fixed | 25 + .../testRemoveOverride_05.php | 26 + ...verride_05.php.testRemoveOverride_05.hints | 4 + ...ride_05.php.testRemoveOverride_Fix05.fixed | 25 + .../testRemoveOverride_06.php | 26 + ...verride_06.php.testRemoveOverride_06.hints | 4 + ...ride_06.php.testRemoveOverride_Fix06.fixed | 26 + .../testRemoveOverride_07.php | 26 + ...verride_07.php.testRemoveOverride_07.hints | 4 + ...ride_07.php.testRemoveOverride_Fix07.fixed | 26 + .../testRemoveOverride_08.php | 27 + ...verride_08.php.testRemoveOverride_08.hints | 4 + ...ride_08.php.testRemoveOverride_Fix08.fixed | 27 + .../testRemoveOverride_09.php | 27 + ...verride_09.php.testRemoveOverride_09.hints | 4 + ...ride_09.php.testRemoveOverride_Fix09.fixed | 27 + .../testRemoveOverride_10.php | 23 + ...verride_10.php.testRemoveOverride_10.hints | 4 + ...ride_10.php.testRemoveOverride_Fix10.fixed | 23 + .../testRemoveOverride_11.php | 23 + ...verride_11.php.testRemoveOverride_11.hints | 4 + ...ride_11.php.testRemoveOverride_Fix11.fixed | 23 + .../testRemoveOverride_12.php | 23 + ...verride_12.php.testRemoveOverride_12.hints | 4 + ...ride_12.php.testRemoveOverride_Fix12.fixed | 23 + .../testRemoveOverride_13.php | 23 + ...verride_13.php.testRemoveOverride_13.hints | 4 + ...ride_13.php.testRemoveOverride_Fix13.fixed | 23 + .../testRemoveOverride_14.php | 23 + ...verride_14.php.testRemoveOverride_14.hints | 4 + ...ride_14.php.testRemoveOverride_Fix14.fixed | 23 + .../testRemoveOverride_15.php | 23 + ...verride_15.php.testRemoveOverride_15.hints | 4 + ...ride_15.php.testRemoveOverride_Fix15.fixed | 22 + .../testRemoveOverride_16.php | 23 + ...verride_16.php.testRemoveOverride_16.hints | 4 + ...ride_16.php.testRemoveOverride_Fix16.fixed | 22 + .../testRemoveOverride_17.php | 26 + ...verride_17.php.testRemoveOverride_17.hints | 4 + ...ride_17.php.testRemoveOverride_Fix17.fixed | 25 + .../testRemoveOverride_18.php | 26 + ...verride_18.php.testRemoveOverride_18.hints | 4 + ...ride_18.php.testRemoveOverride_Fix18.fixed | 25 + .../testRemoveOverride_19.php | 26 + ...verride_19.php.testRemoveOverride_19.hints | 4 + ...ride_19.php.testRemoveOverride_Fix19.fixed | 25 + .../testRemoveOverride_20.php | 26 + ...verride_20.php.testRemoveOverride_20.hints | 4 + ...ride_20.php.testRemoveOverride_Fix20.fixed | 26 + .../AddOverrideAttributeHint/testTrait.php | 38 + .../testTrait.php.testTrait_01.hints | 1 + .../AddOverrideAttributeHintTest.java | 539 +++++++++++++ 179 files changed, 6236 insertions(+), 5 deletions(-) create mode 100644 php/php.editor/src/org/netbeans/modules/php/editor/verification/AddOverrideAttributeHint.java create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/AbstractClass.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/AbstractMethodsTrait.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/Interface1.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/Interface2.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/ParentClass.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractClass.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractClass.php.testAbstractClass_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractClass.php.testAbstractClass_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractTraitMethods_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractTraitMethods_01.php.testAbstractTraitMethods_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractTraitMethods_01.php.testAbstractTraitMethods_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractTraitMethods_01.php.testAbstractTraitMethods_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAbstractTraitMethods_01.php.testAbstractTraitMethods_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_Fix04.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_Fix05.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_Fix06.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_Fix07.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testAnonymousClass_01.php.testAnonymousClass_PHP82_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testEnumImplementsInterface.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testEnumImplementsInterface.php.testEnumImplementsInterface_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testEnumImplementsInterface.php.testEnumImplementsInterface_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testEnumImplementsInterface.php.testEnumImplementsInterface_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testEnumImplementsInterface.php.testEnumImplementsInterface_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testEnumImplementsInterface.php.testEnumImplementsInterface_Fix04.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsAbstractClass.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsAbstractClass.php.testExtendsAbstractClass_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsAbstractClass.php.testExtendsAbstractClass_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsAbstractClass.php.testExtendsAbstractClass_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_Fix04.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_Fix05.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_Fix06.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_Fix07.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClass.php.testExtendsClass_PHP82_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_01.php.testExtendsClassInNamespace01_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_01.php.testExtendsClassInNamespace01_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_01.php.testExtendsClassInNamespace01_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_01.php.testExtendsClassInNamespace01_Fix04.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_01.php.testExtendsClassInNamespace01_Fix05.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_01.php.testExtendsClassInNamespace_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_02.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_02.php.testExtendsClassInNamespace02_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_02.php.testExtendsClassInNamespace02_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testExtendsClassInNamespace_02.php.testExtendsClassInNamespace_02.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testImplementsInterface.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testImplementsInterface.php.testImplementsInterface_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testImplementsInterface.php.testImplementsInterface_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testImplementsInterface.php.testImplementsInterface_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testImplementsInterface.php.testImplementsInterface_PHP82_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAbstractClass_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAbstractClass_01.php.testRemoveOverrideAbstractClass_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAbstractClass_01.php.testRemoveOverrideAbstractClass_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_01.php.testRemoveOverrideAnonymousClass_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_01.php.testRemoveOverrideAnonymousClass_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_02.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_02.php.testRemoveOverrideAnonymousClass_02.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_02.php.testRemoveOverrideAnonymousClass_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_03.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_03.php.testRemoveOverrideAnonymousClass_03.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideAnonymousClass_03.php.testRemoveOverrideAnonymousClass_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideEnum_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideEnum_01.php.testRemoveOverrideEnum_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideEnum_01.php.testRemoveOverrideEnum_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideEnum_02.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideEnum_02.php.testRemoveOverrideEnum_02.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideEnum_02.php.testRemoveOverrideEnum_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_01.php.testRemoveOverrideInNamespace_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_01.php.testRemoveOverrideInNamespace_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_02.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_02.php.testRemoveOverrideInNamespace_02.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_03.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_03.php.testRemoveOverrideInNamespace_03.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_03.php.testRemoveOverrideInNamespace_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_04.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_04.php.testRemoveOverrideInNamespace_04.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_04.php.testRemoveOverrideInNamespace_Fix04.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_05.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_05.php.testRemoveOverrideInNamespace_05.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_05.php.testRemoveOverrideInNamespace_Fix05.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_06.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_06.php.testRemoveOverrideInNamespace_06.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_06.php.testRemoveOverrideInNamespace_Fix06.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_07.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_07.php.testRemoveOverrideInNamespace_07.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_07.php.testRemoveOverrideInNamespace_Fix07.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_08.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_08.php.testRemoveOverrideInNamespace_08.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_08.php.testRemoveOverrideInNamespace_Fix08.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_09.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInNamespace_09.php.testRemoveOverrideInNamespace_09.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInterface_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInterface_01.php.testRemoveOverrideInterface_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInterface_01.php.testRemoveOverrideInterface_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInterface_02.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInterface_02.php.testRemoveOverrideInterface_02.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverrideInterface_02.php.testRemoveOverrideInterface_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_01.php.testRemoveOverride_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_01.php.testRemoveOverride_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_02.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_02.php.testRemoveOverride_02.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_02.php.testRemoveOverride_Fix02.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_03.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_03.php.testRemoveOverride_03.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_03.php.testRemoveOverride_Fix03.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_04.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_04.php.testRemoveOverride_04.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_04.php.testRemoveOverride_Fix04.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_05.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_05.php.testRemoveOverride_05.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_05.php.testRemoveOverride_Fix05.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_06.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_06.php.testRemoveOverride_06.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_06.php.testRemoveOverride_Fix06.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_07.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_07.php.testRemoveOverride_07.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_07.php.testRemoveOverride_Fix07.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_08.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_08.php.testRemoveOverride_08.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_08.php.testRemoveOverride_Fix08.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_09.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_09.php.testRemoveOverride_09.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_09.php.testRemoveOverride_Fix09.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_10.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_10.php.testRemoveOverride_10.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_10.php.testRemoveOverride_Fix10.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_11.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_11.php.testRemoveOverride_11.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_11.php.testRemoveOverride_Fix11.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_12.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_12.php.testRemoveOverride_12.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_12.php.testRemoveOverride_Fix12.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_13.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_13.php.testRemoveOverride_13.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_13.php.testRemoveOverride_Fix13.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_14.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_14.php.testRemoveOverride_14.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_14.php.testRemoveOverride_Fix14.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_15.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_15.php.testRemoveOverride_15.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_15.php.testRemoveOverride_Fix15.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_16.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_16.php.testRemoveOverride_16.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_16.php.testRemoveOverride_Fix16.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_17.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_17.php.testRemoveOverride_17.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_17.php.testRemoveOverride_Fix17.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_18.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_18.php.testRemoveOverride_18.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_18.php.testRemoveOverride_Fix18.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_19.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_19.php.testRemoveOverride_19.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_19.php.testRemoveOverride_Fix19.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_20.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_20.php.testRemoveOverride_20.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testRemoveOverride_20.php.testRemoveOverride_Fix20.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testTrait.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/testTrait.php.testTrait_01.hints create mode 100644 php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/AddOverrideAttributeHintTest.java diff --git a/php/php.api.phpmodule/manifest.mf b/php/php.api.phpmodule/manifest.mf index 9f15527e49c5..d503977a013e 100644 --- a/php/php.api.phpmodule/manifest.mf +++ b/php/php.api.phpmodule/manifest.mf @@ -1,4 +1,4 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.php.api.phpmodule OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/api/phpmodule/resources/Bundle.properties -OpenIDE-Module-Specification-Version: 2.93 +OpenIDE-Module-Specification-Version: 2.94 diff --git a/php/php.api.phpmodule/src/org/netbeans/modules/php/api/PhpVersion.java b/php/php.api.phpmodule/src/org/netbeans/modules/php/api/PhpVersion.java index 275c43a3c69b..8214bca352be 100644 --- a/php/php.api.phpmodule/src/org/netbeans/modules/php/api/PhpVersion.java +++ b/php/php.api.phpmodule/src/org/netbeans/modules/php/api/PhpVersion.java @@ -255,6 +255,17 @@ public boolean hasConstantsInTraits() { return this.compareTo(PhpVersion.PHP_82) >= 0; } + /** + * Check whether this version supports [#\Override] attribute. + * + * @return {@code true} if this version supports [#\Override] attribute, + * {@code false} otherwise + * @since 2.94 + */ + public boolean hasOverrideAttribute() { + return this.compareTo(PhpVersion.PHP_83) >= 0; + } + /** * Check whether this is supported version yet by PHP official. * diff --git a/php/php.editor/nbproject/project.properties b/php/php.editor/nbproject/project.properties index 3425ce348692..f7b602309552 100644 --- a/php/php.editor/nbproject/project.properties +++ b/php/php.editor/nbproject/project.properties @@ -18,7 +18,7 @@ javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial nbjavac.ignore.missing.enclosing=**/CUP$ASTPHP5Parser$actions.class nbm.needs.restart=true -spec.version.base=2.32.0 +spec.version.base=2.33.0 release.external/predefined_vars-1.0.zip=docs/predefined_vars.zip sigtest.gen.fail.on.error=false diff --git a/php/php.editor/nbproject/project.xml b/php/php.editor/nbproject/project.xml index 24aa0160a675..1cab74bd37b2 100644 --- a/php/php.editor/nbproject/project.xml +++ b/php/php.editor/nbproject/project.xml @@ -304,7 +304,7 @@ - 2.93 + 2.94 diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java b/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java index cf6488e7e36d..cfdadb5f43a9 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/CodeUtils.java @@ -89,6 +89,11 @@ public final class CodeUtils { public static final String NULLABLE_TYPE_PREFIX = "?"; // NOI18N public static final String ELLIPSIS = "..."; // NOI18N public static final String VAR_TAG = "@var"; // NOI18N + public static final String OVERRIDE_ATTRIBUTE_NAME = "Override"; // NOI18N + public static final String OVERRIDE_ATTRIBUTE_FQ_NAME = "\\" + OVERRIDE_ATTRIBUTE_NAME; // NOI18N + public static final String OVERRIDE_ATTRIBUTE = "#[" + OVERRIDE_ATTRIBUTE_FQ_NAME + "]"; // NOI18N + public static final String EMPTY_STRING = ""; // NOI18N + public static final String NEW_LINE = "\n"; // NOI18N public static final Pattern WHITE_SPACES_PATTERN = Pattern.compile("\\s+"); // NOI18N public static final Pattern SPLIT_TYPES_PATTERN = Pattern.compile("[()|&]+"); // NOI18N diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java b/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java index fc97de86c744..1d8efaa21e07 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/elements/IndexQueryImpl.java @@ -1174,8 +1174,11 @@ public Set getAccessibleMethods(final TypeElement typeElement, fi final long start = (LOG.isLoggable(Level.FINE)) ? System.currentTimeMillis() : 0; final Set allMethods = getAllMethods(typeElement); Collection subTypes = Collections.emptySet(); - if (calledFromEnclosingType != null && ElementFilter.forEqualTypes(typeElement).isAccepted(calledFromEnclosingType)) { - subTypes = toTypes(allMethods); + if (calledFromEnclosingType != null) { + if (typeElement instanceof TraitElement + || ElementFilter.forEqualTypes(typeElement).isAccepted(calledFromEnclosingType)) { + subTypes = toTypes(allMethods); + } } final ElementFilter filterForAccessible = forAccessibleTypeMembers(calledFromEnclosingType, subTypes); Set retval = filterForAccessible.filter(allMethods); diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml b/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml index dfbbd51c9882..b3aecee6890a 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml +++ b/php/php.editor/src/org/netbeans/modules/php/editor/resources/layer.xml @@ -341,6 +341,7 @@ + diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/verification/AddOverrideAttributeHint.java b/php/php.editor/src/org/netbeans/modules/php/editor/verification/AddOverrideAttributeHint.java new file mode 100644 index 000000000000..3b36aa856ed7 --- /dev/null +++ b/php/php.editor/src/org/netbeans/modules/php/editor/verification/AddOverrideAttributeHint.java @@ -0,0 +1,730 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.php.editor.verification; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.lexer.Token; +import org.netbeans.api.lexer.TokenHierarchy; +import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.api.lexer.TokenUtilities; +import org.netbeans.editor.BaseDocument; +import org.netbeans.modules.csl.api.EditList; +import org.netbeans.modules.csl.api.Hint; +import org.netbeans.modules.csl.api.HintFix; +import org.netbeans.modules.csl.api.OffsetRange; +import org.netbeans.modules.csl.spi.support.CancelSupport; +import org.netbeans.modules.php.api.PhpVersion; +import org.netbeans.modules.php.api.util.StringUtils; +import org.netbeans.modules.php.editor.CodeUtils; +import org.netbeans.modules.php.editor.api.ElementQuery; +import org.netbeans.modules.php.editor.api.QualifiedName; +import org.netbeans.modules.php.editor.api.elements.ElementFilter; +import org.netbeans.modules.php.editor.api.elements.MethodElement; +import org.netbeans.modules.php.editor.api.elements.PhpElement; +import org.netbeans.modules.php.editor.lexer.LexUtilities; +import org.netbeans.modules.php.editor.lexer.PHPTokenId; +import org.netbeans.modules.php.editor.model.ClassScope; +import org.netbeans.modules.php.editor.model.EnumScope; +import org.netbeans.modules.php.editor.model.FileScope; +import org.netbeans.modules.php.editor.model.InterfaceScope; +import org.netbeans.modules.php.editor.model.ModelUtils; +import org.netbeans.modules.php.editor.model.NamespaceScope; +import org.netbeans.modules.php.editor.model.TraitScope; +import org.netbeans.modules.php.editor.model.TypeScope; +import org.netbeans.modules.php.editor.model.impl.VariousUtils; +import org.netbeans.modules.php.editor.parser.PHPParseResult; +import org.netbeans.modules.php.editor.parser.astnodes.Attribute; +import org.netbeans.modules.php.editor.parser.astnodes.AttributeDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation; +import org.netbeans.modules.php.editor.parser.astnodes.EnumDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.Expression; +import org.netbeans.modules.php.editor.parser.astnodes.Identifier; +import org.netbeans.modules.php.editor.parser.astnodes.InterfaceDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.MethodDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.NamespaceDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.NamespaceName; +import org.netbeans.modules.php.editor.parser.astnodes.TraitDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor; +import org.openide.filesystems.FileObject; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.openide.util.Pair; + +/** + * Add/Remove #[\Override] Attributes. + */ +public class AddOverrideAttributeHint extends HintRule { + + private static final String HINT_ID = "Add.Override.Attribute"; // NOI18N + + @Override + public String getId() { + return HINT_ID; + } + + @Override + @NbBundle.Messages({ + "AddOverrideAttributeSuggestion.description=Add #[\\Override] Attribute" + }) + public String getDescription() { + return Bundle.AddOverrideAttributeSuggestion_description(); + } + + @Override + @NbBundle.Messages({ + "AddOverrideAttributeSuggestion.displayName=Add #[\\Override] Attribute" + }) + public String getDisplayName() { + return Bundle.AddOverrideAttributeSuggestion_displayName(); + } + + @Override + public void invoke(PHPRuleContext context, List hints) { + PHPParseResult phpParseResult = (PHPParseResult) context.parserResult; + if (phpParseResult.getProgram() == null) { + return; + } + if (CancelSupport.getDefault().isCancelled()) { + return; + } + final BaseDocument document = context.doc; + FileObject fileObject = phpParseResult.getSnapshot().getSource().getFileObject(); + if (fileObject != null && hasOverrideAttribute(fileObject)) { + FileScope fileScope = context.fileScope; + ElementQuery.Index index = context.getIndex(); + Map> types = getTypesAndInheritedMethods(index, fileScope); + final CheckVisitor checkVisitor = new CheckVisitor(types, fileScope); + phpParseResult.getProgram().accept(checkVisitor); + if (CancelSupport.getDefault().isCancelled()) { + return; + } + TokenHierarchy tokenHierarchy = phpParseResult.getSnapshot().getTokenHierarchy(); + TokenSequence ts = LexUtilities.getPHPTokenSequence(tokenHierarchy, 0); + addAddOverrideHints(checkVisitor, hints, document, fileObject, ts); + addRemoveOverrideHints(checkVisitor, hints, document, fileObject, ts); + } + } + + private Map> getTypesAndInheritedMethods(ElementQuery.Index index, FileScope fileScope) { + List declaredTypes = getDeclaredTypes(fileScope); + Map> typesAndInheritedMethods = new HashMap<>(); + for (TypeScope typeScope : declaredTypes) { + if (CancelSupport.getDefault().isCancelled()) { + Collections.emptyMap(); + } + Set inheritedMethods = toNames(getInheritedMethods(typeScope, index)); + typesAndInheritedMethods.put(typeScope.getName(), inheritedMethods); + } + return typesAndInheritedMethods; + } + + private List getDeclaredTypes(FileScope fileScope) { + List declaredTypes = new ArrayList<>(); + declaredTypes.addAll(ModelUtils.getDeclaredClasses(fileScope)); + declaredTypes.addAll(ModelUtils.getDeclaredInterfaces(fileScope)); + declaredTypes.addAll(ModelUtils.getDeclaredEnums(fileScope)); + return declaredTypes; + } + + private void addAddOverrideHints(CheckVisitor checkVisitor, List hints, BaseDocument document, FileObject fileObject, TokenSequence ts) { + for (MethodDeclaration method : checkVisitor.getMissingOverrideAttributeMethods()) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + Identifier methodName = method.getFunction().getFunctionName(); + AddOverrideFix fix = new AddOverrideFix(document, method, ts); + hints.add(new Hint(AddOverrideAttributeHint.this, + fix.getDescription(), + fileObject, + new OffsetRange(methodName.getStartOffset(), methodName.getEndOffset()), + Collections.singletonList(fix), 500)); + } + } + + private void addRemoveOverrideHints(CheckVisitor checkVisitor, List hints, BaseDocument document, FileObject fileObject, TokenSequence ts) { + for (InvalidOverrideAttributeMethod invalidOverrideMethod : checkVisitor.getInvalidOverrideAttributeMethods()) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + AttributeDeclaration overrideDeclaration = invalidOverrideMethod.getOverrideAttributeDeclaration(); + Expression attributeName = overrideDeclaration.getAttributeName(); + RemoveOverrideFix fix = new RemoveOverrideFix(document, invalidOverrideMethod, ts); + hints.add(new Hint(AddOverrideAttributeHint.this, + fix.getDescription(), + fileObject, + new OffsetRange(attributeName.getStartOffset(), attributeName.getEndOffset()), + Collections.singletonList(fix), 500)); + } + } + + protected boolean hasOverrideAttribute(FileObject fileObject) { + PhpVersion phpVersion = CodeUtils.getPhpVersion(fileObject); + return phpVersion.hasOverrideAttribute(); + } + + private Set getInheritedMethods(final TypeScope typeScope, final ElementQuery.Index index) { + Set inheritedMethods = new HashSet<>(); + Set accessibleSuperMethods = new HashSet<>(); + addAccessibleClassMethods(typeScope, index, accessibleSuperMethods); + addAccessibleInterfaceMethods(typeScope, index, accessibleSuperMethods); + addAccessibleAbstractTraitMethods(typeScope, index, accessibleSuperMethods); + // if some trait methods have #[\Override], also check wheter the parent class has them + // however, attributes are not indexed at the moment + // so, we should improve the indexer + // e.g. + // trait T { + // #[\Override] + // public function traitMethod(): void {} + // } + // class C { + // use T; + // } + inheritedMethods.addAll(accessibleSuperMethods); + return inheritedMethods; + } + + private void addAccessibleClassMethods(TypeScope typeScope, ElementQuery.Index index, Set accessibleSuperMethods) { + Collection superClasses = getSuperClasses(typeScope); + for (ClassScope cls : superClasses) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + Set accessibleMethods = index.getAccessibleMethods(cls, typeScope); + accessibleSuperMethods.addAll(accessibleMethods); + } + } + + private void addAccessibleInterfaceMethods(TypeScope typeScope, ElementQuery.Index index, Set accessibleSuperMethods) { + Collection superInterface = typeScope.getSuperInterfaceScopes(); + for (InterfaceScope interfaceScope : superInterface) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + accessibleSuperMethods.addAll(index.getAccessibleMethods(interfaceScope, typeScope)); + } + } + + private void addAccessibleAbstractTraitMethods(TypeScope typeScope, ElementQuery.Index index, Set accessibleSuperMethods) { + Collection traits = getTraits(typeScope); + for (TraitScope traitScope : traits) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + ElementFilter abstractMethodFilter = new ElementFilter() { + @Override + public boolean isAccepted(PhpElement element) { + if (element instanceof MethodElement) { + MethodElement method = (MethodElement) element; + return method.isAbstract(); + } + return false; + } + }; + // the following case is a fatal error because a trait is not a parent: + // "C::traitMethod() has #[\Override] attribute, but no matching parent method" + // trait T { + // public function traitMethod(): void {} + // } + // class C { + // use T; + // #[\Override] // fatal error + // public function traitMethod(): void {} + // } + Set accessibleTraitMethods = index.getAccessibleMethods(traitScope, typeScope); + accessibleSuperMethods.addAll(abstractMethodFilter.filter(accessibleTraitMethods)); + } + } + + private Collection getSuperClasses(TypeScope typeScope) { + if (typeScope instanceof ClassScope) { + return ((ClassScope) typeScope).getSuperClasses(); + } + return Collections.emptyList(); + } + + private Collection getTraits(TypeScope typeScope) { + if (typeScope instanceof ClassScope) { + return ((ClassScope) typeScope).getTraits(); + } else if (typeScope instanceof TraitScope) { + return ((TraitScope) typeScope).getTraits(); + } else if (typeScope instanceof EnumScope) { + return ((EnumScope) typeScope).getTraits(); + } + return Collections.emptyList(); + } + + private static Set toNames(Set elements) { + Set names = new HashSet<>(); + for (PhpElement elem : elements) { + String name = elem.getName(); + if (!StringUtils.isEmpty(name)) { + names.add(name); + } + } + return names; + } + + //~ inner classes + private static class CheckVisitor extends DefaultVisitor { + + private final Map> typesAndInheritedMethods; + private final Set missingOverrideAttributeMethods = new HashSet<>(); + private final Set invalidOverrideAttributeMethods = new HashSet<>(); + private String currentTypeName = null; + private NamespaceDeclaration currentNamespace = null; + private final FileScope fileScope; + + public CheckVisitor(Map> typesAndInheritedMethods, FileScope fileScope) { + this.typesAndInheritedMethods = new HashMap<>(typesAndInheritedMethods); + this.fileScope = fileScope; + } + + @Override + public void visit(NamespaceDeclaration namespace) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + currentNamespace = namespace; + super.visit(namespace); + } + + @Override + public void visit(ClassInstanceCreation node) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + // anonymous class can be nested + // so, keep the currernt type name to the variable temporally + // e.g. + // class C implements I { + // public function interfaceMethod1(): void {} + // public function method(): void { + // $anon = new class() implements I { + // public function interfaceMethod1(): void {} + // public function nestedAnonymousClass(): void { + // $nestedAnon = new class() implements I { + // public function interfaceMethod1(): void {} + // }; + // } + // public function interfaceMethod2(): void {} + // }; + // } + // public function interfaceMethod2(): void {} + // } + String originalCurrentTypeName = currentTypeName; + currentTypeName = CodeUtils.extractClassName(node); + super.visit(node); + currentTypeName = originalCurrentTypeName; + } + + @Override + public void visit(ClassDeclaration node) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + currentTypeName = node.getName().getName(); + super.visit(node); + } + + @Override + public void visit(InterfaceDeclaration node) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + currentTypeName = node.getName().getName(); + super.visit(node); + } + + @Override + public void visit(EnumDeclaration node) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + currentTypeName = node.getName().getName(); + super.visit(node); + } + + @Override + public void visit(TraitDeclaration node) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + // don't check traits + currentTypeName = null; + super.visit(node); + } + + @Override + public void visit(MethodDeclaration node) { + if (CancelSupport.getDefault().isCancelled()) { + return; + } + String methodName = node.getFunction().getFunctionName().getName(); + if (currentTypeName != null) { + Set inheritedMethods = typesAndInheritedMethods.get(currentTypeName); + if (inheritedMethods == null) { + return; + } + Pair overrideAttribute = getOverrideAttribute(node); + boolean hasOverride = overrideAttribute != null; + boolean isInheritedMethod = inheritedMethods.contains(methodName); + if (isInheritedMethod && !hasOverride) { + missingOverrideAttributeMethods.add(node); + } else if (!isInheritedMethod && hasOverride) { + invalidOverrideAttributeMethods.add(new InvalidOverrideAttributeMethod(methodName, overrideAttribute)); + } + } + super.visit(node); + } + + @CheckForNull + private Pair getOverrideAttribute(MethodDeclaration method) { + for (Attribute attribute : method.getAttributes()) { + if (CancelSupport.getDefault().isCancelled()) { + return null; + } + for (AttributeDeclaration attributeDeclaration : attribute.getAttributeDeclarations()) { + if (CancelSupport.getDefault().isCancelled()) { + return null; + } + Expression attributeNameExpression = attributeDeclaration.getAttributeName(); + String attributeName = CodeUtils.extractQualifiedName(attributeNameExpression); + if (isOverrideAttibute(attributeName, attributeNameExpression.getStartOffset())) { + return Pair.of(attribute, attributeDeclaration); + } + } + } + return null; + } + + public Set getInvalidOverrideAttributeMethods() { + return Collections.unmodifiableSet(invalidOverrideAttributeMethods); + } + + public Set getMissingOverrideAttributeMethods() { + return Collections.unmodifiableSet(missingOverrideAttributeMethods); + } + + private boolean isOverrideAttibute(String attributeName, int offset) { + if (CodeUtils.OVERRIDE_ATTRIBUTE_FQ_NAME.equals(attributeName)) { + return true; + } + if (CodeUtils.OVERRIDE_ATTRIBUTE_NAME.equals(attributeName)) { + Collection declaredNamespaces = fileScope.getDeclaredNamespaces(); + if (isGlobalNamespace()) { + return true; + } + NamespaceName name = currentNamespace.getName(); // null is checked in isGlobalNamespace() + QualifiedName currentNamespaceName = QualifiedName.create(name); + NamespaceScope namespaceScope = null; + for (NamespaceScope declaredNamespace : declaredNamespaces) { + QualifiedName namespaceName = declaredNamespace.getNamespaceName(); + if (currentNamespaceName.equals(namespaceName)) { + namespaceScope = declaredNamespace; + break; + } + } + if (namespaceScope != null) { + // check FQ name because there may be `use \Override;` + QualifiedName fullyQualifiedName = VariousUtils.getFullyQualifiedName(QualifiedName.create(CodeUtils.OVERRIDE_ATTRIBUTE_NAME), offset, namespaceScope); + if (CodeUtils.OVERRIDE_ATTRIBUTE_FQ_NAME.equals(fullyQualifiedName.toString())) { + return true; + } + } + } + return false; + } + + private boolean isGlobalNamespace() { + return currentNamespace == null + || currentNamespace.getName() == null; + } + } + + private static boolean isComma(Token token) { + return token.id() == PHPTokenId.PHP_TOKEN + && TokenUtilities.textEquals(token.text(), ","); // NOI18N + } + + private static boolean isWhitespace(Token token) { + return token.id() == PHPTokenId.WHITESPACE; + } + + //~ inner classes + private static final class InvalidOverrideAttributeMethod { + + private final String methodName; + private final Attribute attribute; + private final AttributeDeclaration overrideAttributeDeclaration; + + public InvalidOverrideAttributeMethod(String methodName, Pair attribute) { + this.methodName = methodName; + this.attribute = attribute.first(); + this.overrideAttributeDeclaration = attribute.second(); + } + + public String getMethodName() { + return methodName; + } + + public Attribute getAttribute() { + return attribute; + } + + public AttributeDeclaration getOverrideAttributeDeclaration() { + return overrideAttributeDeclaration; + } + } + + private static class AddOverrideFix implements HintFix { + + private static final List COMMENT_AND_WS_TOKEN_IDS = Arrays.asList( + PHPTokenId.WHITESPACE, + PHPTokenId.PHP_LINE_COMMENT, + PHPTokenId.PHPDOC_COMMENT_START, + PHPTokenId.PHPDOC_COMMENT, + PHPTokenId.PHPDOC_COMMENT_END, + PHPTokenId.PHP_COMMENT_START, + PHPTokenId.PHP_COMMENT, + PHPTokenId.PHP_COMMENT_END + ); + + private final BaseDocument document; + private final MethodDeclaration methodDeclaration; + private final TokenSequence ts; + + public AddOverrideFix(BaseDocument document, MethodDeclaration methodDeclaration, TokenSequence ts) { + this.document = document; + this.methodDeclaration = methodDeclaration; + this.ts = ts; + } + + @Override + @Messages({ + "AddOverrideFix_description=Add \"#[\\Override]\" Attribute" + }) + public String getDescription() { + return Bundle.AddOverrideFix_description(); + } + + @Override + public void implement() throws Exception { + EditList edits = new EditList(document); + int offset = methodDeclaration.getStartOffset(); + List attributes = methodDeclaration.getAttributes(); + ts.move(offset); + CharSequence indent = CodeUtils.EMPTY_STRING; + if (ts.movePrevious()) { + if (isWhitespace(ts.token())) { + CharSequence text = ts.token().text(); + int lastIndex = TokenUtilities.lastIndexOf(text, CodeUtils.NEW_LINE); + if (lastIndex != -1) { + indent = text.subSequence(lastIndex + 1, text.length()); + } + } + } + if (attributes.isEmpty()) { + edits.replace(offset, 0, CodeUtils.OVERRIDE_ATTRIBUTE + CodeUtils.NEW_LINE + indent, false, 0); + } else { + // #[Attr] // comment + // #[\Override] + // public function example(): void {} + Attribute lastAttribute = attributes.get(attributes.size() - 1); + offset = lastAttribute.getEndOffset(); + ts.move(offset); + if (ts.moveNext()) { + Token findNext = LexUtilities.findNext(ts, COMMENT_AND_WS_TOKEN_IDS); + if (findNext != null) { + offset = ts.offset(); + } + } + edits.replace(offset, 0, CodeUtils.OVERRIDE_ATTRIBUTE + CodeUtils.NEW_LINE + indent, false, 0); + } + edits.apply(); + } + + @Override + public boolean isSafe() { + return true; + } + + @Override + public boolean isInteractive() { + return false; + } + } + + private static class RemoveOverrideFix implements HintFix { + + private final BaseDocument document; + private final InvalidOverrideAttributeMethod invalidOverrideMethod; + private final TokenSequence ts; + + public RemoveOverrideFix(BaseDocument document, InvalidOverrideAttributeMethod invalidOverrideMethod, TokenSequence ts) { + this.document = document; + this.invalidOverrideMethod = invalidOverrideMethod; + this.ts = ts; + } + + @Override + @Messages({ + "# {0} - method name", + "RemoveOverrideFix_description=Remove \"#[\\Override]\" Attribute from \"{0}\" method" + }) + public String getDescription() { + String methodName = invalidOverrideMethod.getMethodName(); + return Bundle.RemoveOverrideFix_description(methodName); + } + + @Override + public void implement() throws Exception { + EditList edits = new EditList(document); + OffsetRange removalRange = getRemovalRange(invalidOverrideMethod); + if (removalRange != OffsetRange.NONE) { + edits.replace(removalRange.getStart(), removalRange.getLength(), CodeUtils.EMPTY_STRING, false, 0); + edits.apply(); + } + } + + private OffsetRange getRemovalRange(InvalidOverrideAttributeMethod invalidAttributedMethod) { + Attribute attribute = invalidAttributedMethod.getAttribute(); + if (attribute.getAttributeDeclarations().size() == 1) { + return getRemovalRange(attribute); + } + + AttributeDeclaration overrideDeclaration = invalidAttributedMethod.getOverrideAttributeDeclaration(); + // #[\Override, Attribute1], #[Attribute1, \Override], #[Attribute1, \Override, Attribute2] + int startOffset = overrideDeclaration.getStartOffset(); + AttributeDeclaration previousDeclaration = null; + for (Iterator iterator = attribute.getAttributeDeclarations().iterator(); iterator.hasNext();) { + AttributeDeclaration attributeDeclaration = iterator.next(); + if (attributeDeclaration.getStartOffset() == startOffset) { + int endOffset; + if (!iterator.hasNext()) { + // e.g. #[Atrr, \Override] + endOffset = attributeDeclaration.getEndOffset(); + ts.move(startOffset); + if (previousDeclaration != null) { + startOffset = previousDeclaration.getEndOffset(); + } + int commentEnd = getPreviousCommentEnd(startOffset); + if (startOffset != commentEnd) { + // e.g. #[Atrr, /* comment */ \Override] + startOffset = commentEnd; + // check comma + // e.g. + // #[ + // Attr, // comment + // \Override, + // ] + endOffset = getCommaEnd(endOffset, attribute); + } + } else { + // e.g. #[\Override, Atrr] + endOffset = iterator.next().getStartOffset(); + endOffset = getNextCommentStart(overrideDeclaration, endOffset); + } + return new OffsetRange(startOffset, endOffset); + } + previousDeclaration = attributeDeclaration; + } + return OffsetRange.NONE; + } + + private int getNextCommentStart(AttributeDeclaration overrideDeclaration, int endOffset) { + int end = endOffset; + ts.move(overrideDeclaration.getEndOffset()); + while (ts.moveNext() && ts.offset() < endOffset) { + if (isComma(ts.token()) || isWhitespace(ts.token())) { + continue; + } + end = ts.offset(); + break; + } + return end; + } + + private int getPreviousCommentEnd(int startOffset) { + // check comments + int start = startOffset; + while (ts.movePrevious() && ts.offset() >= startOffset) { + if (isComma(ts.token()) || isWhitespace(ts.token())) { + continue; + } + start = ts.offset() + ts.token().length(); + if (TokenUtilities.endsWith(ts.token().text(), CodeUtils.NEW_LINE)) { + // line comment may have a new line + start--; + } + break; + } + return start; + } + + private int getCommaEnd(int endOffset, Attribute attribute) { + int end = endOffset; + ts.move(end); + while (ts.moveNext() && ts.offset() < attribute.getEndOffset()) { + if (isComma(ts.token())) { + end = ts.offset() + ts.token().length(); + break; + } else { + if (isWhitespace(ts.token())) { + continue; + } + break; + } + } + return end; + } + + private OffsetRange getRemovalRange(Attribute attribute) { + // #[\Override] + int endOffset = attribute.getEndOffset(); + ts.move(endOffset); + if (ts.moveNext()) { + if (isWhitespace(ts.token())) { + endOffset = ts.offset() + ts.token().text().length(); + } + } + return new OffsetRange(attribute.getStartOffset(), endOffset); + } + + @Override + public boolean isSafe() { + return true; + } + + @Override + public boolean isInteractive() { + return false; + } + } +} diff --git a/php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/AbstractClass.php b/php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/AbstractClass.php new file mode 100644 index 000000000000..fe1e1cea762d --- /dev/null +++ b/php/php.editor/test/unit/data/testfiles/verification/AddOverrideAttributeHint/AbstractClass.php @@ -0,0 +1,32 @@ + Date: Fri, 22 Dec 2023 10:54:53 +0900 Subject: [PATCH 3/4] PHP 8.3 Support: Marking overridden methods (#[\Override]) (Part 2) - https://github.com/apache/netbeans/issues/6701 - https://wiki.php.net/rfc/marking_overriden_methods - Fix the code generator, code completion, and `ImplementAbstractMethodsHintError` - Add/Fix unit tests --- .../modules/php/editor/codegen/CGSInfo.java | 71 ++++++--- .../codegen/InvocationContextResolver.java | 10 +- .../codegen/SinglePropertyMethodCreator.java | 23 ++- .../editor/completion/PHPCodeCompletion.java | 56 +++++-- .../editor/completion/PHPCompletionItem.java | 23 ++- .../php/editor/elements/ElementUtils.java | 46 ++++++ .../editor/elements/MethodElementImpl.java | 34 +++-- .../php/editor/elements/TypeElementImpl.java | 4 +- .../nodes/ClassInstanceCreationInfo.java | 9 +- .../ImplementAbstractMethodsHintError.java | 5 +- .../structure/anonymousClassInClassScope.pass | 8 +- .../anonymousClassInNamespaceScope.pass | 6 +- .../structure/anonymousClassInTraitScope.pass | 8 +- .../php80ConstructorPropertyPromotion.pass | 2 +- .../testOverrideAttribute01.php | 48 ++++++ ...bute01.php.testOverrideAttribute01.codegen | 38 +++++ ....php.testOverrideAttribute01_PHP82.codegen | 26 ++++ .../testOverrideAttributeAnonClass01.php | 48 ++++++ ...p.testOverrideAttributeAnonClass01.codegen | 38 +++++ ...OverrideAttributeAnonClass01_PHP82.codegen | 26 ++++ .../testOverrideAttributeEnum01.php | 40 +++++ ...01.php.testOverrideAttributeEnum01.codegen | 29 ++++ ....testOverrideAttributeEnum01_PHP82.codegen | 22 +++ .../testOverrideAttribute01.php | 49 ++++++ ...01.php.testOverrideAttribute01.cccustomtpl | 76 ++++++++++ ....testOverrideAttribute01_PHP82.cccustomtpl | 64 ++++++++ .../testOverrideAttributeAnonClass01.php | 49 ++++++ ...stOverrideAttributeAnonClass01.cccustomtpl | 76 ++++++++++ ...rideAttributeAnonClass01_PHP82.cccustomtpl | 64 ++++++++ .../testOverrideAttributeEnum01.php | 41 +++++ ...hp.testOverrideAttributeEnum01.cccustomtpl | 46 ++++++ ...tOverrideAttributeEnum01_PHP82.cccustomtpl | 39 +++++ ...80ConstructorPropertyPromotion.php.indexed | 2 +- ...ImplementAbstractMethodsHintFix02_02.hints | 3 +- .../testOverrideAttribute01.php | 47 ++++++ ...ibute01.php.testOverrideAttribute_01.hints | 5 + ...te01.php.testOverrideAttribute_Fix01.fixed | 101 +++++++++++++ ...1.php.testOverrideAttribute_PHP82_01.hints | 5 + ...hp.testOverrideAttribute_PHP82_Fix01.fixed | 90 +++++++++++ .../testOverrideAttributeAnonClass.php | 47 ++++++ ...hp.testOverrideAttributeAnonClass_01.hints | 4 + ...testOverrideAttributeAnonClass_Fix01.fixed | 101 +++++++++++++ .../testOverrideAttributeEnum.php | 39 +++++ ...num.php.testOverrideAttributeEnum_01.hints | 4 + ....php.testOverrideAttributeEnum_Fix01.fixed | 73 +++++++++ .../SelectedPropertyMethodsCreatorTest.java | 48 ++++++ .../completion/PHP83CodeCompletionTest.java | 31 ++++ ...ImplementAbstractMethodsHintErrorTest.java | 140 +++++++++++------- 48 files changed, 1744 insertions(+), 120 deletions(-) create mode 100644 php/php.editor/src/org/netbeans/modules/php/editor/elements/ElementUtils.java create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttribute01/testOverrideAttribute01.php create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttribute01/testOverrideAttribute01.php.testOverrideAttribute01.codegen create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttribute01/testOverrideAttribute01.php.testOverrideAttribute01_PHP82.codegen create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttributeAnonClass01/testOverrideAttributeAnonClass01.php create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttributeAnonClass01/testOverrideAttributeAnonClass01.php.testOverrideAttributeAnonClass01.codegen create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttributeAnonClass01/testOverrideAttributeAnonClass01.php.testOverrideAttributeAnonClass01_PHP82.codegen create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttributeEnum01/testOverrideAttributeEnum01.php create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttributeEnum01/testOverrideAttributeEnum01.php.testOverrideAttributeEnum01.codegen create mode 100644 php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttributeEnum01/testOverrideAttributeEnum01.php.testOverrideAttributeEnum01_PHP82.codegen create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttribute01/testOverrideAttribute01.php create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttribute01/testOverrideAttribute01.php.testOverrideAttribute01.cccustomtpl create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttribute01/testOverrideAttribute01.php.testOverrideAttribute01_PHP82.cccustomtpl create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttributeAnonClass01/testOverrideAttributeAnonClass01.php create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttributeAnonClass01/testOverrideAttributeAnonClass01.php.testOverrideAttributeAnonClass01.cccustomtpl create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttributeAnonClass01/testOverrideAttributeAnonClass01.php.testOverrideAttributeAnonClass01_PHP82.cccustomtpl create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttributeEnum01/testOverrideAttributeEnum01.php create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttributeEnum01/testOverrideAttributeEnum01.php.testOverrideAttributeEnum01.cccustomtpl create mode 100644 php/php.editor/test/unit/data/testfiles/completion/lib/php83/testOverrideAttributeEnum01/testOverrideAttributeEnum01.php.testOverrideAttributeEnum01_PHP82.cccustomtpl create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php.testOverrideAttribute_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php.testOverrideAttribute_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php.testOverrideAttribute_PHP82_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php.testOverrideAttribute_PHP82_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttributeAnonClass.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttributeAnonClass.php.testOverrideAttributeAnonClass_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttributeAnonClass.php.testOverrideAttributeAnonClass_Fix01.fixed create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttributeEnum.php create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttributeEnum.php.testOverrideAttributeEnum_01.hints create mode 100644 php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttributeEnum.php.testOverrideAttributeEnum_Fix01.fixed diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/CGSInfo.java b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/CGSInfo.java index 943690ed459a..99933b9d0f16 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/CGSInfo.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/CGSInfo.java @@ -26,6 +26,8 @@ import java.util.Locale; import java.util.Set; import javax.swing.text.JTextComponent; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.modules.csl.spi.ParserResult; import org.netbeans.modules.parsing.api.ParserManager; import org.netbeans.modules.parsing.api.ResultIterator; @@ -58,7 +60,9 @@ import org.netbeans.modules.php.editor.parser.astnodes.Block; import org.netbeans.modules.php.editor.parser.astnodes.BodyDeclaration; import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation; import org.netbeans.modules.php.editor.parser.astnodes.Comment; +import org.netbeans.modules.php.editor.parser.astnodes.EnumDeclaration; import org.netbeans.modules.php.editor.parser.astnodes.FieldsDeclaration; import org.netbeans.modules.php.editor.parser.astnodes.FormalParameter; import org.netbeans.modules.php.editor.parser.astnodes.Identifier; @@ -251,9 +255,9 @@ private void initProperties(ResultIterator resultIterator) throws ParseException PHPParseResult info = (PHPParseResult) resultIterator.getParserResult(); if (info != null) { int caretOffset = textComp.getCaretPosition(); - TypeDeclaration typeDecl = findEnclosingClassOrTrait(info, caretOffset); + ASTNode typeDecl = findEnclosingType(info, caretOffset); if (typeDecl != null) { - className = typeDecl.getName().getName(); + className = getTypeName(typeDecl); if (className != null) { FileObject fileObject = info.getSnapshot().getSource().getFileObject(); Index index = ElementQueryFactory.getIndexQuery(info); @@ -262,17 +266,19 @@ private void initProperties(ResultIterator resultIterator) throws ParseException QualifiedName.create(className), caretOffset, info.getModel().getVariableScope(caretOffset)); - Set classes = forFilesFilter.filter(index.getClasses(NameKind.exact(fullyQualifiedName))); - for (ClassElement classElement : classes) { - ElementFilter forNotDeclared = ElementFilter.forExcludedElements(index.getDeclaredMethods(classElement)); + Set types = forFilesFilter.filter(index.getTypes(NameKind.exact(fullyQualifiedName))); + for (TypeElement typeElement : types) { + ElementFilter forNotDeclared = ElementFilter.forExcludedElements(index.getDeclaredMethods(typeElement)); final Set accessibleMethods = new HashSet<>(); - accessibleMethods.addAll(forNotDeclared.filter(index.getAccessibleMethods(classElement, classElement))); - accessibleMethods.addAll( - ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getConstructors(classElement)))); + accessibleMethods.addAll(forNotDeclared.filter(index.getAccessibleMethods(typeElement, typeElement))); + if (typeElement instanceof ClassElement) { + accessibleMethods.addAll( + ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getConstructors((ClassElement) typeElement)))); + } accessibleMethods.addAll( - ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getAccessibleMagicMethods(classElement)))); + ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getAccessibleMagicMethods(typeElement)))); final Set preferedTypes = forFilesFilter.prefer(ElementTransformation.toMemberTypes().transform(accessibleMethods)); - final TreeElement enclosingType = index.getInheritedTypesAsTree(classElement, preferedTypes); + final TreeElement enclosingType = index.getInheritedTypesAsTree(typeElement, preferedTypes); final List methodProperties = new ArrayList<>(); final Set methods = ElementFilter.forMembersOfTypes(preferedTypes).filter(accessibleMethods); for (final MethodElement methodElement : methods) { @@ -290,6 +296,11 @@ private void initProperties(ResultIterator resultIterator) throws ParseException PropertiesVisitor visitor = new PropertiesVisitor(existingGetters, existingSetters, Utils.getRoot(info)); visitor.scan(typeDecl); + if (typeDecl instanceof EnumDeclaration) { + // Enum can't have a constructor + // to avoid adding the list of code generators, change this + hasConstructor = true; + } String propertyName; boolean existGetter, existSetter; for (Property property : getProperties()) { @@ -311,24 +322,50 @@ private void initProperties(ResultIterator resultIterator) throws ParseException } /** - * Find out class enclosing caret - * @param info + * Find out the type enclosing the caret. + * + * @param info parser result * @param offset caret offset - * @return class declaration or null + * @return type declaration or class instance creation(anonymous class), + * otherwise {@code null} */ - private TypeDeclaration findEnclosingClassOrTrait(ParserResult info, int offset) { + @CheckForNull + private ASTNode findEnclosingType(ParserResult info, int offset) { List nodes = NavUtils.underCaret(info, offset); int count = nodes.size(); if (count > 2) { // the cursor has to be in class block see issue #142417 ASTNode declaration = nodes.get(count - 2); ASTNode block = nodes.get(count - 1); - if (block instanceof Block && (declaration instanceof ClassDeclaration || declaration instanceof TraitDeclaration)) { - return (TypeDeclaration) declaration; + if (block instanceof Block && isValidEnclosingType(declaration)) { + return declaration; } } return null; } + private boolean isValidEnclosingType(ASTNode typeDeclaration) { + return typeDeclaration instanceof ClassDeclaration + || typeDeclaration instanceof TraitDeclaration + || typeDeclaration instanceof EnumDeclaration + || (typeDeclaration instanceof ClassInstanceCreation && ((ClassInstanceCreation) typeDeclaration).isAnonymous()); + } + + @CheckForNull + private String getTypeName(@NullAllowed ASTNode typeDeclaration) { + if (typeDeclaration == null) { + return null; + } + String typeName = null; + if (typeDeclaration instanceof TypeDeclaration) { + typeName = ((TypeDeclaration) typeDeclaration).getName().getName(); + } else if (typeDeclaration instanceof ClassInstanceCreation) { + typeName = CodeUtils.extractClassName((ClassInstanceCreation) typeDeclaration); + } else { + assert false : "Expected: TypeDeclaration or ClassInstanceCreation, but got" + typeDeclaration.getClass(); // NOI18N + } + return typeName; + } + private class PropertiesVisitor extends DefaultVisitor { private final List existingGetters; @@ -432,7 +469,7 @@ private boolean canBeNull(final PHPDocTypeTag typeTag) { boolean canBeNull = false; if (typeTag.getTypes().size() > 1) { for (PHPDocTypeNode typeNode : typeTag.getTypes()) { - String type = typeNode.getValue().toLowerCase(new Locale("en_US")); // NOI18N + String type = typeNode.getValue().toLowerCase(Locale.ROOT); if (type.equals(Type.NULL)) { canBeNull = true; break; diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/InvocationContextResolver.java b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/InvocationContextResolver.java index 435193ca830d..c67d8fa3bce2 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/InvocationContextResolver.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/InvocationContextResolver.java @@ -34,11 +34,15 @@ import org.netbeans.modules.php.editor.parser.astnodes.ASTNode; import org.netbeans.modules.php.editor.parser.astnodes.Block; import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation; import org.netbeans.modules.php.editor.parser.astnodes.EmptyStatement; +import org.netbeans.modules.php.editor.parser.astnodes.EnumDeclaration; import org.netbeans.modules.php.editor.parser.astnodes.FunctionDeclaration; +import org.netbeans.modules.php.editor.parser.astnodes.InterfaceDeclaration; import org.netbeans.modules.php.editor.parser.astnodes.MethodDeclaration; import org.netbeans.modules.php.editor.parser.astnodes.NamespaceDeclaration; import org.netbeans.modules.php.editor.parser.astnodes.Program; +import org.netbeans.modules.php.editor.parser.astnodes.TraitDeclaration; /** * @@ -93,7 +97,11 @@ public enum InvocationContext { CLASS { @Override boolean isExactlyIn(ASTNode lastNode) { - return lastNode instanceof ClassDeclaration; + return lastNode instanceof ClassDeclaration + || lastNode instanceof TraitDeclaration + || lastNode instanceof InterfaceDeclaration + || lastNode instanceof EnumDeclaration + || (lastNode instanceof ClassInstanceCreation && ((ClassInstanceCreation) lastNode).isAnonymous()); } }, diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SinglePropertyMethodCreator.java b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SinglePropertyMethodCreator.java index 545bf7f764fb..f1c2c727b36d 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SinglePropertyMethodCreator.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SinglePropertyMethodCreator.java @@ -19,10 +19,14 @@ package org.netbeans.modules.php.editor.codegen; import java.util.ArrayList; +import java.util.Collection; import org.netbeans.modules.php.api.PhpVersion; +import org.netbeans.modules.php.editor.CodeUtils; import org.netbeans.modules.php.editor.api.elements.BaseFunctionElement; import org.netbeans.modules.php.editor.api.elements.MethodElement; +import org.netbeans.modules.php.editor.api.elements.TypeResolver; import static org.netbeans.modules.php.editor.codegen.CGSGenerator.NEW_LINE; +import org.netbeans.modules.php.editor.elements.ElementUtils; import org.netbeans.modules.php.editor.model.impl.Type; /** @@ -49,21 +53,32 @@ public InheritedMethodCreator(CGSInfo cgsInfo) { public String create(MethodProperty property) { final StringBuilder inheritedMethod = new StringBuilder(); final MethodElement method = property.getMethod(); - if (method.isAbstract() || method.isMagic() || method.getType().isInterface()) { + inheritedMethod.append(getOverrideAttribute(method)); // PHP 8.3 + Collection returnTypes = method.getReturnTypes(); + if (method.isAbstract() || method.isMagic() || method.getType().isInterface() || method.getType().isTrait() + || ElementUtils.isVoidOrNeverType(returnTypes)) { inheritedMethod.append(method.asString( BaseFunctionElement.PrintAs.DeclarationWithEmptyBody, cgsInfo.createTypeNameResolver(method), - cgsInfo.getPhpVersion()).replace("abstract ", "")); //NOI18N; + cgsInfo.getPhpVersion()).replace("abstract ", CodeUtils.EMPTY_STRING)); //NOI18N; } else { inheritedMethod.append(method.asString( BaseFunctionElement.PrintAs.DeclarationWithParentCallInBody, cgsInfo.createTypeNameResolver(method), - cgsInfo.getPhpVersion()).replace("abstract ", "")); //NOI18N; + cgsInfo.getPhpVersion()).replace("abstract ", CodeUtils.EMPTY_STRING)); //NOI18N; } inheritedMethod.append(NEW_LINE); return inheritedMethod.toString(); } + private String getOverrideAttribute(MethodElement method) { + if (!method.isMagic() + && (!method.getType().isTrait() || ElementUtils.isAbstractTraitMethod(method)) + && cgsInfo.getPhpVersion().hasOverrideAttribute()) { + return CodeUtils.OVERRIDE_ATTRIBUTE + CodeUtils.NEW_LINE; + } + return CodeUtils.EMPTY_STRING; + } } abstract class SinglePropertyMethodCreatorImpl implements SinglePropertyMethodCreator { @@ -83,7 +98,7 @@ protected String getMethodName(Property property) { String changedName = cgsInfo.getHowToGenerate() == CGSGenerator.GenWay.WITHOUT_UNDERSCORE ? CodegenUtils.upFirstLetterWithoutUnderscore(property.getName()) : CodegenUtils.upFirstLetter(property.getName()); - return CodegenUtils.getUnusedMethodName(new ArrayList(), changedName); + return CodegenUtils.getUnusedMethodName(new ArrayList<>(), changedName); } protected String getAccessModifier() { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java index d39e4ed85cef..a8fa21d3e27b 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java @@ -717,17 +717,17 @@ private void autoCompleteMethodName(ParserResult info, int caretOffset, final PH String fullyQualifiedClassName = VariousUtils.qualifyTypeNames(clsName, request.anchor, namespaceScope); if (fullyQualifiedClassName != null) { final FileObject fileObject = request.result.getSnapshot().getSource().getFileObject(); - final ElementFilter classFilter = ElementFilter.allOf( + final ElementFilter typeFilter = ElementFilter.allOf( ElementFilter.forFiles(fileObject), ElementFilter.allOf(superTypeIndices)); - Set classes = classFilter.filter(request.index.getClasses(NameKind.exact(fullyQualifiedClassName))); - for (ClassElement classElement : classes) { + Set types = typeFilter.filter(request.index.getTypes(NameKind.exact(fullyQualifiedClassName))); + for (TypeElement typeElement : types) { if (CancelSupport.getDefault().isCancelled()) { return; } ElementFilter methodFilter = ElementFilter.allOf( - ElementFilter.forExcludedNames(toNames(request.index.getDeclaredMethods(classElement)), PhpElementKind.METHOD), + ElementFilter.forExcludedNames(toNames(request.index.getDeclaredMethods(typeElement)), PhpElementKind.METHOD), ElementFilter.forName(NameKind.caseInsensitivePrefix(QualifiedName.create(request.prefix)))); - Set accessibleMethods = methodFilter.filter(request.index.getAccessibleMethods(classElement, classElement)); + Set accessibleMethods = methodFilter.filter(request.index.getAccessibleMethods(typeElement, typeElement)); for (MethodElement method : accessibleMethods) { if (CancelSupport.getDefault().isCancelled()) { return; @@ -736,7 +736,7 @@ private void autoCompleteMethodName(ParserResult info, int caretOffset, final PH completionResult.add(PHPCompletionItem.MethodDeclarationItem.forMethodName(method, request)); } } - Set magicMethods = methodFilter.filter(request.index.getAccessibleMagicMethods(classElement)); + Set magicMethods = methodFilter.filter(request.index.getAccessibleMagicMethods(typeElement)); for (MethodElement magicMethod : magicMethods) { if (magicMethod != null) { completionResult.add(PHPCompletionItem.MethodDeclarationItem.forMethodName(magicMethod, request)); @@ -1232,17 +1232,17 @@ private void autoCompleteInClassContext( String fullyQualifiedClassName = VariousUtils.qualifyTypeNames(clsName, request.anchor, namespaceScope); if (fullyQualifiedClassName != null) { final FileObject fileObject = request.result.getSnapshot().getSource().getFileObject(); - final ElementFilter classFilter = ElementFilter.allOf( + final ElementFilter typeFilter = ElementFilter.allOf( ElementFilter.forFiles(fileObject), ElementFilter.allOf(superTypeIndices)); - Set classes = classFilter.filter(request.index.getClasses(NameKind.exact(fullyQualifiedClassName))); - for (ClassElement classElement : classes) { + Set types = typeFilter.filter(request.index.getTypes(NameKind.exact(fullyQualifiedClassName))); + for (TypeElement typeElement : types) { if (CancelSupport.getDefault().isCancelled()) { return; } ElementFilter methodFilter = ElementFilter.allOf( - ElementFilter.forExcludedNames(toNames(request.index.getDeclaredMethods(classElement)), PhpElementKind.METHOD), + ElementFilter.forExcludedNames(toNames(request.index.getDeclaredMethods(typeElement)), PhpElementKind.METHOD), ElementFilter.forName(NameKind.caseInsensitivePrefix(QualifiedName.create(request.prefix)))); - Set accessibleMethods = methodFilter.filter(request.index.getAccessibleMethods(classElement, classElement)); + Set accessibleMethods = methodFilter.filter(request.index.getAccessibleMethods(typeElement, typeElement)); for (MethodElement method : accessibleMethods) { if (CancelSupport.getDefault().isCancelled()) { return; @@ -1251,7 +1251,7 @@ private void autoCompleteInClassContext( completionResult.add(PHPCompletionItem.MethodDeclarationItem.getDeclarationItem(method, request)); } } - Set magicMethods = methodFilter.filter(request.index.getAccessibleMagicMethods(classElement)); + Set magicMethods = methodFilter.filter(request.index.getAccessibleMagicMethods(typeElement)); for (MethodElement magicMethod : magicMethods) { if (CancelSupport.getDefault().isCancelled()) { return; @@ -2020,6 +2020,10 @@ private static EnclosingClass findEnclosingClass(ParserResult info, int offset) && node.getEndOffset() > offset) { return EnclosingClass.forClassDeclaration((ClassDeclaration) node); } + if (node instanceof EnumDeclaration + && node.getEndOffset() > offset) { + return EnclosingClass.forEnumDeclaration((EnumDeclaration) node); + } if (node instanceof ClassInstanceCreation && node.getEndOffset() > offset) { ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation) node; @@ -2802,6 +2806,34 @@ public String extractUnqualifiedSuperClassName() { }; } + static EnclosingClass forEnumDeclaration(final EnumDeclaration enumDeclaration) { + return new EnclosingClass() { + @Override + public String getClassName() { + return enumDeclaration.getName().getName(); + } + + @Override + public Expression getSuperClass() { + return null; + } + + @Override + public List getInterfaces() { + return enumDeclaration.getInterfaes(); + } + + @Override + public String extractClassName() { + return CodeUtils.extractTypeName(enumDeclaration); + } + + @Override + public String extractUnqualifiedSuperClassName() { + return null; + } + }; + } } } diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCompletionItem.java b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCompletionItem.java index 524cccd53f72..07ad3343e610 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCompletionItem.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCompletionItem.java @@ -104,6 +104,7 @@ import org.netbeans.modules.php.editor.NavUtils; import org.netbeans.modules.php.editor.api.elements.EnumCaseElement; import org.netbeans.modules.php.editor.api.elements.EnumElement; +import org.netbeans.modules.php.editor.elements.ElementUtils; import org.netbeans.modules.php.editor.options.CodeCompletionPanel.CodeCompletionType; import org.netbeans.modules.php.editor.options.OptionsUtils; import org.netbeans.modules.php.editor.parser.PHPParseResult; @@ -1237,8 +1238,9 @@ public boolean isMagic() { public String getCustomInsertTemplate() { StringBuilder template = new StringBuilder(); String modifierStr = BodyDeclaration.Modifier.toString(getBaseFunctionElement().getFlags()); + template.append(getOverrideAttribute()); // PHP 8.3 if (modifierStr.length() != 0) { - modifierStr = modifierStr.replace("abstract", "").trim(); //NOI18N + modifierStr = modifierStr.replace("abstract", CodeUtils.EMPTY_STRING).trim(); //NOI18N template.append(modifierStr); } template.append(" ").append("function"); //NOI18N @@ -1246,6 +1248,21 @@ public String getCustomInsertTemplate() { return template.toString(); } + private String getOverrideAttribute() { + MethodElement method = (MethodElement) getBaseFunctionElement(); + TypeElement type = method.getType(); + if (!isMagic() + && (!type.isTrait() || ElementUtils.isAbstractTraitMethod(method)) + && request != null) { + FileObject fileObject = request.result.getSnapshot().getSource().getFileObject(); + PhpVersion phpVersion = getPhpVersion(fileObject); + if (phpVersion.hasOverrideAttribute()) { + return CodeUtils.OVERRIDE_ATTRIBUTE + CodeUtils.NEW_LINE; + } + } + return CodeUtils.EMPTY_STRING; + } + protected String getNameAndFunctionBodyForTemplate() { StringBuilder template = new StringBuilder(); TypeNameResolver typeNameResolver = getBaseFunctionElement().getParameters().isEmpty() || request == null @@ -1304,10 +1321,10 @@ protected String getFunctionBodyForTemplate() { StringBuilder template = new StringBuilder(); MethodElement method = (MethodElement) getBaseFunctionElement(); TypeElement type = method.getType(); - if (isMagic() || type.isInterface() || method.isAbstract()) { + Collection returnTypes = getBaseFunctionElement().getReturnTypes(); + if (isMagic() || type.isInterface() || method.isAbstract() || type.isTrait() || ElementUtils.isVoidOrNeverType(returnTypes)) { template.append("${cursor};\n"); //NOI18N } else { - Collection returnTypes = getBaseFunctionElement().getReturnTypes(); if (returnTypes.size() == 1 || getBaseFunctionElement().isReturnUnionType()) { template.append("${cursor}return parent::").append(getSignature().replace("&$", "$")).append(";\n"); //NOI18N } else { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/elements/ElementUtils.java b/php/php.editor/src/org/netbeans/modules/php/editor/elements/ElementUtils.java new file mode 100644 index 000000000000..3cdcc29a496f --- /dev/null +++ b/php/php.editor/src/org/netbeans/modules/php/editor/elements/ElementUtils.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.php.editor.elements; + +import java.util.Collection; +import org.netbeans.modules.php.editor.api.elements.MethodElement; +import org.netbeans.modules.php.editor.api.elements.TypeResolver; +import org.netbeans.modules.php.editor.model.impl.Type; + +public final class ElementUtils { + + private ElementUtils() { + } + + public static boolean isAbstractTraitMethod(MethodElement method) { + return method.getType().isTrait() && method.isAbstract(); + } + + public static boolean isVoidOrNeverType(Collection types) { + if (types.size() == 1) { + for (TypeResolver returnType : types) { + String rawTypeName = returnType.getRawTypeName(); + if (Type.VOID.equals(rawTypeName) || Type.NEVER.equals(rawTypeName)) { + return true; + } + } + } + return false; + } +} diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/elements/MethodElementImpl.java b/php/php.editor/src/org/netbeans/modules/php/editor/elements/MethodElementImpl.java index 0e5fe77fd015..ada178eb2ee4 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/elements/MethodElementImpl.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/elements/MethodElementImpl.java @@ -35,6 +35,7 @@ import org.netbeans.modules.php.editor.api.PhpElementKind; import org.netbeans.modules.php.editor.api.PhpModifiers; import org.netbeans.modules.php.editor.api.elements.BaseFunctionElement.PrintAs; +import org.netbeans.modules.php.editor.api.elements.EnumElement; import org.netbeans.modules.php.editor.api.elements.MethodElement; import org.netbeans.modules.php.editor.api.elements.ParameterElement; import org.netbeans.modules.php.editor.api.elements.TypeElement; @@ -82,23 +83,26 @@ private MethodElementImpl( public static Set getMagicMethods(final TypeElement type) { Set retval = new HashSet<>(); retval.add(createMagicMethod(type, "__callStatic", Modifier.PUBLIC | Modifier.STATIC, "$name", "$arguments")); //NOI18N - retval.add(createMagicMethod(type, "__set_state", Modifier.PUBLIC | Modifier.STATIC, "$array")); //NOI18N retval.add(createMagicMethod(type, "__call", Modifier.PUBLIC, "$name", "$arguments")); //NOI18N - retval.add(createMagicMethod(type, "__clone", Modifier.PUBLIC)); //NOI18N - retval.add(createMagicMethod(type, "__construct", Modifier.PUBLIC)); //NOI18N - retval.add(createMagicMethod(type, "__destruct", Modifier.PUBLIC)); //NOI18N retval.add(createMagicMethod(type, "__invoke", Modifier.PUBLIC)); //NOI18N - retval.add(createMagicMethod(type, "__get", Modifier.PUBLIC, "$name")); //NOI18N - retval.add(createMagicMethod(type, "__set", Modifier.PUBLIC, "$name", "$value")); //NOI18N - retval.add(createMagicMethod(type, "__isset", Modifier.PUBLIC, "$name")); //NOI18N - retval.add(createMagicMethod(type, "__unset", Modifier.PUBLIC, "$name")); //NOI18N - retval.add(createMagicMethod(type, "__sleep", Modifier.PUBLIC)); //NOI18N - retval.add(createMagicMethod(type, "__wakeup", Modifier.PUBLIC)); //NOI18N - retval.add(createMagicMethod(type, "__toString", Modifier.PUBLIC)); //NOI18N - // PHP 7.4 New custom object serialization mechanism - // https://wiki.php.net/rfc/custom_object_serialization - retval.add(createMagicMethod(type, "__serialize", Modifier.PUBLIC)); //NOI18N - retval.add(createMagicMethod(type, "__unserialize", Modifier.PUBLIC, "array $data")); //NOI18N + if (!(type instanceof EnumElement)) { + // Enum can't contain these + retval.add(createMagicMethod(type, "__set_state", Modifier.PUBLIC | Modifier.STATIC, "$array")); //NOI18N + retval.add(createMagicMethod(type, "__clone", Modifier.PUBLIC)); //NOI18N + retval.add(createMagicMethod(type, "__construct", Modifier.PUBLIC)); //NOI18N + retval.add(createMagicMethod(type, "__destruct", Modifier.PUBLIC)); //NOI18N + retval.add(createMagicMethod(type, "__get", Modifier.PUBLIC, "$name")); //NOI18N + retval.add(createMagicMethod(type, "__set", Modifier.PUBLIC, "$name", "$value")); //NOI18N + retval.add(createMagicMethod(type, "__isset", Modifier.PUBLIC, "$name")); //NOI18N + retval.add(createMagicMethod(type, "__unset", Modifier.PUBLIC, "$name")); //NOI18N + retval.add(createMagicMethod(type, "__sleep", Modifier.PUBLIC)); //NOI18N + retval.add(createMagicMethod(type, "__wakeup", Modifier.PUBLIC)); //NOI18N + retval.add(createMagicMethod(type, "__toString", Modifier.PUBLIC)); //NOI18N + // PHP 7.4 New custom object serialization mechanism + // https://wiki.php.net/rfc/custom_object_serialization + retval.add(createMagicMethod(type, "__serialize", Modifier.PUBLIC)); //NOI18N + retval.add(createMagicMethod(type, "__unserialize", Modifier.PUBLIC, "array $data")); //NOI18N + } return retval; } diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/elements/TypeElementImpl.java b/php/php.editor/src/org/netbeans/modules/php/editor/elements/TypeElementImpl.java index 97d1aa677774..207f213077ff 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/elements/TypeElementImpl.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/elements/TypeElementImpl.java @@ -89,7 +89,9 @@ public final boolean isTrait() { @Override public boolean isTraited() { - return getPhpElementKind().equals(PhpElementKind.TRAIT) || getPhpElementKind().equals(PhpElementKind.CLASS); + return getPhpElementKind().equals(PhpElementKind.TRAIT) + || getPhpElementKind().equals(PhpElementKind.CLASS) + || getPhpElementKind().equals(PhpElementKind.ENUM); } @Override diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassInstanceCreationInfo.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassInstanceCreationInfo.java index 4b7030b86330..18620955858d 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassInstanceCreationInfo.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassInstanceCreationInfo.java @@ -28,6 +28,7 @@ import org.netbeans.modules.php.editor.api.QualifiedName; import org.netbeans.modules.php.editor.parser.astnodes.Block; import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation; +import org.netbeans.modules.php.editor.parser.astnodes.ClassName; import org.netbeans.modules.php.editor.parser.astnodes.Expression; import org.netbeans.modules.php.editor.parser.astnodes.UseTraitStatementPart; import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor; @@ -54,7 +55,13 @@ public Kind getKind() { @Override public OffsetRange getRange() { ClassInstanceCreation originalNode = getOriginalNode(); - return new OffsetRange(originalNode.getStartOffset(), originalNode.getEndOffset()); + // class name range is used in ClassDeclarationInfo + // anonymous class doesn't have a class name + // so, just use the range of "class" instead of range of the original node + ClassName className = originalNode.getClassName(); + int start = className.getStartOffset(); + int end = start + "class".length(); // NOI18N + return new OffsetRange(start, end); } public Expression getSuperClass() { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/verification/ImplementAbstractMethodsHintError.java b/php/php.editor/src/org/netbeans/modules/php/editor/verification/ImplementAbstractMethodsHintError.java index 1c55083c3998..eb5acccff30b 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/verification/ImplementAbstractMethodsHintError.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/verification/ImplementAbstractMethodsHintError.java @@ -192,7 +192,10 @@ private Collection checkHints(Collection allTypes, } TypeNameResolver typeNameResolver = TypeNameResolverImpl.forChainOf(typeNameResolvers); String skeleton = methodElement.asString(PrintAs.DeclarationWithEmptyBody, typeNameResolver, phpVersion); - skeleton = skeleton.replace(ABSTRACT_PREFIX, ""); //NOI18N + if (phpVersion.hasOverrideAttribute()) { + skeleton = CodeUtils.OVERRIDE_ATTRIBUTE + CodeUtils.NEW_LINE + skeleton; // PHP 8.3 + } + skeleton = skeleton.replace(ABSTRACT_PREFIX, CodeUtils.EMPTY_STRING); methodSkeletons.add(skeleton); lastMethodElement = methodElement; } diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInClassScope.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInClassScope.pass index 5914b7bf78d8..d7652a6c3c1a 100644 --- a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInClassScope.pass +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInClassScope.pass @@ -4,7 +4,7 @@ |--test1 [130, 1059] : ESCAPED{test1}ESCAPED{(}ESCAPED{)} |--test2 [1081, 1256] : ESCAPED{test2}ESCAPED{(}ESCAPED{)} |--test3 [1278, 1510] : ESCAPED{test3}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInClassScope_php#1 [155, 1052] : ESCAPED{{}}::ESCAPED{ClassScopeTest}:ESCAPED{ClassScopeTestInterface}#ESCAPED{ClassScopeTestTrait} +|-#anon#anonymousClassInClassScope_php#1 [159, 1052] : ESCAPED{{}}::ESCAPED{ClassScopeTest}:ESCAPED{ClassScopeTestInterface}#ESCAPED{ClassScopeTestTrait} |--CONSTANT [288, 296] : ESCAPED{CONSTANT}ESCAPED{ }ESCAPED{"CONSTANT"} |--$publicField [331, 342] : ESCAPED{$publicField} |--$privateField [365, 377] : ESCAPED{$privateField} @@ -19,9 +19,9 @@ |--publicStaticMethod [852, 888] : ESCAPED{publicStaticMethod}ESCAPED{(}ESCAPED{)} |--privateStaticMethod [926, 963] : ESCAPED{privateStaticMethod}ESCAPED{(}ESCAPED{)} |--protectedStaticMethod [1003, 1042] : ESCAPED{protectedStaticMethod}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInClassScope_php#2 [1106, 1249] : ESCAPED{{}} +|-#anon#anonymousClassInClassScope_php#2 [1110, 1249] : ESCAPED{{}} |--publicMethod [1209, 1239] : ESCAPED{publicMethod}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInClassScope_php#4 [1308, 1503] : ESCAPED{{}} +|-#anon#anonymousClassInClassScope_php#4 [1312, 1503] : ESCAPED{{}} |--test [1348, 1493] : ESCAPED{test}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInClassScope_php#3 [1380, 1478] : ESCAPED{{}} +|-#anon#anonymousClassInClassScope_php#3 [1384, 1478] : ESCAPED{{}} |--nested [1428, 1460] : ESCAPED{nested}ESCAPED{(}ESCAPED{)} diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInNamespaceScope.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInNamespaceScope.pass index 7447ed4242ec..25ccd20eb7fa 100644 --- a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInNamespaceScope.pass +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInNamespaceScope.pass @@ -1,7 +1,7 @@ |-NamespaceScopeTestInterface [16, 52] : ESCAPED{NamespaceScopeTestInterface} |-NamespaceScopeTestClass [60, 92] : ESCAPED{NamespaceScopeTestClass} |-NamespaceScopeTestTrait [100, 132] : ESCAPED{NamespaceScopeTestTrait} -|-#anon#anonymousClassInNamespaceScope_php#1 [141, 871] : ESCAPED{{}}::ESCAPED{NamespaceScopeTestClass}:ESCAPED{NamespaceScopeTestInterface}#ESCAPED{NamespaceScopeTestTrait} +|-#anon#anonymousClassInNamespaceScope_php#1 [145, 871] : ESCAPED{{}}::ESCAPED{NamespaceScopeTestClass}:ESCAPED{NamespaceScopeTestInterface}#ESCAPED{NamespaceScopeTestTrait} |--CONSTANT [275, 283] : ESCAPED{CONSTANT}ESCAPED{ }ESCAPED{"CONSTANT"} |--$publicField [310, 321] : ESCAPED{$publicField} |--$privateField [336, 348] : ESCAPED{$privateField} @@ -16,7 +16,7 @@ |--publicStaticMethod [719, 747] : ESCAPED{publicStaticMethod}ESCAPED{(}ESCAPED{)} |--privateStaticMethod [777, 806] : ESCAPED{privateStaticMethod}ESCAPED{(}ESCAPED{)} |--protectedStaticMethod [838, 869] : ESCAPED{protectedStaticMethod}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInNamespaceScope_php#3 [886, 1025] : ESCAPED{{}} +|-#anon#anonymousClassInNamespaceScope_php#3 [890, 1025] : ESCAPED{{}} |--test [918, 1023] : ESCAPED{test}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInNamespaceScope_php#2 [942, 1016] : ESCAPED{{}} +|-#anon#anonymousClassInNamespaceScope_php#2 [946, 1016] : ESCAPED{{}} |--nested [982, 1006] : ESCAPED{nested}ESCAPED{(}ESCAPED{)} diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInTraitScope.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInTraitScope.pass index 48a1b85eaed1..ae838dffb821 100644 --- a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInTraitScope.pass +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/anonymousClassInTraitScope.pass @@ -5,7 +5,7 @@ |--test1 [162, 1091] : ESCAPED{test1}ESCAPED{(}ESCAPED{)} |--test2 [1113, 1288] : ESCAPED{test2}ESCAPED{(}ESCAPED{)} |--test3 [1310, 1542] : ESCAPED{test3}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInTraitScope_php#1 [187, 1084] : ESCAPED{{}}::ESCAPED{TraitScopeTest}:ESCAPED{TraitScopeTestInterface}#ESCAPED{TraitScopeTestTrait} +|-#anon#anonymousClassInTraitScope_php#1 [191, 1084] : ESCAPED{{}}::ESCAPED{TraitScopeTest}:ESCAPED{TraitScopeTestInterface}#ESCAPED{TraitScopeTestTrait} |--CONSTANT [320, 328] : ESCAPED{CONSTANT}ESCAPED{ }ESCAPED{"CONSTANT"} |--$publicField [363, 374] : ESCAPED{$publicField} |--$privateField [397, 409] : ESCAPED{$privateField} @@ -20,9 +20,9 @@ |--publicStaticMethod [884, 920] : ESCAPED{publicStaticMethod}ESCAPED{(}ESCAPED{)} |--privateStaticMethod [958, 995] : ESCAPED{privateStaticMethod}ESCAPED{(}ESCAPED{)} |--protectedStaticMethod [1035, 1074] : ESCAPED{protectedStaticMethod}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInTraitScope_php#2 [1138, 1281] : ESCAPED{{}} +|-#anon#anonymousClassInTraitScope_php#2 [1142, 1281] : ESCAPED{{}} |--publicMethod [1241, 1271] : ESCAPED{publicMethod}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInTraitScope_php#4 [1340, 1535] : ESCAPED{{}} +|-#anon#anonymousClassInTraitScope_php#4 [1344, 1535] : ESCAPED{{}} |--test [1380, 1525] : ESCAPED{test}ESCAPED{(}ESCAPED{)} -|-#anon#anonymousClassInTraitScope_php#3 [1412, 1510] : ESCAPED{{}} +|-#anon#anonymousClassInTraitScope_php#3 [1416, 1510] : ESCAPED{{}} |--nested [1460, 1492] : ESCAPED{nested}ESCAPED{(}ESCAPED{)} diff --git a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/php80ConstructorPropertyPromotion.pass b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/php80ConstructorPropertyPromotion.pass index b0a6c3d57951..6c6cc4cf6e79 100644 --- a/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/php80ConstructorPropertyPromotion.pass +++ b/php/php.editor/test/unit/data/goldenfiles/org/netbeans/modules/php/editor/csl/NavigatorTest/structure/php80ConstructorPropertyPromotion.pass @@ -11,7 +11,7 @@ |--__construct [1276, 1438] : ESCAPED{__construct}ESCAPED{(}ESCAPED{$param1}ESCAPED{, }ESCAPED{int}ESCAPED{ }ESCAPED{$param2}ESCAPED{, }ESCAPED{string}ESCAPED{ }ESCAPED{$param3}ESCAPED{, }ESCAPED{string}ESCAPED{ }ESCAPED{$param4}ESCAPED{)} |--$param2 [1326, 1332] : ESCAPED{$param2}:ESCAPED{int} |--$param4 [1399, 1405] : ESCAPED{$param4}:ESCAPED{string} -|-#anon#php80ConstructorPropertyPromotion_php#1 [1451, 1565] : ESCAPED{{}} +|-#anon#php80ConstructorPropertyPromotion_php#1 [1455, 1565] : ESCAPED{{}} |--__construct [1487, 1563] : ESCAPED{__construct}ESCAPED{(}ESCAPED{int}ESCAPED{ }ESCAPED{$x}ESCAPED{, }ESCAPED{int}ESCAPED{ }ESCAPED{$y}ESCAPED{)} |--$x [1520, 1521] : ESCAPED{$x}:ESCAPED{int} |--$y [1543, 1544] : ESCAPED{$y}:ESCAPED{int} diff --git a/php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttribute01/testOverrideAttribute01.php b/php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttribute01/testOverrideAttribute01.php new file mode 100644 index 000000000000..7487d08105e2 --- /dev/null +++ b/php/php.editor/test/unit/data/testfiles/codegen/testOverrideAttribute01/testOverrideAttribute01.php @@ -0,0 +1,48 @@ +/testPHP80ConstructorPropertyPromotion.php;; + clz : #anon#testphp80constructorpropertypromotion_php#1;#anon#testPHP80ConstructorPropertyPromotion_php#1;2166;;;;1;;0;/testPHP80ConstructorPropertyPromotion.php;; field : x;x;2231;1;int;int;0;/testPHP80ConstructorPropertyPromotion.php;0; field : y;y;2254;1;int;int;0;/testPHP80ConstructorPropertyPromotion.php;0; method : __construct;__construct;2198;$x:int:1::1:0:0:0:1:0:int:,$y:int:1:0:0:0:0:0:1:0:int:;;1;0;/testPHP80ConstructorPropertyPromotion.php;0;0;; diff --git a/php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testImplementAbstractMethodsHintFix02.php.testImplementAbstractMethodsHintFix02_02.hints b/php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testImplementAbstractMethodsHintFix02.php.testImplementAbstractMethodsHintFix02_02.hints index fb477fd9b542..61893f9769df 100644 --- a/php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testImplementAbstractMethodsHintFix02.php.testImplementAbstractMethodsHintFix02_02.hints +++ b/php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testImplementAbstractMethodsHintFix02.php.testImplementAbstractMethodsHintFix02_02.hints @@ -1,5 +1,4 @@ $a = new class implements Iface { - + ----- HINT:Anonymous class is not abstract and does not override abstract method test1() in \Iface FIX:Implement All Abstract Methods -}; diff --git a/php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php b/php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php new file mode 100644 index 000000000000..b2cdcbb3a9e6 --- /dev/null +++ b/php/php.editor/test/unit/data/testfiles/verification/ImplementAbstractMethodsHintError/testOverrideAttribute01.php @@ -0,0 +1,47 @@ + Date: Fri, 22 Dec 2023 21:44:43 +0900 Subject: [PATCH 4/4] Add the `getInterfaces()` method to the `TypeDeclaration` instead of the `getInterfaes()` --- php/php.editor/nbproject/project.properties | 2 +- .../php/editor/codegen/SemiAttribute.java | 6 +++--- .../php/editor/completion/PHPCodeCompletion.java | 4 ++-- .../modules/php/editor/csl/SemanticAnalysis.java | 4 ++-- .../modules/php/editor/indent/FormatVisitor.java | 12 ++++++------ .../php/editor/model/impl/OccurenceBuilder.java | 6 +++--- .../editor/model/nodes/ClassDeclarationInfo.java | 4 ++-- .../editor/model/nodes/EnumDeclarationInfo.java | 4 ++-- .../model/nodes/InterfaceDeclarationInfo.java | 4 ++-- .../editor/parser/astnodes/ClassDeclaration.java | 4 ++-- .../editor/parser/astnodes/EnumDeclaration.java | 4 ++-- .../parser/astnodes/InterfaceDeclaration.java | 2 +- .../editor/parser/astnodes/TypeDeclaration.java | 16 ++++++++++++++-- .../parser/astnodes/visitors/DefaultVisitor.java | 6 +++--- .../verification/IncorrectEnumHintError.java | 4 ++-- .../php/editor/parser/PrintASTVisitor.java | 4 ++-- 16 files changed, 49 insertions(+), 37 deletions(-) diff --git a/php/php.editor/nbproject/project.properties b/php/php.editor/nbproject/project.properties index f7b602309552..f976f182a058 100644 --- a/php/php.editor/nbproject/project.properties +++ b/php/php.editor/nbproject/project.properties @@ -18,7 +18,7 @@ javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial nbjavac.ignore.missing.enclosing=**/CUP$ASTPHP5Parser$actions.class nbm.needs.restart=true -spec.version.base=2.33.0 +spec.version.base=2.34.0 release.external/predefined_vars-1.0.zip=docs/predefined_vars.zip sigtest.gen.fail.on.error=false diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SemiAttribute.java b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SemiAttribute.java index 7ac62d01c340..cc0690eda366 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SemiAttribute.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/codegen/SemiAttribute.java @@ -406,7 +406,7 @@ public void visit(InterfaceDeclaration node) { ClassElementAttribute ce = (ClassElementAttribute) global.enterWrite(name, Kind.IFACE, node); node2Element.put(node, ce); - List interfaes = node.getInterfaes(); + List interfaes = node.getInterfaces(); for (Expression identifier : interfaes) { ClassElementAttribute iface = (ClassElementAttribute) lookup(CodeUtils.extractUnqualifiedName(identifier), Kind.IFACE); ce.ifaces.add(iface); @@ -437,7 +437,7 @@ public void visit(ClassDeclaration node) { if (superClsName != null) { ce.superClass = (ClassElementAttribute) lookup(superClsName.getName(), Kind.CLASS); } - List interfaes = node.getInterfaes(); + List interfaes = node.getInterfaces(); for (Expression identifier : interfaes) { ClassElementAttribute iface = (ClassElementAttribute) lookup(CodeUtils.extractUnqualifiedName(identifier), Kind.IFACE); ce.ifaces.add(iface); @@ -797,7 +797,7 @@ private void performEnterPass(DefinitionScope scope, Collection interfaces = node.getInterfaes(); + List interfaces = node.getInterfaces(); for (Expression identifier : interfaces) { //TODO: ifaces must be fixed; } diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java index a8fa21d3e27b..cc1039a784d6 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java @@ -2761,7 +2761,7 @@ public Expression getSuperClass() { @Override public List getInterfaces() { - return classDeclaration.getInterfaes(); + return classDeclaration.getInterfaces(); } @Override @@ -2820,7 +2820,7 @@ public Expression getSuperClass() { @Override public List getInterfaces() { - return enumDeclaration.getInterfaes(); + return enumDeclaration.getInterfaces(); } @Override diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/csl/SemanticAnalysis.java b/php/php.editor/src/org/netbeans/modules/php/editor/csl/SemanticAnalysis.java index f67e3c2b410f..2ce27d175938 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/csl/SemanticAnalysis.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/csl/SemanticAnalysis.java @@ -406,7 +406,7 @@ public void visit(ClassDeclaration cldec) { typeInfo = new TypeDeclarationTypeInfo(cldec); scan(cldec.getAttributes()); scan(cldec.getSuperClass()); - scan(cldec.getInterfaes()); + scan(cldec.getInterfaces()); Identifier name = cldec.getName(); addColoringForNode(name, createTypeNameColoring(name)); needToScan.put(typeInfo, new ArrayList<>()); @@ -655,7 +655,7 @@ public void visit(EnumDeclaration node) { } addToPath(node); scan(node.getAttributes()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); typeInfo = new TypeDeclarationTypeInfo(node); Identifier name = node.getName(); addColoringForNode(name, createTypeNameColoring(name)); diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatVisitor.java b/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatVisitor.java index 11058e1136c1..19d9dcd926c5 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatVisitor.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatVisitor.java @@ -743,10 +743,10 @@ public void visit(ClassDeclaration node) { addFormatToken(formatTokens); break; case PHP_IMPLEMENTS: - if (!node.getInterfaes().isEmpty()) { + if (!node.getInterfaces().isEmpty()) { formatTokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_BEFORE_EXTENDS_IMPLEMENTS, ts.offset())); ts.movePrevious(); - addListOfNodes(node.getInterfaes(), FormatToken.Kind.WHITESPACE_IN_INTERFACE_LIST); + addListOfNodes(node.getInterfaces(), FormatToken.Kind.WHITESPACE_IN_INTERFACE_LIST); } break; case PHP_EXTENDS: @@ -761,7 +761,7 @@ public void visit(ClassDeclaration node) { ts.movePrevious(); scan(node.getName()); scan(node.getSuperClass()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); scan(node.getBody()); } @@ -807,10 +807,10 @@ public void visit(EnumDeclaration node) { while (ts.moveNext() && ts.token().id() != PHPTokenId.PHP_CURLY_OPEN) { switch (ts.token().id()) { case PHP_IMPLEMENTS: - if (!node.getInterfaes().isEmpty()) { + if (!node.getInterfaces().isEmpty()) { formatTokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_BEFORE_EXTENDS_IMPLEMENTS, ts.offset())); ts.movePrevious(); - addListOfNodes(node.getInterfaes(), FormatToken.Kind.WHITESPACE_IN_INTERFACE_LIST); + addListOfNodes(node.getInterfaces(), FormatToken.Kind.WHITESPACE_IN_INTERFACE_LIST); } break; default: @@ -821,7 +821,7 @@ public void visit(EnumDeclaration node) { ts.movePrevious(); scan(node.getName()); scan(node.getBackingType()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); scan(node.getBody()); } diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/OccurenceBuilder.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/OccurenceBuilder.java index 3dd5cea6efa1..fbb3d8e1e37b 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/OccurenceBuilder.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/impl/OccurenceBuilder.java @@ -425,7 +425,7 @@ void prepare(ClassDeclaration classDeclaration, ClassScope scope) { prepare(Kind.CLASS, classDeclaration.getSuperClass(), scope); } } - List interfaes = classDeclaration.getInterfaes(); + List interfaes = classDeclaration.getInterfaces(); for (Expression iface : interfaes) { QualifiedName ifaceName = QualifiedName.create(iface); if (ifaceName != null && VariousUtils.isAlias(ifaceName, classDeclaration.getStartOffset(), scope)) { @@ -441,7 +441,7 @@ void prepare(InterfaceDeclaration interfaceDeclaration, InterfaceScope scope) { if (canBePrepared(interfaceDeclaration, scope)) { InterfaceDeclarationInfo node = InterfaceDeclarationInfo.create(interfaceDeclaration); ifaceDeclarations.put(node, scope); - List interfaes = interfaceDeclaration.getInterfaes(); + List interfaes = interfaceDeclaration.getInterfaces(); for (Expression iface : interfaes) { prepare(Kind.IFACE, iface, scope); } @@ -459,7 +459,7 @@ void prepare(EnumDeclaration enumDeclaration, EnumScope scope) { if (canBePrepared(enumDeclaration, scope)) { EnumDeclarationInfo node = EnumDeclarationInfo.create(enumDeclaration); enumDeclarations.put(node, scope); - List interfaes = enumDeclaration.getInterfaes(); + List interfaes = enumDeclaration.getInterfaces(); for (Expression iface : interfaes) { QualifiedName ifaceName = QualifiedName.create(iface); if (ifaceName != null && VariousUtils.isAlias(ifaceName, enumDeclaration.getStartOffset(), scope)) { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassDeclarationInfo.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassDeclarationInfo.java index 55465e025f1e..d15f0e2dbd46 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassDeclarationInfo.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/ClassDeclarationInfo.java @@ -78,12 +78,12 @@ public QualifiedName getSuperClassName() { } public List getInterfaces() { - return getOriginalNode().getInterfaes(); + return getOriginalNode().getInterfaces(); } public Set getInterfaceNames() { final Set retval = new HashSet<>(); - final List interfaes = getOriginalNode().getInterfaes(); + final List interfaes = getOriginalNode().getInterfaces(); for (Expression iface : interfaes) { QualifiedName ifaceName = QualifiedName.create(iface); if (ifaceName != null) { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/EnumDeclarationInfo.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/EnumDeclarationInfo.java index b20b9d6cb53b..901f2677cfc7 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/EnumDeclarationInfo.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/EnumDeclarationInfo.java @@ -61,12 +61,12 @@ public QualifiedName getBackingType() { } public List getInterfaces() { - return getOriginalNode().getInterfaes(); + return getOriginalNode().getInterfaces(); } public Set getInterfaceNames() { final Set retval = new HashSet<>(); - final List interfaes = getOriginalNode().getInterfaes(); + final List interfaes = getOriginalNode().getInterfaces(); for (Expression iface : interfaes) { QualifiedName ifaceName = QualifiedName.create(iface); if (ifaceName != null) { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/InterfaceDeclarationInfo.java b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/InterfaceDeclarationInfo.java index 887f2ea732e5..8c56071da54b 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/InterfaceDeclarationInfo.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/model/nodes/InterfaceDeclarationInfo.java @@ -66,12 +66,12 @@ public OffsetRange getRange() { } public List getInterfaces() { - return getOriginalNode().getInterfaes(); + return getOriginalNode().getInterfaces(); } public Set getInterfaceNames() { final Set retval = new HashSet<>(); - final List interfaes = getOriginalNode().getInterfaes(); + final List interfaes = getOriginalNode().getInterfaces(); for (Expression iface : interfaes) { QualifiedName ifaceName = QualifiedName.create(iface); if (ifaceName != null) { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/ClassDeclaration.java b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/ClassDeclaration.java index 47698b5c8ae8..1087e8b8ef65 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/ClassDeclaration.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/ClassDeclaration.java @@ -109,7 +109,7 @@ public static ClassDeclaration create(ClassDeclaration declaration, List sbAttributes.append(attribute).append(" ")); // NOI18N StringBuilder sb = new StringBuilder(); - for (Expression expression : getInterfaes()) { + for (Expression expression : getInterfaces()) { sb.append(expression).append(","); // NOI18N } return sbAttributes.toString() + "enum " + getName() + backingType == null ? "" : ": " + backingType + " implements " + sb + getBody(); // NOI18N diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/InterfaceDeclaration.java b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/InterfaceDeclaration.java index 51acad8c7f18..8b75f788e43f 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/InterfaceDeclaration.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/InterfaceDeclaration.java @@ -55,7 +55,7 @@ public static InterfaceDeclaration create(InterfaceDeclaration declaration, List start, declaration.getEndOffset(), declaration.getName(), - declaration.getInterfaes(), + declaration.getInterfaces(), declaration.getBody(), attributes ); diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/TypeDeclaration.java b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/TypeDeclaration.java index f369c7c4ead0..ce7d41796fc8 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/TypeDeclaration.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/TypeDeclaration.java @@ -75,9 +75,21 @@ public Identifier getName() { * List of interfaces that this type implements / extends. * * @return interfaces + * @deprecated instead, use {@link #getInterfaces()} */ + @Deprecated public List getInterfaes() { - return Collections.unmodifiableList(this.interfaces); + return getInterfaces(); + } + + /** + * List of interfaces that this type implements / extends. + * + * @return interfaces + * @since 2.34.0 + */ + public List getInterfaces() { + return Collections.unmodifiableList(interfaces); } /** @@ -111,7 +123,7 @@ public String toString() { StringBuilder sbAttributes = new StringBuilder(); getAttributes().forEach(attribute -> sbAttributes.append(attribute).append(" ")); // NOI18N StringBuilder sb = new StringBuilder(); - for (Expression expression : getInterfaes()) { + for (Expression expression : getInterfaces()) { sb.append(expression).append(","); //NOI18N } return sbAttributes.toString() + getName() + (sb.length() > 0 ? " " + sb.toString() : " ") + getBody(); //NOI18N diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/visitors/DefaultVisitor.java b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/visitors/DefaultVisitor.java index 6e547f66c6dc..6363d83ad4bd 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/visitors/DefaultVisitor.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/parser/astnodes/visitors/DefaultVisitor.java @@ -249,7 +249,7 @@ public void visit(ClassDeclaration node) { scan(node.getAttributes()); scan(node.getName()); scan(node.getSuperClass()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); scan(node.getBody()); } @@ -326,7 +326,7 @@ public void visit(EnumDeclaration node) { scan(node.getAttributes()); scan(node.getName()); scan(node.getBackingType()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); scan(node.getBody()); } @@ -454,7 +454,7 @@ public void visit(InstanceOfExpression node) { public void visit(InterfaceDeclaration node) { scan(node.getAttributes()); scan(node.getName()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); scan(node.getBody()); } diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/verification/IncorrectEnumHintError.java b/php/php.editor/src/org/netbeans/modules/php/editor/verification/IncorrectEnumHintError.java index 60a9c3d381fb..f5852141612f 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/verification/IncorrectEnumHintError.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/verification/IncorrectEnumHintError.java @@ -284,7 +284,7 @@ public void visit(ClassDeclaration node) { scan(node.getAttributes()); scan(node.getName()); scan(node.getSuperClass()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); checkEnumCases(node.getBody().getStatements()); } @@ -318,7 +318,7 @@ public void visit(EnumDeclaration node) { } scan(node.getAttributes()); scan(node.getName()); - scan(node.getInterfaes()); + scan(node.getInterfaces()); Expression backingType = node.getBackingType(); if (backingType != null) { String name = CodeUtils.extractQualifiedName(backingType); diff --git a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PrintASTVisitor.java b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PrintASTVisitor.java index e55bc84c7428..77c950ebaaac 100644 --- a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PrintASTVisitor.java +++ b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PrintASTVisitor.java @@ -340,7 +340,7 @@ public void visit(ClassDeclaration classDeclaration) { } printNode.addChildrenGroup("ClassName", new ASTNode[]{classDeclaration.getName()}); printNode.addChildrenGroup("SuperClassName", new ASTNode[]{classDeclaration.getSuperClass()}); - printNode.addChildrenGroup("Interfaces", classDeclaration.getInterfaes()); + printNode.addChildrenGroup("Interfaces", classDeclaration.getInterfaces()); printNode.addChild(classDeclaration.getBody()); printNode.print(this); } @@ -450,7 +450,7 @@ public void visit(EnumDeclaration enumDeclaration) { } printNode.addChildrenGroup("EnumName", new ASTNode[]{enumDeclaration.getName()}); printNode.addChildrenGroup("BackingType", new ASTNode[]{enumDeclaration.getBackingType()}); - printNode.addChildrenGroup("Interfaces", enumDeclaration.getInterfaes()); + printNode.addChildrenGroup("Interfaces", enumDeclaration.getInterfaces()); printNode.addChild(enumDeclaration.getBody()); printNode.print(this); }