PHP 8.3 Support: Typed class constants (Part 6)
- https://github.com/apache/netbeans/issues/6701
- https://wiki.php.net/rfc/typed_class_constants
- Fix the formatter (don't add spaces within parens of DNF types)
- Fix the `UnusableTypeHintError`
- Add unit tests
diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatToken.java b/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatToken.java
index 6e812f7..727f8f9 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatToken.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/indent/FormatToken.java
@@ -126,6 +126,7 @@
WHITESPACE_WITHIN_ATTRIBUTE_BRACKETS,
WHITESPACE_WITHIN_ATTRIBUTE_DECL_PARENS,
WHITESPACE_WITHIN_TYPE_CAST_PARENS,
+ WHITESPACE_WITHIN_DNF_TYPE_PARENS, // (A&B)|C
WHITESPACE_WITHIN_DYNAMIC_NAME_BRACES, // {$example}
WHITESPACE_BEFORE_COMMA,
WHITESPACE_AFTER_COMMA,
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 eddae94..11058e1 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
@@ -1080,13 +1080,14 @@
}
}
scan(node.getAttributes());
- while (ts.moveNext() && ts.token().id() != PHPTokenId.PHP_STRING) {
+ while (ts.moveNext() && !isConstTypeToken(ts.token())) {
addFormatToken(formatTokens);
}
+ ts.movePrevious();
FormatToken lastWhitespace = formatTokens.remove(formatTokens.size() - 1);
formatTokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_AFTER_MODIFIERS, lastWhitespace.getOffset(), lastWhitespace.getOldText()));
- addFormatToken(formatTokens);
formatTokens.add(new FormatToken.IndentToken(node.getStartOffset(), options.continualIndentSize));
+ scan(node.getConstType());
scan(node.getNames());
if (node.getNames().size() == 1) {
while (ts.moveNext()
@@ -2499,6 +2500,8 @@
@Override
public void visit(UnionType node) {
processUnionOrIntersectionType(node.getTypes());
+ // add ")" if it exists e.g. (A&B)|(B&C)
+ addAllUntilOffset(node.getEndOffset());
}
@Override
@@ -2729,6 +2732,9 @@
tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_BEFORE_ARRAY_DECL_PAREN, ts.offset()));
tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_AFTER_ARRAY_DECL_LEFT_PAREN, ts.offset() + ts.token().length()));
+ } else if (parent instanceof UnionType) {
+ tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
+ tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_WITHIN_DNF_TYPE_PARENS, ts.offset() + ts.token().length()));
} else {
tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
}
@@ -2767,6 +2773,9 @@
} else if (parent instanceof ArrayCreation) {
tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_BEFORE_ARRAY_DECL_RIGHT_PAREN, ts.offset()));
tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
+ } else if (parent instanceof UnionType) {
+ tokens.add(new FormatToken(FormatToken.Kind.WHITESPACE_WITHIN_DNF_TYPE_PARENS, ts.offset()));
+ tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
} else {
tokens.add(new FormatToken(FormatToken.Kind.TEXT, ts.offset(), ts.token().text().toString()));
}
@@ -3349,7 +3358,11 @@
private boolean isFieldTypeOrVariableToken(Token<PHPTokenId> token) {
return PHPTokenId.PHP_VARIABLE == token.id()
- || PHPTokenId.PHP_STRING == token.id()
+ || isConstTypeToken(token);
+ }
+
+ private boolean isConstTypeToken(Token<PHPTokenId> token) {
+ return PHPTokenId.PHP_STRING == token.id()
|| PHPTokenId.PHP_ARRAY == token.id()
|| PHPTokenId.PHP_ITERABLE == token.id()
|| PHPTokenId.PHP_PARENT == token.id()
@@ -3362,9 +3375,11 @@
|| PHPTokenId.PHP_NULL == token.id()
|| PHPTokenId.PHP_FALSE == token.id()
|| PHPTokenId.PHP_NS_SEPARATOR == token.id() // \
+ || (PHPTokenId.PHP_TOKEN == token.id() && TokenUtilities.textEquals(token.text(), "(")) // NOI18N
|| (PHPTokenId.PHP_TOKEN == token.id() && TokenUtilities.textEquals(token.text(), "?")) // NOI18N
|| PHPTokenId.PHP_TYPE_VOID == token.id() // not supported type but just check it
|| PHPTokenId.PHP_CALLABLE == token.id() // not supported type but just check it
+ || PHPTokenId.PHP_TYPE_NEVER == token.id() // not supported type but just check it
;
}
diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/indent/TokenFormatter.java b/php/php.editor/src/org/netbeans/modules/php/editor/indent/TokenFormatter.java
index 3082147..4764316 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/indent/TokenFormatter.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/indent/TokenFormatter.java
@@ -1555,6 +1555,10 @@
case WHITESPACE_WITHIN_TYPE_CAST_PARENS:
countSpaces = docOptions.spaceWithinTypeCastParens ? 1 : 0;
break;
+ case WHITESPACE_WITHIN_DNF_TYPE_PARENS:
+ // change here if we add the option for it
+ countSpaces = 0;
+ break;
case WHITESPACE_WITHIN_DYNAMIC_NAME_BRACES:
// change here if we add the option for it
countSpaces = 0;
diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintError.java b/php/php.editor/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintError.java
index 227b7ef..a1948e2 100644
--- a/php/php.editor/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintError.java
+++ b/php/php.editor/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintError.java
@@ -40,6 +40,7 @@
import org.netbeans.modules.php.editor.parser.PHPParseResult;
import org.netbeans.modules.php.editor.parser.astnodes.ASTNode;
import org.netbeans.modules.php.editor.parser.astnodes.ArrowFunctionDeclaration;
+import org.netbeans.modules.php.editor.parser.astnodes.ConstantDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.Expression;
import org.netbeans.modules.php.editor.parser.astnodes.FieldsDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.FormalParameter;
@@ -128,7 +129,19 @@
}
Expression fieldType = node.getFieldType();
if (fieldType != null) {
- checkFieldType(fieldType, false);
+ checkFieldAndConstType(fieldType, false);
+ }
+ super.visit(node);
+ }
+
+ @Override
+ public void visit(ConstantDeclaration node) {
+ if (CancelSupport.getDefault().isCancelled()) {
+ return;
+ }
+ Expression constType = node.getConstType();
+ if (constType != null) {
+ checkFieldAndConstType(constType, false);
}
super.visit(node);
}
@@ -239,11 +252,11 @@
super.visit(nullableType);
}
- private void checkFieldType(@NullAllowed Expression fieldType, boolean isInUnionType) {
+ private void checkFieldAndConstType(@NullAllowed Expression declaredType, boolean isInUnionType) {
// unusable types: void and callable PHP 7.4
- Expression type = fieldType;
- if (fieldType instanceof NullableType) {
- type = ((NullableType) fieldType).getType();
+ Expression type = declaredType;
+ if (declaredType instanceof NullableType) {
+ type = ((NullableType) declaredType).getType();
}
if (type == null) {
return;
@@ -262,7 +275,7 @@
checkTrueAndFalseAndNullTypes((NamespaceName) type);
}
} else if (type instanceof UnionType) {
- ((UnionType) type).getTypes().forEach(unionType -> checkFieldType(unionType, true));
+ ((UnionType) type).getTypes().forEach(unionType -> checkFieldAndConstType(unionType, true));
}
}
diff --git a/php/php.editor/test/unit/data/testfiles/formatting/php82/dnfTypes_01.php.formatted b/php/php.editor/test/unit/data/testfiles/formatting/php82/dnfTypes_01.php.formatted
index 933d833..d7bd04d 100644
--- a/php/php.editor/test/unit/data/testfiles/formatting/php82/dnfTypes_01.php.formatted
+++ b/php/php.editor/test/unit/data/testfiles/formatting/php82/dnfTypes_01.php.formatted
@@ -69,7 +69,7 @@
private (ClassX&ClassZ)|(ClassY&ClassZ)|ClassX $privateYField;
protected ClassX|(ClassY&ClassZ)|ClassY $protectedYField;
public static (ClassY&ClassZ)|ClassX $publicStaticYField;
- private static ( ClassY&ClassZ)|ClassY $privateStaticYField;
+ private static (ClassY&ClassZ)|ClassY $privateStaticYField;
protected static (ClassY&ClassZ)|ClassX|ClassY $protectedStaticYField;
public function publicYMethod((ClassY&ClassZ)|ClassX $param1, (ClassY&ClassZ)|ClassX|(ClassX&ClassZ) $param2): ClassX|(ClassY&ClassZ) {
diff --git a/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_01.php b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_01.php
new file mode 100644
index 0000000..cf366b2
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_01.php
@@ -0,0 +1,81 @@
+<?php
+/*
+ * 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.
+ */
+
+class A implements Stringable {
+ public function __toString() {
+ return static::class;
+ }
+}
+
+class B extends A {}
+class C extends A {}
+
+class ClassTest {
+public const WITHOUT_TYPE = 1;
+public const ?int NULLABLE = 1;
+ private const A | B UNION = D_A;
+protected const A & B INTERSECTION = D_B;
+public const (A &B ) | C DNF = D_C;
+public const string STRING = 'a';
+public const int INT = 1;
+public const float FLOAT = 1.5;
+public const bool BOOL = true;
+public const array ARRAY = ['t', 'e', 's', 't'];
+public const iterable ITERABLE = [ 'a', 'b', 'c'];
+public const mixed MIXED = 1;
+public const object OBJECT = D_A;
+public const string | array UNION2 = 'a' , UNION3 = ['a'];
+#[Attr]
+public const int | null UNION4 = null;
+}
+
+interface InterfaceTest {
+ const string STRING = "string";
+public const ? int NULLABLE = 1;
+public const A|B UNION = D_A;
+public const A&B INTERSECTION = D_B;
+public const ( A & B ) |C DNF = D_C;
+}
+
+trait TraitTest {
+const string STRING = "string";
+public const ?int NULLABLE = 1;
+private const A|B UNION = D_A;
+protected const A&B INTERSECTION = D_B;
+public const (A&B)|C DNF = D_C;
+}
+
+enum EnumTest {
+ const string STRING = "string";
+ public const ? int NULLABLE = 1;
+ private const A| B UNION=D_A;
+ protected const A&B INTERSECTION=D_B;
+ public const (A & B) | (A&C ) DNF=D_C;
+ public const static A=EnumTest :: Test;
+
+ case Test;
+}
+
+define("D_A", new A());
+define("D_B", new B());
+define("D_C", new C());
+
+echo ClassTest::DNF . PHP_EOL;
+var_dump(ClassTest::DNF);
diff --git a/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_01.php.testTypedClassConstants_01.formatted b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_01.php.testTypedClassConstants_01.formatted
new file mode 100644
index 0000000..c6a7b4a
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_01.php.testTypedClassConstants_01.formatted
@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * 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.
+ */
+
+class A implements Stringable {
+
+ public function __toString() {
+ return static::class;
+ }
+}
+
+class B extends A {
+
+}
+
+class C extends A {
+
+}
+
+class ClassTest {
+
+ public const WITHOUT_TYPE = 1;
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+ public const string STRING = 'a';
+ public const int INT = 1;
+ public const float FLOAT = 1.5;
+ public const bool BOOL = true;
+ public const array ARRAY = ['t', 'e', 's', 't'];
+ public const iterable ITERABLE = ['a', 'b', 'c'];
+ public const mixed MIXED = 1;
+ public const object OBJECT = D_A;
+ public const string|array UNION2 = 'a', UNION3 = ['a'];
+
+ #[Attr]
+ public const int|null UNION4 = null;
+}
+
+interface InterfaceTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ public const A|B UNION = D_A;
+ public const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+}
+
+trait TraitTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+}
+
+enum EnumTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|(A&C) DNF = D_C;
+ public const static A = EnumTest::Test;
+
+ case Test;
+}
+
+define("D_A", new A());
+define("D_B", new B());
+define("D_C", new C());
+
+echo ClassTest::DNF . PHP_EOL;
+var_dump(ClassTest::DNF);
diff --git a/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_02.php b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_02.php
new file mode 100644
index 0000000..c6a7b4a
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_02.php
@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * 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.
+ */
+
+class A implements Stringable {
+
+ public function __toString() {
+ return static::class;
+ }
+}
+
+class B extends A {
+
+}
+
+class C extends A {
+
+}
+
+class ClassTest {
+
+ public const WITHOUT_TYPE = 1;
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+ public const string STRING = 'a';
+ public const int INT = 1;
+ public const float FLOAT = 1.5;
+ public const bool BOOL = true;
+ public const array ARRAY = ['t', 'e', 's', 't'];
+ public const iterable ITERABLE = ['a', 'b', 'c'];
+ public const mixed MIXED = 1;
+ public const object OBJECT = D_A;
+ public const string|array UNION2 = 'a', UNION3 = ['a'];
+
+ #[Attr]
+ public const int|null UNION4 = null;
+}
+
+interface InterfaceTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ public const A|B UNION = D_A;
+ public const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+}
+
+trait TraitTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+}
+
+enum EnumTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|(A&C) DNF = D_C;
+ public const static A = EnumTest::Test;
+
+ case Test;
+}
+
+define("D_A", new A());
+define("D_B", new B());
+define("D_C", new C());
+
+echo ClassTest::DNF . PHP_EOL;
+var_dump(ClassTest::DNF);
diff --git a/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_02.php.testTypedClassConstants_02.formatted b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_02.php.testTypedClassConstants_02.formatted
new file mode 100644
index 0000000..c6a7b4a
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/formatting/php83/typedClassConstants_02.php.testTypedClassConstants_02.formatted
@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * 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.
+ */
+
+class A implements Stringable {
+
+ public function __toString() {
+ return static::class;
+ }
+}
+
+class B extends A {
+
+}
+
+class C extends A {
+
+}
+
+class ClassTest {
+
+ public const WITHOUT_TYPE = 1;
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+ public const string STRING = 'a';
+ public const int INT = 1;
+ public const float FLOAT = 1.5;
+ public const bool BOOL = true;
+ public const array ARRAY = ['t', 'e', 's', 't'];
+ public const iterable ITERABLE = ['a', 'b', 'c'];
+ public const mixed MIXED = 1;
+ public const object OBJECT = D_A;
+ public const string|array UNION2 = 'a', UNION3 = ['a'];
+
+ #[Attr]
+ public const int|null UNION4 = null;
+}
+
+interface InterfaceTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ public const A|B UNION = D_A;
+ public const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+}
+
+trait TraitTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|C DNF = D_C;
+}
+
+enum EnumTest {
+
+ const string STRING = "string";
+ public const ?int NULLABLE = 1;
+ private const A|B UNION = D_A;
+ protected const A&B INTERSECTION = D_B;
+ public const (A&B)|(A&C) DNF = D_C;
+ public const static A = EnumTest::Test;
+
+ case Test;
+}
+
+define("D_A", new A());
+define("D_B", new B());
+define("D_C", new C());
+
+echo ClassTest::DNF . PHP_EOL;
+var_dump(ClassTest::DNF);
diff --git a/php/php.editor/test/unit/data/testfiles/verification/UnusableTypesHintError/testConstantTypes_01.php b/php/php.editor/test/unit/data/testfiles/verification/UnusableTypesHintError/testConstantTypes_01.php
new file mode 100644
index 0000000..b9691a3
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/verification/UnusableTypesHintError/testConstantTypes_01.php
@@ -0,0 +1,99 @@
+<?php
+/*
+ * 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.
+ */
+
+class ClassTest {
+ const callable TEST1 = T::TEST;
+ const void TEST2 = T::TEST;
+ const never TEST3 = T::TEST;
+ const int|callable callable = T::TEST;
+ const false false = T::TEST;
+ const null null = T::TEST;
+ const true true = T::TEST;
+ const bool|false boolFalse = T::TEST;
+ const true|bool trueBool = T::TEST;
+ const bool|bool duplicatedBool = T::TEST;
+ const true|false bothTrueAndFalse = T::TEST;
+ const int|false|true bothTrueAndFalse2 = T::TEST;
+ const int|INT duplicatedInt = T::TEST;
+ const iterable|array iterable1 = T::TEST;
+ const iterable|Traversable iterable2 = T::TEST;
+ const iterable|array|Traversable iterable3 = T::TEST;
+ const null|false nullFalse = T::TEST; // PHP 8.2: OK
+}
+
+interface InterfaceTest {
+ public const callable TEST1 = T::TEST;
+ public const void TEST2 = T::TEST;
+ public const never TEST3 = T::TEST;
+ public const int|callable callable = T::TEST;
+ public const false false = T::TEST;
+ public const null null = T::TEST;
+ public const true true = T::TEST;
+ public const bool|false boolFalse = T::TEST;
+ public const true|bool trueBool = T::TEST;
+ public const bool|bool duplicatedBool = T::TEST;
+ public const true|false bothTrueAndFalse = T::TEST;
+ public const int|false|true bothTrueAndFalse2 = T::TEST;
+ public const int|INT duplicatedInt = T::TEST;
+ public const iterable|array iterable1 = T::TEST;
+ public const iterable|Traversable iterable2 = T::TEST;
+ public const iterable|array|Traversable iterable3 = T::TEST;
+ public const null|false nullFalse = T::TEST; // PHP 8.2: OK
+}
+
+trait TraitTest {
+ private const callable TEST1 = T::TEST;
+ private const void TEST2 = T::TEST;
+ private const never TEST3 = T::TEST;
+ private const int|callable callable = T::TEST;
+ private const false false = T::TEST;
+ private const null null = T::TEST;
+ private const true true = T::TEST;
+ private const bool|false boolFalse = T::TEST;
+ private const true|bool trueBool = T::TEST;
+ private const bool|bool duplicatedBool = T::TEST;
+ private const true|false bothTrueAndFalse = T::TEST;
+ private const int|false|true bothTrueAndFalse2 = T::TEST;
+ private const int|INT duplicatedInt = T::TEST;
+ private const iterable|array iterable1 = T::TEST;
+ private const iterable|Traversable iterable2 = T::TEST;
+ private const iterable|array|Traversable iterable3 = T::TEST;
+ private const null|false nullFalse = T::TEST; // PHP 8.2: OK
+}
+
+enum EnumTest {
+ protected const callable TEST1 = T::TEST;
+ protected const void TEST2 = T::TEST;
+ protected const never TEST3 = T::TEST;
+ protected const int|callable callable = T::TEST;
+ protected const false false = T::TEST;
+ protected const null null = T::TEST;
+ protected const true true = T::TEST;
+ protected const bool|false boolFalse = T::TEST;
+ protected const true|bool trueBool = T::TEST;
+ protected const bool|bool duplicatedBool = T::TEST;
+ protected const true|false bothTrueAndFalse = T::TEST;
+ protected const int|false|true bothTrueAndFalse2 = T::TEST;
+ protected const int|INT duplicatedInt = T::TEST;
+ protected const iterable|array iterable1 = T::TEST;
+ protected const iterable|Traversable iterable2 = T::TEST;
+ protected const iterable|array|Traversable iterable3 = T::TEST;
+ protected const null|false nullFalse = T::TEST; // PHP 8.2: OK
+}
diff --git a/php/php.editor/test/unit/data/testfiles/verification/UnusableTypesHintError/testConstantTypes_01.php.testConstantTypes_01.hints b/php/php.editor/test/unit/data/testfiles/verification/UnusableTypesHintError/testConstantTypes_01.php.testConstantTypes_01.hints
new file mode 100644
index 0000000..e1f1025
--- /dev/null
+++ b/php/php.editor/test/unit/data/testfiles/verification/UnusableTypesHintError/testConstantTypes_01.php.testConstantTypes_01.hints
@@ -0,0 +1,168 @@
+ const callable TEST1 = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ const void TEST2 = T::TEST;
+ ----
+HINT:"void" cannot be used as a property type.
+ const never TEST3 = T::TEST;
+ -----
+HINT:"never" cannot be used as a property type.
+ const int|callable callable = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ const bool|false boolFalse = T::TEST;
+ -----
+HINT:Type "false" is duplicated.
+ const true|bool trueBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ const bool|bool duplicatedBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ const true|false bothTrueAndFalse = T::TEST;
+ -----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ const int|false|true bothTrueAndFalse2 = T::TEST;
+ ----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ const int|INT duplicatedInt = T::TEST;
+ ---
+HINT:Type "INT" is duplicated.
+ const iterable|array iterable1 = T::TEST;
+ --------------
+HINT:Redundant combination: "iterable|array" contains both "iterable" and "array".
+ const iterable|Traversable iterable2 = T::TEST;
+ --------------------
+HINT:Redundant combination: "iterable|Traversable" contains both "iterable" and "Traversable".
+ const iterable|array|Traversable iterable3 = T::TEST;
+ --------------------------
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "Traversable".
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "array".
+ public const callable TEST1 = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ public const void TEST2 = T::TEST;
+ ----
+HINT:"void" cannot be used as a property type.
+ public const never TEST3 = T::TEST;
+ -----
+HINT:"never" cannot be used as a property type.
+ public const int|callable callable = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ public const bool|false boolFalse = T::TEST;
+ -----
+HINT:Type "false" is duplicated.
+ public const true|bool trueBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ public const bool|bool duplicatedBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ public const true|false bothTrueAndFalse = T::TEST;
+ -----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ public const int|false|true bothTrueAndFalse2 = T::TEST;
+ ----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ public const int|INT duplicatedInt = T::TEST;
+ ---
+HINT:Type "INT" is duplicated.
+ public const iterable|array iterable1 = T::TEST;
+ --------------
+HINT:Redundant combination: "iterable|array" contains both "iterable" and "array".
+ public const iterable|Traversable iterable2 = T::TEST;
+ --------------------
+HINT:Redundant combination: "iterable|Traversable" contains both "iterable" and "Traversable".
+ public const iterable|array|Traversable iterable3 = T::TEST;
+ --------------------------
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "Traversable".
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "array".
+ private const callable TEST1 = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ private const void TEST2 = T::TEST;
+ ----
+HINT:"void" cannot be used as a property type.
+ private const never TEST3 = T::TEST;
+ -----
+HINT:"never" cannot be used as a property type.
+ private const int|callable callable = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ private const bool|false boolFalse = T::TEST;
+ -----
+HINT:Type "false" is duplicated.
+ private const true|bool trueBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ private const bool|bool duplicatedBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ private const true|false bothTrueAndFalse = T::TEST;
+ -----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ private const int|false|true bothTrueAndFalse2 = T::TEST;
+ ----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ private const int|INT duplicatedInt = T::TEST;
+ ---
+HINT:Type "INT" is duplicated.
+ private const iterable|array iterable1 = T::TEST;
+ --------------
+HINT:Redundant combination: "iterable|array" contains both "iterable" and "array".
+ private const iterable|Traversable iterable2 = T::TEST;
+ --------------------
+HINT:Redundant combination: "iterable|Traversable" contains both "iterable" and "Traversable".
+ private const iterable|array|Traversable iterable3 = T::TEST;
+ --------------------------
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "Traversable".
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "array".
+ protected const callable TEST1 = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ protected const void TEST2 = T::TEST;
+ ----
+HINT:"void" cannot be used as a property type.
+ protected const never TEST3 = T::TEST;
+ -----
+HINT:"never" cannot be used as a property type.
+ protected const int|callable callable = T::TEST;
+ --------
+HINT:"callable" cannot be used as a property type.
+ protected const bool|false boolFalse = T::TEST;
+ -----
+HINT:Type "false" is duplicated.
+ protected const true|bool trueBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ protected const bool|bool duplicatedBool = T::TEST;
+ ----
+HINT:Type "bool" is duplicated.
+ protected const true|false bothTrueAndFalse = T::TEST;
+ -----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ protected const int|false|true bothTrueAndFalse2 = T::TEST;
+ ----
+HINT:Contains both "true" and "false", "bool" should be used.
+HINT:Contains both "true" and "false", "bool" should be used.
+ protected const int|INT duplicatedInt = T::TEST;
+ ---
+HINT:Type "INT" is duplicated.
+ protected const iterable|array iterable1 = T::TEST;
+ --------------
+HINT:Redundant combination: "iterable|array" contains both "iterable" and "array".
+ protected const iterable|Traversable iterable2 = T::TEST;
+ --------------------
+HINT:Redundant combination: "iterable|Traversable" contains both "iterable" and "Traversable".
+ protected const iterable|array|Traversable iterable3 = T::TEST;
+ --------------------------
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "Traversable".
+HINT:Redundant combination: "iterable|array|Traversable" contains both "iterable" and "array".
diff --git a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/indent/PHPFormatterTest.java b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/indent/PHPFormatterTest.java
index 130ff79..ddfbd74 100644
--- a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/indent/PHPFormatterTest.java
+++ b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/indent/PHPFormatterTest.java
@@ -1128,4 +1128,15 @@
options.put(FmtOptions.SPACE_AROUND_SCOPE_RESOLUTION_OPS, true);
reformatFileContents("testfiles/formatting/php83/dynamicClassConstantFetch_02.php", options, false, true);
}
+
+ public void testTypedClassConstants_01() throws Exception {
+ HashMap<String, Object> options = new HashMap<>(FmtOptions.getDefaults());
+ reformatFileContents("testfiles/formatting/php83/typedClassConstants_01.php", options, false, true);
+ }
+
+ public void testTypedClassConstants_02() throws Exception {
+ HashMap<String, Object> options = new HashMap<>(FmtOptions.getDefaults());
+ reformatFileContents("testfiles/formatting/php83/typedClassConstants_02.php", options, false, true);
+ }
+
}
diff --git a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintErrorTest.java b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintErrorTest.java
index 81b375a..0a48833 100644
--- a/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintErrorTest.java
+++ b/php/php.editor/test/unit/src/org/netbeans/modules/php/editor/verification/UnusableTypesHintErrorTest.java
@@ -75,6 +75,10 @@
checkHints(new UnusableTypesHintError(), "testDuplicateTypes_01.php");
}
+ public void testConstantTypes_01() throws Exception {
+ checkHints(new UnusableTypesHintError(), "testConstantTypes_01.php");
+ }
+
@Override
protected String getTestDirectory() {
return TEST_DIRECTORY + "UnusableTypesHintError/";