Skip to content

Commit

Permalink
Improved method checks in enum declaration
Browse files Browse the repository at this point in the history
  • Loading branch information
KacerCZ committed Jan 23, 2023
1 parent 31028d2 commit a45983c
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
})));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,8 +58,26 @@
*/
public class IncorrectEnumHintError extends HintErrorRule {

private static final Map<String, String> FORBIDDEN_MAGIC_METHODS = new HashMap<>();
private static final Map<String, String> 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() {
Expand All @@ -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<Hint> hints) {
PHPParseResult phpParseResult = (PHPParseResult) context.parserResult;
if (phpParseResult.getProgram() == null) {
Expand Down Expand Up @@ -113,7 +136,9 @@ public void invoke(PHPRuleContext context, List<Hint> 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);
}
}
}
Expand Down Expand Up @@ -142,13 +167,22 @@ private void checkTraits(Collection<? extends TraitScope> traits, EnumScope enum
}
}

private void checkConstructor(Collection<? extends MethodScope> methods, List<Hint> hints) {
private void checkMethods(Collection<? extends MethodScope> methods, boolean isBackedEnum, List<Hint> 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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit a45983c

Please sign in to comment.