finderrors.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. """Finding bad name and attribute accesses
  2. `find_errors` function can be used to find possible bad name and
  3. attribute accesses. As an example::
  4. errors = find_errors(project, project.get_resource('mod.py'))
  5. for error in errors:
  6. print '%s: %s' % (error.lineno, error.error)
  7. prints possible errors for ``mod.py`` file.
  8. TODO:
  9. * use task handles
  10. * reporting names at most once
  11. * attributes of extension modules that don't appear in
  12. extension_modules project config can be ignored
  13. * not calling `PyScope.get_inner_scope_for_line()` if it is a
  14. bottleneck; needs profiling
  15. * not reporting occurrences where rope cannot infer the object
  16. * rope saves multiple objects for some of the names in its objectdb
  17. use all of them not to give false positives
  18. * ... ;-)
  19. """
  20. from rope.base import ast, evaluate, pyobjects
  21. def find_errors(project, resource):
  22. """Find possible bad name and attribute accesses
  23. It returns a list of `Error`\s.
  24. """
  25. pymodule = project.pycore.resource_to_pyobject(resource)
  26. finder = _BadAccessFinder(pymodule)
  27. ast.walk(pymodule.get_ast(), finder)
  28. return finder.errors
  29. class _BadAccessFinder(object):
  30. def __init__(self, pymodule):
  31. self.pymodule = pymodule
  32. self.scope = pymodule.get_scope()
  33. self.errors = []
  34. def _Name(self, node):
  35. if isinstance(node.ctx, (ast.Store, ast.Param)):
  36. return
  37. scope = self.scope.get_inner_scope_for_line(node.lineno)
  38. pyname = scope.lookup(node.id)
  39. if pyname is None:
  40. self._add_error(node, 'Unresolved variable')
  41. elif self._is_defined_after(scope, pyname, node.lineno):
  42. self._add_error(node, 'Defined later')
  43. def _Attribute(self, node):
  44. if not isinstance(node.ctx, ast.Store):
  45. scope = self.scope.get_inner_scope_for_line(node.lineno)
  46. pyname = evaluate.eval_node(scope, node.value)
  47. if pyname is not None and \
  48. pyname.get_object() != pyobjects.get_unknown():
  49. if node.attr not in pyname.get_object():
  50. self._add_error(node, 'Unresolved attribute')
  51. ast.walk(node.value, self)
  52. def _add_error(self, node, msg):
  53. if isinstance(node, ast.Attribute):
  54. name = node.attr
  55. else:
  56. name = node.id
  57. if name != 'None':
  58. error = Error(node.lineno, msg + ' ' + name)
  59. self.errors.append(error)
  60. def _is_defined_after(self, scope, pyname, lineno):
  61. location = pyname.get_definition_location()
  62. if location is not None and location[1] is not None:
  63. if location[0] == self.pymodule and \
  64. lineno <= location[1] <= scope.get_end():
  65. return True
  66. class Error(object):
  67. def __init__(self, lineno, error):
  68. self.lineno = lineno
  69. self.error = error
  70. def __str__(self):
  71. return '%s: %s' % (self.lineno, self.error)