diff --git a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java new file mode 100644 index 000000000000..6d62805915b8 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi; + +import org.jetbrains.annotations.Nullable; + +/** + * Represents a method or constructor reference. + */ +public interface PsiMethodReferenceExpression extends PsiReferenceExpression { + /** + * Returns the type element used as the qualifier of the reference. + * + * @return the qualifier, or null if the reference is qualified by expression. + */ + @Nullable + PsiTypeElement getQualifierType(); +} diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index b65578b6df00..7e522b6a46b4 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -321,6 +321,7 @@ public class ExpressionParser { private enum BreakPoint {P1, P2, P3, P4} + // todo[r.sh] make 'this', 'super' and 'class' reference expressions @Nullable private PsiBuilder.Marker parsePrimary(final PsiBuilder builder, @Nullable final BreakPoint breakPoint, final int breakOffset) { PsiBuilder.Marker startMarker = builder.mark(); @@ -350,7 +351,7 @@ public class ExpressionParser { final int offset = builder.getCurrentOffset(); startMarker.rollbackTo(); - final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); + final PsiBuilder.Marker classObjAccess = parseClassAccessOrMethodReference(builder); if (classObjAccess == null || builder.getCurrentOffset() < offset) { copy.rollbackTo(); return parsePrimary(builder, BreakPoint.P1, offset); @@ -466,7 +467,7 @@ public class ExpressionParser { final PsiBuilder.Marker copy = startMarker.precede(); startMarker.rollbackTo(); - final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); + final PsiBuilder.Marker classObjAccess = parseClassAccessOrMethodReference(builder); if (classObjAccess == null || builder.getCurrentOffset() <= pos) { copy.rollbackTo(); return parsePrimary(builder, BreakPoint.P4, -1); @@ -498,6 +499,9 @@ public class ExpressionParser { expr = arrayAccess; } } + else if (tokenType == JavaTokenType.DOUBLE_COLON) { + return parseMethodReference(builder, startMarker); + } else { startMarker.drop(); return expr; @@ -515,6 +519,7 @@ public class ExpressionParser { literal.done(JavaElementType.LITERAL_EXPRESSION); return literal; } + if (tokenType == JavaTokenType.LPARENTH) { final PsiBuilder.Marker parenth = builder.mark(); builder.advanceLexer(); @@ -533,12 +538,25 @@ public class ExpressionParser { parenth.done(JavaElementType.PARENTH_EXPRESSION); return parenth; } + if (tokenType == JavaTokenType.LBRACE) { return parseArrayInitializer(builder); } + if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType) || tokenType == JavaTokenType.IDENTIFIER) { + final boolean primitive = tokenType != JavaTokenType.IDENTIFIER; + final PsiBuilder.Marker mark = builder.mark(); + + final ReferenceParser.TypeInfo typeInfo = myReferenceParser.parseTypeInfo(builder, 0); + if (typeInfo != null && (primitive || !typeInfo.hasErrors && typeInfo.isParameterized)) { + final PsiBuilder.Marker result = continueClassAccessOrMethodReference(builder, mark, primitive); + if (result != null) return result; + } + + mark.rollbackTo(); + } + PsiBuilder.Marker annotation = null; - final PsiBuilder.Marker beforeAnnotation = builder.mark(); if (tokenType == JavaTokenType.AT) { annotation = myDeclarationParser.parseAnnotations(builder); tokenType = builder.getTokenType(); @@ -558,17 +576,13 @@ public class ExpressionParser { builder.advanceLexer(); refExpr.done(JavaElementType.REFERENCE_EXPRESSION); - beforeAnnotation.drop(); return refExpr; } if (annotation != null) { - beforeAnnotation.rollbackTo(); + annotation.rollbackTo(); tokenType = builder.getTokenType(); } - else { - beforeAnnotation.drop(); - } PsiBuilder.Marker expr = null; if (tokenType == JavaTokenType.LT) { @@ -599,12 +613,10 @@ public class ExpressionParser { : JavaElementType.SUPER_EXPRESSION); return expr; } + if (tokenType == JavaTokenType.NEW_KEYWORD) { return parseNew(builder, null); } - if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { - return parseClassObjectAccess(builder); - } return null; } @@ -745,27 +757,47 @@ public class ExpressionParser { } @Nullable - private PsiBuilder.Marker parseClassObjectAccess(final PsiBuilder builder) { + private PsiBuilder.Marker parseClassAccessOrMethodReference(final PsiBuilder builder) { final PsiBuilder.Marker expr = builder.mark(); + final boolean primitive = ElementType.PRIMITIVE_TYPE_BIT_SET.contains(builder.getTokenType()); if (myReferenceParser.parseType(builder, 0) == null) { expr.drop(); return null; } - if (builder.getTokenType() != JavaTokenType.DOT) { - expr.rollbackTo(); - return null; + final PsiBuilder.Marker result = continueClassAccessOrMethodReference(builder, expr, primitive); + if (result == null) expr.rollbackTo(); + return result; + } + + @Nullable + private PsiBuilder.Marker continueClassAccessOrMethodReference(final PsiBuilder builder, + final PsiBuilder.Marker expr, + final boolean primitive) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.DOT) { + return parseClassObjectAccess(builder, expr, primitive); } - PsiBuilder.Marker afterType = builder.mark(); + else if (tokenType == JavaTokenType.DOUBLE_COLON) { + return parseMethodReference(builder, expr); + } + + return null; + } + + @Nullable + private static PsiBuilder.Marker parseClassObjectAccess(PsiBuilder builder, PsiBuilder.Marker expr, boolean primitive) { + final PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if (builder.getTokenType() == JavaTokenType.CLASS_KEYWORD) { - afterType.drop(); + mark.drop(); builder.advanceLexer(); } else { - afterType.rollbackTo(); + if (!primitive) return null; + mark.rollbackTo(); builder.error(".class expected"); } @@ -773,6 +805,20 @@ public class ExpressionParser { return expr; } + @NotNull + private PsiBuilder.Marker parseMethodReference(final PsiBuilder builder, final PsiBuilder.Marker start) { + builder.advanceLexer(); + + myReferenceParser.parseReferenceParameterList(builder, false, false); + + if (!expect(builder, JavaTokenType.IDENTIFIER) && !expect(builder, JavaTokenType.NEW_KEYWORD)) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + + start.done(JavaElementType.METHOD_REF_EXPRESSION); + return start; + } + @NotNull public PsiBuilder.Marker parseArgumentList(final PsiBuilder builder) { final PsiBuilder.Marker list = builder.mark(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java index 48b07d17942c..a734707a2da1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java @@ -29,13 +29,12 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen TokenSet KEYWORD_BIT_SET = TokenSet.create( ABSTRACT_KEYWORD, ASSERT_KEYWORD, BOOLEAN_KEYWORD, BREAK_KEYWORD, BYTE_KEYWORD, CASE_KEYWORD, CATCH_KEYWORD, CHAR_KEYWORD, - CLASS_KEYWORD, - CONST_KEYWORD, CONTINUE_KEYWORD, DEFAULT_KEYWORD, DO_KEYWORD, DOUBLE_KEYWORD, ELSE_KEYWORD, ENUM_KEYWORD, EXTENDS_KEYWORD, - FINAL_KEYWORD, - FINALLY_KEYWORD, FLOAT_KEYWORD, FOR_KEYWORD, GOTO_KEYWORD, IF_KEYWORD, IMPLEMENTS_KEYWORD, IMPORT_KEYWORD, INSTANCEOF_KEYWORD, - INT_KEYWORD, INTERFACE_KEYWORD, LONG_KEYWORD, NATIVE_KEYWORD, NEW_KEYWORD, PACKAGE_KEYWORD, PRIVATE_KEYWORD, PROTECTED_KEYWORD, - PUBLIC_KEYWORD, RETURN_KEYWORD, SHORT_KEYWORD, SUPER_KEYWORD, STATIC_KEYWORD, STRICTFP_KEYWORD, SWITCH_KEYWORD, SYNCHRONIZED_KEYWORD, - THIS_KEYWORD, THROW_KEYWORD, THROWS_KEYWORD, TRANSIENT_KEYWORD, TRY_KEYWORD, VOID_KEYWORD, VOLATILE_KEYWORD, WHILE_KEYWORD); + CLASS_KEYWORD, CONST_KEYWORD, CONTINUE_KEYWORD, DEFAULT_KEYWORD, DO_KEYWORD, DOUBLE_KEYWORD, ELSE_KEYWORD, ENUM_KEYWORD, + EXTENDS_KEYWORD, FINAL_KEYWORD, FINALLY_KEYWORD, FLOAT_KEYWORD, FOR_KEYWORD, GOTO_KEYWORD, IF_KEYWORD, IMPLEMENTS_KEYWORD, + IMPORT_KEYWORD, INSTANCEOF_KEYWORD, INT_KEYWORD, INTERFACE_KEYWORD, LONG_KEYWORD, NATIVE_KEYWORD, NEW_KEYWORD, PACKAGE_KEYWORD, + PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, RETURN_KEYWORD, SHORT_KEYWORD, SUPER_KEYWORD, STATIC_KEYWORD, STRICTFP_KEYWORD, + SWITCH_KEYWORD, SYNCHRONIZED_KEYWORD, THIS_KEYWORD, THROW_KEYWORD, THROWS_KEYWORD, TRANSIENT_KEYWORD, TRY_KEYWORD, VOID_KEYWORD, + VOLATILE_KEYWORD, WHILE_KEYWORD); TokenSet LITERAL_BIT_SET = TokenSet.create(TRUE_KEYWORD, FALSE_KEYWORD, NULL_KEYWORD); @@ -45,33 +44,31 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen PLUSEQ, MINUSEQ, ASTERISKEQ, DIVEQ, ANDEQ, OREQ, XOREQ, PERCEQ, LTLTEQ, GTGTEQ, GTGTGTEQ); TokenSet MODIFIER_BIT_SET = TokenSet.create( - PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, STATIC_KEYWORD, ABSTRACT_KEYWORD, FINAL_KEYWORD, NATIVE_KEYWORD, SYNCHRONIZED_KEYWORD, - STRICTFP_KEYWORD, TRANSIENT_KEYWORD, VOLATILE_KEYWORD); + PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, STATIC_KEYWORD, ABSTRACT_KEYWORD, FINAL_KEYWORD, NATIVE_KEYWORD, + SYNCHRONIZED_KEYWORD, STRICTFP_KEYWORD, TRANSIENT_KEYWORD, VOLATILE_KEYWORD); TokenSet PRIMITIVE_TYPE_BIT_SET = TokenSet.create( BOOLEAN_KEYWORD, BYTE_KEYWORD, SHORT_KEYWORD, INT_KEYWORD, LONG_KEYWORD, CHAR_KEYWORD, FLOAT_KEYWORD, DOUBLE_KEYWORD, VOID_KEYWORD); TokenSet EXPRESSION_BIT_SET = TokenSet.create( REFERENCE_EXPRESSION, LITERAL_EXPRESSION, THIS_EXPRESSION, SUPER_EXPRESSION, PARENTH_EXPRESSION, METHOD_CALL_EXPRESSION, - TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, - NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, ARRAY_INITIALIZER_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, - EMPTY_EXPRESSION); + TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, + ASSIGNMENT_EXPRESSION, NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, ARRAY_INITIALIZER_EXPRESSION, INSTANCE_OF_EXPRESSION, + CLASS_OBJECT_ACCESS_EXPRESSION, METHOD_REF_EXPRESSION, EMPTY_EXPRESSION); TokenSet ANNOTATION_MEMBER_VALUE_BIT_SET = TokenSet.orSet(EXPRESSION_BIT_SET, TokenSet.create(ANNOTATION, ANNOTATION_ARRAY_INITIALIZER)); TokenSet ARRAY_DIMENSION_BIT_SET = TokenSet.create( REFERENCE_EXPRESSION, LITERAL_EXPRESSION, THIS_EXPRESSION, SUPER_EXPRESSION, PARENTH_EXPRESSION, METHOD_CALL_EXPRESSION, - TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, - NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, EMPTY_EXPRESSION); + TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, + ASSIGNMENT_EXPRESSION, NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, + EMPTY_EXPRESSION); TokenSet JAVA_STATEMENT_BIT_SET = TokenSet.create( - EMPTY_STATEMENT, BLOCK_STATEMENT, EXPRESSION_STATEMENT, EXPRESSION_LIST_STATEMENT, - DECLARATION_STATEMENT, IF_STATEMENT, WHILE_STATEMENT, FOR_STATEMENT, FOREACH_STATEMENT, - DO_WHILE_STATEMENT, SWITCH_STATEMENT, SWITCH_LABEL_STATEMENT, BREAK_STATEMENT, - CONTINUE_STATEMENT, RETURN_STATEMENT, THROW_STATEMENT, SYNCHRONIZED_STATEMENT, - TRY_STATEMENT, LABELED_STATEMENT, ASSERT_STATEMENT - ); + EMPTY_STATEMENT, BLOCK_STATEMENT, EXPRESSION_STATEMENT, EXPRESSION_LIST_STATEMENT, DECLARATION_STATEMENT, IF_STATEMENT, + WHILE_STATEMENT, FOR_STATEMENT, FOREACH_STATEMENT, DO_WHILE_STATEMENT, SWITCH_STATEMENT, SWITCH_LABEL_STATEMENT, BREAK_STATEMENT, + CONTINUE_STATEMENT, RETURN_STATEMENT, THROW_STATEMENT, SYNCHRONIZED_STATEMENT, TRY_STATEMENT, LABELED_STATEMENT, ASSERT_STATEMENT); TokenSet TYPES_BIT_SET = TokenSet.create(TYPE); TokenSet IMPORT_STATEMENT_BIT_SET = TokenSet.create(IMPORT_STATEMENT); @@ -79,7 +76,6 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen TokenSet IMPORT_STATEMENT_BASE_BIT_SET = TokenSet.create(IMPORT_STATEMENT, IMPORT_STATIC_STATEMENT); TokenSet CLASS_KEYWORD_BIT_SET = TokenSet.create(CLASS_KEYWORD, INTERFACE_KEYWORD, ENUM_KEYWORD); TokenSet MEMBER_BIT_SET = TokenSet.create(CLASS, FIELD, ENUM_CONSTANT, METHOD, ANNOTATION_METHOD); - TokenSet FULL_MEMBER_BIT_SET = TokenSet.orSet(MEMBER_BIT_SET, - TokenSet.create(CLASS_INITIALIZER)); + TokenSet FULL_MEMBER_BIT_SET = TokenSet.orSet(MEMBER_BIT_SET, TokenSet.create(CLASS_INITIALIZER)); TokenSet ANNOTATIONS = TokenSet.create(ANNOTATION); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 69476e9ef24d..7f96a2d003dd 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,6 +104,7 @@ public interface JavaElementType { IElementType INSTANCE_OF_EXPRESSION = new JavaCompositeElementType("INSTANCE_OF_EXPRESSION", PsiInstanceOfExpressionImpl.class); IElementType CLASS_OBJECT_ACCESS_EXPRESSION = new JavaCompositeElementType("CLASS_OBJECT_ACCESS_EXPRESSION", PsiClassObjectAccessExpressionImpl.class); IElementType EMPTY_EXPRESSION = new JavaCompositeElementType("EMPTY_EXPRESSION", PsiEmptyExpressionImpl.class, true); + IElementType METHOD_REF_EXPRESSION = new JavaCompositeElementType("METHOD_REF_EXPRESSION", PsiMethodReferenceExpressionImpl.class); IElementType EXPRESSION_LIST = new JavaCompositeElementType("EXPRESSION_LIST", PsiExpressionListImpl.class, true); IElementType EMPTY_STATEMENT = new JavaCompositeElementType("EMPTY_STATEMENT", PsiEmptyStatementImpl.class); IElementType BLOCK_STATEMENT = new JavaCompositeElementType("BLOCK_STATEMENT", PsiBlockStatementImpl.class); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java new file mode 100644 index 000000000000..ad21de8a54f2 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NotNull; + +public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression { + private static Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl"); + + public PsiMethodReferenceExpressionImpl() { + super(JavaElementType.METHOD_REF_EXPRESSION); + } + + @Override + public PsiTypeElement getQualifierType() { + final PsiElement qualifier = getQualifier(); + return qualifier instanceof PsiTypeElement ? (PsiTypeElement)qualifier : null; + } + + @Override + public PsiExpression getQualifierExpression() { + final PsiElement qualifier = getQualifier(); + return qualifier instanceof PsiExpression ? (PsiExpression)qualifier : null; + } + + @Override + public PsiType getType() { + // todo[r.sh]: implement + return null; + } + + @Override + public PsiElement getReferenceNameElement() { + final PsiElement element = getLastChild(); + return element instanceof PsiIdentifier || PsiUtil.isJavaToken(element, JavaTokenType.NEW_KEYWORD) ? element : null; + } + + @Override + public void processVariants(final PsiScopeProcessor processor) { + // todo[r.sh]: implement + } + + @NotNull + @Override + public JavaResolveResult[] multiResolve(final boolean incompleteCode) { + // todo[r.sh]: implement + return JavaResolveResult.EMPTY_ARRAY; + } + + @Override + public PsiElement getQualifier() { + final PsiElement element = getFirstChild(); + return element instanceof PsiExpression || element instanceof PsiTypeElement ? element : null; + } + + @Override + public TextRange getRangeInElement() { + final PsiElement element = getReferenceNameElement(); + if (element != null) return new TextRange(element.getStartOffsetInParent(), element.getTextLength()); + final PsiElement colons = findPsiChildByType(JavaTokenType.DOUBLE_COLON); + if (colons != null) return new TextRange(colons.getStartOffsetInParent(), colons.getTextLength()); + LOG.error(getText()); + return null; + } + + @NotNull + @Override + public String getCanonicalText() { + return getText(); + } + + @Override + public boolean isReferenceTo(final PsiElement element) { + if (!(element instanceof PsiMethod)) return false; + final PsiMethod method = (PsiMethod)element; + + final PsiElement nameElement = getReferenceNameElement(); + if (nameElement instanceof PsiIdentifier) { + if (!nameElement.getText().equals(method.getName())) return false; + } + else if (PsiUtil.isJavaToken(nameElement, JavaTokenType.NEW_KEYWORD)) { + if (!method.isConstructor()) return false; + } + + return element.getManager().areElementsEquivalent(element, resolve()); + } + + @Override + public String toString() { + return "PsiMethodReferenceExpression:" + getText(); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionBase.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionBase.java new file mode 100644 index 000000000000..df67a536d105 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionBase.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class PsiReferenceExpressionBase extends ExpressionPsiElement implements PsiReferenceExpression { + public PsiReferenceExpressionBase(@NotNull final IElementType type) { + super(type); + } + + @Override + public PsiElement bindToElementViaStaticImport(@NotNull final PsiClass qualifierClass) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public void setQualifierExpression(@Nullable PsiExpression newQualifier) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public PsiElement getElement() { + return this; + } + + @Override + public PsiElement resolve() { + return advancedResolve(false).getElement(); + } + + @Override + public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public boolean isReferenceTo(final PsiElement element) { + return element.getManager().areElementsEquivalent(element, resolve()); + } + + @NotNull + @Override + public Object[] getVariants() { + // this reference's variants are rather obtained with processVariants() + return ArrayUtil.EMPTY_OBJECT_ARRAY; + } + + @Override + public boolean isSoft() { + return false; + } + + @Override + public PsiReference getReference() { + return this; + } + + @NotNull + @Override + public JavaResolveResult advancedResolve(boolean incompleteCode) { + final JavaResolveResult[] results = multiResolve(incompleteCode); + return results.length == 1 ? results[0] : JavaResolveResult.EMPTY; + } + + @Override + public String getReferenceName() { + final PsiElement element = getReferenceNameElement(); + return element != null ? element.getText() : null; + } + + @Override + public PsiReferenceParameterList getParameterList() { + return PsiTreeUtil.getChildOfType(this, PsiReferenceParameterList.class); + } + + @NotNull + @Override + public PsiType[] getTypeParameters() { + final PsiReferenceParameterList parameterList = getParameterList(); + return parameterList != null ? parameterList.getTypeArguments() : PsiType.EMPTY_ARRAY; + } + + @Override + public boolean isQualified() { + return getQualifier() != null; + } + + @Override + public String getQualifiedName() { + return getCanonicalText(); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java index 52472d402077..9974e27bfe30 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements PsiReferenceExpression, SourceJavaCodeReference { +public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiReferenceExpression, SourceJavaCodeReference { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl"); private volatile String myCachedQName = null; @@ -108,7 +108,8 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements } else { PsiManagerEx manager = getManager(); - PsiReferenceExpression classRef = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createReferenceExpression(qualifierClass); + PsiReferenceExpression classRef = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createReferenceExpression( + qualifierClass); final CharTable treeCharTab = SharedImplUtil.findCharTableByTree(this); LeafElement dot = Factory.createSingleLeafElement(JavaTokenType.DOT, ".", 0, 1, treeCharTab, manager); addInternal(dot, dot, SourceTreeToPsiMap.psiElementToTree(getParameterList()), Boolean.TRUE); @@ -122,7 +123,8 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements final String qualifiedName = qualifierClass.getQualifiedName(); final List refs = getImportsFromClass(importList, qualifiedName); if (refs.size() < JavaCodeStyleSettingsFacade.getInstance(qualifierClass.getProject()).getNamesCountToUseImportOnDemand()) { - importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, staticName)); + importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, + staticName)); } else { for (PsiJavaCodeReferenceElement ref : refs) { final PsiImportStaticStatement importStatement = PsiTreeUtil.getParentOfType(ref, PsiImportStaticStatement.class); @@ -130,7 +132,8 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements importStatement.delete(); } } - importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, "*")); + importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, + "*")); } } @@ -174,16 +177,6 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return getQualifierExpression(); } - @Override - public PsiReference getReference() { - return this; - } - - @Override - public PsiElement resolve() { - return advancedResolve(false).getElement(); - } - @Override public void clearCaches() { myCachedQName = null; @@ -316,18 +309,6 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return getCachedTextSkipWhiteSpaceAndComments(); } - @Override - public String getQualifiedName() { - return getCanonicalText(); - } - - @Override - public String getReferenceName() { - PsiElement element = getReferenceNameElement(); - if (element == null) return null; - return element.getText(); - } - private final Function ourTypeEvaluator = new TypeEvaluator(); private static class TypeEvaluator implements NullableFunction { @@ -409,20 +390,7 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements // optimization: methodCallExpression should resolve to a method if (parentIsMethodCall != resolvingToMethod) return false; - return element.getManager().areElementsEquivalent(element, resolve()); - } - - - @Override - @NotNull - public Object[] getVariants() { - //this reference's variants are rather obtained with processVariants() - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - - @Override - public boolean isSoft() { - return false; + return super.isReferenceTo(element); } @Override @@ -435,7 +403,7 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements filter.addFilter(new AndFilter(ElementClassFilter.METHOD, new NotFilter(new ConstructorFilter()))); filter.addFilter(ElementClassFilter.VARIABLE); - FilterScopeProcessor proc = new FilterScopeProcessor(filter, processor) { + FilterScopeProcessor filterProcessor = new FilterScopeProcessor(filter, processor) { private final Set myVarNames = new THashSet(); @Override @@ -454,7 +422,7 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements } }; - PsiScopesUtil.resolveAndWalk(proc, this, null, true); + PsiScopesUtil.resolveAndWalk(filterProcessor, this, null, true); } private static boolean seemsScrambled(PsiClass element) { @@ -469,24 +437,11 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements Character.isLowerCase(qualifiedName.charAt(0)); } - @Override - @NotNull - public JavaResolveResult advancedResolve(boolean incompleteCode) { - final JavaResolveResult[] results = multiResolve(incompleteCode); - if (results.length == 1) return results[0]; - return JavaResolveResult.EMPTY; - } - @Override public PsiElement getReferenceNameElement() { return findChildByRoleAsPsiElement(ChildRole.REFERENCE_NAME); } - @Override - public PsiReferenceParameterList getParameterList() { - return (PsiReferenceParameterList)findChildByRoleAsPsiElement(ChildRole.REFERENCE_PARAMETER_LIST); - } - @Override public int getTextOffset() { ASTNode refName = findChildByRole(ChildRole.REFERENCE_NAME); @@ -684,20 +639,6 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return new TextRange(nameChild.getStartOffsetInParent(), getTextLength()); } - @Override - public PsiElement getElement() { - return this; - } - - @Override - @NotNull - public PsiType[] getTypeParameters() { - final PsiReferenceParameterList parameterList = getParameterList(); - if (parameterList == null) return PsiType.EMPTY_ARRAY; - return parameterList.getTypeArguments(); - } - - @Override public String getClassNameText() { String cachedQName = myCachedQName; @@ -744,4 +685,3 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return whiteSpaceAndComments; } } - diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MethodReferences.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MethodReferences.java new file mode 100644 index 000000000000..150cf88144fe --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MethodReferences.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class C { + interface Simplest { + void m(); + } + void simplest() { } + + void test() { + Simplest simplest = this::simplest; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef0.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef0.txt new file mode 100644 index 000000000000..28d943ef1c0e --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef0.txt @@ -0,0 +1,20 @@ +PsiJavaFile:MethodRef0.java + PsiMethodReferenceExpression:a.b.C::m + PsiReferenceExpression:a.b.C + PsiReferenceExpression:a.b + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:b('b') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:C('C') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:m('m') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef1.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef1.txt new file mode 100644 index 000000000000..16de47b23d45 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef1.txt @@ -0,0 +1,27 @@ +PsiJavaFile:MethodRef1.java + PsiMethodReferenceExpression:a.b.C::new + PsiTypeElement:a.b.C + PsiJavaCodeReferenceElement:a.b.C + PsiJavaCodeReferenceElement:a.b + PsiJavaCodeReferenceElement:a + PsiIdentifier:a('a') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:b('b') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:C('C') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiKeyword:new('new') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef2.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef2.txt new file mode 100644 index 000000000000..3763aa5e3eba --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef2.txt @@ -0,0 +1,16 @@ +PsiJavaFile:MethodRef2.java + PsiMethodReferenceExpression:C::m + PsiReferenceExpression:C + PsiReferenceParameterList + + PsiIdentifier:C('C') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiIdentifier:m('m') diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef3.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef3.txt new file mode 100644 index 000000000000..f13cc341917b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef3.txt @@ -0,0 +1,17 @@ +PsiJavaFile:MethodRef3.java + PsiMethodReferenceExpression:a[i]::m + PsiArrayAccessExpression:a[i] + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiJavaToken:LBRACKET('[') + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiJavaToken:RBRACKET(']') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:m('m') diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef4.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef4.txt new file mode 100644 index 000000000000..31cf24bdb999 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef4.txt @@ -0,0 +1,11 @@ +PsiJavaFile:MethodRef4.java + PsiMethodReferenceExpression:int[]::clone + PsiTypeElement:int[] + PsiTypeElement:int + PsiKeyword:int('int') + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:clone('clone') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef5.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef5.txt new file mode 100644 index 000000000000..4a82070c8b37 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef5.txt @@ -0,0 +1,55 @@ +PsiJavaFile:MethodRef5.java + PsiMethodReferenceExpression:(f ? list.map(String::length) : Collections.emptyList())::iterator + PsiParenthesizedExpression:(f ? list.map(String::length) : Collections.emptyList()) + PsiJavaToken:LPARENTH('(') + PsiConditionalExpression:f ? list.map(String::length) : Collections.emptyList() + PsiReferenceExpression:f + PsiReferenceParameterList + + PsiIdentifier:f('f') + PsiWhiteSpace(' ') + PsiJavaToken:QUEST('?') + PsiWhiteSpace(' ') + PsiMethodCallExpression:list.map(String::length) + PsiReferenceExpression:list.map + PsiReferenceExpression:list + PsiReferenceParameterList + + PsiIdentifier:list('list') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:map('map') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiMethodReferenceExpression:String::length + PsiReferenceExpression:String + PsiReferenceParameterList + + PsiIdentifier:String('String') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:length('length') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiMethodCallExpression:Collections.emptyList() + PsiReferenceExpression:Collections.emptyList + PsiReferenceExpression:Collections + PsiReferenceParameterList + + PsiIdentifier:Collections('Collections') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:emptyList('emptyList') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:iterator('iterator') diff --git a/java/java-tests/testData/psi/parser-partial/expressions/PrimitiveFieldAccess.txt b/java/java-tests/testData/psi/parser-partial/expressions/PrimitiveFieldAccess.txt new file mode 100644 index 000000000000..13e35abdce09 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/PrimitiveFieldAccess.txt @@ -0,0 +1,11 @@ +PsiJavaFile:PrimitiveFieldAccess.java + PsiReferenceExpression:int.x + PsiClassObjectAccessExpression:int + PsiTypeElement:int + PsiKeyword:int('int') + PsiErrorElement:.class expected + + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:x('x') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java index aa839c1bae79..ac076e6a6c0f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java @@ -184,4 +184,5 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { public void testIDEA84533() throws Exception { doTest(false, false); } public void testClassLiteral() throws Exception { doTest(false, false); } public void testExtensionMethods() throws Exception { doTest(false, false); } + public void testMethodReferences() throws Exception { doTest(false, false); } } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index f43e356abed7..838d0c4fcf2a 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -111,9 +111,17 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testQualifiedSuperMethodCall1() { doParserTest("d.super(0)"); } public void testSuperMethodCallTypeParameterList() { doParserTest("super()"); } public void testPrimitiveClassObjectAccess() { doParserTest("int.class"); } + public void testPrimitiveFieldAccess() { doParserTest("int.x"); } public void testChainedClassObjectAccess() { doParserTest("A.class.B.class"); } public void testChainedThisObjectAccess() { doParserTest("A.this.B.this"); } + public void testMethodRef0() { doParserTest("a.b.C::m"); } + public void testMethodRef1() { doParserTest("a.b.C::new"); } + public void testMethodRef2() { doParserTest("C::m"); } + public void testMethodRef3() { doParserTest("a[i]::m"); } + public void testMethodRef4() { doParserTest("int[]::clone"); } + public void testMethodRef5() { doParserTest("(f ? list.map(String::length) : Collections.emptyList())::iterator"); } + private void doParserTest(@NonNls final String text) { doParserTest(text, new MyTestParser()); }