mirror of
https://github.com/Instagram/LibCST.git
synced 2025-12-23 10:35:53 +00:00
This is somewhat complicated by the fact that we need to not just allow construction of nodes/matchers using `ExtSlice` still for backwards compatibility, but we also need to be able to call `visit_ExtSlice` and `leave_ExtSlice` on old visitors even though the new node is named `SubscriptElement`. The construction/instance check/matching side of things will work since internally we refer to everything as `SubscriptElement` and alias `ExtSlice` to this everywhere, but for string-based function lookup, we need to get a little more clever and make the default `visit_SubscriptElement` delegate onward to `visit_ExtSlice` so that either form works. This can all be removed again once we're past the deprecation period for ExtSlice.
175 lines
7.2 KiB
Python
175 lines
7.2 KiB
Python
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
#
|
|
# This source code is licensed under the MIT license found in the
|
|
# LICENSE file in the root directory of this source tree.
|
|
|
|
# pyre-strict
|
|
from dataclasses import fields
|
|
from typing import List
|
|
|
|
from libcst.codegen.gather import imports, nodebases, nodeuses
|
|
|
|
|
|
generated_code: List[str] = []
|
|
generated_code.append("# Copyright (c) Facebook, Inc. and its affiliates.")
|
|
generated_code.append("#")
|
|
generated_code.append(
|
|
"# This source code is licensed under the MIT license found in the"
|
|
)
|
|
generated_code.append("# LICENSE file in the root directory of this source tree.")
|
|
generated_code.append("")
|
|
generated_code.append("# pyre-strict")
|
|
generated_code.append("")
|
|
generated_code.append("# This file was generated by libcst.codegen.gen_matcher_classes")
|
|
generated_code.append("from typing import Optional, Union, TYPE_CHECKING")
|
|
generated_code.append("")
|
|
generated_code.append("from libcst._maybe_sentinel import MaybeSentinel")
|
|
generated_code.append("from libcst._removal_sentinel import RemovalSentinel")
|
|
generated_code.append("from libcst._typed_visitor_base import mark_no_op")
|
|
|
|
# Import the types we use. These have to be type guarded since it would
|
|
# cause an import cycle otherwise.
|
|
generated_code.append("")
|
|
generated_code.append("")
|
|
generated_code.append(f"if TYPE_CHECKING:")
|
|
for module, objects in imports.items():
|
|
generated_code.append(f" from {module} import ( # noqa: F401")
|
|
generated_code.append(f" {', '.join(sorted(list(objects)))}")
|
|
generated_code.append(" )")
|
|
# TODO: Remove this once we completely remove ExtSlice.
|
|
generated_code.append(" ExtSlice = SubscriptElement")
|
|
|
|
|
|
# Generate the base visit_ methods
|
|
generated_code.append("")
|
|
generated_code.append("")
|
|
generated_code.append("class CSTTypedBaseFunctions:")
|
|
for node in sorted(nodebases.keys(), key=lambda node: node.__name__):
|
|
name = node.__name__
|
|
if name.startswith("Base"):
|
|
continue
|
|
# TODO: Remove this hack once we completely remove ExtSlice.
|
|
if name == "SubscriptElement":
|
|
# HACK! Point SubscriptElement visitors at ExtSlice visitors for now,
|
|
# so that deprecated visitors still work. This requires us to drop the
|
|
# optimization @mark_no_op for the time being.
|
|
generated_code.append("")
|
|
generated_code.append(
|
|
f' def visit_SubscriptElement(self, node: "SubscriptElement") -> Optional[bool]:'
|
|
)
|
|
generated_code.append(" return self.visit_ExtSlice(node)")
|
|
for field in fields(node) or []:
|
|
if field.name == "_metadata":
|
|
continue
|
|
generated_code.append("")
|
|
generated_code.append(
|
|
f' def visit_SubscriptElement_{field.name}(self, node: "SubscriptElement") -> None:'
|
|
)
|
|
generated_code.append(f" self.visit_ExtSlice_{field.name}(node)")
|
|
generated_code.append("")
|
|
generated_code.append(
|
|
f' def leave_SubscriptElement_{field.name}(self, node: "SubscriptElement") -> None:'
|
|
)
|
|
generated_code.append(f" self.leave_ExtSlice_{field.name}(node)")
|
|
|
|
# HACK: Pretend we didn't output code and change the name to ExtSlice,
|
|
# so we can use the common code below.
|
|
name = "ExtSlice"
|
|
|
|
generated_code.append("")
|
|
generated_code.append(" @mark_no_op")
|
|
generated_code.append(
|
|
f' def visit_{name}(self, node: "{name}") -> Optional[bool]:'
|
|
)
|
|
generated_code.append(" pass")
|
|
for field in fields(node) or []:
|
|
if field.name == "_metadata":
|
|
continue
|
|
generated_code.append("")
|
|
generated_code.append(" @mark_no_op")
|
|
generated_code.append(
|
|
f' def visit_{name}_{field.name}(self, node: "{name}") -> None:'
|
|
)
|
|
generated_code.append(" pass")
|
|
generated_code.append("")
|
|
generated_code.append(" @mark_no_op")
|
|
generated_code.append(
|
|
f' def leave_{name}_{field.name}(self, node: "{name}") -> None:'
|
|
)
|
|
generated_code.append(" pass")
|
|
|
|
# Generate the visitor leave_ methods
|
|
generated_code.append("")
|
|
generated_code.append("")
|
|
generated_code.append("class CSTTypedVisitorFunctions(CSTTypedBaseFunctions):")
|
|
for node in sorted(nodebases.keys(), key=lambda node: node.__name__):
|
|
name = node.__name__
|
|
if name.startswith("Base"):
|
|
continue
|
|
# TODO: Remove this hack once we completely remove ExtSlice.
|
|
if name == "SubscriptElement":
|
|
# HACK! Point SubscriptElement visitors at ExtSlice visitors for now,
|
|
# so that deprecated visitors still work. This requires us to drop the
|
|
# optimization @mark_no_op for the time being.
|
|
generated_code.append("")
|
|
generated_code.append(
|
|
f' def leave_SubscriptElement(self, original_node: "SubscriptElement") -> None:'
|
|
)
|
|
generated_code.append(" self.leave_ExtSlice(original_node)")
|
|
# HACK: Pretend we didn't output code and change the name to ExtSlice,
|
|
# so we can use the common code below.
|
|
name = "ExtSlice"
|
|
|
|
generated_code.append("")
|
|
generated_code.append(" @mark_no_op")
|
|
generated_code.append(
|
|
f' def leave_{name}(self, original_node: "{name}") -> None:'
|
|
)
|
|
generated_code.append(" pass")
|
|
|
|
# Generate the transformer leave_ methods
|
|
generated_code.append("")
|
|
generated_code.append("")
|
|
generated_code.append("class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):")
|
|
generated_code.append(" pass")
|
|
for node in sorted(nodebases.keys(), key=lambda node: node.__name__):
|
|
name = node.__name__
|
|
if name.startswith("Base"):
|
|
continue
|
|
generated_code.append("")
|
|
generated_code.append(" @mark_no_op")
|
|
valid_return_types: List[str] = [f'"{nodebases[node].__name__}"']
|
|
node_uses = nodeuses[node]
|
|
base_uses = nodeuses[nodebases[node]]
|
|
if node_uses.maybe or base_uses.maybe:
|
|
valid_return_types.append("MaybeSentinel")
|
|
if (
|
|
node_uses.optional
|
|
or node_uses.sequence
|
|
or base_uses.optional
|
|
or base_uses.sequence
|
|
):
|
|
valid_return_types.append("RemovalSentinel")
|
|
# TODO: Remove this hack once we completely remove ExtSlice.
|
|
if name == "SubscriptElement":
|
|
# HACK! Point SubscriptElement visitors at ExtSlice visitors for now,
|
|
# so that deprecated visitors still work. This requires us to drop the
|
|
# optimization @mark_no_op for the time being.
|
|
generated_code.append(
|
|
f' def leave_SubscriptElement(self, original_node: "SubscriptElement", updated_node: "SubscriptElement") -> Union[{", ".join(valid_return_types)}]:'
|
|
)
|
|
generated_code.append(
|
|
" return self.leave_ExtSlice(original_node, updated_node)"
|
|
)
|
|
# HACK: Pretend we didn't output code and change the name to ExtSlice,
|
|
# so we can use the common code below.
|
|
name = "ExtSlice"
|
|
|
|
generated_code.append(
|
|
f' def leave_{name}(self, original_node: "{name}", updated_node: "{name}") -> Union[{", ".join(valid_return_types)}]:'
|
|
)
|
|
generated_code.append(" return updated_node")
|
|
|
|
if __name__ == "__main__":
|
|
# Output the code
|
|
print("\n".join(generated_code))
|