diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/PredefinedSymbols.java b/php/php.editor/src/org/netbeans/modules/php/editor/PredefinedSymbols.java index 006641fed3c1..440bc9592d9b 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/PredefinedSymbols.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/PredefinedSymbols.java @@ -101,6 +101,8 @@ public final class PredefinedSymbols { "__sleep", // NOI18N "__wakeup", // NOI18N "__toString", // NOI18N + "__invoke", // NOI18N + "__debugInfo", // NOI18N "__serialize", // NOI18N PHP 7.4 "__unserialize", // NOI18N PHP 7.4 }))); 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 1c0d10d3015b..faa3dcf11add 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 @@ -30,6 +30,7 @@ import org.netbeans.modules.csl.api.OffsetRange; import org.netbeans.modules.csl.spi.support.CancelSupport; import org.netbeans.modules.php.editor.CodeUtils; +import org.netbeans.modules.php.editor.PredefinedSymbols; import org.netbeans.modules.php.editor.model.EnumScope; import org.netbeans.modules.php.editor.model.FieldElement; import org.netbeans.modules.php.editor.model.FileScope; @@ -57,8 +58,26 @@ */ public class IncorrectEnumHintError extends HintErrorRule { + private static final Map FORBIDDEN_MAGIC_METHODS = new HashMap<>(); + private static final Map FORBIDDEN_BACKED_ENUM_METHODS = new HashMap<>(); + private FileObject fileObject; + static { + for (String methodName : PredefinedSymbols.MAGIC_METHODS) { + // Methods __call, __callStatic and __invoke are allowed in enum. + if ("__call".equals(methodName) // NOI18N + || "__callStatic".equals(methodName) // NOI18N + || "__invoke".equals(methodName)) { // NOI18N + continue; + } + FORBIDDEN_MAGIC_METHODS.put(methodName.toLowerCase(), methodName); + } + + FORBIDDEN_BACKED_ENUM_METHODS.put("from", "from"); // NOI18N + FORBIDDEN_BACKED_ENUM_METHODS.put("tryfrom", "tryFrom"); // NOI18N + } + @Override @NbBundle.Messages("IncorrectEnumHintError.displayName=Incorrect Declaration of Enumeration") public String getDisplayName() { @@ -73,7 +92,11 @@ public String getDisplayName() { "# {0} - the trait name", "IncorrectEnumHintError.incorrectEnumPropertiesWithTrait=Enum cannot have properties, but \"{0}\" has properties", "IncorrectEnumHintError.incorrectEnumConstructor=Enum cannot have a constructor", - }) + "IncorrectEnumHintError.incorrectEnumMethodCases=Enum cannot redeclare method \"cases\"", + "# {0} - the method name", + "IncorrectEnumHintError.incorrectEnumMagicMethod=Enum cannot contain magic method method \"{0}\"", + "# {0} - the method name", + "IncorrectEnumHintError.incorrectBackedEnumMethod=Backed enum cannot redeclare method \"{0}\"",}) public void invoke(PHPRuleContext context, List hints) { PHPParseResult phpParseResult = (PHPParseResult) context.parserResult; if (phpParseResult.getProgram() == null) { @@ -113,7 +136,9 @@ public void invoke(PHPRuleContext context, List hints) { return; } checkTraits(declaredEnum.getTraits(), declaredEnum, hints, checkVisitor.getUseTraits()); - checkConstructor(declaredEnum.getDeclaredMethods(), hints); + // Forbidden methods from traits are ignored by PHP + boolean isBackedEnum = declaredEnum.getBackingType() != null; + checkMethods(declaredEnum.getDeclaredMethods(), isBackedEnum, hints); } } } @@ -142,13 +167,22 @@ private void checkTraits(Collection traits, EnumScope enum } } - private void checkConstructor(Collection methods, List hints) { + private void checkMethods(Collection methods, boolean isBackedEnum, List hints) { for (MethodScope method : methods) { if (CancelSupport.getDefault().isCancelled()) { return; } if (method.isConstructor()) { addHint(method, Bundle.IncorrectEnumHintError_incorrectEnumConstructor(), hints); + continue; + } + String methodName = method.getName().toLowerCase(); + if ("cases".equals(methodName)) { // NOI18N + addHint(method, Bundle.IncorrectEnumHintError_incorrectEnumMethodCases(), hints); + } else if (FORBIDDEN_MAGIC_METHODS.containsKey(methodName)) { + addHint(method, Bundle.IncorrectEnumHintError_incorrectEnumMagicMethod(FORBIDDEN_MAGIC_METHODS.get(methodName)), hints); + } else if (isBackedEnum && FORBIDDEN_BACKED_ENUM_METHODS.containsKey(methodName)) { + addHint(method, Bundle.IncorrectEnumHintError_incorrectBackedEnumMethod(FORBIDDEN_BACKED_ENUM_METHODS.get(methodName)), hints); } } } diff --git a/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php b/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php index 1c87b7710a5b..67bb8b04d255 100644 --- a/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php +++ b/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php @@ -40,6 +40,52 @@ public function __construct() { } } +enum IncorrectMethods { + public function cases():array {} + public function __get($name) {} + public function __set($name,$value) {} + public function __isset($name) {} + public function __unset($name) {} + public function __sleep() {} + public function __wakeup() {} + public function __serialize() {} + public function __unserialize($array) {} + public function __toString() {} + public static function __set_state($state) {} + public function __clone() {} + public function __debugInfo() {} +} + +enum IncorrectMethodsBacked: string { + public function cases():array {} + public function from(string|int $value):self {} + public function tryFrom(string|int $value):self {} +} + +enum IncorrectMethodsCorrectCaseInMessage { + public function Cases():array {} + public function __GET($name) {} + public function __seT($name,$value) {} + public function __ISset($name) {} + public function __unSET($name) {} + public function __sleeP() {} + public function __wAkeup() {} + public function __Serialize() {} + public function __Unserialize($array) {} + public function __ToString() {} + public static function __Set_State($state) {} + public function __clONe() {} + public function __DebugInfo() {} +} + +enum CorrectMethods { + public function from(string|int $value):self {} + public function tryFrom(string|int $value):self {} + public function __call($name,$args) {} + public static function __callStatic($name,$args) {} + public function __invoke() {} +} + enum CorrectBackingTypeString: string { case CASE_NAME; } diff --git a/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php.testIncorrectEnums.hints b/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php.testIncorrectEnums.hints index 3ead2c1f2787..36c0309db3af 100644 --- a/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php.testIncorrectEnums.hints +++ b/php/php.editor/test/unit/data/testfiles/verification/IncorrectEnumHintError/testIncorrectEnums.php.testIncorrectEnums.hints @@ -20,6 +20,93 @@ HINT:Enum cannot have properties public function __construct() { ----------- HINT:Enum cannot have a constructor + public function cases():array {} + ----- +HINT:Enum cannot redeclare method "cases" + public function __get($name) {} + ----- +HINT:Enum cannot contain magic method method "__get" + public function __set($name,$value) {} + ----- +HINT:Enum cannot contain magic method method "__set" + public function __isset($name) {} + ------- +HINT:Enum cannot contain magic method method "__isset" + public function __unset($name) {} + ------- +HINT:Enum cannot contain magic method method "__unset" + public function __sleep() {} + ------- +HINT:Enum cannot contain magic method method "__sleep" + public function __wakeup() {} + -------- +HINT:Enum cannot contain magic method method "__wakeup" + public function __serialize() {} + ----------- +HINT:Enum cannot contain magic method method "__serialize" + public function __unserialize($array) {} + ------------- +HINT:Enum cannot contain magic method method "__unserialize" + public function __toString() {} + ---------- +HINT:Enum cannot contain magic method method "__toString" + public static function __set_state($state) {} + ----------- +HINT:Enum cannot contain magic method method "__set_state" + public function __clone() {} + ------- +HINT:Enum cannot contain magic method method "__clone" + public function __debugInfo() {} + ----------- +HINT:Enum cannot contain magic method method "__debugInfo" + public function cases():array {} + ----- +HINT:Enum cannot redeclare method "cases" + public function from(string|int $value):self {} + ---- +HINT:Backed enum cannot redeclare method "from" + public function tryFrom(string|int $value):self {} + ------- +HINT:Backed enum cannot redeclare method "tryFrom" + public function Cases():array {} + ----- +HINT:Enum cannot redeclare method "cases" + public function __GET($name) {} + ----- +HINT:Enum cannot contain magic method method "__get" + public function __seT($name,$value) {} + ----- +HINT:Enum cannot contain magic method method "__set" + public function __ISset($name) {} + ------- +HINT:Enum cannot contain magic method method "__isset" + public function __unSET($name) {} + ------- +HINT:Enum cannot contain magic method method "__unset" + public function __sleeP() {} + ------- +HINT:Enum cannot contain magic method method "__sleep" + public function __wAkeup() {} + -------- +HINT:Enum cannot contain magic method method "__wakeup" + public function __Serialize() {} + ----------- +HINT:Enum cannot contain magic method method "__serialize" + public function __Unserialize($array) {} + ------------- +HINT:Enum cannot contain magic method method "__unserialize" + public function __ToString() {} + ---------- +HINT:Enum cannot contain magic method method "__toString" + public static function __Set_State($state) {} + ----------- +HINT:Enum cannot contain magic method method "__set_state" + public function __clONe() {} + ------- +HINT:Enum cannot contain magic method method "__clone" + public function __DebugInfo() {} + ----------- +HINT:Enum cannot contain magic method method "__debugInfo" case C; ------- HINT:"case" can only be in enums