PY-19130 Fixed: Type inference: decorators behaving oddly

If class is decorated, PyArgumentListInspection doesn't check its constructor arguments
This commit is contained in:
Semyon Proshev
2016-05-18 19:21:20 +03:00
parent 4925746bf7
commit 0929934a04
4 changed files with 51 additions and 5 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -21,6 +21,8 @@ import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
@@ -35,7 +37,9 @@ import com.jetbrains.python.psi.types.PyTypeChecker;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -115,9 +119,13 @@ public class PyArgumentListInspection extends PyInspection {
final PyCallExpression.PyMarkedCallee callee = mapping.getMarkedCallee();
if (callee != null) {
final PyCallable callable = callee.getCallable();
// Decorate functions may have different parameter lists. We don't match arguments with parameters of decorators yet
if (callable instanceof PyFunction && PyUtil.hasCustomDecorators((PyFunction)callable)) {
return;
if (callable instanceof PyFunction) {
final PyFunction function = (PyFunction)callable;
// Decorate functions may have different parameter lists. We don't match arguments with parameters of decorators yet
if (PyUtil.hasCustomDecorators(function) || decoratedClassInitCall(callExpr.getCallee(), function)) {
return;
}
}
}
highlightParametersMismatch(node, holder, mapping);
@@ -128,6 +136,19 @@ public class PyArgumentListInspection extends PyInspection {
inspectPyArgumentList(node, holder, context, 0);
}
private static boolean decoratedClassInitCall(@Nullable PyExpression callee, @NotNull PyFunction function) {
if (callee instanceof PyReferenceExpression && PyUtil.isInit(function)) {
final PsiPolyVariantReference classReference = ((PyReferenceExpression)callee).getReference();
return Arrays
.stream(classReference.multiResolve(false))
.map(ResolveResult::getElement)
.anyMatch(element -> element instanceof PyClass && PyUtil.hasCustomDecorators((PyClass)element));
}
return false;
}
private static void highlightStarArgumentTypeMismatch(PyArgumentList node, ProblemsHolder holder, TypeEvalContext context) {
for (PyExpression arg : node.getArguments()) {
if (arg instanceof PyStarArgument) {
@@ -0,0 +1,8 @@
import attr
class A(object):
a = attr.ib()
A = attr.s(A)
A(a="test")
@@ -0,0 +1,7 @@
import attr
@attr.s
class A(object):
a = attr.ib()
A(a="test")
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -44,6 +44,16 @@ public class PyArgumentListInspectionTest extends PyTestCase {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
}
}
// PY-19130
public void testClassDecoratedThroughDecorator() {
doTest();
}
// PY-19130
public void testClassDecoratedThroughCall() {
doTest();
}
public void testTupleVsLiteralList() {
doTest();