diff --git a/python/src/com/jetbrains/python/highlighting/PyRainbowVisitor.kt b/python/src/com/jetbrains/python/highlighting/PyRainbowVisitor.kt index f466a7bb5ec5..a65651449e27 100644 --- a/python/src/com/jetbrains/python/highlighting/PyRainbowVisitor.kt +++ b/python/src/com/jetbrains/python/highlighting/PyRainbowVisitor.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. @@ -19,7 +19,9 @@ import com.intellij.codeInsight.daemon.RainbowVisitor import com.intellij.openapi.editor.colors.TextAttributesKey import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil import com.jetbrains.python.PyNames +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil import com.jetbrains.python.psi.* import com.jetbrains.python.psi.resolve.PyResolveContext @@ -43,7 +45,7 @@ class PyRainbowVisitor : RainbowVisitor() { override fun clone() = PyRainbowVisitor() private fun processReference(referenceExpression: PyReferenceExpression) { - val context = getReferenceContext(referenceExpression) ?: return + val context = getReferenceContext(referenceExpression, mutableSetOf()) ?: return val name = updateNameIfGlobal(context, referenceExpression.name) ?: return addInfo(context, referenceExpression, name) @@ -65,14 +67,18 @@ class PyRainbowVisitor : RainbowVisitor() { } } - private fun getReferenceContext(referenceExpression: PyReferenceExpression): PsiElement? { + private fun getReferenceContext(referenceExpression: PyReferenceExpression, + visitedReferenceExpressions: MutableSet): PsiElement? { if (referenceExpression.isQualified || referenceExpression.name in IGNORED_NAMES) return null val resolved = referenceExpression.reference.resolve() return when (resolved) { is PyTargetExpression -> getTargetContext(resolved) is PyNamedParameter -> getNamedParameterContext(resolved) - is PyReferenceExpression -> if (resolved.parent is PyAugAssignmentStatement) getReferenceContext(resolved) else null + is PyReferenceExpression -> { + if (!visitedReferenceExpressions.add(resolved)) return getLeastCommonScope(visitedReferenceExpressions) + return if (resolved.parent is PyAugAssignmentStatement) getReferenceContext(resolved, visitedReferenceExpressions) else null + } else -> null } } @@ -129,4 +135,20 @@ class PyRainbowVisitor : RainbowVisitor() { private fun addInfo(context: PsiElement, rainbowElement: PsiElement, name: String, colorKey: TextAttributesKey? = null) { addInfo(getInfo(context, rainbowElement, name, colorKey)) } + + private fun getLeastCommonScope(elements: Collection): ScopeOwner? { + var result: ScopeOwner? = null + + elements.forEach { + val currentScopeOwner = ScopeUtil.getScopeOwner(it) + if (result == null) { + result = currentScopeOwner + } + else if (result != currentScopeOwner && currentScopeOwner != null && PsiTreeUtil.isAncestor(result, currentScopeOwner, true)) { + result = currentScopeOwner + } + } + + return result + } } \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyRainbowHighlightingTest.java b/python/testSrc/com/jetbrains/python/PyRainbowHighlightingTest.java index bd50a256fd40..e67e051a61a2 100644 --- a/python/testSrc/com/jetbrains/python/PyRainbowHighlightingTest.java +++ b/python/testSrc/com/jetbrains/python/PyRainbowHighlightingTest.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. @@ -146,6 +146,15 @@ public class PyRainbowHighlightingTest extends PyTestCase { doTest("l = lambda x: sum(y * y for y in x)"); } + // EA-96587 + public void testEa96587() { + doTest("a = 10\n" + + "a += 10\n" + + "for i in range(10):\n" + + " a += 10\n" + + " a += 10"); + } + // negative tests public void testSelfParameter() {