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

PHP 8.0 Support: Attribute Syntax (Part 4) #6929

Merged
merged 1 commit into from
Jan 9, 2024
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
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.35.0
spec.version.base=2.36.0
release.external/predefined_vars-1.0.zip=docs/predefined_vars.zip
sigtest.gen.fail.on.error=false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ 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 ATTRIBUTE_ATTRIBUTE_NAME = "Attribute"; // NOI18N
public static final String ATTRIBUTE_ATTRIBUTE_FQ_NAME = "\\" + ATTRIBUTE_ATTRIBUTE_NAME; // 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,17 @@ public interface Index extends ElementQuery {

Set<ClassElement> getClasses(NameKind query, Set<AliasedName> aliases, AliasedElement.Trait trait);

/**
* Get classes marked as Attribute(#[\Attribute]).
*
* @param query the query
* @param aliases aliased names
* @param trait the trait
* @return classes marked as #[\Attribute]
* @since 2.36.0
*/
Set<ClassElement> getAttributeClasses(NameKind query, Set<AliasedName> aliases, AliasedElement.Trait trait);

Set<InterfaceElement> getInterfaces(NameKind query, Set<AliasedName> aliases, AliasedElement.Trait trait);

Set<EnumElement> getEnums(NameKind query, Set<AliasedName> aliases, AliasedElement.Trait trait);
Expand All @@ -153,6 +164,23 @@ public interface Index extends ElementQuery {

Set<MethodElement> getConstructors(NameKind typeQuery, Set<AliasedName> aliases, AliasedElement.Trait trait);

/**
* Get constructors of attribute classes.
* <pre>
* #[\Attribute]
* class MyAttibute {
* public function __construct(int $int, string $string) {}
* }
* </pre>
*
* @param typeQuery the query
* @param aliases aliased names
* @param trait the trait
* @return constructors of attribute classes
* @since 2.36.0
*/
Set<MethodElement> getAttributeClassConstructors(NameKind typeQuery, Set<AliasedName> aliases, AliasedElement.Trait trait);

Set<NamespaceElement> getNamespaces(NameKind query, Set<AliasedName> aliasedNames, AliasedElement.Trait trait);

Set<PhpElement> getTopLevelElements(NameKind query, Set<AliasedName> aliases, AliasedElement.Trait trait);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,9 @@ public Collection<QualifiedName> getFQMixinClassNames() {
public Collection<QualifiedName> getUsedTraits() {
return getClassElement().getUsedTraits();
}

@Override
public boolean isAttribute() {
return getClassElement().isAttribute();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,12 @@ public interface ClassElement extends TraitedElement {
boolean isAbstract();
boolean isReadonly();
boolean isAnonymous();
/**
* Check whether a class is marked with #[\Attribute].
*
* @return {@code true} if a class is marked with #[\Attribute],
* {@code false} otherwise
* @since 2.36.0
*/
boolean isAttribute();
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ public static enum CompletionContext {
CLASS_CONTEXT_KEYWORDS, SERVER_ENTRY_CONSTANTS, NONE, NEW_CLASS, GLOBAL, NAMESPACE_KEYWORD,
GROUP_USE_KEYWORD, GROUP_USE_CONST_KEYWORD, GROUP_USE_FUNCTION_KEYWORD,
USE_KEYWORD, USE_CONST_KEYWORD, USE_FUNCTION_KEYWORD, DEFAULT_PARAMETER_VALUE, OPEN_TAG, THROW, THROW_NEW, CATCH, CLASS_MEMBER_IN_STRING,
INTERFACE_CONTEXT_KEYWORDS, USE_TRAITS
INTERFACE_CONTEXT_KEYWORDS, USE_TRAITS, ATTRIBUTE, ATTRIBUTE_EXPRESSION
};

static enum KeywordCompletionType {
Expand Down Expand Up @@ -328,6 +328,15 @@ static CompletionContext findCompletionContext(ParserResult info, int caretOffse
return CompletionContext.GROUP_USE_CONST_KEYWORD;
} else if (acceptTokenChains(tokenSequence, GROUP_USE_FUNCTION_KEYWORD_TOKENS, moveNextSucces)) {
return CompletionContext.GROUP_USE_FUNCTION_KEYWORD;
} else if (isInAttribute(caretOffset, tokenSequence, true)) {
if (isInAttribute(caretOffset, tokenSequence, false)) {
return CompletionContext.ATTRIBUTE;
}
CompletionContext namedArgumentsContext = getNamedArgumentsContext(caretOffset, tokenSequence);
if (namedArgumentsContext != null) {
return namedArgumentsContext;
}
return CompletionContext.ATTRIBUTE_EXPRESSION;
} else if (isInsideInterfaceDeclarationBlock(info, caretOffset, tokenSequence)) {
CompletionContext paramContext = getParamaterContext(token, caretOffset, tokenSequence);
if (paramContext != null) {
Expand Down Expand Up @@ -719,7 +728,7 @@ private static boolean consumeConstDeclaredTypes(TokenSequence tokenSequence) {
do {
if (lastTokenId == PHPTokenId.WHITESPACE) {
if (!isTypeSeparator(tokenSequence.token())
|| (isRightBracket(tokenSequence.token()) && !isVerticalBar(lastTokenExceptForWS))) {
|| (isRightParen(tokenSequence.token()) && !isVerticalBar(lastTokenExceptForWS))) {
// check the following case: const string CONST_NAME
// ^
isConstType = false;
Expand Down Expand Up @@ -749,8 +758,8 @@ private static boolean consumeConstDeclaredTypes(TokenSequence tokenSequence) {
private static boolean isTypeSeparator(Token<PHPTokenId> token) {
return isVerticalBar(token)
|| isReference(token)
|| isLeftBracket(token)
|| isRightBracket(token);
|| isLeftParen(token)
|| isRightParen(token);
}

private static boolean consumeMultiCatchExceptions(TokenSequence tokenSequence) {
Expand All @@ -774,7 +783,7 @@ private static boolean consumeMultiCatchExceptions(TokenSequence tokenSequence)
}
}
}
if (isLeftBracket(tokenSequence.token())) {
if (isLeftParen(tokenSequence.token())) {
hasParenOpen = true;
}
if (!tokenSequence.movePrevious()) {
Expand All @@ -784,7 +793,7 @@ private static boolean consumeMultiCatchExceptions(TokenSequence tokenSequence)
break;
}
} while (isVerticalBar(tokenSequence.token())
|| isLeftBracket(tokenSequence.token())
|| isLeftParen(tokenSequence.token())
|| tokenSequence.token().id() == PHPTokenId.WHITESPACE
|| consumeNameSpace(tokenSequence));

Expand Down Expand Up @@ -1118,7 +1127,7 @@ private static CompletionContext getParamaterContext(Token<PHPTokenId> token, in
try {
// e.g. function &my_sort5(&^$data) {, function &my_sort5(^&$data) {
Token<? extends PHPTokenId> previous = LexUtilities.findPrevious(tokenSequence, Arrays.asList(PHPTokenId.WHITESPACE, PHPTokenId.PHP_OPERATOR));
if (isComma(previous) || isLeftBracket(previous)) {
if (isComma(previous) || isLeftParen(previous)) {
int offset = cToken.offset(null) + cToken.text().length();
if (carretOffset >= offset) {
testCompletionSeparator = false;
Expand Down Expand Up @@ -1155,7 +1164,7 @@ private static CompletionContext getParamaterContext(Token<PHPTokenId> token, in
if (carretOffset > offset) {
testCompletionSeparator = false;
}
} else if (isReference(cToken) || isRightBracket(cToken) || isVariable(cToken)) {
} else if (isReference(cToken) || isRightParen(cToken) || isVariable(cToken)) {
int offset = cToken.offset(null) + cToken.text().length();
if (carretOffset >= offset) {
testCompletionSeparator = false;
Expand Down Expand Up @@ -1220,23 +1229,33 @@ private static boolean isVariadic(Token<PHPTokenId> token) {
&& TokenUtilities.textEquals(token.text(), "..."); // NOI18N
}

static boolean isLeftBracket(Token<? extends PHPTokenId> token) {
static boolean isLeftParen(Token<? extends PHPTokenId> token) {
return token.id().equals(PHPTokenId.PHP_TOKEN)
&& TokenUtilities.textEquals(token.text(), "("); // NOI18N
}

private static boolean isRightBracket(Token<PHPTokenId> token) {
private static boolean isRightParen(Token<PHPTokenId> token) {
return token.id().equals(PHPTokenId.PHP_TOKEN)
&& TokenUtilities.textEquals(token.text(), ")"); // NOI18N
}

private static boolean isLeftBracket(Token<? extends PHPTokenId> token) {
return token.id().equals(PHPTokenId.PHP_TOKEN)
&& TokenUtilities.textEquals(token.text(), "["); // NOI18N
}

private static boolean isRightBracket(Token<PHPTokenId> token) {
return token.id().equals(PHPTokenId.PHP_TOKEN)
&& TokenUtilities.textEquals(token.text(), "]"); // NOI18N
}

private static boolean isEqualSign(Token<PHPTokenId> token) {
return token.id().equals(PHPTokenId.PHP_OPERATOR)
&& TokenUtilities.textEquals(token.text(), "="); // NOI18N
}

private static boolean isParamSeparator(Token<PHPTokenId> token) {
return isComma(token) || isLeftBracket(token);
return isComma(token) || isLeftParen(token);
}

private static boolean isReturnTypeSeparator(Token<PHPTokenId> token) {
Expand Down Expand Up @@ -1288,7 +1307,7 @@ private static boolean isIterable(Token<PHPTokenId> token) {

private static boolean isAcceptedPrefix(Token<PHPTokenId> token) {
return isVariable(token) || isReference(token)
|| isRightBracket(token) || isString(token) || isWhiteSpace(token) || isNamespaceSeparator(token)
|| isRightParen(token) || isString(token) || isWhiteSpace(token) || isNamespaceSeparator(token)
|| isType(token);
}

Expand Down Expand Up @@ -1584,7 +1603,7 @@ static Token<? extends PHPTokenId> findFunctionInvocationName(TokenSequence<PHPT
}
}

if (isLeftBracket(previousToken) && ts.movePrevious()) {
if (isLeftParen(previousToken) && ts.movePrevious()) {
// find a label "label("
previousToken = LexUtilities.findPrevious(ts, Arrays.asList(PHPTokenId.WHITESPACE));
if (previousToken == null) {
Expand All @@ -1608,7 +1627,8 @@ private static CompletionContext getNamedArgumentsContext(final int caretOffset,
retval = CompletionContext.CLASS_MEMBER_PARAMETER_NAME;
} else if (acceptTokenChains(ts, STATIC_CLASS_MEMBER_TOKENCHAINS, true)) {
retval = CompletionContext.STATIC_CLASS_MEMBER_PARAMETER_NAME;
} else if (acceptTokenChains(ts, CLASS_NAME_TOKENCHAINS, true)) {
} else if (acceptTokenChains(ts, CLASS_NAME_TOKENCHAINS, true)
|| isInAttribute(caretOffset, ts, true)) {
retval = CompletionContext.CONSTRUCTOR_PARAMETER_NAME;
} else {
retval = CompletionContext.FUNCTION_PARAMETER_NAME;
Expand Down Expand Up @@ -1641,6 +1661,43 @@ private static boolean isInMatchExpression(final int caretOffset, final TokenSeq
return result;
}

static boolean isInAttribute(final int caretOffset, final TokenSequence ts, boolean allowInArgs) {
// e.g. #[MyAttr^ibute] ("^": caret)
boolean result = false;
int bracketBalance = 0;
int parenBalance = 0;
ts.move(caretOffset);
while (ts.movePrevious()) {
if (isLeftBracket(ts.token())) {
bracketBalance--;
} else if (isRightBracket(ts.token())) {
bracketBalance++;
} else if (isLeftParen(ts.token())) {
parenBalance--;
} else if (isRightParen(ts.token())) {
parenBalance++;
}
TokenId tokenId = ts.token().id();
if (tokenId == PHPTokenId.PHP_ATTRIBUTE) {
if (allowInArgs) {
result = bracketBalance == 0;
} else {
result = bracketBalance == 0
&& parenBalance == 0;
}
break;
}
if (tokenId == PHPTokenId.PHP_SEMICOLON
|| isFunctionDeclaration(ts.token())
|| isVisibilityModifier(ts.token())) {
break;
}
}
ts.move(caretOffset);
ts.moveNext();
return result;
}

static CompletionContext getCompletionContextInComment(TokenSequence<PHPTokenId> tokenSeq, final int caretOffset, ParserResult info) {
Token<PHPTokenId> token = tokenSeq.token();
CharSequence text = token.text();
Expand Down
Loading
Loading