From a361fee76e6fbdf0553bd0cabb0f9b75e3eddd85 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Fri, 7 Jul 2017 17:06:39 +0300 Subject: [PATCH] PY-25045 Fixed: False positive: expected type '{__div__}', got 'Union[int, float]' instead Update PyBinaryExpressionImpl to return `__truediv__` as referenced name instead of `__div__` when it is enabled. --- .../python/psi/impl/PyBinaryExpressionImpl.java | 14 ++++++++++++++ .../psi/impl/references/PyOperatorReference.java | 15 +-------------- ...nOfIntAndFloatShouldBeConsideredAsDividable.py | 8 ++++++++ .../aaa.pyi | 2 +- .../testSrc/com/jetbrains/python/Py3TypeTest.java | 2 +- .../inspections/Py3TypeCheckerInspectionTest.java | 5 +++++ 6 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/UnionOfIntAndFloatShouldBeConsideredAsDividable.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java index 8b3083e162ee..a400c5a3d5e3 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java @@ -17,6 +17,7 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; @@ -24,6 +25,7 @@ import com.intellij.psi.util.QualifiedName; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyNames; +import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.references.PyOperatorReference; import com.jetbrains.python.psi.resolve.PyResolveContext; @@ -209,6 +211,9 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp @Override public String getReferencedName() { final PyElementType t = getOperator(); + if (t == PyTokenTypes.DIV && isTrueDivEnabled(this)) { + return PyNames.TRUEDIV; + } return t != null ? t.getSpecialMethodName() : null; } @@ -217,4 +222,13 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp final PsiElement op = getPsiOperator(); return op != null ? op.getNode() : null; } + + private static boolean isTrueDivEnabled(@NotNull PyElement anchor) { + final PsiFile file = anchor.getContainingFile(); + if (file instanceof PyFile) { + final PyFile pyFile = (PyFile)file; + return FutureFeature.DIVISION.requiredAt(pyFile.getLanguageLevel()) || pyFile.hasImportFromFuture(FutureFeature.DIVISION); + } + return false; + } } diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyOperatorReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyOperatorReference.java index 8edf2423ef23..8cc85013c903 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyOperatorReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyOperatorReference.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. @@ -17,7 +17,6 @@ package com.jetbrains.python.psi.impl.references; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.psi.*; @@ -54,9 +53,6 @@ public class PyOperatorReference extends PyReferenceImpl { res = resolveMember(expr.getRightExpression(), name); } else { - if (PyNames.DIV.equals(name) && isTrueDivEnabled(myElement)) { - resolveLeftAndRightOperators(res, expr, PyNames.TRUEDIV); - } resolveLeftAndRightOperators(res, expr, name); } } @@ -125,15 +121,6 @@ public class PyOperatorReference extends PyReferenceImpl { return name.replaceFirst("__([a-z]+)__", "__r$1__"); } - private static boolean isTrueDivEnabled(@NotNull PyElement anchor) { - final PsiFile file = anchor.getContainingFile(); - if (file instanceof PyFile) { - final PyFile pyFile = (PyFile)file; - return FutureFeature.DIVISION.requiredAt(pyFile.getLanguageLevel()) || pyFile.hasImportFromFuture(FutureFeature.DIVISION); - } - return false; - } - private void resolveLeftAndRightOperators(List res, PyBinaryExpression expr, String name) { final TypeEvalContext typeEvalContext = myContext.getTypeEvalContext(); typeEvalContext.trace("Trying to resolve left operator"); diff --git a/python/testData/inspections/PyTypeCheckerInspection/UnionOfIntAndFloatShouldBeConsideredAsDividable.py b/python/testData/inspections/PyTypeCheckerInspection/UnionOfIntAndFloatShouldBeConsideredAsDividable.py new file mode 100644 index 000000000000..1546a8d495ec --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/UnionOfIntAndFloatShouldBeConsideredAsDividable.py @@ -0,0 +1,8 @@ +from typing import Union + + +def foo(x): + return x / (60 * 60) + +bar = 0 # type: Union[int, float] +foo(bar) \ No newline at end of file diff --git a/python/testData/types/NumpyResolveRaterDoesNotIncreaseRateForNotNdarrayRightOperatorFoundInStub/aaa.pyi b/python/testData/types/NumpyResolveRaterDoesNotIncreaseRateForNotNdarrayRightOperatorFoundInStub/aaa.pyi index 5891129d9f8e..6f564b56d480 100644 --- a/python/testData/types/NumpyResolveRaterDoesNotIncreaseRateForNotNdarrayRightOperatorFoundInStub/aaa.pyi +++ b/python/testData/types/NumpyResolveRaterDoesNotIncreaseRateForNotNdarrayRightOperatorFoundInStub/aaa.pyi @@ -1,5 +1,5 @@ class D1(object): - def __div__(self, other) -> "D1": ... + def __truediv__(self, other) -> "D1": ... class D2(object): def __rtruediv__(self, other) -> "D2": ... \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index 20d6ad6db0fa..369f8f464aa0 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -483,7 +483,7 @@ public class Py3TypeTest extends PyTestCase { public void testNumpyResolveRaterDoesNotIncreaseRateForNotNdarrayRightOperatorFoundInStub() { myFixture.copyDirectoryToProject(TEST_DIRECTORY + getTestName(false), ""); - doTest("Union[D2, D1]", + doTest("Union[D1, D2]", "class D1(object):\n" + " pass\n" + "class D2(object):\n" + diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index 8dfe81e8f7bb..1ebb42943c46 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -255,4 +255,9 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase { public void testPromotingBytearrayToBytes() { doTest(); } + + // PY-25045 + public void testUnionOfIntAndFloatShouldBeConsideredAsDividable() { + doTest(); + } }