From 04b1b9be4985a71079438633b3677804d41cfb8c Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Fri, 20 Jan 2017 23:05:12 +0300 Subject: [PATCH 1/2] [groovy] add ability to force right value resolve is operator assignments (IDEA-165470) --- .../expressions/GrReferenceExpression.java | 9 +- .../GrAssignmentExpressionImpl.java | 18 +- .../GrFakeReferenceExpressionElement.kt | 30 +++ .../GrReferenceExpressionImpl.java | 222 +++++++++++------- .../GrReferenceExpressionReference.kt | 34 +++ .../plugins/groovy/lang/psi/util/PsiUtil.java | 18 +- .../GroovyAllVariantsProcessor.java | 4 +- .../processors/GroovyResolverProcessor.java | 7 +- .../GroovyResolverProcessorBuilder.java | 11 +- .../GroovyResolverProcessorImpl.java | 6 +- .../GroovyEncapsulateFieldHelper.java | 7 +- .../ResolveOperatorAssignmentTest.groovy | 69 ++++++ 12 files changed, 324 insertions(+), 111 deletions(-) create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt create mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionReference.kt create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorAssignmentTest.groovy diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrReferenceExpression.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrReferenceExpression.java index 2efdcb11bf08..3dba0a5e718b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrReferenceExpression.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrReferenceExpression.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. @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,4 +51,10 @@ public interface GrReferenceExpression extends GrExpression, GrReferenceElement< GrReferenceExpression bindToElementViaStaticImport(@NotNull PsiMember member); + @Nullable + @Override + default PsiType getType() { return getType(false); } + + @Nullable + PsiType getType(boolean forceRValue); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java index 15bb02431b5d..b7e9586bdc49 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrAssignmentExpressionImpl.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. @@ -43,6 +43,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.GrOperatorExpressio import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; +import java.util.Objects; import java.util.concurrent.ConcurrentMap; /** @@ -61,7 +62,7 @@ public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl impleme @Override @NotNull public GrExpression getLValue() { - return findExpressionChild(this); + return Objects.requireNonNull(findExpressionChild(this)); } @Override @@ -161,6 +162,16 @@ public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl impleme return rValue == null ? null : rValue.getType(); } + @Nullable + @Override + public PsiType getType() { + if (TokenSets.ASSIGNMENTS_TO_OPERATORS.containsKey(getOperationTokenType())) { + return super.getType(); + } else { + return getRightType(); + } + } + private static final ResolveCache.PolyVariantResolver RESOLVER = new ResolveCache.PolyVariantResolver() { @NotNull @Override @@ -177,6 +188,9 @@ public class GrAssignmentExpressionImpl extends GrOperatorExpressionImpl impleme */ lType = ((GrIndexProperty)lValue).getGetterType(); } + else if (lValue instanceof GrReferenceExpression) { + lType = ((GrReferenceExpression)lValue).getType(true); + } else { lType = lValue.getType(); } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt new file mode 100644 index 000000000000..4398dca5fb51 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt @@ -0,0 +1,30 @@ +/* + * 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 org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions + +import com.intellij.psi.impl.light.LightElement +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement + +class GrFakeReferenceExpressionElement(val original: GrReferenceExpressionImpl) + : LightElement(original.manager, original.language), GroovyPsiElement { + + override fun accept(visitor: GroovyElementVisitor?) = Unit + + override fun acceptChildren(visitor: GroovyElementVisitor?) = Unit + + override fun toString() = "fake reference expression element" +} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 792e44cc1692..e953addadec9 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.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. @@ -18,15 +18,15 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.AtomicNotNullLazyValue; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; -import com.intellij.psi.impl.source.resolve.ResolveCache.PolyVariantResolver; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.profiling.ResolveProfiler; @@ -61,6 +61,7 @@ import org.jetbrains.plugins.groovy.lang.resolve.processors.GroovyResolverProces import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; /** * @author ilyas @@ -73,6 +74,18 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl myFakeGetterReference = AtomicNotNullLazyValue.createValue( + () -> new GrReferenceExpressionReference(this, true) + ); + + private final NotNullLazyValue myFakeGetterElement = AtomicNotNullLazyValue.createValue( + () -> new GrFakeReferenceExpressionElement(this) + ); + + private final NotNullLazyValue myFakeReference = AtomicNotNullLazyValue.createValue( + () -> new GrReferenceExpressionReference(this, false) + ); + @NotNull private static List filterMembersFromSuperClasses(GroovyResolveResult[] results) { List filtered = new ArrayList<>(); @@ -98,7 +111,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl POLY_RESOLVER = new PolyVariantResolver() { - @Override - @NotNull - public GroovyResolveResult[] resolve(@NotNull final GrReferenceExpressionImpl refExpr, final boolean incompleteCode) { - return refExpr.doPolyResolve(incompleteCode); - } - }; - private static final OurTypesCalculator TYPES_CALCULATOR = new OurTypesCalculator(); @Override @Nullable public PsiType getNominalType() { - final GroovyResolveResult resolveResult = advancedResolve(); + return getNominalType(false); + } + + @Nullable + private PsiType getNominalType(boolean forceRValue) { + final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(multiResolve(false, forceRValue)); PsiElement resolved = resolveResult.getElement(); for (GrReferenceTypeEnhancer enhancer : GrReferenceTypeEnhancer.EP_NAME.getExtensions()) { @@ -362,72 +372,68 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl { - @Override - @Nullable - public PsiType fun(GrReferenceExpressionImpl refExpr) { - final PsiElement resolved = refExpr.resolve(); + private static PsiType calculate(GrReferenceExpressionImpl refExpr, boolean forceRValue) { + final GroovyResolveResult[] results = refExpr.multiResolve(false, forceRValue); + final GroovyResolveResult result = PsiImplUtil.extractUniqueResult(results); + final PsiElement resolved = result.getElement(); - for (GrExpressionTypeCalculator calculator : GrExpressionTypeCalculator.EP_NAME.getExtensions()) { - PsiType type = calculator.calculateType(refExpr, resolved); - if (type != null) return type; + for (GrExpressionTypeCalculator calculator : GrExpressionTypeCalculator.EP_NAME.getExtensions()) { + PsiType type = calculator.calculateType(refExpr, resolved); + if (type != null) return type; + } + + if (ResolveUtil.isClassReference(refExpr)) { + GrExpression qualifier = refExpr.getQualifier(); + LOG.assertTrue(qualifier != null); + return qualifier.getType(); + } + + if (PsiUtil.isCompileStatic(refExpr)) { + final PsiType type; + if (resolved instanceof GrField) { + type = ((GrField)resolved).getType(); } - - if (ResolveUtil.isClassReference(refExpr)) { - GrExpression qualifier = refExpr.getQualifier(); - LOG.assertTrue(qualifier != null); - return qualifier.getType(); + else if (resolved instanceof GrVariable && !(resolved instanceof GrParameter)) { + type = ((GrVariable)resolved).getDeclaredType(); } + else if (resolved instanceof GrAccessorMethod) { + type = ((GrAccessorMethod)resolved).getProperty().getType(); + } + else { + type = null; + } + if (type != null) { + return result.getSubstitutor().substitute(type); + } + } - if (PsiUtil.isCompileStatic(refExpr)) { - final GroovyResolveResult resolveResult = refExpr.advancedResolve(); - final PsiElement resolvedF = resolveResult.getElement(); - final PsiType type; - if (resolvedF instanceof GrField) { - type = ((GrField)resolvedF).getType(); - } - else if (resolvedF instanceof GrVariable && !(resolvedF instanceof GrParameter)) { - type = ((GrVariable)resolvedF).getDeclaredType(); - } - else if (resolvedF instanceof GrAccessorMethod) { - type = ((GrAccessorMethod)resolvedF).getProperty().getType(); - } - else { - type = null; - } - if (type != null) { - return resolveResult.getSubstitutor().substitute(type); + final PsiType nominal = refExpr.getNominalType(forceRValue); + + Boolean reassigned = GrReassignedLocalVarsChecker.isReassignedVar(refExpr); + if (reassigned != null && reassigned.booleanValue()) { + return GrReassignedLocalVarsChecker.getReassignedVarType(refExpr, true); + } + + final PsiType inferred = getInferredTypes(refExpr, resolved); + if (inferred == null) { + if (nominal == null) { + //inside nested closure we could still try to infer from variable initializer. Not sound, but makes sense + if (resolved instanceof GrVariable) { + LOG.assertTrue(resolved.isValid()); + return ((GrVariable)resolved).getTypeGroovy(); } } - final PsiType nominal = refExpr.getNominalType(); - - Boolean reassigned = GrReassignedLocalVarsChecker.isReassignedVar(refExpr); - if (reassigned != null && reassigned.booleanValue()) { - return GrReassignedLocalVarsChecker.getReassignedVarType(refExpr, true); - } - - final PsiType inferred = getInferredTypes(refExpr, resolved); - if (inferred == null) { - if (nominal == null) { - //inside nested closure we could still try to infer from variable initializer. Not sound, but makes sense - if (resolved instanceof GrVariable) { - LOG.assertTrue(resolved.isValid()); - return ((GrVariable)resolved).getTypeGroovy(); - } - } + return nominal; + } + if (nominal == null) return inferred; + if (!TypeConversionUtil.isAssignable(TypeConversionUtil.erasure(nominal), inferred, false)) { + if (resolved instanceof GrVariable && ((GrVariable)resolved).getTypeElementGroovy() != null) { return nominal; } - - if (nominal == null) return inferred; - if (!TypeConversionUtil.isAssignable(TypeConversionUtil.erasure(nominal), inferred, false)) { - if (resolved instanceof GrVariable && ((GrVariable)resolved).getTypeElementGroovy() != null) { - return nominal; - } - } - return inferred; } + return inferred; } @Nullable @@ -452,8 +458,17 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl calculate(e.getOriginal(), true) + ); + } + else { + return TypeInferenceHelper.getCurrentContext().getExpressionType( + this, e -> calculate(e, false) + ); + } } @Override @@ -462,7 +477,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl { + GroovyResolveResult[] regularResults = ref.multiResolve(incomplete, false); + if (PsiUtil.isLValueOfOperatorAssignment(this)) { + Set result = ContainerUtil.newLinkedHashSet(); + ContainerUtil.addAll(result, ref.multiResolve(incomplete, true)); + ContainerUtil.addAll(result, regularResults); + return result.toArray(GroovyResolveResult.EMPTY_ARRAY); + } + else { + return regularResults; + } + }); + } + + @NotNull + public GroovyResolveResult[] multiResolve(boolean incomplete, boolean forceRValue) { + return (forceRValue ? myFakeGetterReference : myFakeReference).getValue().multiResolve(incomplete); } @Override @NotNull public GroovyResolveResult[] getSameNameVariants() { - return doPolyResolve(true); + return doPolyResolve(true, false); } @Override diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionReference.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionReference.kt new file mode 100644 index 000000000000..fd5846dba7d3 --- /dev/null +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionReference.kt @@ -0,0 +1,34 @@ +/* + * 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 org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions + +import com.intellij.psi.PsiPolyVariantReferenceBase +import com.intellij.util.ArrayUtil +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult +import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper + +class GrReferenceExpressionReference(private val ref: GrReferenceExpressionImpl, private val forceRValue: Boolean) + : PsiPolyVariantReferenceBase(ref) { + + override fun getVariants(): Array = ArrayUtil.EMPTY_OBJECT_ARRAY + + override fun multiResolve(incompleteCode: Boolean): Array { + val results = TypeInferenceHelper.getCurrentContext().multiResolve(this, incompleteCode) { ref, incomplete -> + ref.ref.doPolyResolve(incompleteCode, forceRValue) + } + return if (results.isEmpty()) GroovyResolveResult.EMPTY_ARRAY else results + } +} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 464a0452fa97..897bad9caabc 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.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. @@ -159,6 +159,13 @@ public class PsiUtil { return false; } + public static boolean isLValueOfOperatorAssignment(@NotNull GrReferenceExpression element) { + PsiElement parent = PsiTreeUtil.skipParentsOfType(element, GrParenthesizedExpression.class); + return parent instanceof GrAssignmentExpression + && ((GrAssignmentExpression)parent).getOperationTokenType() != GroovyTokenTypes.mASSIGN + && PsiTreeUtil.isAncestor(((GrAssignmentExpression)parent).getLValue(), element, false); + } + public static boolean isApplicable(@Nullable PsiType[] argumentTypes, PsiMethod method, PsiSubstitutor substitutor, @@ -280,12 +287,13 @@ public class PsiUtil { ); } } - else if (parent instanceof GrBinaryExpression || parent instanceof GrAssignmentExpression) { - GrExpression right = parent instanceof GrBinaryExpression - ? ((GrBinaryExpression)parent).getRightOperand() - : ((GrAssignmentExpression)parent).getRValue(); + else if (parent instanceof GrBinaryExpression) { + GrExpression right = ((GrBinaryExpression)parent).getRightOperand(); PsiType type = right != null ? right.getType() : null; return new PsiType[]{notNullizeType(type, nullAsBottom, parent)}; + } else if (parent instanceof GrAssignmentExpression) { + PsiType type = ((GrAssignmentExpression)parent).getType(); + return new PsiType[]{notNullizeType(type, nullAsBottom, parent)}; } return null; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyAllVariantsProcessor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyAllVariantsProcessor.java index 4d36eff7f746..2d7b25cd15b6 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyAllVariantsProcessor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyAllVariantsProcessor.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. @@ -31,7 +31,7 @@ class GroovyAllVariantsProcessor extends GroovyResolverProcessor { GroovyAllVariantsProcessor(@NotNull GrReferenceExpression ref, @NotNull EnumSet kinds, @Nullable GrExpression myUpToArgument) { - super(ref, kinds, myUpToArgument); + super(ref, kinds, myUpToArgument, false); } @NotNull 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 6900497a999b..5ad69f61e043 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 @@ -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. @@ -87,12 +87,13 @@ public abstract class GroovyResolverProcessor implements PsiScopeProcessor, Elem GroovyResolverProcessor(@NotNull GrReferenceExpression ref, @NotNull EnumSet kinds, - @Nullable GrExpression myUpToArgument) { + @Nullable GrExpression myUpToArgument, + boolean forceRValue) { myRef = ref; myAcceptableKinds = kinds; myName = getReferenceName(ref); - myIsLValue = PsiUtil.isLValue(myRef); + myIsLValue = !forceRValue && PsiUtil.isLValue(myRef); myThisType = PsiImplUtil.getQualifierType(ref); myTypeArguments = ref.getTypeArguments(); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorBuilder.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorBuilder.java index 9c438c0d940d..0bdeb651b1c0 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorBuilder.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorBuilder.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. @@ -30,6 +30,7 @@ public final class GroovyResolverProcessorBuilder { private boolean myIncomplete = false; private boolean myAllVariants = false; private GrExpression myUpToArgument = null; + private boolean myForceRValue = false; @NotNull public GroovyResolverProcessor build(GrReferenceExpression ref) { @@ -38,7 +39,7 @@ public final class GroovyResolverProcessorBuilder { return new GroovyAllVariantsProcessor(ref, kinds, myUpToArgument); } else { - return new GroovyResolverProcessorImpl(ref, kinds); + return new GroovyResolverProcessorImpl(ref, kinds, myForceRValue); } } @@ -64,6 +65,12 @@ public final class GroovyResolverProcessorBuilder { return this; } + @NotNull + public GroovyResolverProcessorBuilder setForceRValue(boolean forceRValue) { + myForceRValue = forceRValue; + return this; + } + @NotNull private static EnumSet computeKinds(@NotNull GrReferenceExpression ref) { if (ref.hasAt()) return EnumSet.of(FIELD); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorImpl.java index ad623b0ffb64..0b2c5b65a397 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/resolve/processors/GroovyResolverProcessorImpl.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. @@ -38,8 +38,8 @@ class GroovyResolverProcessorImpl extends GroovyResolverProcessor implements GrM private final boolean myIsPartOfFqn; - GroovyResolverProcessorImpl(@NotNull final GrReferenceExpression ref, @NotNull EnumSet kinds) { - super(ref, kinds, null); + GroovyResolverProcessorImpl(@NotNull final GrReferenceExpression ref, @NotNull EnumSet kinds, boolean forceRValue) { + super(ref, kinds, null, forceRValue); myIsPartOfFqn = ResolveUtil.isPartOfFQN(ref); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/encapsulateFields/GroovyEncapsulateFieldHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/encapsulateFields/GroovyEncapsulateFieldHelper.java index c7dd2c63fd46..38869441db24 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/encapsulateFields/GroovyEncapsulateFieldHelper.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/encapsulateFields/GroovyEncapsulateFieldHelper.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. @@ -24,6 +24,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.encapsulateFields.*; import com.intellij.util.IncorrectOperationException; import com.intellij.util.VisibilityUtil; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,6 +32,7 @@ import org.jetbrains.plugins.groovy.codeInspection.utils.JavaStylePropertiesUtil import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; @@ -151,7 +153,8 @@ public class GroovyEncapsulateFieldHelper extends EncapsulateFieldHelper { PsiClass accessObjectClass = getAccessObject(expr); final PsiResolveHelper helper = JavaPsiFacade.getInstance((expr).getProject()).getResolveHelper(); if (helper.isAccessible(fieldDescriptor.getField(), newModifierList, expr, accessObjectClass, null)) { - if (expr.resolve() instanceof PsiMethod) { + GroovyResolveResult[] results = expr.multiResolve(false); + if (ContainerUtil.or(results, it -> it.isValidResult() && it.getElement() instanceof PsiMethod)) { addMemberOperator(expr, field); } return true; diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorAssignmentTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorAssignmentTest.groovy new file mode 100644 index 000000000000..44049931a93f --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/ResolveOperatorAssignmentTest.groovy @@ -0,0 +1,69 @@ +/* + * 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 org.jetbrains.plugins.groovy.lang.resolve + +import com.intellij.testFramework.LightProjectDescriptor +import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor +import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection +import org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.GrUnresolvedAccessInspection +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression + +class ResolveOperatorAssignmentTest extends GroovyResolveTestCase { + + final LightProjectDescriptor projectDescriptor = GroovyLightProjectDescriptor.GROOVY_LATEST + + void 'test resolve both getter & setter'() { + fixture.with { + configureByText '_.groovy', '''\ +class A { + C plus(B b) {b} +} +class B {} +class C {} +class Foo { + A getProp() {} + Long setProp(C c) {c} +} +def foo = new Foo() +foo.prop += new B() +''' + enableInspections GrUnresolvedAccessInspection, GroovyUnusedDeclarationInspection + checkHighlighting() + def ref = file.findReferenceAt(editor.caretModel.offset) as GrReferenceExpression + def results = ref.multiResolve(false) + assert results.size() == 2 + for (result in results) { + assert result.isValidResult() + } + } + } + + void 'type reassigned'() { + fixture.with { + def file = configureByText '_.groovy', '''\ +class ClassWithPlus { + AnotherClass plus(a) {new AnotherClass()} +} +class AnotherClass {} +def c = new ClassWithPlus() +c += 1 +c +''' + def expression = file.children.last() as GrReferenceExpression + assert expression.type.equalsToText("AnotherClass") + } + } +} From fe31bbdf72475cbcfec42485689d32d3c127a603 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 23 Jan 2017 13:26:57 +0300 Subject: [PATCH 2/2] [groovy] operator assignment: do not cache forced rValue type --- .../GrFakeReferenceExpressionElement.kt | 30 ------------------- .../GrReferenceExpressionImpl.java | 12 +++----- 2 files changed, 4 insertions(+), 38 deletions(-) delete mode 100644 plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt deleted file mode 100644 index 4398dca5fb51..000000000000 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrFakeReferenceExpressionElement.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions - -import com.intellij.psi.impl.light.LightElement -import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement - -class GrFakeReferenceExpressionElement(val original: GrReferenceExpressionImpl) - : LightElement(original.manager, original.language), GroovyPsiElement { - - override fun accept(visitor: GroovyElementVisitor?) = Unit - - override fun acceptChildren(visitor: GroovyElementVisitor?) = Unit - - override fun toString() = "fake reference expression element" -} \ No newline at end of file diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index e953addadec9..c39883fb2d3c 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -78,10 +78,6 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl new GrReferenceExpressionReference(this, true) ); - private final NotNullLazyValue myFakeGetterElement = AtomicNotNullLazyValue.createValue( - () -> new GrFakeReferenceExpressionElement(this) - ); - private final NotNullLazyValue myFakeReference = AtomicNotNullLazyValue.createValue( () -> new GrReferenceExpressionReference(this, false) ); @@ -372,7 +368,8 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl calculate(e.getOriginal(), true) - ); + return calculate(this, true); } else { return TypeInferenceHelper.getCurrentContext().getExpressionType(