Include type inference for tuple multiplication in PyStdlibTypeProvider

Removed related logic from overly convoluted PyStringFormatInspection.
This commit is contained in:
Mikhail Golubev
2014-10-29 12:55:08 +03:00
parent 3d1e308a47
commit 71fdaee679
4 changed files with 52 additions and 25 deletions
@@ -135,7 +135,40 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
}
}
else if ("__builtin__.tuple.__add__".equals(qname) && callSite instanceof PyBinaryExpression) {
return getTupleConcatenationResultType(((PyBinaryExpression)callSite), context);
return getTupleConcatenationResultType((PyBinaryExpression)callSite, context);
}
else if ("__builtin__.tuple.__mul__".equals(qname) && callSite instanceof PyBinaryExpression) {
return getTupleMultiplicationResultType((PyBinaryExpression)callSite, context);
}
}
return null;
}
@Nullable
private static PyType getTupleMultiplicationResultType(@NotNull PyBinaryExpression multiplication, @NotNull TypeEvalContext context) {
final PyTupleType leftTupleType = as(context.getType(multiplication.getLeftExpression()), PyTupleType.class);
if (leftTupleType == null) {
return null;
}
PyExpression rightExpression = multiplication.getRightExpression();
if (rightExpression instanceof PyReferenceExpression) {
final PsiElement target = ((PyReferenceExpression)rightExpression).getReference().resolve();
if (target instanceof PyTargetExpression) {
rightExpression = ((PyTargetExpression)target).findAssignedValue();
}
}
if (rightExpression instanceof PyNumericLiteralExpression && ((PyNumericLiteralExpression)rightExpression).isIntegerLiteral()) {
final int multiplier = ((PyNumericLiteralExpression)rightExpression).getBigIntegerValue().intValue();
final int originalSize = leftTupleType.getElementCount();
// Heuristic
if (originalSize * multiplier <= 20) {
final PyType[] elementTypes = new PyType[leftTupleType.getElementCount() * multiplier];
for (int i = 0; i < multiplier; i++) {
for (int j = 0; j < originalSize; j++) {
elementTypes[i * originalSize + j] = leftTupleType.getElementType(j);
}
}
return PyTupleType.create(multiplication, elementTypes);
}
}
return null;
@@ -102,39 +102,25 @@ public class PyStringFormatInspection extends PyInspection {
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(problemTarget);
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext);
//in case tuple multiplies integer PY-4647
if (rightExpression instanceof PyBinaryExpression) {
PyBinaryExpression binaryExpression = (PyBinaryExpression)rightExpression;
if (binaryExpression.getOperator() == PyTokenTypes.MULT && binaryExpression.getLeftExpression() instanceof PyParenthesizedExpression
&& binaryExpression.getRightExpression() instanceof PyNumericLiteralExpression) {
PyParenthesizedExpression parenthesizedExpression = (PyParenthesizedExpression)binaryExpression.getLeftExpression();
if (parenthesizedExpression.getContainedExpression() instanceof PyTupleExpression) {
PyExpression[] tupleElements = ((PyTupleExpression)parenthesizedExpression.getContainedExpression()).getElements();
final PyExpression expression = ((PyBinaryExpression)rightExpression).getRightExpression();
if (expression != null) {
return ((PyNumericLiteralExpression)expression).getBigIntegerValue().intValue() * tupleElements.length;
}
}
}
}
final String s = myFormatSpec.get("1");
if (PyUtil.instanceOf(rightExpression, SIMPLE_RHS_EXPRESSIONS)) {
if (s != null) {
assert rightExpression != null;
PyType right_type = myTypeEvalContext.getType(rightExpression);
if (right_type instanceof PySubscriptableType) {
PySubscriptableType tuple_type = (PySubscriptableType)right_type;
for (int i=0; i <= tuple_type.getElementCount(); i += 1) {
PyType elementType = tuple_type.getElementType(i);
final PyType rightType = myTypeEvalContext.getType(rightExpression);
if (rightType instanceof PySubscriptableType) {
final PySubscriptableType tupleType = (PySubscriptableType)rightType;
for (int i = 0; i <= tupleType.getElementCount(); i += 1) {
final PyType elementType = tupleType.getElementType(i);
if (elementType != null) {
final String typeName = myFormatSpec.get(String.valueOf(i + 1));
final PyType type = typeName != null ? PyTypeParser.getTypeByName(problemTarget, typeName) : null;
checkTypeCompatible(problemTarget, elementType, type);
}
}
return tuple_type.getElementCount();
return tupleType.getElementCount();
}
else {
checkExpressionType(rightExpression, s, problemTarget);
}
else checkExpressionType(rightExpression, s, problemTarget);
}
return 1;
}
@@ -10,4 +10,7 @@ my_dict['tmp'] = 'classes %(claz)s' % <warning descr="Key 'claz' has no followin
#PY-4647
argument_pattern = re.compile(r'(%s)\s*(\(\s*(%s)\s*\)\s*)?$'
% ((states.Inliner.simplename,) * 2))
% ((states.Inliner.simplename,) * 2))
t, num = ('foo',), 2
res = '%d %d' % (<warning descr="Unexpected type str"><warning descr="Unexpected type str">t * num</warning></warning>)
@@ -848,6 +848,11 @@ public class PyTypeTest extends PyTestCase {
"expr = (1,) + (True, 'spam') + ()");
}
public void testTupleMultiplication() {
doTest("(int, bool, int, bool)",
"expr = (1, False) * 2");
}
public void testConstructorUnification() {
doTest("C[int]",
"class C(object):\n" +