Update inspection for Final variables without assigned values (PEP 591) (PY-34945)

Raise a warning about explicit type argument only in stubs or on class level
In other scopes raise a warning about necessity to initialize such values

GitOrigin-RevId: 97759220d67b174b54d4ff90186b4109a5256354
This commit is contained in:
Semyon Proshev
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent 163bddf739
commit eee21530db
4 changed files with 81 additions and 7 deletions
@@ -5,6 +5,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.source.resolve.FileContextUtil
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyParameterTypeList
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider.getFunctionTypeAnnotation
@@ -80,8 +81,17 @@ class PyFinalInspection : PyInspection() {
super.visitPyTargetExpression(node)
if (!node.hasAssignedValue()) {
node.annotation?.value.takeIf(this::resolvesToFinal).let {
registerProblem(it, "If assigned value is omitted, there should be an explicit type argument to 'Final'")
node.annotation?.value?.let {
if (PyiUtil.isInsideStub(node) || ScopeUtil.getScopeOwner(node) is PyClass) {
if (resolvesToFinal(it)) {
registerProblem(it, "If assigned value is omitted, there should be an explicit type argument to 'Final'")
}
}
else {
if (resolvesToFinal(if (it is PySubscriptionExpression) it.operand else it)) {
registerProblem(node, "'Final' name should be initialized with a value")
}
}
}
}
}
@@ -0,0 +1,13 @@
from typing_extensions import Final
class A:
a: <warning descr="If assigned value is omitted, there should be an explicit type argument to 'Final'">Final</warning>
b: Final[int]
c: int
MY_FINAL = Final
MY_FINAL_INT = Final[int]
class B:
с: <warning descr="If assigned value is omitted, there should be an explicit type argument to 'Final'">MY_FINAL</warning>
d: MY_FINAL_INT
@@ -0,0 +1,7 @@
from typing_extensions import Final
a: Final[int]
b: <warning descr="If assigned value is omitted, there should be an explicit type argument to 'Final'">Final</warning>
b = "10"
c: Final[str] = "10"
d: int
@@ -69,22 +69,66 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
}
// PY-34945
public void testOmittedAssignedValue() {
public void testOmittedAssignedValueOnModuleLevel() {
runWithLanguageLevel(
LanguageLevel.PYTHON36,
() -> doTestByText("from typing_extensions import Final\n" +
"\n" +
"a: <warning descr=\"If assigned value is omitted, there should be an explicit type argument to 'Final'\">Final</warning>\n" +
"b: Final[int]\n" +
"<warning descr=\"'Final' name should be initialized with a value\">a</warning>: Final[int]\n" +
"<warning descr=\"'Final' name should be initialized with a value\">b</warning>: Final\n" +
"b = \"10\"\n" +
"c: Final[str] = \"10\"\n" +
"d: int\n")
);
}
// PY-34945
public void testOmittedAssignedValueOnClassLevel() {
runWithLanguageLevel(
LanguageLevel.PYTHON36,
() -> doTestByText("from typing_extensions import Final\n" +
"\n" +
"class A:\n" +
" a: <warning descr=\"If assigned value is omitted, there should be an explicit type argument to 'Final'\">Final</warning>\n" +
" b: Final[int]\n" +
" c: int\n" +
"\n" +
"MY_FINAL = Final\n" +
"MY_FINAL_INT = Final[int]\n" +
"\n" +
"с: <warning descr=\"If assigned value is omitted, there should be an explicit type argument to 'Final'\">MY_FINAL</warning>\n" +
"в: MY_FINAL_INT")
"class B:\n" +
" с: <warning descr=\"If assigned value is omitted, there should be an explicit type argument to 'Final'\">MY_FINAL</warning>\n" +
" d: MY_FINAL_INT")
);
}
// PY-34945
public void testOmittedAssignedValueOnFunctionLevel() {
runWithLanguageLevel(
LanguageLevel.PYTHON36,
() -> doTestByText("from typing_extensions import Final\n" +
"\n" +
"def foo(self):\n" +
" <warning descr=\"'Final' name should be initialized with a value\">a</warning>: Final[int]\n" +
" <warning descr=\"'Final' name should be initialized with a value\">b</warning>: Final\n" +
" c: Final[str] = \"10\"\n")
);
}
// PY-34945
public void testOmittedAssignedValueInStubOnModuleLevel() {
final PsiFile currentFile = myFixture.configureByFile(getTestFilePath() + "i");
configureInspection();
assertSdkRootsNotParsed(currentFile);
}
// PY-34945
public void testOmittedAssignedValueInStubOnClassLevel() {
final PsiFile currentFile = myFixture.configureByFile(getTestFilePath() + "i");
configureInspection();
assertSdkRootsNotParsed(currentFile);
}
// PY-34945
public void testOverloadedFinalMethod() {
runWithLanguageLevel(