Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Preserve trivial bodies and nested blocks
  • Loading branch information
JukkaL committed Nov 11, 2022
commit 20b45821f9ab565c522148bd2d7eea6e77976a08
32 changes: 27 additions & 5 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ def get_lineno(self, node: ast3.expr | ast3.stmt) -> int:
return node.lineno

def translate_stmt_list(
self, stmts: Sequence[ast3.stmt], ismodule: bool = False
self, stmts: Sequence[ast3.stmt], *, ismodule: bool = False, can_strip: bool = False
) -> list[Statement]:
# A "# type: ignore" comment before the first statement of a module
# ignores the whole module:
Expand Down Expand Up @@ -519,7 +519,7 @@ def translate_stmt_list(
node = self.visit(stmt)
res.append(node)

if self.ignore_errors and len(stack) == 2 and stack[-2:] == ["C", "F"]:
if (self.ignore_errors and can_strip and len(stack) == 2 and stack[-2:] == ["C", "F"] and not is_possible_trivial_body(res)):
v = FindAttributeAssign()
for n in res:
n.accept(v)
Expand Down Expand Up @@ -591,9 +591,9 @@ def as_block(self, stmts: list[ast3.stmt], lineno: int) -> Block | None:
b.set_line(lineno)
return b

def as_required_block(self, stmts: list[ast3.stmt], lineno: int) -> Block:
def as_required_block(self, stmts: list[ast3.stmt], lineno: int, *, can_strip: bool = False) -> Block:
assert stmts # must be non-empty
b = Block(self.fix_function_overloads(self.translate_stmt_list(stmts)))
b = Block(self.fix_function_overloads(self.translate_stmt_list(stmts, can_strip=can_strip)))
# TODO: in most call sites line is wrong (includes first line of enclosing statement)
# TODO: also we need to set the column, and the end position here.
b.set_line(lineno)
Expand Down Expand Up @@ -979,7 +979,7 @@ def do_func_def(
end_column = getattr(n, "end_col_offset", None)

self.class_and_function_stack.append("F")
body = self.as_required_block(n.body, lineno)
body = self.as_required_block(n.body, lineno, can_strip=True)
func_def = FuncDef(n.name, args, body, func_type)
if isinstance(func_def.type, CallableType):
# semanal.py does some in-place modifications we want to avoid
Expand Down Expand Up @@ -2136,3 +2136,25 @@ def visit_member_expr(self, e: MemberExpr) -> None:
print(e, self.lvalue)
if self.lvalue:
self.found = True


def is_possible_trivial_body(s: list[Statement]) -> bool:
"""Could the statements form a "trivial" function body, such as 'pass'?

This mimics mypy.semanal.is_trivial_body, but this runs before
semantic analysis so some checks must be conservative.
"""
l = len(s)
if l == 0:
return False
i = 0
if isinstance(s[0], ExpressionStmt) and isinstance(s[0].expr, StrExpr):
# Skip docstring
i += 1
if i == l:
return True
if l > i + 1:
return False
stmt = s[i]
return isinstance(stmt, (PassStmt, RaiseStmt)) or (isinstance(stmt, ExpressionStmt) and
isinstance(stmt.expr, EllipsisExpr))
11 changes: 9 additions & 2 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -6391,7 +6391,7 @@ def is_trivial_body(block: Block) -> bool:
"..." (ellipsis), or "raise NotImplementedError()". A trivial body may also
start with a statement containing just a string (e.g. a docstring).

Note: functions that raise other kinds of exceptions do not count as
Note: Functions that raise other kinds of exceptions do not count as
"trivial". We use this function to help us determine when it's ok to
relax certain checks on body, but functions that raise arbitrary exceptions
are more likely to do non-trivial work. For example:
Expand All @@ -6401,11 +6401,18 @@ def halt(self, reason: str = ...) -> NoReturn:

A function that raises just NotImplementedError is much less likely to be
this complex.

Note: If you update this, you may also need to update
mypy.fastparse.is_possible_trivial_body!
"""
body = block.body
if not body:
# Functions have empty bodies only if the body is stripped or the function is
# generated or deserialized. In these cases the body is unknown.
return False

# Skip a docstring
if body and isinstance(body[0], ExpressionStmt) and isinstance(body[0].expr, StrExpr):
if isinstance(body[0], ExpressionStmt) and isinstance(body[0].expr, StrExpr):
body = block.body[1:]

if len(body) == 0:
Expand Down
11 changes: 11 additions & 0 deletions test-data/unit/check-inline-config.test
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ class D(C):
# E: Call to abstract method "m3" of "C" with trivial body via super() is unsafe
super().m4() \
# E: Call to abstract method "m4" of "C" with trivial body via super() is unsafe
super().m5() \
# E: Call to abstract method "m5" of "C" with trivial body via super() is unsafe

def m1(self) -> int:
return 0
Expand All @@ -268,13 +270,17 @@ class D(C):
def m4(self) -> int:
return 0

def m5(self) -> int:
return 0

[file m.py]
# mypy: ignore-errors=True
import abc

class C:
@abc.abstractmethod
def m1(self) -> int:
"""x"""
return 0

@abc.abstractmethod
Expand All @@ -287,3 +293,8 @@ class C:

@abc.abstractmethod
def m4(self) -> int: ...

@abc.abstractmethod
def m5(self) -> int:
"""doc"""
...
30 changes: 23 additions & 7 deletions test-data/unit/parse.test
Original file line number Diff line number Diff line change
Expand Up @@ -3581,6 +3581,8 @@ class C:
def m3(self):
if x:
self.y = 0
else:
x = 4
[out]
MypyFile:1(
ClassDef:2(
Expand Down Expand Up @@ -3626,7 +3628,11 @@ MypyFile:1(
MemberExpr:11(
NameExpr(self)
y)
IntExpr(0))))))))
IntExpr(0)))
Else(
AssignmentStmt:13(
NameExpr(x)
IntExpr(4))))))))

[case testDoNotStripMethodThatDefinesAttributeWithoutAssignment]
# mypy: ignore-errors=True
Expand All @@ -3653,7 +3659,8 @@ MypyFile:1(
MemberExpr:4(
NameExpr(self)
x))
Block:4())))
Block:4(
PassStmt:5()))))
FuncDef:6(
m2
Args(
Expand All @@ -3664,7 +3671,8 @@ MypyFile:1(
NameExpr(self)
y)
NameExpr(x)
Block:7())))))
Block:7(
PassStmt:8()))))))

[case testStripDecoratedFunctionOrMethod]
# mypy: ignore-errors=True
Expand Down Expand Up @@ -3742,7 +3750,9 @@ MypyFile:1(
Var(self)
Var(x))
def (self: Any, x: int?) -> None?
Block:4()))
Block:4(
ExpressionStmt:4(
Ellipsis))))
Decorator:5(
Var(m1)
NameExpr(overload)
Expand All @@ -3752,7 +3762,9 @@ MypyFile:1(
Var(self)
Var(x))
def (self: Any, x: str?) -> None?
Block:6()))
Block:6(
ExpressionStmt:6(
Ellipsis))))
FuncDef:7(
m1
Args(
Expand All @@ -3769,7 +3781,9 @@ MypyFile:1(
Var(self)
Var(x))
def (self: Any, x: int?) -> None?
Block:11()))
Block:11(
ExpressionStmt:11(
Ellipsis))))
Decorator:12(
Var(m2)
NameExpr(overload)
Expand All @@ -3779,7 +3793,9 @@ MypyFile:1(
Var(self)
Var(x))
def (self: Any, x: str?) -> None?
Block:13()))
Block:13(
ExpressionStmt:13(
Ellipsis))))
FuncDef:14(
m2
Args(
Expand Down