Skip to content

Commit

Permalink
Merge pull request #6929 from junichi11/php-gh-4300-attribute-cc
Browse files Browse the repository at this point in the history
PHP 8.0 Support: Attribute Syntax (Part 4)
  • Loading branch information
junichi11 committed Jan 9, 2024
2 parents cac40a3 + 4b4b377 commit bf409a9
Show file tree
Hide file tree
Showing 194 changed files with 3,814 additions and 241 deletions.
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

0 comments on commit bf409a9

Please sign in to comment.