Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py

changeset 6882
65d1bf4b7427
parent 6645
ad476851d7e0
child 6889
334257ef9435
equal deleted inserted replaced
6881:054667c5c270 6882:65d1bf4b7427
10 import sys 10 import sys
11 import ast 11 import ast
12 import re 12 import re
13 import itertools 13 import itertools
14 from string import Formatter 14 from string import Formatter
15 from collections import defaultdict
15 16
16 17
17 def composeCallPath(node): 18 def composeCallPath(node):
18 """ 19 """
19 Generator function to assemble the call path of a given node. 20 Generator function to assemble the call path of a given node.
58 "M711", 59 "M711",
59 60
60 "M801", 61 "M801",
61 "M811", 62 "M811",
62 "M821", "M822", 63 "M821", "M822",
64 "M831", "M832", "M833", "M834",
63 65
64 "M901", 66 "M901",
65 ] 67 ]
66 68
67 Formatter = Formatter() 69 Formatter = Formatter()
137 (self.__checkFuture, ("M701", "M702")), 139 (self.__checkFuture, ("M701", "M702")),
138 (self.__checkGettext, ("M711",)), 140 (self.__checkGettext, ("M711",)),
139 (self.__checkPrintStatements, ("M801",)), 141 (self.__checkPrintStatements, ("M801",)),
140 (self.__checkTuple, ("M811", )), 142 (self.__checkTuple, ("M811", )),
141 (self.__checkMutableDefault, ("M821", "M822")), 143 (self.__checkMutableDefault, ("M821", "M822")),
144 (self.__checkReturn, ("M831", "M832", "M833", "M834")),
142 ] 145 ]
143 146
144 self.__defaultArgs = { 147 self.__defaultArgs = {
145 "BuiltinsChecker": { 148 "BuiltinsChecker": {
146 "chr": ["unichr", ], 149 "chr": ["unichr", ],
735 for violation in visitor.violations: 738 for violation in visitor.violations:
736 node = violation[0] 739 node = violation[0]
737 reason = violation[1] 740 reason = violation[1]
738 params = violation[2:] 741 params = violation[2:]
739 self.__error(node.lineno - 1, node.col_offset, reason, *params) 742 self.__error(node.lineno - 1, node.col_offset, reason, *params)
743
744 def __checkReturn(self):
745 """
746 Private method to check return statements.
747 """
748 visitor = ReturnVisitor()
749 visitor.visit(self.__tree)
750 for violation in visitor.violations:
751 node = violation[0]
752 reason = violation[1]
753 self.__error(node.lineno - 1, node.col_offset, reason)
740 754
741 755
742 class TextVisitor(ast.NodeVisitor): 756 class TextVisitor(ast.NodeVisitor):
743 """ 757 """
744 Class implementing a node visitor for bytes and str instances. 758 Class implementing a node visitor for bytes and str instances.
1287 @return dictionary containing the names as keys and the list of nodes 1301 @return dictionary containing the names as keys and the list of nodes
1288 @rtype dict 1302 @rtype dict
1289 """ 1303 """
1290 return self.__names 1304 return self.__names
1291 1305
1306
1307 class ReturnVisitor(ast.NodeVisitor):
1308 """
1309 Class implementing a node visitor to check return statements.
1310 """
1311 Assigns = 'assigns'
1312 Refs = 'refs'
1313 Returns = 'returns'
1314
1315 def __init__(self):
1316 """
1317 Constructor
1318 """
1319 super(ReturnVisitor, self).__init__()
1320
1321 self.__stack = []
1322 self.violations = []
1323
1324 @property
1325 def assigns(self):
1326 """
1327 Public method to get the Assign nodes.
1328
1329 @return dictionary containing the node name as key and line number
1330 as value
1331 @rtype dict
1332 """
1333 return self.__stack[-1][ReturnVisitor.Assigns]
1334
1335 @property
1336 def refs(self):
1337 """
1338 Public method to get the References nodes.
1339
1340 @return dictionary containing the node name as key and line number
1341 as value
1342 @rtype dict
1343 """
1344 return self.__stack[-1][ReturnVisitor.Refs]
1345
1346 @property
1347 def returns(self):
1348 """
1349 Public method to get the Return nodes.
1350
1351 @return dictionary containing the node name as key and line number
1352 as value
1353 @rtype dict
1354 """
1355 return self.__stack[-1][ReturnVisitor.Returns]
1356
1357 def __visitWithStack(self, node):
1358 """
1359 Private method to traverse a given function node using a stack.
1360
1361 @param node AST node to be traversed
1362 @type ast.FunctionDef or ast.AsyncFunctionDef
1363 """
1364 self.__stack.append({
1365 ReturnVisitor.Assigns: defaultdict(list),
1366 ReturnVisitor.Refs: defaultdict(list),
1367 ReturnVisitor.Returns: []
1368 })
1369
1370 self.generic_visit(node)
1371 self.__checkFunction(node)
1372 self.__stack.pop()
1373
1374 def visit_FunctionDef(self, node):
1375 """
1376 Public method to handle a function definition.
1377
1378 @param node reference to the node to handle
1379 @type ast.FunctionDef
1380 """
1381 self.__visitWithStack(node)
1382
1383 def visit_AsyncFunctionDef(self, node):
1384 """
1385 Public method to handle a function definition.
1386
1387 @param node reference to the node to handle
1388 @type ast.AsyncFunctionDef
1389 """
1390 self.__visitWithStack(node)
1391
1392 def visit_Return(self, node):
1393 """
1394 Public method to handle a return node.
1395
1396 @param node reference to the node to handle
1397 @type ast.Return
1398 """
1399 self.returns.append(node)
1400 self.generic_visit(node)
1401
1402 def visit_Assign(self, node):
1403 """
1404 Public method to handle an assign node.
1405
1406 @param node reference to the node to handle
1407 @type ast.Assign
1408 """
1409 if not self.__stack:
1410 return
1411
1412 for target in node.targets:
1413 self.__visitAssignTarget(target)
1414 self.generic_visit(node.value)
1415
1416 def visit_Name(self, node):
1417 """
1418 Public method to handle a name node.
1419
1420 @param node reference to the node to handle
1421 @type ast.Name
1422 """
1423 if self.__stack:
1424 self.refs[node.id].append(node.lineno)
1425
1426 def __visitAssignTarget(self, node):
1427 """
1428 Private method to handle an assign target node.
1429
1430 @param node reference to the node to handle
1431 @type ast.AST
1432 """
1433 if isinstance(node, ast.Tuple):
1434 for elt in node.elts:
1435 self.__visitAssignTarget(elt)
1436 return
1437
1438 if isinstance(node, ast.Name):
1439 self.assigns[node.id].append(node.lineno)
1440 return
1441
1442 self.generic_visit(node)
1443
1444 def __checkFunction(self, node):
1445 """
1446 Private method to check a function definition node.
1447
1448 @param node reference to the node to check
1449 @type ast.AsyncFunctionDef or ast.FunctionDef
1450 """
1451 if not self.returns or not node.body:
1452 return
1453
1454 if len(node.body) == 1 and isinstance(node.body[-1], ast.Return):
1455 # skip functions that consist of `return None` only
1456 return
1457
1458 if not self.__resultExists():
1459 self.__checkUnnecessaryReturnNone()
1460 return
1461
1462 self.__checkImplicitReturnValue()
1463 self.__checkImplicitReturn(node.body[-1])
1464
1465 for n in self.returns:
1466 if n.value:
1467 self.__checkUnnecessaryAssign(n.value)
1468
1469 def __isNone(self, node):
1470 """
1471 Private method to check, if a node value is None.
1472
1473 @param node reference to the node to check
1474 @type ast.AST
1475 @return flag indicating the node contains a None value
1476 """
1477 return isinstance(node, ast.NameConstant) and node.value is None
1478
1479 def __resultExists(self):
1480 """
1481 Private method to check the existance of a return result.
1482
1483 @return flag indicating the existence of a return result
1484 @rtype bool
1485 """
1486 for node in self.returns:
1487 value = node.value
1488 if value and not self.__isNone(value):
1489 return True
1490
1491 return False
1492
1493 def __checkImplicitReturnValue(self):
1494 """
1495 Private method to check for implicit return values.
1496 """
1497 for node in self.returns:
1498 if not node.value:
1499 self.violations.append((node, "M832"))
1500
1501 def __checkUnnecessaryReturnNone(self):
1502 """
1503 Private method to check for an unnecessary 'return None' statement.
1504 """
1505 for node in self.returns:
1506 if self.__isNone(node.value):
1507 self.violations.append((node, "M831"))
1508
1509 def __checkImplicitReturn(self, node):
1510 """
1511 Private method to check for an implicit return statement.
1512
1513 @param node reference to the node to check
1514 @type ast.AST
1515 """
1516 if isinstance(node, ast.If):
1517 if not node.body or not node.orelse:
1518 self.violations.append((node, "M833"))
1519 return
1520
1521 self.__checkImplicitReturn(node.body[-1])
1522 self.__checkImplicitReturn(node.orelse[-1])
1523 return
1524
1525 if isinstance(node, ast.For) and node.orelse:
1526 self.__checkImplicitReturn(node.orelse[-1])
1527 return
1528
1529 if isinstance(node, ast.With):
1530 self.__checkImplicitReturn(node.body[-1])
1531 return
1532
1533 if not isinstance(node,
1534 (ast.Return, ast.Raise, ast.While, ast.Try)):
1535 self.violations.append((node, "M833"))
1536
1537 def __checkUnnecessaryAssign(self, node):
1538 """
1539 Private method to check for an unnecessary assign statement.
1540
1541 @param node reference to the node to check
1542 @type ast.AST
1543 """
1544 if not isinstance(node, ast.Name):
1545 return
1546
1547 varname = node.id
1548 returnLineno = node.lineno
1549
1550 if varname not in self.assigns:
1551 return
1552
1553 if varname not in self.refs:
1554 self.violations.append((node, "M834"))
1555 return
1556
1557 if self.__hasRefsBeforeNextAssign(varname, returnLineno):
1558 return
1559
1560 self.violations.append((node, "M834"))
1561
1562 def __hasRefsBeforeNextAssign(self, varname, returnLineno):
1563 """
1564 Private method to check for references before a following assign
1565 statement.
1566
1567 @param varname variable name to check for
1568 @type str
1569 @param returnLineno line number of the return statement
1570 @type int
1571 @return flag indicating the existence of references
1572 @rtype bool
1573 """
1574 beforeAssign = 0
1575 afterAssign = None
1576
1577 for lineno in sorted(self.assigns[varname]):
1578 if lineno > returnLineno:
1579 afterAssign = lineno
1580 break
1581
1582 if lineno <= returnLineno:
1583 beforeAssign = lineno
1584
1585 for lineno in self.refs[varname]:
1586 if lineno == returnLineno:
1587 continue
1588
1589 if afterAssign:
1590 if beforeAssign < lineno <= afterAssign:
1591 return True
1592
1593 elif beforeAssign < lineno:
1594 return True
1595
1596 return False
1292 # 1597 #
1293 # eflag: noqa = M702 1598 # eflag: noqa = M702

eric ide

mercurial