diff --git a/platform/util/src/com/intellij/openapi/util/RecursionManager.java b/platform/util/src/com/intellij/openapi/util/RecursionManager.java index fe45e3436ff2..021ab7912671 100644 --- a/platform/util/src/com/intellij/openapi/util/RecursionManager.java +++ b/platform/util/src/com/intellij/openapi/util/RecursionManager.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2013 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-2018 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.openapi.util; import com.intellij.openapi.Disposable; @@ -361,8 +347,8 @@ public class RecursionManager { } @TestOnly - public static void assertOnRecursionPrevention(@NotNull Disposable parentDisposable) { - ourAssertOnPrevention = true; + public static void assertOnRecursionPrevention(@NotNull Disposable parentDisposable, boolean enableAssert) { + ourAssertOnPrevention = enableAssert; Disposer.register(parentDisposable, new Disposable() { @Override public void dispose() { @@ -372,4 +358,8 @@ public class RecursionManager { }); } + @TestOnly + public static void assertOnRecursionPrevention(@NotNull Disposable parentDisposable) { + assertOnRecursionPrevention(parentDisposable, true); + } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrNewExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrNewExpressionImpl.java index 4fcc85f90629..c6830aaa0b32 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrNewExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrNewExpressionImpl.java @@ -33,6 +33,8 @@ import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import java.util.ArrayList; import java.util.List; +import static org.jetbrains.plugins.groovy.lang.resolve.processors.inference.InferenceKt.buildTopLevelArgumentTypes; + /** * @author ilyas */ @@ -196,11 +198,9 @@ public class GrNewExpressionImpl extends GrCallExpressionImpl implements GrNewEx } } - PsiType[] types = PsiUtil.getArgumentTypes(ref, true); + PsiType[] types = buildTopLevelArgumentTypes(ref); - if (types != null) { - types = GrInnerClassConstructorUtil.addEnclosingArgIfNeeded(types, this, (PsiClass)classCandidate.getElement()); - } + types = GrInnerClassConstructorUtil.addEnclosingArgIfNeeded(types, this, (PsiClass)classCandidate.getElement()); return PsiUtil.getConstructorCandidates(ref, classCandidate, types); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/DefaultDelegatesToProvider.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/DefaultDelegatesToProvider.java index 16782b81511d..9c3690dd8ff0 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/DefaultDelegatesToProvider.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/delegatesTo/DefaultDelegatesToProvider.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-2018 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 org.jetbrains.plugins.groovy.lang.resolve.delegatesTo; import com.intellij.openapi.util.text.StringUtil; @@ -23,6 +9,7 @@ import com.intellij.psi.util.TypeConversionUtil; import groovy.lang.Closure; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature; import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature; @@ -212,6 +199,9 @@ public class DefaultDelegatesToProvider implements GrDelegatesToProvider { PsiElement context = FromStringHintProcessor.createContext((PsiMethod)element); PsiType type = JavaPsiFacade.getElementFactory(context.getProject()).createTypeFromText(typeValue, context); + if (result instanceof GroovyMethodResult) { + return ((GroovyMethodResult)result).getSubstitutor(false).substitute(type); + } return result.getSubstitutor().substitute(type); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java index 101ebdc3de61..e7dfaec0cf8f 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessor.java @@ -9,26 +9,20 @@ import com.intellij.psi.scope.ElementClassHint; import com.intellij.psi.scope.JavaScopeProcessorEvent; import com.intellij.psi.scope.NameHint; import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; -import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyMethodResultImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.GrResolverProcessor; @@ -38,13 +32,15 @@ import org.jetbrains.plugins.groovy.lang.resolve.processors.inference.GroovyInfe import org.jetbrains.plugins.groovy.lang.resolve.processors.inference.GroovyInferenceSessionBuilder; import org.jetbrains.plugins.groovy.lang.resolve.processors.inference.MethodCandidate; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; import static org.jetbrains.plugins.groovy.lang.psi.util.PropertyUtilKt.isPropertyName; import static org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil.isAccessible; import static org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil.isStaticsOK; -import static org.jetbrains.plugins.groovy.lang.resolve.processors.inference.InferenceKt.getTopLevelTypeCached; +import static org.jetbrains.plugins.groovy.lang.resolve.processors.inference.InferenceKt.*; public abstract class GroovyResolverProcessor implements PsiScopeProcessor, ElementClassHint, NameHint, DynamicMembersHint { @@ -74,7 +70,7 @@ public abstract class GroovyResolverProcessor implements PsiScopeProcessor, Elem myTypeArguments = ref.getTypeArguments(); if (kinds.contains(GroovyResolveKind.METHOD) || myIsLValue) { - myArgumentTypes = NullableLazyValue.createValue(() -> buildArgumentTypes()); + myArgumentTypes = NullableLazyValue.createValue(() -> buildTopLevelArgumentTypes(myRef)); } else { myArgumentTypes = NullableLazyValue.createValue(() -> null); @@ -196,7 +192,7 @@ public abstract class GroovyResolverProcessor implements PsiScopeProcessor, Elem final PsiSubstitutor siteSubstitutor = updateSubst(myRef.getTypeArguments(), method.getTypeParameters(), substitutor); GrExpression qualifierExpression = myRef.getQualifierExpression(); Argument qualifierConstraint = buildQualifier(state, qualifierExpression); - List argumentConstraints = buildArguments(); + List argumentConstraints = buildArguments(myRef); if (method instanceof GrGdkMethod) { ArrayList arguments = new ArrayList<>(); @@ -225,46 +221,8 @@ public abstract class GroovyResolverProcessor implements PsiScopeProcessor, Elem return new Argument(resolvedThis != null ? resolvedThis : type, null); } - @NotNull - private List buildArguments() { - PsiElement parent = myRef.getParent(); - if (parent instanceof GrCall) { - List result = new ArrayList<>(); - GrCall call = (GrCall)parent; - GrNamedArgument[] namedArgs = call.getNamedArguments(); - GrExpression[] expressions = call.getExpressionArguments(); - GrClosableBlock[] closures = call.getClosureArguments(); - if (namedArgs.length > 0) { - GrNamedArgument context = namedArgs[0]; - result.add(new Argument(GrMapType.createFromNamedArgs(context, namedArgs), null)); - } - GrExpression[] argExp = ArrayUtil.mergeArrays(expressions, closures); - - List constraints = Arrays.stream(argExp).map((exp) -> new Argument(null, exp)).collect(Collectors.toList()); - result.addAll(constraints); - return result; - } - - PsiType[] argumentTypes = PsiUtil.getArgumentTypes(myRef, false, null); - if (argumentTypes == null) return Collections.emptyList(); - return Arrays.stream(argumentTypes).map((t) -> new Argument(t, null)).collect(Collectors.toList()); - } - - @NotNull - private PsiType[] buildArgumentTypes() { - return buildArguments().stream().map(it -> { - if (it.getExpression() != null) { - return getTopLevelTypeCached(it.getExpression()); - } else { - if (it.getType() instanceof GrMapType) { - return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_UTIL_MAP, myRef); - } - return it.getType(); - } - }).toArray(PsiType[]::new); - } @NotNull private static PsiSubstitutor updateSubst(@NotNull PsiType[] arguments, diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/GroovyInferenceSessionBuilder.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/GroovyInferenceSessionBuilder.kt index ae26eb73ff9f..872ac21397c5 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/GroovyInferenceSessionBuilder.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/GroovyInferenceSessionBuilder.kt @@ -10,6 +10,7 @@ import com.intellij.psi.util.TypeConversionUtil import com.intellij.util.ArrayUtil import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils import org.jetbrains.plugins.groovy.lang.psi.GroovyFile +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable @@ -21,7 +22,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.* import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod -import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil class GroovyInferenceSessionBuilder(val ref: GrReferenceExpression, val candidate: MethodCandidate) { @@ -111,18 +111,19 @@ class GroovyInferenceSessionBuilder(val ref: GrReferenceExpression, val candidat else if (parent is GrAssignmentExpression && call == parent.rValue) { val lValue = PsiUtil.skipParentheses(parent.lValue, false) return if (lValue is GrExpression && lValue !is GrIndexProperty) lValue.nominalType else null - } else if (parent is GrArgumentList && gparent is GrNewExpression) { // TODO: fix with moving constructor resolve to new API - val resolveResult = gparent.advancedResolve() - val parameters = GrClosureSignatureUtil.mapArgumentsToParameters( - resolveResult, - gparent, - false, - true, - gparent.namedArguments, - gparent.expressionArguments, - gparent.closureArguments) - return parameters?.get(call)?.second - } else if (parent is GrVariable) { + } + else if (parent is GrArgumentList && gparent is GrNewExpression) { // TODO: fix with moving constructor resolve to new API + with(gparent) { + val resolveResult = advancedResolve() + if (resolveResult is GroovyMethodResult) { + val methodCandidate = MethodCandidate(resolveResult.element, resolveResult.getSubstitutor(false), null, + buildArguments(referenceElement!!), call) + return methodCandidate.argumentMapping[Argument(null, call)]?.second + } + } + return null + } + else if (parent is GrVariable) { return parent.declaredType } return null diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/inference.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/inference.kt index 418c98360702..55222b772768 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/inference.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/inference/inference.kt @@ -1,25 +1,29 @@ // Copyright 2000-2018 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 org.jetbrains.plugins.groovy.lang.resolve.processors.inference +import com.intellij.psi.CommonClassNames +import com.intellij.psi.PsiElement import com.intellij.psi.PsiType +import com.intellij.util.ArrayUtil import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant +import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil +import java.util.* fun getTopLevelType(expression: GrExpression): PsiType? { if (expression is GrMethodCall) { val resolved = expression.advancedResolve() (resolved as? GroovyMethodResult)?.candidate?.let { - val session = GroovyInferenceSessionBuilder(expression.invokedExpression as GrReferenceExpression, it) - .resolveMode(false) - .build() - + val session = GroovyInferenceSessionBuilder(expression.invokedExpression as GrReferenceExpression, it).build() return session.inferSubst().substitute(PsiUtil.getSmartReturnType(it.method)) } } @@ -33,4 +37,42 @@ fun getTopLevelType(expression: GrExpression): PsiType? { fun getTopLevelTypeCached(expression: GrExpression): PsiType? { return GroovyPsiManager.getInstance(expression.project).getTopLevelType(expression) +} + +fun buildArguments(place: PsiElement): List { + val parent = place as? GrEnumConstant ?: place.parent + if (parent is GrCall) { + val result = ArrayList() + val namedArgs = parent.namedArguments + val expressions = parent.expressionArguments + val closures = parent.closureArguments + + if (namedArgs.isNotEmpty()) { + val context = namedArgs[0] + result.add(Argument(GrMapType.createFromNamedArgs(context, namedArgs), null)) + } + + val argExp = ArrayUtil.mergeArrays(expressions, closures) + result.addAll(argExp.map { exp -> Argument(null, exp) }) + return result + } + + val argumentTypes = PsiUtil.getArgumentTypes(place, false, null) ?: return emptyList() + return argumentTypes.map { t -> Argument(t, null) } +} + + +fun buildTopLevelArgumentTypes(place: PsiElement): Array { + return buildArguments(place).map { (type, expression) -> + if (expression != null) { + getTopLevelTypeCached(expression) + } + else { + if (type is GrMapType) { + TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_UTIL_MAP, place) + } else { + type + } + } + }.toTypedArray() } \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy index 5b34864e9195..d8d6f9893a6a 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyStressPerformanceTest.groovy @@ -238,6 +238,31 @@ class SomeClass { measureHighlighting(text, 14_000) } + void "test constructor call's"() { + def text = """ +class Cl { + + Cl(Map a, Condition con, String s) { + } + + interface Condition {} + + static Condition alwaysFalse() { + return (Condition)null + } + + + static m() { + ${'''new Cl(alwaysFalse(), name: 1, m: 2, new Object().toString(), sad: 12)\n'''*100} + } +} +""" + IdeaTestUtil.startPerformanceTest(getTestName(false), 750, configureAndHighlight(text)) + .attempts(20) + .usesAllCPUCores() + .assertTiming() + } + void "test infer only the variable types that are needed"() { addGdsl '''contribute(currentType(String.name)) { println 'sleeping' diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrLatestHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrLatestHighlightingTest.groovy index 78892ec65d08..c8dade18382e 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrLatestHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/GrLatestHighlightingTest.groovy @@ -2,6 +2,7 @@ package org.jetbrains.plugins.groovy.lang.highlighting import com.intellij.codeInspection.InspectionProfileEntry +import com.intellij.openapi.util.RecursionManager import com.intellij.testFramework.LightProjectDescriptor import org.jetbrains.annotations.NotNull import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor @@ -23,6 +24,12 @@ class GrLatestHighlightingTest extends GrHighlightingTestBase { new MissingReturnInspection()] } + @Override + void setUp() throws Exception { + super.setUp() + RecursionManager.assertOnRecursionPrevention(myFixture.testRootDisposable) + } + void 'test IDEA-184690'() { testHighlighting '''\ import groovy.transform.CompileStatic @@ -383,6 +390,7 @@ class GoodCodeRed { } void 'test recursive generics'() { + RecursionManager.assertOnRecursionPrevention(myFixture.testRootDisposable, false) testHighlighting ''' import groovy.transform.CompileStatic @@ -435,6 +443,32 @@ class Cl { ''' } + void 'test constructor parameter'() { + testHighlighting ''' +import groovy.transform.CompileStatic + +@CompileStatic +class Cl { + + Cl(Map a, Condition con, String s) { + } + + interface Condition {} + + static Condition alwaysFalse() { + return (Condition)null + } + + + static m() { + new Cl(alwaysFalse(), name: 1, m: 2, new Object().toString(), sad: 12) + } +} +''' + } + + + void 'test call without reference'() { testHighlighting ''' class E {