third_party.pylibs.pylint.src/pylint/utils/ast_walker.py
Pierre Sassoulas 9e0baf370a Simplify hard to maintain copyright notice
git is the source of truth for the copyright, copyrite (the tool)
was taking exponentially longer with each release, and it's
polluting the code with sometime as much as 50 lines of names.
2022-03-24 13:06:15 +01:00

86 lines
3.2 KiB
Python

# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/PyCQA/pylint/graphs/contributors
import collections
import traceback
from astroid import nodes
class ASTWalker:
def __init__(self, linter):
# callbacks per node types
self.nbstatements = 0
self.visit_events = collections.defaultdict(list)
self.leave_events = collections.defaultdict(list)
self.linter = linter
self.exception_msg = False
def _is_method_enabled(self, method):
if not hasattr(method, "checks_msgs"):
return True
return any(self.linter.is_message_enabled(m) for m in method.checks_msgs)
def add_checker(self, checker):
"""Walk to the checker's dir and collect visit and leave methods."""
vcids = set()
lcids = set()
visits = self.visit_events
leaves = self.leave_events
for member in dir(checker):
cid = member[6:]
if cid == "default":
continue
if member.startswith("visit_"):
v_meth = getattr(checker, member)
# don't use visit_methods with no activated message:
if self._is_method_enabled(v_meth):
visits[cid].append(v_meth)
vcids.add(cid)
elif member.startswith("leave_"):
l_meth = getattr(checker, member)
# don't use leave_methods with no activated message:
if self._is_method_enabled(l_meth):
leaves[cid].append(l_meth)
lcids.add(cid)
visit_default = getattr(checker, "visit_default", None)
if visit_default:
for cls in nodes.ALL_NODE_CLASSES:
cid = cls.__name__.lower()
if cid not in vcids:
visits[cid].append(visit_default)
# For now, we have no "leave_default" method in Pylint
def walk(self, astroid):
"""Call visit events of astroid checkers for the given node, recurse on
its children, then leave events.
"""
cid = astroid.__class__.__name__.lower()
# Detect if the node is a new name for a deprecated alias.
# In this case, favour the methods for the deprecated
# alias if any, in order to maintain backwards
# compatibility.
visit_events = self.visit_events.get(cid, ())
leave_events = self.leave_events.get(cid, ())
try:
if astroid.is_statement:
self.nbstatements += 1
# generate events for this node on each checker
for callback in visit_events or ():
callback(astroid)
# recurse on children
for child in astroid.get_children():
self.walk(child)
for callback in leave_events or ():
callback(astroid)
except Exception:
if self.exception_msg is False:
file = getattr(astroid.root(), "file", None)
print(f"Exception on node {repr(astroid)} in file '{file}'")
traceback.print_exc()
self.exception_msg = True
raise