Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDawid Pakuła2019-12-01 10:29:25 -0500
committerDawid Pakuła2019-12-01 21:31:52 -0500
commitb0abc6143fd7bf89f3dcc7dd3794e46118f56082 (patch)
tree91e634480593bc223bd5bda39450c8cb778d628a
parent1cbb458a836a3709d705f43f6fed9b0546acc8c0 (diff)
downloadorg.eclipse.pdt-b0abc6143fd7bf89f3dcc7dd3794e46118f56082.tar.gz
org.eclipse.pdt-b0abc6143fd7bf89f3dcc7dd3794e46118f56082.tar.xz
org.eclipse.pdt-b0abc6143fd7bf89f3dcc7dd3794e46118f56082.zip
Bug 551301 - PHP 7.4 Arrow function CA
Change-Id: If702f04e5c346de5a1c7f35c62741f7b7d44a965 Signed-off-by: Dawid Pakuła <zulus@w3des.net>
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/IPHPModifiers.java7
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/PHPFlags.java21
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionDeclarationContext.java44
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterContext.java22
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterTypeContext.java33
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionReturnTypeContext.java5
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/MethodNameContext.java5
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/scope/ScopeParser.java91
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/compiler/PHPSourceElementRequestor.java103
-rw-r--r--plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/util/text/PHPTextSequenceUtilities.java99
-rw-r--r--tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/CodeAssistTests.java3
-rw-r--r--tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/scope/CodeAssistScopeTests.java11
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow01.pdtt11
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow02.pdtt11
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow03.pdtt8
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow04.pdtt9
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php7/returnType01.pdtt14
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php71/returnType01.pdtt17
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow01.pdtt10
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow02.pdtt10
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow03.pdtt10
-rw-r--r--tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow04.pdtt10
22 files changed, 425 insertions, 129 deletions
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/IPHPModifiers.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/IPHPModifiers.java
index 09f291df4..32a3fa067 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/IPHPModifiers.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/IPHPModifiers.java
@@ -66,11 +66,16 @@ public interface IPHPModifiers extends Modifiers {
public static final int AccNullable = (1 << Modifiers.USER_MODIFIER + 10);
/**
+ * @since 7.0
+ */
+ public static final int AccArrow = (1 << Modifiers.USER_MODIFIER + 11);
+
+ /**
* All access modifiers as one for compiler validation
*
* @since 4.0
*/
public static final int AccessMask = AccPublic | AccPrivate | AccProtected;
- public static final int USER_MODIFIER = Modifiers.USER_MODIFIER + 11;
+ public static final int USER_MODIFIER = Modifiers.USER_MODIFIER + 12;
}
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/PHPFlags.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/PHPFlags.java
index d3d89cd26..cf6f7f8e5 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/PHPFlags.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/core/compiler/PHPFlags.java
@@ -18,13 +18,14 @@ import org.eclipse.dltk.core.Flags;
public class PHPFlags extends Flags implements IPHPModifiers {
/**
- * Returns whether the given integer includes the <code>default</code> modifier.
- * That usually means that the element has no 'public', 'protected' or 'private'
- * modifiers at all.
+ * Returns whether the given integer includes the <code>default</code>
+ * modifier. That usually means that the element has no 'public',
+ * 'protected' or 'private' modifiers at all.
*
* @param flags
* the flags
- * @return <code>true</code> if the <code>default</code> modifier is included
+ * @return <code>true</code> if the <code>default</code> modifier is
+ * included
*/
public static boolean isDefault(int flags) {
return !isPrivate(flags) && !isProtected(flags) && !isPublic(flags);
@@ -36,7 +37,8 @@ public class PHPFlags extends Flags implements IPHPModifiers {
*
* @param flags
* the flags
- * @return <code>true</code> if the <code>namespace</code> modifier is included
+ * @return <code>true</code> if the <code>namespace</code> modifier is
+ * included
*/
public static boolean isNamespace(int flags) {
return (flags & AccNameSpace) != 0;
@@ -48,7 +50,8 @@ public class PHPFlags extends Flags implements IPHPModifiers {
*
* @param flags
* the flags
- * @return <code>true</code> if the <code>namespace</code> modifier is included
+ * @return <code>true</code> if the <code>namespace</code> modifier is
+ * included
*/
public static boolean isTrait(int flags) {
return (flags & AccTrait) != 0;
@@ -64,14 +67,16 @@ public class PHPFlags extends Flags implements IPHPModifiers {
*
* @param flags
* the flags
- * @return <code>true</code> if the <code>constant</code> modifier is included
+ * @return <code>true</code> if the <code>constant</code> modifier is
+ * included
*/
public static boolean isConstant(int flags) {
return (flags & AccConstant) != 0;
}
/**
- * Returns whether the given integer includes the <code>class</code> modifier.
+ * Returns whether the given integer includes the <code>class</code>
+ * modifier.
*
* @param flags
* the flags
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionDeclarationContext.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionDeclarationContext.java
index 7b8c4e500..f7909b5f5 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionDeclarationContext.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionDeclarationContext.java
@@ -16,7 +16,9 @@ package org.eclipse.php.internal.core.codeassist.contexts;
import org.eclipse.dltk.annotations.NonNull;
import org.eclipse.dltk.core.CompletionRequestor;
import org.eclipse.dltk.core.ISourceModule;
-import org.eclipse.php.internal.core.util.text.PHPTextSequenceUtilities;
+import org.eclipse.php.core.PHPVersion;
+import org.eclipse.php.core.codeassist.ICompletionScope;
+import org.eclipse.php.core.codeassist.ICompletionScope.Type;
import org.eclipse.php.internal.core.util.text.TextSequence;
/**
@@ -34,7 +36,7 @@ import org.eclipse.php.internal.core.util.text.TextSequence;
*/
public abstract class FunctionDeclarationContext extends DeclarationContext {
- private int functionEnd;
+ private int functionEnd = -1;
@Override
public boolean isValid(@NonNull ISourceModule sourceModule, int offset, CompletionRequestor requestor) {
@@ -42,17 +44,43 @@ public abstract class FunctionDeclarationContext extends DeclarationContext {
return false;
}
- TextSequence statementText = getStatementText();
- functionEnd = PHPTextSequenceUtilities.isInFunctionDeclaration(statementText);
- if (functionEnd == -1) {
+ ICompletionScope currentScope = getCompanion().getScope();
+ if (currentScope.getType() != Type.FUNCTION
+ && !(currentScope.getType() == Type.HEAD && currentScope.getParent().getType() == Type.FUNCTION)) {
return false;
}
- return true;
+
+ TextSequence statementText = this.getStatementText();
+ StringBuilder sb = new StringBuilder();
+ int pos = 0;
+ while (statementText.length() > pos) {
+ char ch = statementText.charAt(pos);
+ if (Character.isJavaIdentifierPart(ch)) {
+ sb.append(ch);
+ } else if (ch == '(' || Character.isWhitespace(ch)) {
+ if (sb.toString().equalsIgnoreCase("function")) { //$NON-NLS-1$
+ functionEnd = pos;
+ return true;
+ } else if (PHPVersion.PHP7_3.isLessThan(getCompanion().getPHPVersion()) && sb.toString().equals("fn")) { //$NON-NLS-1$
+ functionEnd = pos;
+ return true;
+ }
+ if (ch == '(') {
+ return false;
+ }
+ sb.setLength(0);
+ }
+
+ pos++;
+ }
+
+ return false;
+
}
/**
- * Returns the end offset of word 'function' in function declaration relative to
- * the statement text.
+ * Returns the end offset of word 'function' in function declaration
+ * relative to the statement text.
*
* @see #getStatementText()
* @return
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterContext.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterContext.java
index 2823e0b55..efe46dfdd 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterContext.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterContext.java
@@ -13,6 +13,10 @@
*******************************************************************************/
package org.eclipse.php.internal.core.codeassist.contexts;
+import org.eclipse.dltk.annotations.NonNull;
+import org.eclipse.dltk.core.CompletionRequestor;
+import org.eclipse.dltk.core.ISourceModule;
+import org.eclipse.php.core.codeassist.ICompletionScope.Type;
import org.eclipse.php.internal.core.util.text.TextSequence;
/**
@@ -30,10 +34,22 @@ import org.eclipse.php.internal.core.util.text.TextSequence;
*/
public abstract class FunctionParameterContext extends FunctionDeclarationContext {
+ @Override
+ public boolean isValid(@NonNull ISourceModule sourceModule, int offset, CompletionRequestor requestor) {
+ if (!super.isValid(sourceModule, offset, requestor)) {
+ return false;
+ }
+ if (this.getCompanion().getScope().getType() == Type.HEAD) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
- * Scans the function parameters from the end to the beginning, and looks for
- * the special character that determines what kind of code assist should we
- * invoke:
+ * Scans the function parameters from the end to the beginning, and looks
+ * for the special character that determines what kind of code assist should
+ * we invoke:
* <ul>
* <li>'$' means: variable code assist</li>
* <li>'=' means: variable initializer code assist</li>
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterTypeContext.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterTypeContext.java
index d816a1c1c..1cb0c5743 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterTypeContext.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionParameterTypeContext.java
@@ -15,6 +15,7 @@ package org.eclipse.php.internal.core.codeassist.contexts;
import org.eclipse.dltk.annotations.NonNull;
import org.eclipse.dltk.core.*;
+import org.eclipse.wst.sse.core.internal.Logger;
/**
* This context represents the state when staying in a function parameter
@@ -41,22 +42,28 @@ public class FunctionParameterTypeContext extends FunctionParameterContext {
char triggerChar = getTriggerChar();
if (triggerChar == '(' || triggerChar == ',') {
// check whether enclosing element is a method
- IModelElement enclosingElement = getEnclosingElement();
- while (enclosingElement instanceof IField) {
- enclosingElement = enclosingElement.getParent();
- }
- if (!(enclosingElement instanceof IMethod)) {
- return false;
- }
- enclosingElement = enclosingMethod = (IMethod) enclosingElement;
+ try {
+ IModelElement enclosingElement = sourceModule.getElementAt(offset);
+ while (enclosingElement instanceof IField) {
+ enclosingElement = enclosingElement.getParent();
+ }
+ if (!(enclosingElement instanceof IMethod)) {
+ return false;
+ }
+ enclosingElement = enclosingMethod = (IMethod) enclosingElement;
- // find the most outer enclosing type if exists
- while (enclosingElement != null && !(enclosingElement instanceof IType)) {
- enclosingElement = enclosingElement.getParent();
+ // find the most outer enclosing type if exists
+ while (enclosingElement != null && !(enclosingElement instanceof IType)) {
+ enclosingElement = enclosingElement.getParent();
+ }
+ enclosingType = (IType) enclosingElement;
+
+ return true;
+ } catch (ModelException e) {
+ Logger.logException(e);
+ return false;
}
- enclosingType = (IType) enclosingElement;
- return true;
} else {
return false;
}
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionReturnTypeContext.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionReturnTypeContext.java
index d23607b82..dc03a6b7d 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionReturnTypeContext.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/FunctionReturnTypeContext.java
@@ -16,6 +16,7 @@ import org.eclipse.dltk.annotations.NonNull;
import org.eclipse.dltk.core.CompletionRequestor;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.php.core.PHPVersion;
+import org.eclipse.php.core.codeassist.ICompletionScope.Type;
import org.eclipse.php.internal.core.util.text.PHPTextSequenceUtilities;
import org.eclipse.php.internal.core.util.text.TextSequence;
@@ -39,6 +40,10 @@ public class FunctionReturnTypeContext extends FunctionDeclarationContext {
if (getCompanion().getPHPVersion().isLessThan(PHPVersion.PHP7_0)) {
return false;
}
+ if (this.getCompanion().getScope().getType() != Type.HEAD
+ && this.getCompanion().getScope().getParent().getType() != Type.FUNCTION) {
+ return false;
+ }
TextSequence statementText = getStatementText();
int curr = statementText.length();
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/MethodNameContext.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/MethodNameContext.java
index bfa377b6a..7df51b14b 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/MethodNameContext.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/contexts/MethodNameContext.java
@@ -17,6 +17,7 @@ import org.eclipse.dltk.annotations.NonNull;
import org.eclipse.dltk.core.CompletionRequestor;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.IType;
+import org.eclipse.php.core.codeassist.ICompletionScope.Type;
import org.eclipse.php.internal.core.typeinference.PHPModelUtils;
import org.eclipse.php.internal.core.util.text.TextSequence;
@@ -43,6 +44,10 @@ public class MethodNameContext extends FunctionDeclarationContext {
return false;
}
+ if (this.getCompanion().getScope().getType() != Type.FUNCTION) {
+ return false;
+ }
+
TextSequence statementText = getStatementText();
int functionEnd = getFunctionEnd();
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/scope/ScopeParser.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/scope/ScopeParser.java
index f51d26883..5683a3fb6 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/scope/ScopeParser.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/codeassist/scope/ScopeParser.java
@@ -50,13 +50,23 @@ public class ScopeParser {
this.start = start;
this.length = length;
}
+
+ public String toString() {
+ return type.name() + "(" + start + ", " + (start + length - 1) + "," + length + ")";
+ }
+
}
private class SimpleBlockState extends State {
public SimpleBlockState(Type type, int start) {
super(type, start);
}
+ }
+ private class OneLinerState extends State {
+ public OneLinerState(Type type, int start) {
+ super(type, start);
+ }
}
private class ControlState extends State {
@@ -76,6 +86,7 @@ public class ScopeParser {
}
private Deque<State> states;
+ private State previous = null;
private Deque<State> memory;
private LinkedList<ITextRegion> buffer;
private boolean collectName = false;
@@ -90,6 +101,7 @@ public class ScopeParser {
}
public ICompletionScope parse(int offset) {
+ previous = null;
states = new LinkedList<>();
states.push(new State(Type.FILE, parentScope.getOffset(), parentScope.getLength()));
memory = new LinkedList<>();
@@ -105,6 +117,7 @@ public class ScopeParser {
ICompletionScope scope = mainScope;
internalOffset = offset;
ITextRegion previousRegion = null;
+ int lastNonWhiteSpaceOffset = 0;
for (IStructuredDocumentRegion region : document.getStructuredDocumentRegions()) {
if (region.getStartOffset() > offset) {
break;
@@ -173,6 +186,24 @@ public class ScopeParser {
case PHPRegionTypes.PHP_NAMESPACE:
pushState(new NamedState(Type.NAMESPACE, tokenStart));
break;
+ case PHPRegionTypes.PHP_FN:
+ pushState(new NamedState(Type.FUNCTION, tokenStart));
+ break;
+ case PHPRegionTypes.PHP_OPERATOR:
+ if (phpToken.getLength() > 1 && document.getChar(tokenStart) == '='
+ && document.getChar(tokenStart + 1) == '>') {
+ if (current.type == Type.HEAD && current.nest == -1) {
+ popState(lastNonWhiteSpaceOffset);
+ current = states.peek();
+ if (current.type != Type.FUNCTION) {
+ pushState(previous);
+ }
+ }
+ if (current.type == Type.FUNCTION) {
+ pushState(new OneLinerState(Type.BLOCK, tokenEnd + 1));
+ }
+ }
+ break;
case PHPRegionTypes.PHP_FUNCTION:
if (current.type == Type.USE) {
states.pop();
@@ -187,6 +218,10 @@ public class ScopeParser {
}
break;
case PHPRegionTypes.PHP_CURLY_OPEN:
+ if (current.type == Type.HEAD && current.nest == -1) {
+ popState(tokenStart);
+ previous = null;
+ }
if (current.type == Type.USE || current.type == Type.USE_CONST
|| current.type == Type.USE_FUNCTION) {
pushState(new State(Type.USE_GROUP, tokenStart));
@@ -278,6 +313,17 @@ public class ScopeParser {
break;
case PHPRegionTypes.PHP_SEMICOLON:
collectName = false;
+ if (current.type == Type.HEAD && current.nest == -1) {
+ popState(tokenEnd);
+ } else if (current instanceof OneLinerState && current.nest == 0) {
+ while (current instanceof OneLinerState && current.nest == 0) {
+ popState(tokenEnd);
+ popState(tokenEnd);
+ current = states.peek();
+ }
+ break;
+
+ }
switch (current.type) {
case NAMESPACE:
current.length = parentScope.getLength() - current.start;
@@ -311,6 +357,7 @@ public class ScopeParser {
popState(tokenEnd);
break;
+
default:
}
break;
@@ -326,10 +373,22 @@ public class ScopeParser {
pushState(new State(Type.BLOCK, tokenStart));
collectName = false;
break;
+ case FUNCTION:
+ if (previous != null && previous.type == Type.HEAD) {
+ pushState(previous);
+ previous.nest = -1;
+ previous = null;
+
+ break;
+ }
default:
}
break;
case '(':
+ if (current instanceof OneLinerState) {
+ current.nest++;
+ break;
+ }
switch (current.type) {
case SWITCH:
case WHILE:
@@ -342,6 +401,7 @@ public class ScopeParser {
pushState(new State(Type.HEAD, tokenStart));
collectName = false;
break;
+
case HEAD:
current.nest++;
break;
@@ -351,10 +411,22 @@ public class ScopeParser {
if (current.type == Type.HEAD) {
if (current.nest > 0) {
current.nest--;
- } else {
+ } else if (current.nest == 0) {
popState(tokenStart + 1); // ignore
// whitespace
}
+ } else if (current instanceof OneLinerState) {
+ if (current.nest == 0) {
+ while (current instanceof OneLinerState && current.nest == 0) {
+ popState(tokenStart - 1);
+ popState(tokenStart - 1);
+ current = states.peek();
+ }
+ break;
+
+ } else if (current.nest > 0) {
+ current.nest--;
+ }
}
}
break;
@@ -443,10 +515,14 @@ public class ScopeParser {
}
// System.out.println(phpToken);
- // System.out.println("'" + document.get(tokenStart,
- // phpToken.getLength()) +
- // "'");
+ // System.out.println(tokenStart + ":" + tokenEnd +
+ // ":" + "'"
+ // + document.get(tokenStart, phpToken.getLength())
+ // + "'");
previousRegion = phpToken;
+ if (phpToken.getType() != PHPRegionTypes.WHITESPACE) {
+ lastNonWhiteSpaceOffset = tokenEnd;
+ }
}
} catch (BadLocationException e) {
Logger.logException(e);
@@ -477,8 +553,11 @@ public class ScopeParser {
private void popState(int end) {
State state = states.pop();
state.length = end - state.start;
- if (state.start <= internalOffset && end >= internalOffset) {
- memory.addFirst(state);
+ previous = state;
+ if (state.start < internalOffset && end >= internalOffset) {
+ if (memory.size() == 0 || memory.getFirst() != state) {
+ memory.addFirst(state);
+ }
}
if (states.peek() instanceof SimpleBlockState) {
popState(end);
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/compiler/PHPSourceElementRequestor.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/compiler/PHPSourceElementRequestor.java
index 8a3bceae5..16195bfb6 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/compiler/PHPSourceElementRequestor.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/compiler/PHPSourceElementRequestor.java
@@ -65,21 +65,21 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
private static final String GLOBAL_NAMESPACE_CONTAINER_NAME = "global namespace"; //$NON-NLS-1$
private static final String ANONYMOUS_CLASS_TEMPLATE = "new %s() {...}"; //$NON-NLS-1$
/**
- * This should replace the need for fInClass, fInMethod and fCurrentMethod since
- * in php the type declarations can be nested.
+ * This should replace the need for fInClass, fInMethod and fCurrentMethod
+ * since in php the type declarations can be nested.
*/
protected Stack<ASTNode> declarations = new Stack<>();
private PHPSourceElementRequestorExtension[] extensions;
/**
- * Deferred elements that where declared in method/function but should belong to
- * the global scope.
+ * Deferred elements that where declared in method/function but should
+ * belong to the global scope.
*/
protected List<ASTNode> deferredDeclarations = new LinkedList<>();
/**
- * Deferred elements that where declared in method/function but should belong to
- * current namespace scope
+ * Deferred elements that where declared in method/function but should
+ * belong to current namespace scope
*/
protected List<ASTNode> deferredNamespacedDeclarations = new LinkedList<>();
@@ -141,9 +141,6 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
fInfoStack.pop();
fNodes.pop();
}
- for (PHPSourceElementRequestorExtension visitor : extensions) {
- visitor.endvisit(lambdaMethod);
- }
return true;
}
@@ -164,6 +161,18 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
return true;
}
+ public boolean endvisit(ArrowFunctionDeclaration arrowFunction) throws Exception {
+ methodGlobalVars.pop();
+ this.fInMethod = false;
+
+ if (!fNodes.isEmpty() && fNodes.peek() == arrowFunction) {
+ fRequestor.exitMethod(arrowFunction.sourceEnd() - 1);
+ fInfoStack.pop();
+ fNodes.pop();
+ }
+ return true;
+ }
+
public boolean endvisit(ClassInstanceCreation cic) throws Exception {
this.fLastInstanceCreation = null;
return true;
@@ -211,6 +220,65 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
return super.endvisit(type);
}
+ public boolean visit(ArrowFunctionDeclaration arrowMethod) throws Exception {
+ fNodes.push(arrowMethod);
+ methodGlobalVars.add(new HashSet<String>());
+
+ Collection<FormalParameter> arguments = arrowMethod.getArguments();
+ StringBuilder metadata = new StringBuilder();
+ String[] parameters;
+ ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo();
+ mi.modifiers = Modifiers.AccPublic;
+ if (arguments != null) {
+ parameters = new String[arguments.size()];
+ Iterator<FormalParameter> i = arguments.iterator();
+ int indx = 0;
+ while (i.hasNext()) {
+ FormalParameter arg = i.next();
+ metadata.append(arg.getName());
+ parameters[indx] = arg.getName();
+ if (arg.isVariadic()) {
+ mi.modifiers |= IPHPModifiers.AccVariadic;
+ }
+ indx++;
+ if (i.hasNext()) {
+ metadata.append(","); //$NON-NLS-1$
+ }
+ }
+ } else {
+ parameters = new String[0];
+ }
+
+ mi.parameterNames = parameters;
+ mi.name = PHPCoreConstants.ANONYMOUS;
+
+ if (arrowMethod.isStatic()) {
+ mi.modifiers |= Modifiers.AccStatic;
+ }
+ mi.nameSourceStart = arrowMethod.sourceStart();
+ mi.nameSourceEnd = arrowMethod.sourceEnd();
+ mi.declarationStart = mi.nameSourceStart;
+ mi.isConstructor = false;
+
+ fInfoStack.push(mi);
+ this.fRequestor.enterMethod(mi);
+ this.fInMethod = true;
+
+ if (arguments != null) {
+ for (Argument arg : arguments) {
+ ISourceElementRequestor.FieldInfo info = new ISourceElementRequestor.FieldInfo();
+ info.name = arg.getName();
+ info.modifiers = Modifiers.AccPublic;
+ info.nameSourceStart = arg.getNameStart();
+ info.nameSourceEnd = arg.getNameEnd() - 1;
+ info.declarationStart = arg.sourceStart();
+ fRequestor.enterField(info);
+ fRequestor.exitField(arg.sourceEnd() - 1);
+ }
+ }
+ return true;
+ }
+
public boolean visit(LambdaFunctionDeclaration lambdaMethod) throws Exception {
fNodes.push(lambdaMethod);
@@ -230,7 +298,7 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
StringBuilder metadata = new StringBuilder();
String[] parameters;
ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo();
- mi.modifiers = Modifiers.AccPublic;
+ mi.modifiers = Modifiers.AccPublic | IPHPModifiers.AccArrow;
if (arguments != null) {
parameters = new String[arguments.size()];
Iterator<FormalParameter> i = arguments.iterator();
@@ -251,11 +319,6 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
parameters = new String[0];
}
- // Add method declaration:
- for (PHPSourceElementRequestorExtension visitor : extensions) {
- visitor.visit(lambdaMethod);
- }
-
mi.parameterNames = parameters;
mi.name = PHPCoreConstants.ANONYMOUS;
@@ -300,10 +363,6 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
fNodes.push(anonymousClassDeclaration);
declarations.push(anonymousClassDeclaration);
- for (PHPSourceElementRequestorExtension visitor : extensions) {
- visitor.visit(anonymousClassDeclaration);
- }
-
List<String> superClasses = new ArrayList<>();
String name = null;
if (anonymousClassDeclaration.getSuperClass() != null) {
@@ -1200,6 +1259,9 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
if (node instanceof LambdaFunctionDeclaration) {
return visit((LambdaFunctionDeclaration) node);
}
+ if (node instanceof ArrowFunctionDeclaration) {
+ return visit((ArrowFunctionDeclaration) node);
+ }
if (node instanceof AnonymousClassDeclaration) {
return visit((AnonymousClassDeclaration) node);
}
@@ -1223,6 +1285,9 @@ public class PHPSourceElementRequestor extends SourceElementRequestVisitor {
if (node instanceof LambdaFunctionDeclaration) {
return endvisit((LambdaFunctionDeclaration) node);
}
+ if (node instanceof ArrowFunctionDeclaration) {
+ return endvisit((ArrowFunctionDeclaration) node);
+ }
if (node instanceof AnonymousClassDeclaration) {
return endvisit((AnonymousClassDeclaration) node);
}
diff --git a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/util/text/PHPTextSequenceUtilities.java b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/util/text/PHPTextSequenceUtilities.java
index d9ce6b2e2..f2d3bbcb1 100644
--- a/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/util/text/PHPTextSequenceUtilities.java
+++ b/plugins/org.eclipse.php.core/src/org/eclipse/php/internal/core/util/text/PHPTextSequenceUtilities.java
@@ -69,16 +69,16 @@ public class PHPTextSequenceUtilities {
/**
* This function returns statement text depending on the current offset. It
- * searches backwards (starting from offset - 1) until it finds delimiter ';',
- * '{' or '}'.
+ * searches backwards (starting from offset - 1) until it finds delimiter
+ * ';', '{' or '}'.
*
* @param offset
* The absolute offset in the document
* @param sdRegion
* Structured document region of the offset
* @param removeComments
- * Flag determining whether to remove comments in the resulted text
- * sequence
+ * Flag determining whether to remove comments in the resulted
+ * text sequence
*
* @return text sequence of the statement, cannot be null
*/
@@ -89,31 +89,32 @@ public class PHPTextSequenceUtilities {
/**
* This function returns statement text depending on the current offset. It
- * searches backwards (starting from offset - 1) until it finds delimiter ';',
- * '{' or '}'.
+ * searches backwards (starting from offset - 1) until it finds delimiter
+ * ';', '{' or '}'.
*
* @param offset
* The absolute offset in the document
* @param sdRegion
* Structured document region of the offset
* @param removeComments
- * Flag determining whether to remove comments in the resulted text
- * sequence
+ * Flag determining whether to remove comments in the resulted
+ * text sequence
* @param ignoreDelimiters
* Delimiter types that will be ignored while searching backwards
* (ignoreDelimiters can be null). Supported delimiter types are
- * PHPRegionTypes.PHP_CURLY_OPEN, PHPRegionTypes.PHP_CURLY_CLOSE and
- * PHPRegionTypes.PHP_SEMICOLON
+ * PHPRegionTypes.PHP_CURLY_OPEN, PHPRegionTypes.PHP_CURLY_CLOSE
+ * and PHPRegionTypes.PHP_SEMICOLON
* @param limit
- * Controls how many times a delimiter (from ignoreDelimiters) should
- * be ignored. 0 or less means no limit.
+ * Controls how many times a delimiter (from ignoreDelimiters)
+ * should be ignored. 0 or less means no limit.
* @param foundDelimiter
- * If foundDelimiter is not null and foundDelimiter length is greater
- * than 0 then foundDelimiter[0] will contain the delimiter region
- * found while searching backwards (or null if no delimiter was
- * found, typically when the backward search reached the beginning of
- * sdRegion). Note that the foundDelimiter[0] offset will be
- * <b>relative to the beginning of the document</b>.
+ * If foundDelimiter is not null and foundDelimiter length is
+ * greater than 0 then foundDelimiter[0] will contain the
+ * delimiter region found while searching backwards (or null if
+ * no delimiter was found, typically when the backward search
+ * reached the beginning of sdRegion). Note that the
+ * foundDelimiter[0] offset will be <b>relative to the beginning
+ * of the document</b>.
*
* @return text sequence of the statement, cannot be null
*/
@@ -207,13 +208,14 @@ public class PHPTextSequenceUtilities {
/**
* <p>
- * This function returns statement region depending on the current offset. It
- * searches backwards (starting from offset - 1) until it finds ';', '{' or '}'.
+ * This function returns statement region depending on the current offset.
+ * It searches backwards (starting from offset - 1) until it finds ';', '{'
+ * or '}'.
* </p>
* <p>
- * <b> Be careful, empty region can be returned (i.e. region's length is 0) when
- * no statement was found. In this case, the offset from the returned region has
- * no special meaning.
+ * <b> Be careful, empty region can be returned (i.e. region's length is 0)
+ * when no statement was found. In this case, the offset from the returned
+ * region has no special meaning.
* </p>
* </b>
*
@@ -287,51 +289,6 @@ public class PHPTextSequenceUtilities {
return -1;
}
- /**
- * Checks if we are inside function declaration statement. If yes the start
- * offset of the function, otherwise returns -1.
- */
- public static int isInFunctionDeclaration(@NonNull TextSequence textSequence) {
- Matcher matcher = FUNCTION_PATTERN.matcher(textSequence);
- // search for the 'function' word.
- while (matcher.find()) {
- // verify char before 'function' word.
- int functionStart = matcher.start();
- if (functionStart != 0 && Character.isJavaIdentifierStart(textSequence.charAt(functionStart - 1))) {
- continue;
- }
-
- // verfy state
- String type = TextSequenceUtilities.getType(textSequence, functionStart + 1);
- if (PHPPartitionTypes.isPHPRegularState(type)) {
- // verify the function is not closed.
- int offset;
- boolean possibleReturnType = false;
- boolean returnType = false;
- for (offset = matcher.end(); offset < textSequence.length(); offset++) {
- if (textSequence.charAt(offset) == ')') {
- // verify state
- type = TextSequenceUtilities.getType(textSequence, offset);
- if (PHPPartitionTypes.isPHPRegularState(type)) {
- possibleReturnType = true;
- }
- } else if ((possibleReturnType || returnType) && textSequence.charAt(offset) == '{') {
- break;
- } else if (possibleReturnType && textSequence.charAt(offset) == ':') {
- possibleReturnType = false;
- returnType = true;
- } else if (possibleReturnType && !Character.isWhitespace(textSequence.charAt(offset))) {
- break;
- }
- }
- if (offset == textSequence.length()) {
- return functionStart;
- }
- }
- }
- return -1;
- }
-
private static boolean isClassOrInterfaceKeyword(@NonNull TextSequence textSequence, int classStartOffset) {
if (classStartOffset == 0) {
return true;
@@ -565,7 +522,8 @@ public class PHPTextSequenceUtilities {
}
/**
- * Returns the next position on the text where one the given delimiters start
+ * Returns the next position on the text where one the given delimiters
+ * start
*
* @param textSequence
* - The input text sequence
@@ -738,7 +696,8 @@ public class PHPTextSequenceUtilities {
textSequence = textSequence.subSequence(1, textSequence.length());
}
if (textSequence == null) {
- // should never happen (but makes @Nullable control for parameter textSequence
+ // should never happen (but makes @Nullable control for
+ // parameter textSequence
// happy)
return args.toArray(new String[args.size()]);
}
diff --git a/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/CodeAssistTests.java b/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/CodeAssistTests.java
index 39521770a..95948b6ec 100644
--- a/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/CodeAssistTests.java
+++ b/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/CodeAssistTests.java
@@ -143,7 +143,8 @@ public class CodeAssistTests {
TESTS.put(PHPVersion.PHP7_4,
new String[] { "/workspace/codeassist/php5", "/workspace/codeassist/php53",
"/workspace/codeassist/php54", "/workspace/codeassist/php55", "/workspace/codeassist/php56",
- "/workspace/codeassist/php7", "/workspace/codeassist/php71", "/workspace/codeassist/php72" });
+ "/workspace/codeassist/php7", "/workspace/codeassist/php71", "/workspace/codeassist/php72",
+ "/workspace/codeassist/php74" });
};
private IProject project;
diff --git a/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/scope/CodeAssistScopeTests.java b/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/scope/CodeAssistScopeTests.java
index 2e7e3ddd9..a73f1ad6d 100644
--- a/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/scope/CodeAssistScopeTests.java
+++ b/tests/org.eclipse.php.core.tests/src/org/eclipse/php/core/tests/codeassist/scope/CodeAssistScopeTests.java
@@ -136,6 +136,17 @@ public class CodeAssistScopeTests {
"/workspace/codeassist_scope/php54", "/workspace/codeassist_scope/php55",
"/workspace/codeassist_scope/php56", "/workspace/codeassist_scope/php7",
"/workspace/codeassist_scope/php71", "/workspace/codeassist_scope/php72" });
+ TESTS.put(PHPVersion.PHP7_3,
+ new String[] { "/workspace/codeassist_scope/php5", "/workspace/codeassist_scope/php53",
+ "/workspace/codeassist_scope/php54", "/workspace/codeassist_scope/php55",
+ "/workspace/codeassist_scope/php56", "/workspace/codeassist_scope/php7",
+ "/workspace/codeassist_scope/php71", "/workspace/codeassist_scope/php72" });
+ TESTS.put(PHPVersion.PHP7_4,
+ new String[] { "/workspace/codeassist_scope/php5", "/workspace/codeassist_scope/php53",
+ "/workspace/codeassist_scope/php54", "/workspace/codeassist_scope/php55",
+ "/workspace/codeassist_scope/php56", "/workspace/codeassist_scope/php7",
+ "/workspace/codeassist_scope/php71", "/workspace/codeassist_scope/php72",
+ "/workspace/codeassist_scope/php74" });
};
private IProject project;
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow01.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow01.pdtt
new file mode 100644
index 000000000..3236012c1
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow01.pdtt
@@ -0,0 +1,11 @@
+--TEST--
+
+--FILE--
+<?php
+fn(arr| $cl) => $cl;
+
+--EXPECT--
+type(ArrayAccess)
+type(ArrayObject)
+type(ArrayIterator)
+keyword(array)
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow02.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow02.pdtt
new file mode 100644
index 000000000..9c0f3a8fa
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow02.pdtt
@@ -0,0 +1,11 @@
+--TEST--
+
+--FILE--
+<?php
+class Foo {
+ public function bar();
+}
+fn(Foo $cl) => $cl->|;
+
+--EXPECT--
+method(bar) \ No newline at end of file
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow03.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow03.pdtt
new file mode 100644
index 000000000..5b04a6458
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow03.pdtt
@@ -0,0 +1,8 @@
+--TEST--
+
+--FILE--
+<?php
+fn(): stri| => $cl;
+
+--EXPECT--
+keyword(string)
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow04.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow04.pdtt
new file mode 100644
index 000000000..5bf7f5df9
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist/php74/arrow04.pdtt
@@ -0,0 +1,9 @@
+--TEST--
+
+--FILE--
+<?php
+class Foo {}
+fn() => new Fo|;
+
+--EXPECT--
+method(Foo)
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php7/returnType01.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php7/returnType01.pdtt
new file mode 100644
index 000000000..619ebf12b
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php7/returnType01.pdtt
@@ -0,0 +1,14 @@
+--TEST--
+Test function return type completion
+--PREFERENCES--
+org.eclipse.php.core/contentAssistShowStrictOptions=false
+--FILE--
+<?php
+class FooBar {}
+function test(): Foo| {
+}
+
+--EXPECT--
+HEAD(35,8)
+FUNCTION(22,24,test)
+FILE(0,48)
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php71/returnType01.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php71/returnType01.pdtt
new file mode 100644
index 000000000..a0897fa80
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php71/returnType01.pdtt
@@ -0,0 +1,17 @@
+--TEST--
+Test function return type completion
+--PREFERENCES--
+org.eclipse.php.core/contentAssistShowStrictOptions=false
+--FILE--
+<?php
+class FooBar {
+function test() : voi|{
+}
+}
+
+--EXPECT--
+HEAD(34,8)
+FUNCTION(21,24,test)
+BLOCK(19,28)
+CLASS(6,41,FooBar)
+FILE(0,49) \ No newline at end of file
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow01.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow01.pdtt
new file mode 100644
index 000000000..26b28624c
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow01.pdtt
@@ -0,0 +1,10 @@
+--TEST--
+
+--FILE--
+<?php
+fn(My| $cl) => $cl;
+
+--EXPECT--
+HEAD(9,8)
+FUNCTION(7,18)
+FILE(0,27)
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow02.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow02.pdtt
new file mode 100644
index 000000000..29c0e5c85
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow02.pdtt
@@ -0,0 +1,10 @@
+--TEST--
+
+--FILE--
+<?php
+fn(My $cl) => $|cl;
+
+--EXPECT--
+BLOCK(21,4)
+FUNCTION(7,18)
+FILE(0,27)
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow03.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow03.pdtt
new file mode 100644
index 000000000..6d60609e6
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow03.pdtt
@@ -0,0 +1,10 @@
+--TEST--
+
+--FILE--
+<?php
+fn(My $cl): \Date|Time => $cl;
+
+--EXPECT--
+HEAD(9,19)
+FUNCTION(7,29)
+FILE(0,38)
diff --git a/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow04.pdtt b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow04.pdtt
new file mode 100644
index 000000000..550b1c73f
--- /dev/null
+++ b/tests/org.eclipse.php.core.tests/workspace/codeassist_scope/php74/arrow04.pdtt
@@ -0,0 +1,10 @@
+--TEST--
+
+--FILE--
+<?php
+fn(My $cl): \DateTime => $c|l;
+
+--EXPECT--
+BLOCK(32,4)
+FUNCTION(7,29)
+FILE(0,38)

Back to the top