mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-9662 Fixed: Type of expression * int literal results is detected as int while it's unknown
Update PyBinaryExpressionImpl to return weak type if one of operands is unknown
This commit is contained in:
@@ -159,15 +159,9 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp
|
||||
|
||||
boolean matched = true;
|
||||
for (Map.Entry<PyExpression, PyCallableParameter> entry : result.getMappedParameters().entrySet()) {
|
||||
final PyExpression argument = entry.getKey();
|
||||
final PyCallableParameter parameter = entry.getValue();
|
||||
if (parameter.isPositionalContainer() || parameter.isKeywordContainer()) {
|
||||
continue;
|
||||
}
|
||||
final Map<PyGenericType, PyType> substitutions = new HashMap<>();
|
||||
final PyType parameterType = parameter.getType(context);
|
||||
final PyType argumentType = context.getType(argument);
|
||||
if (!PyTypeChecker.match(parameterType, argumentType, context, substitutions)) {
|
||||
final PyType parameterType = entry.getValue().getArgumentType(context);
|
||||
final PyType argumentType = context.getType(entry.getKey());
|
||||
if (!PyTypeChecker.match(parameterType, argumentType, context, new HashMap<>())) {
|
||||
matched = false;
|
||||
}
|
||||
}
|
||||
@@ -179,11 +173,11 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matchedTypes.isEmpty()) {
|
||||
return PyUnionType.union(matchedTypes);
|
||||
}
|
||||
if (!types.isEmpty()) {
|
||||
return PyUnionType.union(types);
|
||||
final boolean bothOperandsAreKnown = operandIsKnown(getLeftExpression(), context) && operandIsKnown(getRightExpression(), context);
|
||||
final List<PyType> resultTypes = !matchedTypes.isEmpty() ? matchedTypes : types;
|
||||
if (!resultTypes.isEmpty()) {
|
||||
final PyType result = PyUnionType.union(resultTypes);
|
||||
return bothOperandsAreKnown ? result : PyUnionType.createWeakType(result);
|
||||
}
|
||||
}
|
||||
if (PyNames.COMPARISON_OPERATORS.contains(getReferencedName())) {
|
||||
@@ -223,6 +217,15 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp
|
||||
return op != null ? op.getNode() : null;
|
||||
}
|
||||
|
||||
private static boolean operandIsKnown(@Nullable PyExpression operand, @NotNull TypeEvalContext context) {
|
||||
if (operand == null) return false;
|
||||
|
||||
final PyType operandType = context.getType(operand);
|
||||
if (operandType instanceof PyStructuralType || PyTypeChecker.isUnknown(operandType, context)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isTrueDivEnabled(@NotNull PyElement anchor) {
|
||||
final PsiFile file = anchor.getContainingFile();
|
||||
if (file instanceof PyFile) {
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
from typing import Any
|
||||
|
||||
|
||||
def f(x: Any) -> str:
|
||||
return x * 2
|
||||
|
||||
|
||||
def f(x: Any) -> str:
|
||||
return 2 * x
|
||||
|
||||
|
||||
def f(x) -> str:
|
||||
return x * 2
|
||||
|
||||
|
||||
def f(x) -> str:
|
||||
return 2 * x
|
||||
@@ -1,5 +1,5 @@
|
||||
def test():
|
||||
xs = map(lambda x: x + 1, [1, 2, 3])
|
||||
print('foo' + <warning descr="Expected type 'TypeVar('AnyStr', str, unicode)', got 'int' instead">xs[0]</warning>)
|
||||
print('foo' + xs[0])
|
||||
ys = map(tuple, iter([1, 2, 3]))
|
||||
print(1 + <warning descr="Expected type 'int', got 'tuple' instead">ys[0]</warning>, 'bar' + <warning descr="Expected type 'TypeVar('AnyStr', str, unicode)', got 'tuple' instead">ys[1]</warning>)
|
||||
|
||||
@@ -2237,6 +2237,32 @@ public class PyTypeTest extends PyTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-9662
|
||||
public void testBinaryExpressionWithUnknownOperand() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON35,
|
||||
() -> {
|
||||
doTest("Union[int, Any]",
|
||||
"from typing import Any\n" +
|
||||
"x: Any\n" +
|
||||
"expr = x * 2");
|
||||
|
||||
doTest("Union[int, Any]",
|
||||
"from typing import Any\n" +
|
||||
"x: Any\n" +
|
||||
"expr = 2 * x");
|
||||
|
||||
doTest("Union[int, Any]",
|
||||
"def f(x):\n" +
|
||||
" expr = x * 2");
|
||||
|
||||
doTest("Union[int, Any]",
|
||||
"def f(x):\n" +
|
||||
" expr = 2 * x");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static List<TypeEvalContext> getTypeEvalContexts(@NotNull PyExpression element) {
|
||||
return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(),
|
||||
TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing());
|
||||
|
||||
@@ -442,6 +442,11 @@ public class PyTypeCheckerInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-9662
|
||||
public void testBinaryExpressionWithUnknownOperand() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest);
|
||||
}
|
||||
|
||||
// PY-16066
|
||||
public void testBasestringMatchesType() {
|
||||
doTest();
|
||||
|
||||
Reference in New Issue
Block a user