Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved method checks in enum declaration #5341

Merged
merged 1 commit into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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