diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java index e4f3b792af90..8f5f222d365e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/ChainSearcher.java @@ -1,18 +1,4 @@ -/* - * 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.compiler.chainsSearch; import com.intellij.compiler.backwardRefs.CompilerReferenceServiceEx; @@ -153,17 +139,20 @@ public class ChainSearcher { } } - private static boolean addChainIfTerminal(OperationChain currentChain, List result, int pathMaximalLength, + private static boolean addChainIfTerminal(OperationChain currentChain, List result, + int pathMaximalLength, ChainCompletionContext context) { RefChainOperation signature = currentChain.getHeadMethodCall(); if (signature == null) return false; RefChainOperation head = currentChain.getHead(); - if (((MethodCall)signature).isStatic() || - context.hasQualifier(context.resolvePsiClass(head.getQualifierDef())) || - currentChain.length() >= pathMaximalLength) { + if (((MethodCall)signature).isStatic() || context.hasQualifier(context.resolvePsiClass(head.getQualifierDef()))) { addChainIfNotPresent(currentChain, result); return true; } + else if (currentChain.length() >= pathMaximalLength) { + addChainIfNotPresent(currentChain.getHead() == signature ? currentChain : currentChain.removeHeadCast(context), result); + return true; + } return false; } diff --git a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/OperationChain.java b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/OperationChain.java index 837468503227..6b63b900bc8d 100644 --- a/java/compiler/impl/src/com/intellij/compiler/chainsSearch/OperationChain.java +++ b/java/compiler/impl/src/com/intellij/compiler/chainsSearch/OperationChain.java @@ -1,21 +1,8 @@ -/* - * 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.compiler.chainsSearch; import com.intellij.compiler.chainsSearch.context.ChainCompletionContext; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; @@ -27,11 +14,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; public class OperationChain { + private static final Logger LOG = Logger.getInstance(OperationChain.class); + @NotNull private final ChainOperation[] myReverseOperations; private final RefChainOperation myHeadOperation; @@ -142,6 +132,18 @@ public class OperationChain { return new OperationChain(head.getQualifierClass(), newReverseOperations, head.getHead(), myHeadMethodCall, getChainWeight()); } + @NotNull + OperationChain removeHeadCast(@NotNull ChainCompletionContext context) { + LOG.assertTrue(getHead() instanceof TypeCast); + ChainOperation[] newReverseOperations = new ChainOperation[length() - 1]; + System.arraycopy(myReverseOperations, 0, newReverseOperations, 0, length() - 1); + return new OperationChain(Objects.requireNonNull(context.resolvePsiClass(myHeadMethodCall.getQualifierDef())), + newReverseOperations, + myHeadMethodCall, + myHeadMethodCall, + getChainWeight()); + } + @Override public String toString() { ChainOperation[] path = getPath(); diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testLongChainWithCast/TestCompletion.java b/java/java-tests/testData/codeInsight/completion/methodChains/testLongChainWithCast/TestCompletion.java new file mode 100644 index 000000000000..30bff3387150 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testLongChainWithCast/TestCompletion.java @@ -0,0 +1,31 @@ +class Main { + void m() { + D d = + } +} + +class A { + +} + +class AImpl extends A { + B getB() { + return new B(); + } +} + +class B { + C getC() { + return new C(); + } +} + +class C { + D getD() { + return new D(); + } +} + +class D { + +} diff --git a/java/java-tests/testData/codeInsight/completion/methodChains/testLongChainWithCast/TestIndex.java b/java/java-tests/testData/codeInsight/completion/methodChains/testLongChainWithCast/TestIndex.java new file mode 100644 index 000000000000..11ed055a821d --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/methodChains/testLongChainWithCast/TestIndex.java @@ -0,0 +1,44 @@ +class Main { + void m(A a) { + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + ((AImpl) a).getB().getC().getD(); + } +} + +class A { + +} + +class AImpl extends A { + B getB() { + return new B(); + } +} + +class B { + C getC() { + return new C(); + } +} + +class C { + D getD() { + return new D(); + } +} + +class D { + +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java index dd4dfd51da3b..8b3c7d56645a 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/MethodChainsCompletionTest.java @@ -1,18 +1,4 @@ -/* - * 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.java.codeInsight.completion; import com.intellij.JavaTestUtil; @@ -21,6 +7,7 @@ import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.compiler.chainsSearch.ChainRelevance; +import com.intellij.compiler.chainsSearch.ChainSearchMagicConstants; import com.intellij.compiler.chainsSearch.completion.MethodChainCompletionContributor; import com.intellij.compiler.chainsSearch.completion.lookup.JavaRelevantChainLookupElement; import com.intellij.openapi.roots.LanguageLevelProjectExtension; @@ -262,6 +249,11 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest { assertEquals("(InspectionManagerEx)getInstance", element.toString()); } + public void testLongChainWithCast() { + assertEquals("the test should be modified when MAX_CHAIN_SIZE is changed", 4, ChainSearchMagicConstants.MAX_CHAIN_SIZE); + assertEquals("a.getB().getC().getD", assertOneElement(doCompletion()).toString()); + } + public void assertAdvisorLookupElementEquals(String lookupText, int unreachableParametersCount, int chainSize,