Skip to content

Commit

Permalink
Merge pull request #6873 from junichi11/php83-marking-overridden-methods
Browse files Browse the repository at this point in the history
PHP 8.3 Support: Marking overridden methods (#[\Override]) #6701
  • Loading branch information
junichi11 committed Dec 23, 2023
2 parents a9f5776 + af05cd8 commit 2141d37
Show file tree
Hide file tree
Showing 261 changed files with 8,376 additions and 209 deletions.
2 changes: 1 addition & 1 deletion php/php.api.phpmodule/manifest.mf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.modules.php.api.phpmodule
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/api/phpmodule/resources/Bundle.properties
OpenIDE-Module-Specification-Version: 2.93
OpenIDE-Module-Specification-Version: 2.94
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,17 @@ public boolean hasConstantsInTraits() {
return this.compareTo(PhpVersion.PHP_82) >= 0;
}

/**
* Check whether this version supports [#\Override] attribute.
*
* @return {@code true} if this version supports [#\Override] attribute,
* {@code false} otherwise
* @since 2.94
*/
public boolean hasOverrideAttribute() {
return this.compareTo(PhpVersion.PHP_83) >= 0;
}

/**
* Check whether this is supported version yet by PHP official.
*
Expand Down
2 changes: 1 addition & 1 deletion php/php.editor/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
nbjavac.ignore.missing.enclosing=**/CUP$ASTPHP5Parser$actions.class
nbm.needs.restart=true
spec.version.base=2.32.0
spec.version.base=2.34.0
release.external/predefined_vars-1.0.zip=docs/predefined_vars.zip
sigtest.gen.fail.on.error=false

Expand Down
2 changes: 1 addition & 1 deletion php/php.editor/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>2.93</specification-version>
<specification-version>2.94</specification-version>
</run-dependency>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ public final class CodeUtils {
public static final String NULLABLE_TYPE_PREFIX = "?"; // NOI18N
public static final String ELLIPSIS = "..."; // NOI18N
public static final String VAR_TAG = "@var"; // NOI18N
public static final String OVERRIDE_ATTRIBUTE_NAME = "Override"; // NOI18N
public static final String OVERRIDE_ATTRIBUTE_FQ_NAME = "\\" + OVERRIDE_ATTRIBUTE_NAME; // NOI18N
public static final String OVERRIDE_ATTRIBUTE = "#[" + OVERRIDE_ATTRIBUTE_FQ_NAME + "]"; // NOI18N
public static final String EMPTY_STRING = ""; // NOI18N
public static final String NEW_LINE = "\n"; // NOI18N

public static final Pattern WHITE_SPACES_PATTERN = Pattern.compile("\\s+"); // NOI18N
public static final Pattern SPLIT_TYPES_PATTERN = Pattern.compile("[()|&]+"); // NOI18N
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.util.Locale;
import java.util.Set;
import javax.swing.text.JTextComponent;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.modules.csl.spi.ParserResult;
import org.netbeans.modules.parsing.api.ParserManager;
import org.netbeans.modules.parsing.api.ResultIterator;
Expand Down Expand Up @@ -58,7 +60,9 @@
import org.netbeans.modules.php.editor.parser.astnodes.Block;
import org.netbeans.modules.php.editor.parser.astnodes.BodyDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation;
import org.netbeans.modules.php.editor.parser.astnodes.Comment;
import org.netbeans.modules.php.editor.parser.astnodes.EnumDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.FieldsDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.FormalParameter;
import org.netbeans.modules.php.editor.parser.astnodes.Identifier;
Expand Down Expand Up @@ -251,9 +255,9 @@ private void initProperties(ResultIterator resultIterator) throws ParseException
PHPParseResult info = (PHPParseResult) resultIterator.getParserResult();
if (info != null) {
int caretOffset = textComp.getCaretPosition();
TypeDeclaration typeDecl = findEnclosingClassOrTrait(info, caretOffset);
ASTNode typeDecl = findEnclosingType(info, caretOffset);
if (typeDecl != null) {
className = typeDecl.getName().getName();
className = getTypeName(typeDecl);
if (className != null) {
FileObject fileObject = info.getSnapshot().getSource().getFileObject();
Index index = ElementQueryFactory.getIndexQuery(info);
Expand All @@ -262,17 +266,19 @@ private void initProperties(ResultIterator resultIterator) throws ParseException
QualifiedName.create(className),
caretOffset,
info.getModel().getVariableScope(caretOffset));
Set<ClassElement> classes = forFilesFilter.filter(index.getClasses(NameKind.exact(fullyQualifiedName)));
for (ClassElement classElement : classes) {
ElementFilter forNotDeclared = ElementFilter.forExcludedElements(index.getDeclaredMethods(classElement));
Set<TypeElement> types = forFilesFilter.filter(index.getTypes(NameKind.exact(fullyQualifiedName)));
for (TypeElement typeElement : types) {
ElementFilter forNotDeclared = ElementFilter.forExcludedElements(index.getDeclaredMethods(typeElement));
final Set<MethodElement> accessibleMethods = new HashSet<>();
accessibleMethods.addAll(forNotDeclared.filter(index.getAccessibleMethods(classElement, classElement)));
accessibleMethods.addAll(
ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getConstructors(classElement))));
accessibleMethods.addAll(forNotDeclared.filter(index.getAccessibleMethods(typeElement, typeElement)));
if (typeElement instanceof ClassElement) {
accessibleMethods.addAll(
ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getConstructors((ClassElement) typeElement))));
}
accessibleMethods.addAll(
ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getAccessibleMagicMethods(classElement))));
ElementFilter.forExcludedElements(accessibleMethods).filter(forNotDeclared.filter(index.getAccessibleMagicMethods(typeElement))));
final Set<TypeElement> preferedTypes = forFilesFilter.prefer(ElementTransformation.toMemberTypes().transform(accessibleMethods));
final TreeElement<TypeElement> enclosingType = index.getInheritedTypesAsTree(classElement, preferedTypes);
final TreeElement<TypeElement> enclosingType = index.getInheritedTypesAsTree(typeElement, preferedTypes);
final List<MethodProperty> methodProperties = new ArrayList<>();
final Set<MethodElement> methods = ElementFilter.forMembersOfTypes(preferedTypes).filter(accessibleMethods);
for (final MethodElement methodElement : methods) {
Expand All @@ -290,6 +296,11 @@ private void initProperties(ResultIterator resultIterator) throws ParseException

PropertiesVisitor visitor = new PropertiesVisitor(existingGetters, existingSetters, Utils.getRoot(info));
visitor.scan(typeDecl);
if (typeDecl instanceof EnumDeclaration) {
// Enum can't have a constructor
// to avoid adding the list of code generators, change this
hasConstructor = true;
}
String propertyName;
boolean existGetter, existSetter;
for (Property property : getProperties()) {
Expand All @@ -311,24 +322,50 @@ private void initProperties(ResultIterator resultIterator) throws ParseException
}

/**
* Find out class enclosing caret
* @param info
* Find out the type enclosing the caret.
*
* @param info parser result
* @param offset caret offset
* @return class declaration or null
* @return type declaration or class instance creation(anonymous class),
* otherwise {@code null}
*/
private TypeDeclaration findEnclosingClassOrTrait(ParserResult info, int offset) {
@CheckForNull
private ASTNode findEnclosingType(ParserResult info, int offset) {
List<ASTNode> nodes = NavUtils.underCaret(info, offset);
int count = nodes.size();
if (count > 2) { // the cursor has to be in class block see issue #142417
ASTNode declaration = nodes.get(count - 2);
ASTNode block = nodes.get(count - 1);
if (block instanceof Block && (declaration instanceof ClassDeclaration || declaration instanceof TraitDeclaration)) {
return (TypeDeclaration) declaration;
if (block instanceof Block && isValidEnclosingType(declaration)) {
return declaration;
}
}
return null;
}

private boolean isValidEnclosingType(ASTNode typeDeclaration) {
return typeDeclaration instanceof ClassDeclaration
|| typeDeclaration instanceof TraitDeclaration
|| typeDeclaration instanceof EnumDeclaration
|| (typeDeclaration instanceof ClassInstanceCreation && ((ClassInstanceCreation) typeDeclaration).isAnonymous());
}

@CheckForNull
private String getTypeName(@NullAllowed ASTNode typeDeclaration) {
if (typeDeclaration == null) {
return null;
}
String typeName = null;
if (typeDeclaration instanceof TypeDeclaration) {
typeName = ((TypeDeclaration) typeDeclaration).getName().getName();
} else if (typeDeclaration instanceof ClassInstanceCreation) {
typeName = CodeUtils.extractClassName((ClassInstanceCreation) typeDeclaration);
} else {
assert false : "Expected: TypeDeclaration or ClassInstanceCreation, but got" + typeDeclaration.getClass(); // NOI18N
}
return typeName;
}

private class PropertiesVisitor extends DefaultVisitor {

private final List<String> existingGetters;
Expand Down Expand Up @@ -432,7 +469,7 @@ private boolean canBeNull(final PHPDocTypeTag typeTag) {
boolean canBeNull = false;
if (typeTag.getTypes().size() > 1) {
for (PHPDocTypeNode typeNode : typeTag.getTypes()) {
String type = typeNode.getValue().toLowerCase(new Locale("en_US")); // NOI18N
String type = typeNode.getValue().toLowerCase(Locale.ROOT);
if (type.equals(Type.NULL)) {
canBeNull = true;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@
import org.netbeans.modules.php.editor.parser.astnodes.ASTNode;
import org.netbeans.modules.php.editor.parser.astnodes.Block;
import org.netbeans.modules.php.editor.parser.astnodes.ClassDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation;
import org.netbeans.modules.php.editor.parser.astnodes.EmptyStatement;
import org.netbeans.modules.php.editor.parser.astnodes.EnumDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.FunctionDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.InterfaceDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.MethodDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.NamespaceDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.Program;
import org.netbeans.modules.php.editor.parser.astnodes.TraitDeclaration;

/**
*
Expand Down Expand Up @@ -93,7 +97,11 @@ public enum InvocationContext {
CLASS {
@Override
boolean isExactlyIn(ASTNode lastNode) {
return lastNode instanceof ClassDeclaration;
return lastNode instanceof ClassDeclaration
|| lastNode instanceof TraitDeclaration
|| lastNode instanceof InterfaceDeclaration
|| lastNode instanceof EnumDeclaration
|| (lastNode instanceof ClassInstanceCreation && ((ClassInstanceCreation) lastNode).isAnonymous());
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ public void visit(InterfaceDeclaration node) {
ClassElementAttribute ce = (ClassElementAttribute) global.enterWrite(name, Kind.IFACE, node);

node2Element.put(node, ce);
List<Expression> interfaes = node.getInterfaes();
List<Expression> interfaes = node.getInterfaces();
for (Expression identifier : interfaes) {
ClassElementAttribute iface = (ClassElementAttribute) lookup(CodeUtils.extractUnqualifiedName(identifier), Kind.IFACE);
ce.ifaces.add(iface);
Expand Down Expand Up @@ -437,7 +437,7 @@ public void visit(ClassDeclaration node) {
if (superClsName != null) {
ce.superClass = (ClassElementAttribute) lookup(superClsName.getName(), Kind.CLASS);
}
List<Expression> interfaes = node.getInterfaes();
List<Expression> interfaes = node.getInterfaces();
for (Expression identifier : interfaes) {
ClassElementAttribute iface = (ClassElementAttribute) lookup(CodeUtils.extractUnqualifiedName(identifier), Kind.IFACE);
ce.ifaces.add(iface);
Expand Down Expand Up @@ -797,7 +797,7 @@ private void performEnterPass(DefinitionScope scope, Collection<? extends ASTNod
ce.superClass = (ClassElementAttribute) lookup(superClsName.getName(), Kind.CLASS);
node2Element.put(node.getSuperClass(), ce.superClass);
}
List<Expression> interfaces = node.getInterfaes();
List<Expression> interfaces = node.getInterfaces();
for (Expression identifier : interfaces) {
//TODO: ifaces must be fixed;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
package org.netbeans.modules.php.editor.codegen;

import java.util.ArrayList;
import java.util.Collection;
import org.netbeans.modules.php.api.PhpVersion;
import org.netbeans.modules.php.editor.CodeUtils;
import org.netbeans.modules.php.editor.api.elements.BaseFunctionElement;
import org.netbeans.modules.php.editor.api.elements.MethodElement;
import org.netbeans.modules.php.editor.api.elements.TypeResolver;
import static org.netbeans.modules.php.editor.codegen.CGSGenerator.NEW_LINE;
import org.netbeans.modules.php.editor.elements.ElementUtils;
import org.netbeans.modules.php.editor.model.impl.Type;

/**
Expand All @@ -49,21 +53,32 @@ public InheritedMethodCreator(CGSInfo cgsInfo) {
public String create(MethodProperty property) {
final StringBuilder inheritedMethod = new StringBuilder();
final MethodElement method = property.getMethod();
if (method.isAbstract() || method.isMagic() || method.getType().isInterface()) {
inheritedMethod.append(getOverrideAttribute(method)); // PHP 8.3
Collection<TypeResolver> returnTypes = method.getReturnTypes();
if (method.isAbstract() || method.isMagic() || method.getType().isInterface() || method.getType().isTrait()
|| ElementUtils.isVoidOrNeverType(returnTypes)) {
inheritedMethod.append(method.asString(
BaseFunctionElement.PrintAs.DeclarationWithEmptyBody,
cgsInfo.createTypeNameResolver(method),
cgsInfo.getPhpVersion()).replace("abstract ", "")); //NOI18N;
cgsInfo.getPhpVersion()).replace("abstract ", CodeUtils.EMPTY_STRING)); //NOI18N;
} else {
inheritedMethod.append(method.asString(
BaseFunctionElement.PrintAs.DeclarationWithParentCallInBody,
cgsInfo.createTypeNameResolver(method),
cgsInfo.getPhpVersion()).replace("abstract ", "")); //NOI18N;
cgsInfo.getPhpVersion()).replace("abstract ", CodeUtils.EMPTY_STRING)); //NOI18N;
}
inheritedMethod.append(NEW_LINE);
return inheritedMethod.toString();
}

private String getOverrideAttribute(MethodElement method) {
if (!method.isMagic()
&& (!method.getType().isTrait() || ElementUtils.isAbstractTraitMethod(method))
&& cgsInfo.getPhpVersion().hasOverrideAttribute()) {
return CodeUtils.OVERRIDE_ATTRIBUTE + CodeUtils.NEW_LINE;
}
return CodeUtils.EMPTY_STRING;
}
}

abstract class SinglePropertyMethodCreatorImpl implements SinglePropertyMethodCreator<Property> {
Expand All @@ -83,7 +98,7 @@ protected String getMethodName(Property property) {
String changedName = cgsInfo.getHowToGenerate() == CGSGenerator.GenWay.WITHOUT_UNDERSCORE
? CodegenUtils.upFirstLetterWithoutUnderscore(property.getName())
: CodegenUtils.upFirstLetter(property.getName());
return CodegenUtils.getUnusedMethodName(new ArrayList<String>(), changedName);
return CodegenUtils.getUnusedMethodName(new ArrayList<>(), changedName);
}

protected String getAccessModifier() {
Expand Down

0 comments on commit 2141d37

Please sign in to comment.