PY-22087 Don't mark comprehension variables used in f-strings as unused

I needed to tweak anchor element (that has the corresponding instruction
in CFG) to flow-sensitively resolve names used in f-strings inside
comprehensions.
This commit is contained in:
Mikhail Golubev
2017-03-20 12:55:42 +03:00
parent 4120e431a8
commit 7a81f4dfba
4 changed files with 27 additions and 6 deletions
@@ -99,15 +99,20 @@ public class PyDocReference extends PyReferenceImpl {
@Nullable
private PsiElement getScopeControlFlowAnchor(@NotNull PsiLanguageInjectionHost host) {
if (isInsideFormattedStringNode(host)) {
final PsiElement comprehensionPart = PsiTreeUtil.findFirstParent(host, PyDocReference::isComprehensionResultOrComponent);
if (comprehensionPart != null) {
return comprehensionPart;
}
return PsiTreeUtil.getParentOfType(host, PyStatement.class);
return getControlFlowAnchorForFString((PyStringLiteralExpression)host);
}
return null;
}
@Nullable
public static PsiElement getControlFlowAnchorForFString(@NotNull PyStringLiteralExpression host) {
final PsiElement comprehensionPart = PsiTreeUtil.findFirstParent(host, PyDocReference::isComprehensionResultOrComponent);
if (comprehensionPart != null) {
return comprehensionPart;
}
return PsiTreeUtil.getParentOfType(host, PyStatement.class);
}
private static boolean isComprehensionResultOrComponent(@NotNull PsiElement element) {
// Any comprehension component and its result are represented as children expressions of the comprehension element.
// Only they have respective nodes in CFG and thus can be used as anchors for getResultsFromProcessor().
@@ -34,6 +34,7 @@ import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.documentation.doctest.PyDocReference;
import com.jetbrains.python.inspections.quickfix.AddFieldQuickFix;
import com.jetbrains.python.inspections.quickfix.PyRemoveParameterQuickFix;
import com.jetbrains.python.inspections.quickfix.PyRemoveStatementQuickFix;
@@ -104,7 +105,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
public void visitPyStringLiteralExpression(PyStringLiteralExpression pyString) {
final ScopeOwner owner = ScopeUtil.getScopeOwner(pyString);
if (owner != null && !(owner instanceof PsiFile)) {
final PyStatement instrAnchor = PsiTreeUtil.getParentOfType(pyString, PyStatement.class);
final PsiElement instrAnchor = PyDocReference.getControlFlowAnchorForFString(pyString);
if (instrAnchor == null) return;
final Instruction[] instructions = ControlFlowCache.getControlFlow(owner).getInstructions();
final int startInstruction = ControlFlowUtil.findInstructionNumberByElement(instructions, instrAnchor);
@@ -0,0 +1,10 @@
def f():
[f'{x1}' for x1 in range(10)]
[[f'{x2}' for _ in range(10)] for x2 in range(10)]
[[42 for _ in range(10) if f'{x3}'] for x3 in range(10)]
[42 for x4 in range(10) if f'{x4}']
[42 for x5 in range(10) if [f'{x5}' for _ in range(10)]]
[42 for x6 in range(10) if [42 for _ in range(10) if f'{x6}']]
[f'{x7}' for _ in range(10) for x7 in range(10)]
@@ -123,6 +123,11 @@ public class PythonInspectionsTest extends PyTestCase {
doHighlightingTest(PyUnusedLocalInspection.class, LanguageLevel.PYTHON36);
}
// PY-22087
public void testUnusedLocalFStringReferencesInComprehensions() {
doHighlightingTest(PyUnusedLocalInspection.class, LanguageLevel.PYTHON36);
}
// PY-8219
public void testUnusedLocalDoctestReference() {
doHighlightingTest(PyUnusedLocalInspection.class);