diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java index d569262db22b..b7843126b343 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/PostHighlightingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt index b20da169d7e9..d96bfa6c776f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -17,15 +17,12 @@ package com.intellij.codeInspection.dataFlow import com.intellij.lang.LighterAST import com.intellij.lang.LighterASTNode -import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.psi.impl.source.JavaLightStubBuilder -import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.psi.impl.source.PsiMethodImpl -import com.intellij.psi.impl.source.tree.JavaElementType import com.intellij.psi.impl.source.tree.JavaElementType.* import com.intellij.psi.impl.source.tree.LightTreeUtil import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor -import com.intellij.psi.util.PsiUtil +import com.intellij.psi.stub.JavaStubImplUtil import com.intellij.util.gist.GistManager import java.util.* @@ -96,18 +93,4 @@ private fun createData(body: LighterASTNode, return MethodData(nullity, purity, contracts, notNullParams, body.startOffset, body.endOffset) } -fun getIndexedData(method: PsiMethodImpl): MethodData? = gist.getFileData(method.containingFile)?.get(methodIndex(method)) - -private fun methodIndex(method: PsiMethodImpl): Int? { - val file = method.containingFile as PsiFileImpl - if (file.elementTypeForStubBuilder == null) return null - - val stubTree = try { - file.stubTree ?: file.calcStubTree() - } catch (e: ProcessCanceledException) { - throw e - } catch (e: RuntimeException) { - throw RuntimeException("While inferring contract for " + PsiUtil.getMemberQualifiedName(method), e) - } - return stubTree.plainList.filter { it.stubType == JavaElementType.METHOD }.map { it.psi }.indexOf(method) -} \ No newline at end of file +fun getIndexedData(method: PsiMethodImpl): MethodData? = gist.getFileData(method.containingFile)?.get(JavaStubImplUtil.getMethodStubIndex(method)) \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspectionBase.java index e037ec3fe119..5430b6dcc8f1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocLocalInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java index d322ceaceb01..dd34eb662dbc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -73,13 +73,12 @@ public class PreferByKindWeigher extends LookupElementWeigher { psiElement(PsiReferenceExpression.class).withParent(PsiResourceExpression.class))); private final CompletionType myCompletionType; - @NotNull private final PsiElement myPosition; private final Set myNonInitializedFields; private final Condition myRequiredSuper; private final ExpectedTypeInfo[] myExpectedTypes; - public PreferByKindWeigher(CompletionType completionType, @NotNull final PsiElement position, ExpectedTypeInfo[] expectedTypes) { + public PreferByKindWeigher(CompletionType completionType, final PsiElement position, ExpectedTypeInfo[] expectedTypes) { super("kind"); myCompletionType = completionType; myPosition = position; diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java index 3ec283406529..b573cadb38a7 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. diff --git a/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanProperty.java b/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanProperty.java index fabc1b623602..a21a9abae12f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanProperty.java +++ b/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 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. diff --git a/java/java-indexing-impl/java-indexing-impl.iml b/java/java-indexing-impl/java-indexing-impl.iml index 28435c9a92c4..6442f30fd02b 100644 --- a/java/java-indexing-impl/java-indexing-impl.iml +++ b/java/java-indexing-impl/java-indexing-impl.iml @@ -15,5 +15,6 @@ + \ No newline at end of file diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/JavaSimplePropertyIndex.kt b/java/java-indexing-impl/src/com/intellij/psi/impl/JavaSimplePropertyIndex.kt new file mode 100644 index 000000000000..0828da78e88e --- /dev/null +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/JavaSimplePropertyIndex.kt @@ -0,0 +1,201 @@ +/* + * Copyright 2000-2017 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 + +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.lang.LighterASTNode +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.JavaTokenType +import com.intellij.psi.PsiField +import com.intellij.psi.impl.cache.RecordUtil +import com.intellij.psi.impl.java.stubs.JavaStubElementTypes +import com.intellij.psi.impl.source.JavaLightStubBuilder +import com.intellij.psi.impl.source.JavaLightTreeUtil +import com.intellij.psi.impl.source.PsiMethodImpl +import com.intellij.psi.impl.source.tree.ElementType +import com.intellij.psi.impl.source.tree.JavaElementType +import com.intellij.psi.impl.source.tree.LightTreeUtil +import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stub.JavaStubImplUtil +import com.intellij.psi.util.PropertyUtil +import com.intellij.psi.util.PropertyUtilBase +import com.intellij.util.containers.ContainerUtil +import com.intellij.util.indexing.* +import com.intellij.util.io.DataExternalizer +import com.intellij.util.io.EnumeratorIntegerDescriptor +import com.intellij.util.io.EnumeratorStringDescriptor +import com.intellij.util.io.KeyDescriptor +import java.io.DataInput +import java.io.DataOutput + +private val indexId = ID.create("java.simple.property") +private val log = Logger.getInstance(JavaSimplePropertyIndex::class.java) + +fun getFieldOfGetter(method: PsiMethodImpl): PsiField? = resolveFieldFromIndexValue(method, true) + +fun getFieldOfSetter(method: PsiMethodImpl): PsiField? = resolveFieldFromIndexValue(method, false) + +private fun resolveFieldFromIndexValue(method: PsiMethodImpl, isGetter: Boolean): PsiField? { + val id = JavaStubImplUtil.getMethodStubIndex(method) + if (id != -1) { + val values = FileBasedIndex.getInstance().getValues(indexId, id, GlobalSearchScope.fileScope(method.containingFile)) + when (values.size) { + 0 -> return null + 1 -> { + val indexValue = values[0] + if (isGetter != indexValue.getter) return null + val psiClass = method.containingClass + val project = psiClass!!.project + val expr = JavaPsiFacade.getElementFactory(project).createExpressionFromText(indexValue.propertyRefText, psiClass) + return PropertyUtil.getSimplyReturnedField(expr) + } + else -> { + log.error("multiple index values for method $method") + } + } + } + return null +} + +data class PropertyIndexValue(val propertyRefText: String, val getter: Boolean) + +class JavaSimplePropertyIndex : FileBasedIndexExtension(), PsiDependentIndex { + override fun getIndexer(): DataIndexer = DataIndexer { inputData -> + val result = ContainerUtil.newHashMap() + val tree = (inputData as FileContentImpl).lighterASTForPsiDependentIndex + + object : RecursiveLighterASTNodeWalkingVisitor(tree) { + var methodIndex = 0 + + override fun visitNode(element: LighterASTNode) { + if (JavaLightStubBuilder.isCodeBlockWithoutStubs(element)) return + + if (element.tokenType === JavaElementType.METHOD) { + extractProperty(element)?.let { result.put(methodIndex, it) } + methodIndex++ + } + + super.visitNode(element) + } + + private fun extractProperty(method: LighterASTNode): PropertyIndexValue? { + var isConstructor = true + var isGetter = true + + var isBooleanReturnType = false + var isVoidReturnType = false + var setterParameterName: String? = null + + var refText: String? = null + + for (child in tree.getChildren(method)) { + when (child.tokenType) { + JavaElementType.TYPE -> { + val children = tree.getChildren(child) + if (children.size != 1) return null + val typeElement = children[0] + if (typeElement.tokenType == JavaTokenType.VOID_KEYWORD) isVoidReturnType = true + if (typeElement.tokenType == JavaTokenType.BOOLEAN_KEYWORD) isBooleanReturnType = true + isConstructor = false + } + JavaElementType.PARAMETER_LIST -> { + if (isGetter) { + if (LightTreeUtil.firstChildOfType(tree, child, JavaElementType.PARAMETER) != null) return null + } else { + val parameters = LightTreeUtil.getChildrenOfType(tree, child, JavaElementType.PARAMETER) + if (parameters.size != 1) return null + setterParameterName = JavaLightTreeUtil.getNameIdentifierText(tree, parameters[0]) + if (setterParameterName == null) return null + } + } + JavaElementType.CODE_BLOCK -> { + refText = if (isGetter) getGetterPropertyRefText(child) else getSetterPropertyRefText(child, setterParameterName!!) + if (refText == null) return null + } + JavaTokenType.IDENTIFIER -> { + if (isConstructor) return null + val name = RecordUtil.intern(tree.charTable, child) + val flavour = PropertyUtil.getMethodNameGetterFlavour(name) + when (flavour) { + PropertyUtilBase.GetterFlavour.NOT_A_GETTER -> { + if (PropertyUtil.isSetterName(name)) { + isGetter = false + } + else { + return null + } + } + PropertyUtilBase.GetterFlavour.BOOLEAN -> if (!isBooleanReturnType) return null + else -> { } + } + if (isVoidReturnType && isGetter) return null + } + } + } + + return refText?.let { PropertyIndexValue(it, isGetter) } + } + + private fun getSetterPropertyRefText(codeBlock: LighterASTNode, setterParameterName: String): String? { + val assignment = tree + .getChildren(codeBlock) + .singleOrNull { ElementType.JAVA_STATEMENT_BIT_SET.contains(it.tokenType) } + ?.takeIf { it.tokenType == JavaElementType.EXPRESSION_STATEMENT } + ?.let { LightTreeUtil.firstChildOfType(tree, it, JavaElementType.ASSIGNMENT_EXPRESSION) } + if (assignment == null || LightTreeUtil.firstChildOfType(tree, assignment, JavaTokenType.EQ) == null) return null + val operands = LightTreeUtil.getChildrenOfType(tree, assignment, ElementType.EXPRESSION_BIT_SET) + if (operands.size != 2 || LightTreeUtil.toFilteredString(tree, operands[1], null) != setterParameterName) return null + val lhsText = LightTreeUtil.toFilteredString(tree, operands[0], null) + if (lhsText == setterParameterName) return null + return lhsText + } + + private fun getGetterPropertyRefText(codeBlock: LighterASTNode): String? = tree + .getChildren(codeBlock) + .singleOrNull { ElementType.JAVA_STATEMENT_BIT_SET.contains(it.tokenType) } + ?.takeIf { it.tokenType == JavaElementType.RETURN_STATEMENT} + ?.let { LightTreeUtil.firstChildOfType(tree, it, ElementType.EXPRESSION_BIT_SET) } + ?.let { LightTreeUtil.toFilteredString(tree, it, null) } + + }.visitNode(tree.root) + result + } + + override fun getKeyDescriptor(): KeyDescriptor = EnumeratorIntegerDescriptor.INSTANCE + + override fun getValueExternalizer(): DataExternalizer = object: DataExternalizer { + override fun save(out: DataOutput, value: PropertyIndexValue?) { + value!! + EnumeratorStringDescriptor.INSTANCE.save(out, value.propertyRefText) + out.writeBoolean(value.getter) + } + + override fun read(input: DataInput): PropertyIndexValue = PropertyIndexValue(EnumeratorStringDescriptor.INSTANCE.read(input), input.readBoolean()) + } + + override fun getName(): ID = indexId + + override fun getInputFilter(): FileBasedIndex.InputFilter = object : DefaultFileTypeSpecificInputFilter(JavaFileType.INSTANCE) { + override fun acceptInput(file: VirtualFile): Boolean = JavaStubElementTypes.JAVA_FILE.shouldBuildStubFor(file) + } + + override fun dependsOnFileContent(): Boolean = true + + override fun getVersion(): Int = 0 +} \ No newline at end of file diff --git a/java/java-indexing-impl/src/com/intellij/psi/util/PropertyUtil.java b/java/java-indexing-impl/src/com/intellij/psi/util/PropertyUtil.java new file mode 100644 index 000000000000..367f85037f82 --- /dev/null +++ b/java/java-indexing-impl/src/com/intellij/psi/util/PropertyUtil.java @@ -0,0 +1,257 @@ +/* + * Copyright 2000-2017 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.util; + +import com.intellij.lang.jvm.JvmModifier; +import com.intellij.psi.*; +import com.intellij.psi.impl.JavaSimplePropertyIndexKt; +import com.intellij.psi.impl.source.PsiMethodImpl; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +/** + * @author Mike + */ +public class PropertyUtil extends PropertyUtilBase { + private PropertyUtil() { + } + + @Nullable + public static PsiField getFieldOfGetter(PsiMethod method) { + return getFieldOfGetter(method, true); + } + + @Nullable + private static PsiField getFieldOfGetter(PsiMethod method, boolean useIndex) { + PsiField field = useIndex && method instanceof PsiMethodImpl && method.isPhysical() + ? JavaSimplePropertyIndexKt.getFieldOfGetter((PsiMethodImpl)method) + : getSimplyReturnedField(getGetterReturnExpression(method)); + if (field == null || !checkFieldLocation(method, field)) return null; + final PsiType returnType = method.getReturnType(); + return returnType != null && field.getType().equals(returnType) ? field : null; + } + + @Nullable + public static PsiField getSimplyReturnedField(@Nullable PsiExpression value) { + if (!(value instanceof PsiReferenceExpression)) { + return null; + } + + final PsiReferenceExpression reference = (PsiReferenceExpression)value; + if (hasSubstantialQualifier(reference)) { + return null; + } + + final PsiElement referent = reference.resolve(); + if (!(referent instanceof PsiField)) { + return null; + } + + return (PsiField)referent; + } + + private static boolean hasSubstantialQualifier(PsiReferenceExpression reference) { + final PsiExpression qualifier = reference.getQualifierExpression(); + if (qualifier == null) return false; + + if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) { + return false; + } + + if (qualifier instanceof PsiReferenceExpression) { + return !(((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass); + } + return true; + } + + public static boolean isSimpleGetter(PsiMethod method) { + //noinspection TestOnlyProblems + return isSimpleGetter(method, true); + } + + @TestOnly + public static boolean isSimpleGetter(PsiMethod method, boolean useIndex) { + return getFieldOfGetter(method, useIndex) != null; + } + + @Nullable + public static PsiField getFieldOfSetter(@Nullable PsiMethod method) { + return getFieldOfSetter(method, true); + } + + @Nullable + private static PsiField getFieldOfSetter(@Nullable PsiMethod method, boolean useIndex) { + if (method == null) { + return null; + } + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() != 1) { + return null; + } + + PsiField field; + if (useIndex && method instanceof PsiMethodImpl && method.isPhysical()) { + field = JavaSimplePropertyIndexKt.getFieldOfSetter((PsiMethodImpl)method); + } + else { + @NonNls final String name = method.getName(); + if (!name.startsWith("set")) { + return null; + } + if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { + return null; + } + final PsiCodeBlock body = method.getBody(); + if (body == null) { + return null; + } + final PsiStatement[] statements = body.getStatements(); + if (statements.length != 1) { + return null; + } + final PsiStatement statement = statements[0]; + if (!(statement instanceof PsiExpressionStatement)) { + return null; + } + final PsiExpressionStatement possibleAssignmentStatement = (PsiExpressionStatement)statement; + final PsiExpression possibleAssignment = possibleAssignmentStatement.getExpression(); + if (!(possibleAssignment instanceof PsiAssignmentExpression)) { + return null; + } + final PsiAssignmentExpression assignment = (PsiAssignmentExpression)possibleAssignment; + if (!JavaTokenType.EQ.equals(assignment.getOperationTokenType())) { + return null; + } + final PsiExpression lhs = assignment.getLExpression(); + if (!(lhs instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression reference = (PsiReferenceExpression)lhs; + final PsiExpression qualifier = reference.getQualifierExpression(); + if (qualifier instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiClass)) { + return null; + } + } + else if (qualifier != null && !(qualifier instanceof PsiThisExpression) && !(qualifier instanceof PsiSuperExpression)) { + return null; + } + final PsiElement referent = reference.resolve(); + if (referent == null) { + return null; + } + if (!(referent instanceof PsiField)) { + return null; + } + field = (PsiField)referent; + + final PsiExpression rhs = assignment.getRExpression(); + if (!(rhs instanceof PsiReferenceExpression)) { + return null; + } + final PsiReferenceExpression rReference = (PsiReferenceExpression)rhs; + final PsiExpression rQualifier = rReference.getQualifierExpression(); + if (rQualifier != null) { + return null; + } + final PsiElement rReferent = rReference.resolve(); + if (rReferent == null) { + return null; + } + if (!(rReferent instanceof PsiParameter)) { + return null; + } + } + return field != null && field.getType().equals(parameterList.getParameters()[0].getType()) && checkFieldLocation(method, field) + ? field + : null; + } + + public static boolean isSimpleSetter(PsiMethod method) { + //noinspection TestOnlyProblems + return isSimpleSetter(method, true); + } + + @TestOnly + public static boolean isSimpleSetter(PsiMethod method, boolean useIndex) { + return getFieldOfSetter(method, useIndex) != null; + } + + @Nullable + public static PsiMethod getReversePropertyMethod(PsiMethod propertyMethod) { + if (propertyMethod == null) { + return null; + } + final PsiClass aClass = propertyMethod.getContainingClass(); + if (aClass == null) { + return null; + } + final String methodName = propertyMethod.getName(); + final String prefix; + if (methodName.startsWith("get")) { + prefix = "get"; + } + else if (methodName.startsWith(IS_PREFIX)) { + prefix = IS_PREFIX; + } + else if (methodName.startsWith("set")) { + prefix = "set"; + } + else { + return null; + } + final String name = methodName.substring(prefix.length()); + final PsiField field = prefix.equals("set") ? getFieldOfSetter(propertyMethod) : getFieldOfGetter(propertyMethod); + if (field == null) { + return null; + } + if (prefix.equals("set")) { + final PsiMethod result = findPropertyMethod(aClass, "get", name, field); + if (result != null) { + return result; + } + return findPropertyMethod(aClass, IS_PREFIX, name, field); + } + else { + return findPropertyMethod(aClass, "set", name, field); + } + } + + private static PsiMethod findPropertyMethod(@NotNull PsiClass aClass, + @NotNull String prefix, + @NotNull String propertyName, + @NotNull PsiField field1) { + final PsiMethod[] methods = aClass.findMethodsByName(prefix + propertyName, true); + for (PsiMethod method : methods) { + final PsiField field2 = prefix.equals("set") ? getFieldOfSetter(method) : getFieldOfGetter(method); + if (field1.equals(field2)) { + return method; + } + } + return null; + } + + private static boolean checkFieldLocation(PsiMethod method, PsiField field) { + return PsiResolveHelper.SERVICE.getInstance(method.getProject()).isAccessible(field, method, null) && + (!method.hasModifier(JvmModifier.STATIC) || field.hasModifier(JvmModifier.STATIC)) && + InheritanceUtil.isInheritorOrSelf(method.getContainingClass(), field.getContainingClass(), true); + } + +} diff --git a/java/java-psi-api/src/com/intellij/psi/util/PropertyUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PropertyUtilBase.java similarity index 74% rename from java/java-psi-api/src/com/intellij/psi/util/PropertyUtil.java rename to java/java-psi-api/src/com/intellij/psi/util/PropertyUtilBase.java index b8b1bee5cbab..6897c55fb217 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PropertyUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PropertyUtilBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -35,101 +35,14 @@ import org.jetbrains.annotations.Nullable; import java.beans.Introspector; import java.util.*; -/** - * @author Mike - */ -public class PropertyUtil { - @NonNls private static final String IS_PREFIX = "is"; +public class PropertyUtilBase { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PropertyUtil"); - private PropertyUtil() { - } - - @Contract("null -> false") - public static boolean isSimplePropertyGetter(@Nullable PsiMethod method) { - return hasGetterName(method) && method.getParameterList().getParametersCount() == 0; - } - - @SuppressWarnings("HardCodedStringLiteral") - public static boolean hasGetterName(final PsiMethod method) { - if (method == null) return false; - - if (method.isConstructor()) return false; - - String methodName = method.getName(); - int methodNameLength = methodName.length(); - if (methodName.startsWith("get") && methodNameLength > "get".length()) { - if (Character.isLowerCase(methodName.charAt("get".length())) - && (methodNameLength == "get".length() + 1 || Character.isLowerCase(methodName.charAt("get".length() + 1)))) { - return false; - } - PsiType returnType = method.getReturnType(); - if (returnType != null && PsiType.VOID.equals(returnType)) return false; - } - else if (methodName.startsWith(IS_PREFIX) && methodNameLength > IS_PREFIX.length()) { - if (Character.isLowerCase(methodName.charAt(IS_PREFIX.length())) - && (methodNameLength == IS_PREFIX.length() + 1 || Character.isLowerCase(methodName.charAt(IS_PREFIX.length() + 1)))) { - return false; - } - PsiType returnType = method.getReturnType(); - return isBoolean(returnType); - } - else { - return false; - } - return true; - } - - @SuppressWarnings("HardCodedStringLiteral") - public static boolean isSimplePropertySetter(@Nullable PsiMethod method) { - if (method == null) return false; - - if (method.isConstructor()) return false; - - String methodName = method.getName(); - - if (!(methodName.startsWith("set") && methodName.length() > "set".length())) return false; - if (Character.isLowerCase(methodName.charAt("set".length())) - && (methodName.length() == "set".length() + 1 || Character.isLowerCase(methodName.charAt("set".length() + 1)))) { - return false; - } - - if (method.getParameterList().getParametersCount() != 1) { - return false; - } - - final PsiType returnType = method.getReturnType(); - - if (returnType == null || PsiType.VOID.equals(returnType)) { - return true; - } - - return Comparing.equal(PsiUtil.resolveClassInType(TypeConversionUtil.erasure(returnType)), method.getContainingClass()); - } + @NonNls protected static final String IS_PREFIX = "is"; @Nullable - public static String getPropertyName(@NotNull PsiMethod method) { - if (isSimplePropertyGetter(method)) { - return getPropertyNameByGetter(method); - } - if (isSimplePropertySetter(method)) { - return getPropertyNameBySetter(method); - } - return null; - } - - @NotNull - public static String getPropertyNameByGetter(PsiMethod getterMethod) { - @NonNls String methodName = getterMethod.getName(); - if (methodName.startsWith("get")) return StringUtil.decapitalize(methodName.substring(3)); - if (methodName.startsWith("is")) return StringUtil.decapitalize(methodName.substring(2)); - return methodName; - } - - @NotNull - public static String getPropertyNameBySetter(@NotNull PsiMethod setterMethod) { - String methodName = setterMethod.getName(); - return Introspector.decapitalize(methodName.substring(3)); + public static String getPropertyName(@NonNls @NotNull String methodName) { + return StringUtil.getPropertyName(methodName); } @NotNull @@ -207,6 +120,92 @@ public class PropertyUtil { return ContainerUtil.concat(getGetters(psiClass, propertyName), getSetters(psiClass, propertyName)); } + @NotNull + public static String[] getReadableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) { + List result = new ArrayList<>(); + + PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods(); + + for (PsiMethod method : methods) { + if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue; + + if (isSimplePropertyGetter(method)) { + result.add(getPropertyName(method)); + } + } + + return ArrayUtil.toStringArray(result); + } + + @NotNull + public static String[] getWritableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) { + List result = new ArrayList<>(); + + PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods(); + + for (PsiMethod method : methods) { + if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue; + + if (isSimplePropertySetter(method)) { + result.add(getPropertyName(method)); + } + } + + return ArrayUtil.toStringArray(result); + } + + @Nullable + public static PsiType getPropertyType(final PsiMember member) { + if (member instanceof PsiField) { + return ((PsiField)member).getType(); + } + if (member instanceof PsiMethod) { + final PsiMethod psiMethod = (PsiMethod)member; + if (isSimplePropertyGetter(psiMethod)) { + return psiMethod.getReturnType(); + } + else if (isSimplePropertySetter(psiMethod)) { + return psiMethod.getParameterList().getParameters()[0].getType(); + } + } + return null; + } + + + @Nullable + public static PsiMethod findPropertySetter(PsiClass aClass, + @NotNull String propertyName, + boolean isStatic, + boolean checkSuperClasses) { + if (aClass == null) return null; + String setterName = suggestSetterName(propertyName); + PsiMethod[] methods = aClass.findMethodsByName(setterName, checkSuperClasses); + + for (PsiMethod method : methods) { + if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; + + if (isSimplePropertySetter(method)) { + if (getPropertyNameBySetter(method).equals(propertyName)) { + return method; + } + } + } + + return null; + } + + @Nullable + public static PsiField findPropertyField(PsiClass aClass, String propertyName, boolean isStatic) { + PsiField[] fields = aClass.getAllFields(); + + for (PsiField field : fields) { + if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; + if (propertyName.equals(suggestPropertyName(field))) return field; + } + + return null; + } + @Nullable public static PsiMethod findPropertyGetter(PsiClass aClass, @NotNull String propertyName, @@ -249,28 +248,6 @@ public class PropertyUtil { return isSimplePropertyGetter(method) || isSimplePropertySetter(method); } - @Nullable - public static PsiMethod findPropertySetter(PsiClass aClass, - @NotNull String propertyName, - boolean isStatic, - boolean checkSuperClasses) { - if (aClass == null) return null; - String setterName = suggestSetterName(propertyName); - PsiMethod[] methods = aClass.findMethodsByName(setterName, checkSuperClasses); - - for (PsiMethod method : methods) { - if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; - - if (isSimplePropertySetter(method)) { - if (getPropertyNameBySetter(method).equals(propertyName)) { - return method; - } - } - } - - return null; - } - @Nullable public static PsiMethod findPropertySetterWithType(String propertyName, boolean isStatic, PsiType type, Iterator methods) { while (methods.hasNext()) { @@ -287,21 +264,158 @@ public class PropertyUtil { return null; } - @Nullable - public static PsiField findPropertyField(PsiClass aClass, String propertyName, boolean isStatic) { - PsiField[] fields = aClass.getAllFields(); + public enum GetterFlavour { + BOOLEAN, + GENERIC, + NOT_A_GETTER + } - for (PsiField field : fields) { - if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; - if (propertyName.equals(suggestPropertyName(field))) return field; + @NotNull + public static GetterFlavour getMethodNameGetterFlavour(@NotNull String methodName) { + if (checkPrefix(methodName, "get")) { + return GetterFlavour.GENERIC; } + else if (checkPrefix(methodName, IS_PREFIX)) { + return GetterFlavour.BOOLEAN; + } + return GetterFlavour.NOT_A_GETTER; + } - return null; + + @Contract("null -> false") + public static boolean isSimplePropertyGetter(@Nullable PsiMethod method) { + return hasGetterName(method) && method.getParameterList().getParametersCount() == 0; + } + + + @SuppressWarnings("HardCodedStringLiteral") + public static boolean hasGetterName(final PsiMethod method) { + if (method == null) return false; + + if (method.isConstructor()) return false; + + String methodName = method.getName(); + GetterFlavour flavour = getMethodNameGetterFlavour(methodName); + switch (flavour) { + case GENERIC: + PsiType returnType = method.getReturnType(); + return returnType == null || !PsiType.VOID.equals(returnType); + case BOOLEAN: + return isBoolean(method.getReturnType()); + case NOT_A_GETTER: + default: + return false; + } + } + + + private static boolean isBoolean(@Nullable PsiType propertyType) { + return PsiType.BOOLEAN.equals(propertyType); + } + + + public static String suggestPropertyName(@NotNull PsiField field) { + return suggestPropertyName(field, field.getName()); + } + + @NotNull + public static String suggestPropertyName(@NotNull PsiField field, @NotNull String fieldName) { + JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(field.getProject()); + VariableKind kind = codeStyleManager.getVariableKind(field); + String name = codeStyleManager.variableNameToPropertyName(fieldName, kind); + if (!field.hasModifierProperty(PsiModifier.STATIC) && isBoolean(field.getType())) { + if (name.startsWith(IS_PREFIX) && name.length() > IS_PREFIX.length() && Character.isUpperCase(name.charAt(IS_PREFIX.length()))) { + name = Introspector.decapitalize(name.substring(IS_PREFIX.length())); + } + } + return name; + } + + public static String suggestGetterName(PsiField field) { + String propertyName = suggestPropertyName(field); + return suggestGetterName(propertyName, field.getType()); + } + + public static String suggestSetterName(PsiField field) { + String propertyName = suggestPropertyName(field); + return suggestSetterName(propertyName); } @Nullable - public static String getPropertyName(@NonNls @NotNull String methodName) { - return StringUtil.getPropertyName(methodName); + public static String getPropertyName(final PsiMember member) { + if (member instanceof PsiMethod) { + return getPropertyName((PsiMethod)member); + } + if (member instanceof PsiField) { + return member.getName(); + } + return null; + } + + + @SuppressWarnings("HardCodedStringLiteral") + public static boolean isSimplePropertySetter(@Nullable PsiMethod method) { + if (method == null) return false; + + if (method.isConstructor()) return false; + + String methodName = method.getName(); + + if (!isSetterName(methodName)) return false; + + if (method.getParameterList().getParametersCount() != 1) { + return false; + } + + final PsiType returnType = method.getReturnType(); + + if (returnType == null || PsiType.VOID.equals(returnType)) { + return true; + } + + return Comparing.equal(PsiUtil.resolveClassInType(TypeConversionUtil.erasure(returnType)), method.getContainingClass()); + } + + public static boolean isSetterName(@NotNull String methodName) { + return checkPrefix(methodName, "set"); + } + + @Nullable + public static String getPropertyName(@NotNull PsiMethod method) { + if (isSimplePropertyGetter(method)) { + return getPropertyNameByGetter(method); + } + if (isSimplePropertySetter(method)) { + return getPropertyNameBySetter(method); + } + return null; + } + + @NotNull + public static String getPropertyNameByGetter(PsiMethod getterMethod) { + @NonNls String methodName = getterMethod.getName(); + if (methodName.startsWith("get")) return StringUtil.decapitalize(methodName.substring(3)); + if (methodName.startsWith("is")) return StringUtil.decapitalize(methodName.substring(2)); + return methodName; + } + + @NotNull + public static String getPropertyNameBySetter(@NotNull PsiMethod setterMethod) { + String methodName = setterMethod.getName(); + return Introspector.decapitalize(methodName.substring(3)); + } + + private static boolean checkPrefix(@NotNull String methodName, @NotNull String prefix) { + boolean hasPrefix = methodName.startsWith(prefix) && methodName.length() > prefix.length(); + return hasPrefix && !(Character.isLowerCase(methodName.charAt(prefix.length())) && + (methodName.length() == prefix.length() + 1 || Character.isLowerCase(methodName.charAt(prefix.length() + 1)))); + } + + @NonNls + @NotNull + public static String[] suggestGetterNames(@NotNull String propertyName) { + final String str = StringUtil.capitalizeWithJavaBeanConvention(StringUtil.sanitizeJavaIdentifier(propertyName)); + return new String[]{IS_PREFIX + str, "get" + str}; } public static String suggestGetterName(@NonNls @NotNull String propertyName, @Nullable PsiType propertyType) { @@ -326,16 +440,6 @@ public class PropertyUtil { return name.toString(); } - private static boolean isBoolean(@Nullable PsiType propertyType) { - return PsiType.BOOLEAN.equals(propertyType); - } - - @NonNls - @NotNull - public static String[] suggestGetterNames(@NotNull String propertyName) { - final String str = StringUtil.capitalizeWithJavaBeanConvention(StringUtil.sanitizeJavaIdentifier(propertyName)); - return new String[]{IS_PREFIX + str, "get" + str}; - } public static String suggestSetterName(@NonNls @NotNull String propertyName) { return suggestSetterName(propertyName, "set"); @@ -351,40 +455,6 @@ public class PropertyUtil { return name.toString(); } - @NotNull - public static String[] getReadableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) { - List result = new ArrayList<>(); - - PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods(); - - for (PsiMethod method : methods) { - if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue; - - if (isSimplePropertyGetter(method)) { - result.add(getPropertyName(method)); - } - } - - return ArrayUtil.toStringArray(result); - } - - @NotNull - public static String[] getWritableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) { - List result = new ArrayList<>(); - - PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods(); - - for (PsiMethod method : methods) { - if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue; - - if (isSimplePropertySetter(method)) { - result.add(getPropertyName(method)); - } - } - - return ArrayUtil.toStringArray(result); - } - /** * Consider using {@link com.intellij.codeInsight.generation.GenerateMembersUtil#generateGetterPrototype(com.intellij.psi.PsiField)} or * {@link com.intellij.codeInsight.generation.GenerateMembersUtil#generateSimpleGetterPrototype(com.intellij.psi.PsiField)} @@ -498,61 +568,6 @@ public class PropertyUtil { NullableNotNullManager.getInstance(field.getProject()).copyNullableOrNotNullAnnotation(field, listOwner); } - public static String suggestPropertyName(@NotNull PsiField field) { - return suggestPropertyName(field, field.getName()); - } - - @NotNull - public static String suggestPropertyName(@NotNull PsiField field, @NotNull String fieldName) { - JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(field.getProject()); - VariableKind kind = codeStyleManager.getVariableKind(field); - String name = codeStyleManager.variableNameToPropertyName(fieldName, kind); - if (!field.hasModifierProperty(PsiModifier.STATIC) && isBoolean(field.getType())) { - if (name.startsWith(IS_PREFIX) && name.length() > IS_PREFIX.length() && Character.isUpperCase(name.charAt(IS_PREFIX.length()))) { - name = Introspector.decapitalize(name.substring(IS_PREFIX.length())); - } - } - return name; - } - - public static String suggestGetterName(PsiField field) { - String propertyName = suggestPropertyName(field); - return suggestGetterName(propertyName, field.getType()); - } - - public static String suggestSetterName(PsiField field) { - String propertyName = suggestPropertyName(field); - return suggestSetterName(propertyName); - } - - @Nullable - public static String getPropertyName(final PsiMember member) { - if (member instanceof PsiMethod) { - return getPropertyName((PsiMethod)member); - } - if (member instanceof PsiField) { - return member.getName(); - } - return null; - } - - @Nullable - public static PsiType getPropertyType(final PsiMember member) { - if (member instanceof PsiField) { - return ((PsiField)member).getType(); - } - if (member instanceof PsiMethod) { - final PsiMethod psiMethod = (PsiMethod)member; - if (isSimplePropertyGetter(psiMethod)) { - return psiMethod.getReturnType(); - } - else if (isSimplePropertySetter(psiMethod)) { - return psiMethod.getParameterList().getParameters()[0].getType(); - } - } - return null; - } - @Nullable public static PsiTypeElement getPropertyTypeElement(final PsiMember member) { if (member instanceof PsiField) { @@ -633,7 +648,7 @@ public class PropertyUtil { * returns the returned expression. Otherwise, returns null. * * @param method the method to check - * @return the return value, or null if it doesn't match the condotions. + * @return the return value, or null if it doesn't match the conditions. */ @Nullable public static PsiExpression getGetterReturnExpression(PsiMethod method) { @@ -655,201 +670,4 @@ public class PropertyUtil { return statement instanceof PsiReturnStatement ? ((PsiReturnStatement)statement).getReturnValue() : null; } - @Nullable - public static PsiField getFieldOfGetter(PsiMethod method) { - PsiField field = getSimplyReturnedField(method, getGetterReturnExpression(method)); - if (field != null) { - final PsiType returnType = method.getReturnType(); - if (returnType != null && field.getType().equalsToText(returnType.getCanonicalText())) { - return field; - } - } - return null; - } - - @Nullable - public static PsiField getSimplyReturnedField(PsiMethod method, @Nullable PsiExpression value) { - if (!(value instanceof PsiReferenceExpression)) { - return null; - } - - final PsiReferenceExpression reference = (PsiReferenceExpression)value; - if (hasSubstantialQualifier(reference)) { - return null; - } - - final PsiElement referent = reference.resolve(); - if (!(referent instanceof PsiField)) { - return null; - } - - final PsiField field = (PsiField)referent; - return InheritanceUtil.isInheritorOrSelf(method.getContainingClass(), field.getContainingClass(), true) ? field : null; - } - - private static boolean hasSubstantialQualifier(PsiReferenceExpression reference) { - final PsiExpression qualifier = reference.getQualifierExpression(); - if (qualifier == null) return false; - - if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) { - return false; - } - - if (qualifier instanceof PsiReferenceExpression) { - return !(((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass); - } - return true; - } - - public static boolean isSimpleGetter(PsiMethod method) { - return getFieldOfGetter(method) != null; - } - - @Nullable - public static PsiField getFieldOfSetter(PsiMethod method) { - if (method == null) { - return null; - } - final PsiParameterList parameterList = method.getParameterList(); - if (parameterList.getParametersCount() != 1) { - return null; - } - @NonNls final String name = method.getName(); - if (!name.startsWith("set")) { - return null; - } - if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { - return null; - } - final PsiCodeBlock body = method.getBody(); - if (body == null) { - return null; - } - final PsiStatement[] statements = body.getStatements(); - if (statements.length != 1) { - return null; - } - final PsiStatement statement = statements[0]; - if (!(statement instanceof PsiExpressionStatement)) { - return null; - } - final PsiExpressionStatement possibleAssignmentStatement = (PsiExpressionStatement)statement; - final PsiExpression possibleAssignment = possibleAssignmentStatement.getExpression(); - if (!(possibleAssignment instanceof PsiAssignmentExpression)) { - return null; - } - final PsiAssignmentExpression assignment = (PsiAssignmentExpression)possibleAssignment; - if (!JavaTokenType.EQ.equals(assignment.getOperationTokenType())) { - return null; - } - final PsiExpression lhs = assignment.getLExpression(); - if (!(lhs instanceof PsiReferenceExpression)) { - return null; - } - final PsiReferenceExpression reference = (PsiReferenceExpression)lhs; - final PsiExpression qualifier = reference.getQualifierExpression(); - if (qualifier instanceof PsiReferenceExpression) { - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; - final PsiElement target = referenceExpression.resolve(); - if (!(target instanceof PsiClass)) { - return null; - } - } - else if (qualifier != null && !(qualifier instanceof PsiThisExpression) && !(qualifier instanceof PsiSuperExpression)) { - return null; - } - final PsiElement referent = reference.resolve(); - if (referent == null) { - return null; - } - if (!(referent instanceof PsiField)) { - return null; - } - final PsiField field = (PsiField)referent; - final PsiClass fieldContainingClass = field.getContainingClass(); - final PsiClass methodContainingClass = method.getContainingClass(); - if (!InheritanceUtil.isInheritorOrSelf(methodContainingClass, fieldContainingClass, true)) { - return null; - } - final PsiExpression rhs = assignment.getRExpression(); - if (!(rhs instanceof PsiReferenceExpression)) { - return null; - } - final PsiReferenceExpression rReference = (PsiReferenceExpression)rhs; - final PsiExpression rQualifier = rReference.getQualifierExpression(); - if (rQualifier != null) { - return null; - } - final PsiElement rReferent = rReference.resolve(); - if (rReferent == null) { - return null; - } - if (!(rReferent instanceof PsiParameter)) { - return null; - } - final PsiType fieldType = field.getType(); - final PsiType parameterType = ((PsiVariable)rReferent).getType(); - if (fieldType.equalsToText(parameterType.getCanonicalText())) { - return field; - } - return null; - } - - public static boolean isSimpleSetter(PsiMethod method) { - return getFieldOfSetter(method) != null; - } - - @Nullable - public static PsiMethod getReversePropertyMethod(PsiMethod propertyMethod) { - if (propertyMethod == null) { - return null; - } - final PsiClass aClass = propertyMethod.getContainingClass(); - if (aClass == null) { - return null; - } - final String methodName = propertyMethod.getName(); - final String prefix; - if (methodName.startsWith("get")) { - prefix = "get"; - } - else if (methodName.startsWith(IS_PREFIX)) { - prefix = IS_PREFIX; - } - else if (methodName.startsWith("set")) { - prefix = "set"; - } - else { - return null; - } - final String name = methodName.substring(prefix.length()); - final PsiField field = prefix.equals("set") ? getFieldOfSetter(propertyMethod) : getFieldOfGetter(propertyMethod); - if (field == null) { - return null; - } - if (prefix.equals("set")) { - final PsiMethod result = findPropertyMethod(aClass, "get", name, field); - if (result != null) { - return result; - } - return findPropertyMethod(aClass, IS_PREFIX, name, field); - } - else { - return findPropertyMethod(aClass, "set", name, field); - } - } - - private static PsiMethod findPropertyMethod(@NotNull PsiClass aClass, - @NotNull String prefix, - @NotNull String propertyName, - @NotNull PsiField field1) { - final PsiMethod[] methods = aClass.findMethodsByName(prefix + propertyName, true); - for (PsiMethod method : methods) { - final PsiField field2 = prefix.equals("set") ? getFieldOfSetter(method) : getFieldOfGetter(method); - if (field1.equals(field2)) { - return method; - } - } - return null; - } } diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java index 303f8afdbdc7..0260d5ee7625 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java @@ -39,10 +39,7 @@ import com.intellij.psi.impl.source.tree.JavaDocElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.PropertyUtil; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.psi.util.PsiUtilCore; +import com.intellij.psi.util.*; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -108,7 +105,7 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem if (statements.length == 0) return false; PsiStatement statement = statements[0]; - if (PropertyUtil.isSimplePropertyGetter(method)) { + if (PropertyUtilBase.isSimplePropertyGetter(method)) { if (statement instanceof PsiReturnStatement) { return ((PsiReturnStatement)statement).getReturnValue() instanceof PsiReferenceExpression; } @@ -127,7 +124,7 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem return lhs instanceof PsiReferenceExpression && rhs instanceof PsiReferenceExpression && !((PsiReferenceExpression)rhs).isQualified() && - PropertyUtil.isSimplePropertySetter(method); // last check because it can perform long return type resolve + PropertyUtilBase.isSimplePropertySetter(method); // last check because it can perform long return type resolve } } return false; diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/highlighting/JavaReadWriteAccessDetector.java b/java/java-psi-impl/src/com/intellij/codeInsight/highlighting/JavaReadWriteAccessDetector.java index 3c93c6a1b1b3..bfac72c3d078 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/highlighting/JavaReadWriteAccessDetector.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/highlighting/JavaReadWriteAccessDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -16,7 +16,7 @@ package com.intellij.codeInsight.highlighting; import com.intellij.psi.*; -import com.intellij.psi.util.PropertyUtil; +import com.intellij.psi.util.PropertyUtilBase; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; @@ -61,7 +61,7 @@ public class JavaReadWriteAccessDetector extends ReadWriteAccessDetector { if (!writeAccess && expr instanceof PsiReferenceExpression) { //when searching usages of fields, should show all found setters as a "only write usage" PsiElement actualReferee = ((PsiReferenceExpression) expr).resolve(); - if (actualReferee instanceof PsiMethod && PropertyUtil.isSimplePropertySetter((PsiMethod)actualReferee)) { + if (actualReferee instanceof PsiMethod && PropertyUtilBase.isSimplePropertySetter((PsiMethod)actualReferee)) { writeAccess = true; readAccess = false; } diff --git a/java/java-psi-impl/src/com/intellij/psi/stub/JavaStubImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/stub/JavaStubImplUtil.java new file mode 100644 index 000000000000..9b997c580403 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/stub/JavaStubImplUtil.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2017 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.stub; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.impl.source.PsiFileImpl; +import com.intellij.psi.impl.source.PsiMethodImpl; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.stubs.StubTree; +import com.intellij.util.ArrayUtil; + +public class JavaStubImplUtil { + public static int getMethodStubIndex(PsiMethod method) { + if (!(method instanceof PsiMethodImpl)) return -1; + PsiFileImpl file = (PsiFileImpl)method.getContainingFile(); + if (file.getElementTypeForStubBuilder() == null) return -1; + StubTree stubTree = file.getStubTree(); + if (stubTree == null) { + stubTree = file.calcStubTree(); + } + + PsiElement[] stubs = stubTree + .getPlainList() + .stream() + .filter(e -> e.getStubType() == JavaElementType.METHOD) + .map(StubElement::getPsi) + .toArray(PsiElement[]::new); + return ArrayUtil.indexOf(stubs, method); + } +} diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PropertyGroup.java b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PropertyGroup.java index c29c04935211..d69c21bf8804 100644 --- a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PropertyGroup.java +++ b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/PropertyGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -28,7 +28,7 @@ import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; import com.intellij.psi.*; -import com.intellij.psi.util.PropertyUtil; +import com.intellij.psi.util.PropertyUtilBase; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -65,7 +65,7 @@ public class PropertyGroup implements Group, ColoredItemPresentation, AccessLeve public static PropertyGroup createOn(PsiElement object, final TreeElement treeElement) { if (object instanceof PsiField) { PsiField field = (PsiField)object; - PropertyGroup group = new PropertyGroup(PropertyUtil.suggestPropertyName(field), field.getType(), + PropertyGroup group = new PropertyGroup(PropertyUtilBase.suggestPropertyName(field), field.getType(), field.hasModifierProperty(PsiModifier.STATIC), object.getProject()); group.setField(field); group.myChildren.add(treeElement); @@ -73,16 +73,16 @@ public class PropertyGroup implements Group, ColoredItemPresentation, AccessLeve } else if (object instanceof PsiMethod) { PsiMethod method = (PsiMethod)object; - if (PropertyUtil.isSimplePropertyGetter(method)) { - PropertyGroup group = new PropertyGroup(PropertyUtil.getPropertyNameByGetter(method), method.getReturnType(), + if (PropertyUtilBase.isSimplePropertyGetter(method)) { + PropertyGroup group = new PropertyGroup(PropertyUtilBase.getPropertyNameByGetter(method), method.getReturnType(), method.hasModifierProperty(PsiModifier.STATIC), object.getProject()); group.setGetter(method); group.myChildren.add(treeElement); return group; } - else if (PropertyUtil.isSimplePropertySetter(method)) { + else if (PropertyUtilBase.isSimplePropertySetter(method)) { PropertyGroup group = - new PropertyGroup(PropertyUtil.getPropertyNameBySetter(method), method.getParameterList().getParameters()[0].getType(), + new PropertyGroup(PropertyUtilBase.getPropertyNameBySetter(method), method.getParameterList().getParameters()[0].getType(), method.hasModifierProperty(PsiModifier.STATIC), object.getProject()); group.setSetter(method); group.myChildren.add(treeElement); diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testPreferGetterToMethodChain/TestCompletion.java b/java/java-tests/testData/codeInsight/completion/methodChains/testPreferGetterToMethodChain/TestCompletion.java index d0de47283a60..151b2871377e 100644 --- a/java/java-tests/testData/codeInsight/completion/methodChains/testPreferGetterToMethodChain/TestCompletion.java +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testPreferGetterToMethodChain/TestCompletion.java @@ -13,8 +13,6 @@ class Editor { } class Main { - - void stats() { Editor e = } diff --git a/java/java-tests/testSrc/com/intellij/util/JavaPropertyDetectionTest.kt b/java/java-tests/testSrc/com/intellij/util/JavaPropertyDetectionTest.kt new file mode 100644 index 000000000000..9c0730153e17 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/util/JavaPropertyDetectionTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2000-2017 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.util + +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.PropertyMemberType +import com.intellij.psi.util.PropertyUtil +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import kotlin.test.assertNotEquals + +class JavaPropertyDetectionTest : LightCodeInsightFixtureTestCase() { + + // getter field test + + fun testFieldRef() { + assertPropertyMember("""class Some {private String name;public String getName() { return name; }}""", PropertyMemberType.GETTER) + } + + fun testUnresolvedRef() { + assertNotPropertyMember("class Some { public String getName() { return name; }}", PropertyMemberType.GETTER) + } + + fun testUnresolvedRef2() { + assertNotPropertyMember("""class Some {private String name;public static String getName() { return name; }}""", PropertyMemberType.GETTER) + } + + fun testSuperFieldRef() { + doTest("""class Some extends SomeBase {public String getName() { return name; }}class SomeBase { private String name;}""", PropertyMemberType.GETTER, + false) + } + + fun testSuperFieldRef2() { + assertPropertyMember("""class Some extends SomeBase + |{public String getName() { return name; }}class SomeBase { protected String name;}""".trimMargin(), PropertyMemberType.GETTER) + } + + fun testInconsistentType() { + assertNotPropertyMember("""class Some + |{String name;public long getName() { return name;}}""".trimMargin(), PropertyMemberType.GETTER) + + } + + fun testWithSuperKeyWord() { + assertPropertyMember("""import java.util.List; class Some extends SomeBase + |{ List getList() { return Some.super.list;}} class SomeBase {List list;} """.trimMargin(), PropertyMemberType.GETTER) + } + + fun testRedundantParenthesises() { + assertNotPropertyMember("""import java.util.List; + class Some { + Some a; + List list; + List getList() {return (a).list;} + }""", PropertyMemberType.GETTER) + } + + + + // setter field test + + fun testSimpleSetter() { + assertPropertyMember("""class Some { + |private String name; public void setName(String name) { this.name = name; }}""".trimMargin(), PropertyMemberType.SETTER) + } + + fun testFieldNotResolved() { + assertNotPropertyMember("""class Some { + | public void setName(String name) { this.name = name; }}""".trimMargin(), PropertyMemberType.SETTER) + } + + fun testInvalidExpression() { + assertNotPropertyMember("""class Some { + |public void setName(String name) { = name; }}""".trimMargin(), PropertyMemberType.SETTER) + } + + fun testInvalidReference() { + assertNotPropertyMember("""class Some { + |public void setName(String name) { name = name; }}""".trimMargin(), PropertyMemberType.SETTER) + } + + fun testSuperClassField() { + assertPropertyMember( """class Some extends SomeBase { + | public void setName(String name) { this.name = name; }} + | class SomeBase {protected String name;}""".trimMargin(), PropertyMemberType.SETTER) + } + + fun testRefWithSuperKeyword() { + assertPropertyMember( """class Some extends SomeBase { + |public void setName(String name) { Some.super.name = name; }} + | class SomeBase {protected String name;}""".trimMargin(), PropertyMemberType.SETTER) + } + + fun testOuterClassFieldSetter() { + assertNotPropertyMember("""class Outer {String name; class Some { + |public void setName(String name) { this.name = name; }}}""".trimMargin(), PropertyMemberType.SETTER) + } + + private fun assertPropertyMember(text: String, memberType: PropertyMemberType) { + doTest(text, memberType, true) + } + + private fun assertNotPropertyMember(text: String, memberType: PropertyMemberType) { + doTest(text, memberType, false) + } + + private fun doTest(text: String, memberType: PropertyMemberType, expectedDecision: Boolean) { + assertNotEquals(PropertyMemberType.FIELD, memberType) + myFixture.configureByText(JavaFileType.INSTANCE, text) + val method = PsiTreeUtil.getNonStrictParentOfType(myFixture.elementAtCaret, PsiMethod::class.java) + assertNotNull(method) + //use index + assertEquals(expectedDecision, if (memberType == PropertyMemberType.GETTER) PropertyUtil.isSimpleGetter(method) else PropertyUtil.isSimpleSetter(method)) + //use ast + assertEquals(expectedDecision, if (memberType == PropertyMemberType.GETTER) PropertyUtil.isSimpleGetter(method, false) else PropertyUtil.isSimpleSetter(method, false)) + } +} diff --git a/java/openapi/src/com/intellij/util/xml/converters/AbstractMemberResolveConverter.java b/java/openapi/src/com/intellij/util/xml/converters/AbstractMemberResolveConverter.java index 23b16bb6499b..d8990bb03f5e 100644 --- a/java/openapi/src/com/intellij/util/xml/converters/AbstractMemberResolveConverter.java +++ b/java/openapi/src/com/intellij/util/xml/converters/AbstractMemberResolveConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -24,7 +24,7 @@ import com.intellij.ide.TypePresentationService; import com.intellij.psi.*; import com.intellij.psi.search.ProjectScope; import com.intellij.psi.util.PropertyMemberType; -import com.intellij.psi.util.PropertyUtil; +import com.intellij.psi.util.PropertyUtilBase; import com.intellij.util.xml.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -67,11 +67,11 @@ public abstract class AbstractMemberResolveConverter extends ResolvingConverter< if (field != null) return field; break; case GETTER: - final PsiMethod getter = PropertyUtil.findPropertyGetter(psiClass, getPropertyName(s, context), false, isLookDeep()); + final PsiMethod getter = PropertyUtilBase.findPropertyGetter(psiClass, getPropertyName(s, context), false, isLookDeep()); if (getter != null) return getter; break; case SETTER: - final PsiMethod setter = PropertyUtil.findPropertySetter(psiClass, getPropertyName(s, context), false, isLookDeep()); + final PsiMethod setter = PropertyUtilBase.findPropertySetter(psiClass, getPropertyName(s, context), false, isLookDeep()); if (setter != null) return setter; break; } @@ -110,7 +110,7 @@ public abstract class AbstractMemberResolveConverter extends ResolvingConverter< } protected boolean methodSuits(final PsiMethod psiMethod) { - return !psiMethod.isConstructor() && !psiMethod.hasModifierProperty(PsiModifier.STATIC) && PropertyUtil.getPropertyName(psiMethod) != null; + return !psiMethod.isConstructor() && !psiMethod.hasModifierProperty(PsiModifier.STATIC) && PropertyUtilBase.getPropertyName(psiMethod) != null; } protected boolean fieldSuits(final PsiField psiField) { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocFieldReferenceImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocFieldReferenceImpl.java index 69d5691d0472..71669d52039e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocFieldReferenceImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocFieldReferenceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocMethodReferenceImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocMethodReferenceImpl.java index 34f53c04ef58..7e38a596ec05 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocMethodReferenceImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/groovydoc/psi/impl/GrDocMethodReferenceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. diff --git a/python/pluginJava/com/jetbrains/python/psi/impl/PyConstructorArgumentCompletionContributor.java b/python/pluginJava/com/jetbrains/python/psi/impl/PyConstructorArgumentCompletionContributor.java index d3b28a436649..4e9e27cd3158 100644 --- a/python/pluginJava/com/jetbrains/python/psi/impl/PyConstructorArgumentCompletionContributor.java +++ b/python/pluginJava/com/jetbrains/python/psi/impl/PyConstructorArgumentCompletionContributor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -19,7 +19,7 @@ import com.intellij.codeInsight.completion.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; -import com.intellij.psi.util.PropertyUtil; +import com.intellij.psi.util.PropertyUtilBase; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.psi.*; @@ -62,8 +62,8 @@ public class PyConstructorArgumentCompletionContributor extends CompletionContri // see PyJavaType.init() in Jython source code for matching logic for (PsiMethod method : containingClass.getAllMethods()) { final Project project = containingClass.getProject(); - if (PropertyUtil.isSimplePropertySetter(method)) { - final String propName = PropertyUtil.getPropertyName(method); + if (PropertyUtilBase.isSimplePropertySetter(method)) { + final String propName = PropertyUtilBase.getPropertyName(method); result.addElement(PyUtil.createNamedParameterLookup(propName, project)); } else if (method.getName().startsWith("add") && method.getName().endsWith("Listener") && PsiType.VOID.equals(method.getReturnType())) { diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index c8dda0b73398..e4b585a09022 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -1769,6 +1769,8 @@ + + @@ -1993,8 +1995,6 @@ - -