PY-22801 Fixed: False positive for long(number) in Python 2

Callee could be resolved to a class and we have to take all overloads while replacing resolved class with its `__init__` or `__new__` method.
As a result PyClass.multiFindInitOrNew is introduced and is used inside PyCallExpressionHelper.clarifyResolveResult.
This commit is contained in:
Semyon Proshev
2017-06-20 11:36:46 +03:00
committed by Semyon Proshev
parent 59a909ff25
commit 302caea766
5 changed files with 50 additions and 17 deletions
@@ -140,12 +140,25 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, PyDocStrin
* Since __new__ only makes sense for new-style classes, an old-style class never finds it with this method.
*
* @param inherited true: search in superclasses, too.
* @param context TODO: DOC
* @param context context to be used to resolve ancestors and check if this class is a new-style class
* @return a method that would be called first when an instance of this class is instantiated.
*/
@Nullable
PyFunction findInitOrNew(boolean inherited, @Nullable TypeEvalContext context);
/**
* Finds either __init__ or __new__, whichever is defined for given class, and all its overloads.
* If __init__ is defined, it is found first. This mimics the way initialization methods
* are searched for and called by Python when a constructor call is made.
* Since __new__ only makes sense for new-style classes, an old-style class never finds it with this method.
*
* @param inherited true: search in superclasses, too.
* @param context context to be used to resolve ancestors and check if this class is a new-style class
* @return a method that would be called first when an instance of this class is instantiated and all its overloads.
*/
@NotNull
List<PyFunction> multiFindInitOrNew(boolean inherited, @Nullable TypeEvalContext context);
/**
* Finds a property with the specified name in the class or one of its ancestors.
*
@@ -268,6 +281,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, PyDocStrin
@Nullable
List<String> getOwnSlots();
@Override
@Nullable
String getDocStringValue();
@@ -130,13 +130,12 @@ public class PyCallExpressionHelper {
final List<PyCallExpression.PyRatedMarkedCallee> ratedMarkedCallees = new ArrayList<>();
for (QualifiedRatedResolveResult resolveResult : multiResolveCallee(call.getCallee(), resolveContext)) {
final ClarifiedResolveResult clarifiedResolveResult = clarifyResolveResult(resolveResult, resolveContext);
if (clarifiedResolveResult == null) continue;
for (ClarifiedResolveResult clarifiedResolveResult : clarifyResolveResult(resolveResult, resolveContext)) {
final PyCallExpression.PyRatedMarkedCallee markedCallee = markResolveResult(clarifiedResolveResult, context, implicitOffset);
if (markedCallee == null) continue;
final PyCallExpression.PyRatedMarkedCallee markedCallee = markResolveResult(clarifiedResolveResult, context, implicitOffset);
if (markedCallee == null) continue;
ratedMarkedCallees.add(markedCallee);
ratedMarkedCallees.add(markedCallee);
}
}
return forEveryScopeTakeOverloadsOtherwiseImplementations(ratedMarkedCallees, PyCallExpression.PyRatedMarkedCallee::getElement, context)
@@ -187,14 +186,14 @@ public class PyCallExpressionHelper {
return Collections.emptyList();
}
@Nullable
private static ClarifiedResolveResult clarifyResolveResult(@NotNull QualifiedRatedResolveResult resolveResult,
@NotNull PyResolveContext resolveContext) {
@NotNull
private static List<ClarifiedResolveResult> clarifyResolveResult(@NotNull QualifiedRatedResolveResult resolveResult,
@NotNull PyResolveContext resolveContext) {
final PsiElement resolved = resolveResult.getElement();
if (resolved instanceof PyClass) {
final PyFunction clarifiedResolved = ((PyClass)resolved).findInitOrNew(true, resolveContext.getTypeEvalContext());
return clarifiedResolved == null ? null : new ClarifiedResolveResult(resolveResult, clarifiedResolved, null, true);
return ContainerUtil.map(((PyClass)resolved).multiFindInitOrNew(true, resolveContext.getTypeEvalContext()),
function -> new ClarifiedResolveResult(resolveResult, function, null, true));
}
else if (resolved instanceof PyCallExpression) { // foo = classmethod(foo)
final Pair<String, PyFunction> wrapperInfo = interpretAsModifierWrappingCall((PyCallExpression)resolved);
@@ -206,7 +205,8 @@ public class PyCallExpressionHelper {
? PyFunction.Modifier.STATICMETHOD
: null;
return new ClarifiedResolveResult(resolveResult, wrapperInfo.getSecond(), wrappedModifier, false);
final ClarifiedResolveResult result = new ClarifiedResolveResult(resolveResult, wrapperInfo.getSecond(), wrappedModifier, false);
return Collections.singletonList(result);
}
}
else if (resolved instanceof PyFunction) {
@@ -217,12 +217,14 @@ public class PyCallExpressionHelper {
final PyType type = context.getReturnType(function);
return type instanceof PyFunctionType
? new ClarifiedResolveResult(resolveResult, ((PyFunctionType)type).getCallable(), null, false)
: null;
? Collections.singletonList(new ClarifiedResolveResult(resolveResult, ((PyFunctionType)type).getCallable(), null, false))
: Collections.emptyList();
}
}
return resolved == null ? null : new ClarifiedResolveResult(resolveResult, resolved, null, false);
return resolved != null
? Collections.singletonList(new ClarifiedResolveResult(resolveResult, resolved, null, false))
: Collections.emptyList();
}
@Nullable
@@ -422,7 +422,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
@Override
public ItemPresentation getPresentation() {
return new PyElementPresentation(this) {
@Nullable
@NotNull
@Override
public String getPresentableText() {
PyPsiUtils.assertValid(PyClassImpl.this);
@@ -709,6 +709,17 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
return proc.getResult();
}
@NotNull
@Override
public List<PyFunction> multiFindInitOrNew(boolean inherited, @Nullable TypeEvalContext context) {
final MultiNameFinder<PyFunction> processor = isNewStyleClass(context)
? new MultiNameFinder<>(PyNames.INIT, PyNames.NEW)
: new MultiNameFinder<>(PyNames.INIT);
visitMethods(processor, inherited, true, context);
return processor.myResult;
}
private final static Maybe<PyCallable> UNKNOWN_CALL = new Maybe<>(); // denotes _not_ a PyFunction, actually
private final static Maybe<PyCallable> NONE = new Maybe<>(null); // denotes an explicit None
@@ -0,0 +1 @@
long("abc")
@@ -312,6 +312,11 @@ public class PyArgumentListInspectionTest extends PyTestCase {
runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest);
}
// PY-24286
public void testBuiltinLong() {
doTest();
}
private void doMultiFileTest() {
final String folderPath = "inspections/PyArgumentListInspection/" + getTestName(false) + "/";