Skip to content

Commit

Permalink
PHP8.* Support - Basic validation support #148, #149, #150
Browse files Browse the repository at this point in the history
* Enum checks
* Constant expression validation
* Enum case check (AST fix)
* Implement methods quick fixes for enums
  • Loading branch information
zulus committed May 7, 2023
1 parent 1c3f33b commit 8eeb8c5
Show file tree
Hide file tree
Showing 20 changed files with 277 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,11 @@ public boolean visit(ClassDeclaration classDeclaration) {
return !isProgramScope;
}

@Override
public boolean visit(EnumDeclaration classDeclaration) {
return !isProgramScope;
}

@Override
public boolean visit(InterfaceDeclaration interfaceDeclaration) {
return !isProgramScope;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
* }
* </pre>
*/
public class EnumDeclaration extends TypeDeclaration
implements IPHPDocAwareDeclaration, IRecoverable, IAttributed {
public class EnumDeclaration extends TypeDeclaration implements IPHPDocAwareDeclaration, IRecoverable, IAttributed {

private PHPDocBlock phpDoc;
private boolean isRecovered;
Expand Down Expand Up @@ -64,6 +63,7 @@ public EnumDeclaration(int start, int end, int nameStart, int nameEnd, String en
setBody(body);

setModifier(IPHPModifiers.AccEnum | Modifiers.AccFinal);
this.backingType = backingType;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public class Messages extends NLS {
public static String CannotUseReservedWord;
public static String UnexpectedNamespaceDeclaration;
public static String FirstTypeMustMatchFileName;
public static String PropertyInEnum;
public static String EnumCaseInClass;
public static String EnumCaseWithType;
public static String EnumCaseWithoutType;

static {
// initialize resource bundle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
public enum PHPProblemIdentifier implements IProblemIdentifier, IProblemIdentifierExtension {

SYNTAX, USE_STATEMENTS /* deprecated */, AbstractMethodInAbstractClass, BodyForAbstractMethod, MethodRequiresBody, AbstractMethodsInConcreteClass, UndefinedType, ClassExtendFinalClass, CannotInstantiateType, ImportNotFound, DuplicateImport, UnusedImport, UnnecessaryImport, DuplicateDeclaration, DuplicateMethodDeclaration, DuplicateConstantDeclaration, DuplicateFieldDeclaration, AbstractMethodMustBeImplemented, SuperclassMustBeAClass, SuperInterfaceMustBeAnInterface, CannotUseTypeAsTrait, NestedNamespaceDeclarations, InvalidConstantExpression, ReassignAutoGlobalVariable, CannotUseReservedWord, UndefinedVariable, UnusedVariable, UnexpectedNamespaceDeclaration, FirstClassMustMatchFileName
SYNTAX, USE_STATEMENTS /* deprecated */, AbstractMethodInAbstractClass, BodyForAbstractMethod, MethodRequiresBody, AbstractMethodsInConcreteClass, UndefinedType, ClassExtendFinalClass, CannotInstantiateType, ImportNotFound, DuplicateImport, UnusedImport, UnnecessaryImport, DuplicateDeclaration, DuplicateMethodDeclaration, DuplicateConstantDeclaration, DuplicateFieldDeclaration, AbstractMethodMustBeImplemented, SuperclassMustBeAClass, SuperInterfaceMustBeAnInterface, CannotUseTypeAsTrait, NestedNamespaceDeclarations, InvalidConstantExpression, ReassignAutoGlobalVariable, CannotUseReservedWord, UndefinedVariable, UnusedVariable, UnexpectedNamespaceDeclaration, FirstClassMustMatchFileName, InvalidClassBodyStatement

;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ HeredocInvalidIndentation=Invalid body indentation level (expecting an indentati
CannotUseReservedWord=Cannot use ''{0}'' as {1} name as it is a reserved word
UnexpectedNamespaceDeclaration=The declared namespace "{0}" does not match the expected namespace "{1}"
FirstTypeMustMatchFileName=The declared {0} "{1}" must be named to "{2}"
PropertyInEnum=Enum may not include properties
EnumCaseInClass=Enum case can only be used in enums
EnumCaseWithType=Case {0} of non-backed enum {1} must not have a value
EnumCaseWithoutType=Case {0} of backed enum {1} must have a value
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,17 @@ public class ValidatorVisitor extends PHPASTVisitor implements IValidatorVisitor
RESERVED_WORDS.add(new SimpleProposal("null", PHPVersion.PHP7_0)); //$NON-NLS-1$
RESERVED_WORDS.add(new SimpleProposal("true", PHPVersion.PHP7_0)); //$NON-NLS-1$
RESERVED_WORDS.add(new SimpleProposal("false", PHPVersion.PHP7_0)); //$NON-NLS-1$
RESERVED_WORDS.add(new SimpleProposal("never", PHPVersion.PHP8_1)); //$NON-NLS-1$
}

static {
TYPE_SKIP.add("parent"); //$NON-NLS-1$
TYPE_SKIP.add("self"); //$NON-NLS-1$
TYPE_SKIP.add("static"); //$NON-NLS-1$
TYPE_SKIP.add("null"); //$NON-NLS-1$
TYPE_SKIP.add("false"); //$NON-NLS-1$
TYPE_SKIP.add("true"); //$NON-NLS-1$

COMMENT_TYPE_SKIP.add("true"); //$NON-NLS-1$
COMMENT_TYPE_SKIP.add("false"); //$NON-NLS-1$
}
Expand Down Expand Up @@ -254,6 +258,10 @@ public boolean visit(PHPFieldDeclaration s) throws Exception {
String id = "f" + s.getName(); //$NON-NLS-1$
Map<String, ISourceNode> childs = declarationScope.peekLast();
ISourceNode parentType = typeDeclarations.peekLast();
if (parentType instanceof EnumDeclaration) {
reportProblem(s, Messages.PropertyInEnum, PHPProblemIdentifier.InvalidClassBodyStatement, new String[] {},
ProblemSeverities.Error);
}
if (childs.containsKey(id) && !(parentType instanceof NamespaceDeclaration)
&& !(parentType instanceof IModuleDeclaration)) {
reportProblem(s.getNameStart(), s.getNameEnd(), Messages.DuplicateFieldDeclaration,
Expand Down Expand Up @@ -374,6 +382,21 @@ public boolean visit(ClassDeclaration s) throws Exception {
return super.visit(s);
}

@Override
public boolean visit(EnumDeclaration s) throws Exception {
currentDeclaration = s;
checkReservedWord(s, "enum"); //$NON-NLS-1$
checkUnimplementedMethods(s, s.getRef());
if (s.getSuperClasses() != null) {
for (ASTNode node : s.getSuperClasses().getChilds()) {
checkSuperclass((TypeReference) node, true, s.getName());
}

}

return super.visit(s);
}

@Override
public boolean visit(ClassInstanceCreation s) throws Exception {
if (s.getClassName() instanceof TypeReference) {
Expand Down Expand Up @@ -1121,7 +1144,27 @@ public boolean visit(Attribute s) throws Exception {
@Override
public boolean visit(ConstantDeclaration s) throws Exception {
currentDeclaration = s;
validateConstantExpression(s.getConstantValue(), ALLOW_NEW);
if (PHPFlags.isEnumCase(s.getModifiers())) {
ISourceNode parentType = typeDeclarations.peekLast();
if (parentType instanceof EnumDeclaration) {
EnumDeclaration decl = (EnumDeclaration) parentType;
if (decl.getBackingType() != null && s.getConstantValue() == null) {
reportProblem(s.getConstantName(), Messages.EnumCaseWithoutType,
PHPProblemIdentifier.InvalidClassBodyStatement,
new String[] { decl.getName(), s.getName() }, ProblemSeverities.Error);
} else if (decl.getBackingType() == null && s.getConstantValue() != null) {
reportProblem(s.getConstantName(), Messages.EnumCaseWithType,
PHPProblemIdentifier.InvalidClassBodyStatement,
new String[] { decl.getName(), s.getName() }, ProblemSeverities.Error);
}
} else {
reportProblem(s.getConstantName(), Messages.EnumCaseInClass,
PHPProblemIdentifier.InvalidClassBodyStatement, new String[] { s.getName() },
ProblemSeverities.Error);
}
} else {
validateConstantExpression(s.getConstantValue(), ALLOW_NEW);
}
String id = "c" + s.getName(); //$NON-NLS-1$
Map<String, ISourceNode> childs = declarationScope.peekLast();
if (childs.containsKey(id)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public void initializeDefaultPreferences() {
DefaultScope.INSTANCE);
problemPreferences.setSeverity(PHPProblemIdentifier.MethodRequiresBody, ProblemSeverity.ERROR,
DefaultScope.INSTANCE);
problemPreferences.setSeverity(PHPProblemIdentifier.InvalidClassBodyStatement, ProblemSeverity.ERROR,
DefaultScope.INSTANCE);
problemPreferences.setSeverity(PHPProblemIdentifier.AbstractMethodsInConcreteClass, ProblemSeverity.ERROR,
DefaultScope.INSTANCE);
problemPreferences.setSeverity(PHPProblemIdentifier.UndefinedType, ProblemSeverity.ERROR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private GoalEvaluator createExpressionEvaluator(ExpressionTypeGoal exprGoal) {
Class<?> expressionClass = expression.getClass();

if (expressionClass == InterfaceDeclaration.class || expressionClass == ClassDeclaration.class
|| expressionClass == TraitDeclaration.class) {
|| expressionClass == TraitDeclaration.class || expressionClass == EnumDeclaration.class) {
return new PHPClassEvaluator(exprGoal, (TypeDeclaration) expression);
}
if (expressionClass == Assignment.class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public final class AddUnimplementedMethodsOperation implements IWorkspaceRunnabl
* @param type
* the type to add the methods to
* @param methodsToImplement
* the method bindings to implement or <code>null</code> to implement
* all unimplemented methods
* the method bindings to implement or <code>null</code> to
* implement all unimplemented methods
* @param insertPos
* the insertion point, or <code>-1</code>
* @param imports
Expand All @@ -83,8 +83,8 @@ public final class AddUnimplementedMethodsOperation implements IWorkspaceRunnabl
* <code>true</code> if the resulting edit should be applied,
* <code>false</code> otherwise
* @param save
* <code>true</code> if the changed compilation unit should be saved,
* <code>false</code> otherwise
* <code>true</code> if the changed compilation unit should be
* saved, <code>false</code> otherwise
* @param doc
*/
public AddUnimplementedMethodsOperation(Program astRoot, IType element, ITypeBinding type,
Expand Down Expand Up @@ -142,6 +142,9 @@ public final void run(IProgressMonitor monitor) throws CoreException {
if (node instanceof ClassDeclaration) {
memberRewriter = astRewrite.getListRewrite(((ClassDeclaration) node).getBody(),
Block.STATEMENTS_PROPERTY);
} else if (node instanceof EnumDeclaration) {
memberRewriter = astRewrite.getListRewrite(((EnumDeclaration) node).getBody(),
Block.STATEMENTS_PROPERTY);
} else if (node instanceof AnonymousClassDeclaration) {
memberRewriter = astRewrite.getListRewrite(((AnonymousClassDeclaration) node).getBody(),
Block.STATEMENTS_PROPERTY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ public static void addUnimplementedMethodsProposals(IInvocationContext context,
hasProposal = true;
}
break;
} else if (typeNode instanceof EnumDeclaration) {
if (((EnumDeclaration) typeNode).resolveTypeBinding() != null) {
hasProposal = true;
}
break;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ protected ASTRewrite getRewrite() throws CoreException {
listRewrite = rewrite.getListRewrite(decl.getBody(), Block.STATEMENTS_PROPERTY);
methods = PHPModelUtils.getUnimplementedMethods((IType) binding.getPHPElement(), null);
isInterface = binding.isInterface();
} else if (fTypeNode instanceof EnumDeclaration) {
EnumDeclaration decl = (EnumDeclaration) fTypeNode;
ITypeBinding binding = decl.resolveTypeBinding();
listRewrite = rewrite.getListRewrite(decl.getBody(), Block.STATEMENTS_PROPERTY);
methods = PHPModelUtils.getUnimplementedMethods((IType) binding.getPHPElement(), null);
isInterface = binding.isInterface();
} else if (fTypeNode instanceof AnonymousClassDeclaration) {
AnonymousClassDeclaration decl = (AnonymousClassDeclaration) fTypeNode;
listRewrite = rewrite.getListRewrite(decl.getBody(), Block.STATEMENTS_PROPERTY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ enum Status: string
?>
--EXPECT--
<ModuleDeclaration start="0" end="126">
<EnumDeclaration start="6" end="122" modifiers="final" name="Status">
<EnumDeclaration start="6" end="122" modifiers="final" name="Status" type="string">
<FullyQualifiedReference start="19" end="25" name="string">
</FullyQualifiedReference>
<ConstantDeclaration start="32" end="53" modifiers="final,public,enum_case">
<ConstantReference start="37" end="42" name="Draft">
</ConstantReference>
Expand All @@ -33,4 +35,4 @@ enum Status: string
</EnumDeclaration>
<EmptyStatement start="123" end="125">
</EmptyStatement>
</ModuleDeclaration>
</ModuleDeclaration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
New in initializers
--FILE--
<?php
enum Foo {
case A;
case X =1;
private $el;
}
?>
--EXPECT--
[line=4, start=32, end=33] Case Foo of non-backed enum X must not have a value
[line=5, start=47, end=50] Enum may not include properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
New in initializers
--FILE--
<?php
enum Foo : int{
case A;
case X = 1;
private $el;
}
?>
--EXPECT--
[line=3, start=28, end=29] Case Foo of backed enum A must have a value
[line=5, start=53, end=56] Enum may not include properties

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
New in initializers
--FILE--
<?php
class MyClass {
case X;
}
?>
--EXPECT--
[line=3, start=28, end=29] Enum case can only be used in enums
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
New in initializers
--FILE--
<?php
interface FooInterface
{
public function bar();
}

enum Test implements FooInterface {
}
?>
--EXPECT--
[line=7, start=63, end=67] The type Test must implement the inherited abstract method FooInterface::bar()
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public class UnimplementMethodsTests {
TESTS.put(PHPVersion.PHP5_6, new String[] { "/workspace/generation/unimplement_methods_php56" }); //$NON-NLS-1$
TESTS.put(PHPVersion.PHP7_0, new String[] { "/workspace/generation/unimplement_methods_php56", //$NON-NLS-1$
"/workspace/generation/unimplement_methods_php7" }); //$NON-NLS-1$
TESTS.put(PHPVersion.PHP8_1, new String[] { "/workspace/generation/unimplement_methods_php56", //$NON-NLS-1$
"/workspace/generation/unimplement_methods_php7", "/workspace/generation/unimplement_methods_php81" }); //$NON-NLS-1$
};

protected static final char SELECTION_CHAR = '|';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
--TEST--
Test method generation
--FILE--
<?php

namespace Foo;

use Something\Test;

enum |Service| implements \ArrayAccess {
}
--EXPECT--
<?php

namespace Foo;

use Something\Test;

enum Service implements \ArrayAccess {
/**
* {@inheritDoc}
* @see ArrayAccess::offsetExists()
*/
public function offsetExists($offset) {
// TODO Auto-generated method stub

}

/**
* {@inheritDoc}
* @see ArrayAccess::offsetGet()
*/
public function offsetGet($offset) {
// TODO Auto-generated method stub

}

/**
* {@inheritDoc}
* @see ArrayAccess::offsetSet()
*/
public function offsetSet($offset, $value) {
// TODO Auto-generated method stub

}

/**
* {@inheritDoc}
* @see ArrayAccess::offsetUnset()
*/
public function offsetUnset($offset) {
// TODO Auto-generated method stub

}

}

0 comments on commit 8eeb8c5

Please sign in to comment.