diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index dbf606cb014a..0677ce7e72cf 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -540,6 +540,9 @@ + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java index bb8e0c33488e..8804c7459000 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java @@ -209,11 +209,15 @@ public class GroovyPsiManager { } } if (!type.isValid()) { - LOG.error("Type is invalid: " + type + "; element: " + element + " of class " + element.getClass()); + error(element, type); } return UNKNOWN_TYPE == type ? null : type; } + private static void error(PsiElement element, PsiType type) { + LOG.error("Type is invalid: " + type + "; element: " + element + " of class " + element.getClass()); + } + @Nullable public GrTypeDefinition getArrayClass(@NotNull PsiType type) { final String typeText = type.getCanonicalText(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java index dac07251e6c7..d77f5ec59d5c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.blocks; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; @@ -49,6 +50,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt import org.jetbrains.plugins.groovy.lang.psi.impl.statements.params.GrParameterListImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.ClosureSyntheticParameter; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightVariable; +import org.jetbrains.plugins.groovy.lang.resolve.KnownRecursionManager; import org.jetbrains.plugins.groovy.lang.resolve.MethodTypeInferencer; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; @@ -335,7 +337,12 @@ public class GrClosableBlockImpl extends GrBlockImpl implements GrClosableBlock @Nullable public PsiType getReturnType() { - return TypeInferenceHelper.getCurrentContext().getExpressionType(this, ourTypesCalculator); + return KnownRecursionManager.getInstance().run(this, new Computable() { + @Override + public PsiType compute() { + return TypeInferenceHelper.getCurrentContext().getExpressionType(GrClosableBlockImpl.this, ourTypesCalculator); + } + }, getAllParameters()); } @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java index f74185bbd5af..274b013371b9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.params; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.search.LocalSearchScope; @@ -48,6 +49,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt import org.jetbrains.plugins.groovy.lang.psi.stubs.GrParameterStub; import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureParameterEnhancer; import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrVariableEnhancer; +import org.jetbrains.plugins.groovy.lang.resolve.KnownRecursionManager; /** * @author: Dmitry.Krasilschikov @@ -100,7 +102,12 @@ public class GrParameterImpl extends GrVariableBaseImpl impleme return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, this); } - return GrVariableEnhancer.getEnhancedType(this); + return KnownRecursionManager.getInstance().run(this, new Computable() { + @Override + public PsiType compute() { + return GrVariableEnhancer.getEnhancedType(GrParameterImpl.this); + } + }, getDeclarationScope()); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/KnownRecursionManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/KnownRecursionManager.java new file mode 100644 index 000000000000..6542a315864a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/KnownRecursionManager.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.groovy.lang.resolve; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.util.Computable; +import com.intellij.util.containers.ContainerUtil; + +import java.util.Set; + +/** + * Created by Max Medvedev on 10/04/14 + */ +public class KnownRecursionManager { + private final ThreadLocal myThreads = new ThreadLocal() { + @Override + protected ThreadInfo initialValue() { + return new ThreadInfo(); + } + }; + + public T run(Object key, Computable computable, Object... stopAt) { + try { + if (!startInference(key, stopAt)) { + return null; + } + return computable.compute(); + } + finally { + try { + finishInference(key); + } + catch (Throwable e) { + //noinspection ThrowFromFinallyBlock + throw new RuntimeException("exception in finishInference", e); + } + } + } + + private boolean startInference(Object key, Object[] stopAt) { + ThreadInfo info = myThreads.get(); + + for (Object o : stopAt) { + if (info.myObjects.contains(o)) return false; + } + if (!info.myObjects.add(key)) return false; + + return true; + } + + private void finishInference(Object key) { + ThreadInfo info = myThreads.get(); + + info.myObjects.remove(key); + } + + public static KnownRecursionManager getInstance() { + return ServiceManager.getService(KnownRecursionManager.class); + } + + private static class ThreadInfo { + private final Set myObjects = ContainerUtil.newLinkedHashSet(); + } +}