diff --git a/docs/source/metadata.rst b/docs/source/metadata.rst index d8b6b8e2..b6c9b8a3 100644 --- a/docs/source/metadata.rst +++ b/docs/source/metadata.rst @@ -94,6 +94,8 @@ Expression Context Metadata .. autoclass:: libcst.metadata.ExpressionContext +.. _libcst-scope-metadata: + Scope Metadata -------------- Scope is the block of naming binding. The bind name is not available diff --git a/docs/source/scope_tutorial.ipynb b/docs/source/scope_tutorial.ipynb index 5b011e37..fe887ce3 100644 --- a/docs/source/scope_tutorial.ipynb +++ b/docs/source/scope_tutorial.ipynb @@ -9,15 +9,13 @@ "==============\n", "Scope Analysis\n", "==============\n", - "Scope analysis keeps track of assignments and accesses which could be useful for code automatic refactoring. If you're not familiar with Scope analysis, see :doc:`Metadata ` for more detail about Scope metadata. This tutorial demonstrates some use cases of Scope analysis. \n", - "Given source codes, Scope analysis parses all variable :class:`~libcst.metadata.Assignment` (or a :class:`~libcst.metadata.BuiltinAssignment` if it's a builtin) and :class:`~libcst.metadata.Access` to store in :class:`~libcst.metadata.Scope` containers.\n", - "\n", - "Given the following example source code contains a couple of unused imports (``f``, ``i``, ``m`` and ``n``) and undefined variable references (``func_undefined`` and ``var_undefined``). Scope analysis helps us identifying those unused imports and undefined variables to automatically provide warnings to developers to prevent bugs while they're developing.\n", - "With a parsed :class:`~libcst.Module`, we construct a :class:`~libcst.metadata.MetadataWrapper` object and it provides a :func:`~libcst.metadata.MetadataWrapper.resolve` function to resolve metadata given a metadata provider.\n", - ":class:`~libcst.metadata.ScopeProvider` is used here for analysing scope and there are three types of scopes (:class:`~libcst.metadata.GlobalScope`, :class:`~libcst.metadata.FunctionScope` and :class:`~libcst.metadata.ClassScope`) in this example.\n", + "Scope analysis keeps track of assignments and accesses which could be useful for code automatic refactoring. If you're not familiar with scope analysis, see :ref:`Scope Metadata ` for more detail about scope metadata. This tutorial demonstrates some use cases of scope analysis. If you're new to metadata, see :doc:`Metadata Tutorial ` to get started.\n", + "Given source codes, scope analysis parses all variable :class:`~libcst.metadata.Assignment` (or a :class:`~libcst.metadata.BuiltinAssignment` if it's a builtin) and :class:`~libcst.metadata.Access` to store in :class:`~libcst.metadata.Scope` containers.\n", "\n", ".. note::\n", - " The scope analysis only handles local variable name access and cannot handle simple string type annotation forward references. See :class:`~libcst.metadata.Access`\n" + " The scope analysis only handles local variable name access and cannot handle simple string type annotation forward references. See :class:`~libcst.metadata.Access`\n", + "\n", + "Given the following example source code contains a couple of unused imports (``f``, ``i``, ``m`` and ``n``) and undefined variable references (``func_undefined`` and ``var_undefined``). Scope analysis helps us identifying those unused imports and undefined variables to automatically provide warnings to developers to prevent bugs while they're developing.\n" ] }, { @@ -38,8 +36,6 @@ "metadata": {}, "outputs": [], "source": [ - "import libcst as cst\n", - "\n", "source = \"\"\"\\\n", "import a, b, c as d, e as f # expect to keep: a, c as d\n", "from g import h, i, j as k, l as m # expect to keep: h, j as k\n", @@ -56,7 +52,28 @@ " def __new__(self) -> \"Cls\":\n", " var = k.method()\n", " func_undefined(var_undefined)\n", - "\"\"\"\n", + "\"\"\"" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + "With a parsed :class:`~libcst.Module`, we construct a :class:`~libcst.metadata.MetadataWrapper` object and it provides a :func:`~libcst.metadata.MetadataWrapper.resolve` function to resolve metadata given a metadata provider.\n", + ":class:`~libcst.metadata.ScopeProvider` is used here for analysing scope and there are three types of scopes (:class:`~libcst.metadata.GlobalScope`, :class:`~libcst.metadata.FunctionScope` and :class:`~libcst.metadata.ClassScope`) in this example.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import libcst as cst\n", + "\n", + "\n", "wrapper = cst.metadata.MetadataWrapper(cst.parse_module(source))\n", "scopes = set(wrapper.resolve(cst.metadata.ScopeProvider).values())\n", "for scope in scopes:\n", @@ -176,7 +193,7 @@ "raw_mimetype": "text/restructuredtext" }, "source": [ - "After the transform, we use ``.code`` to generate fixed code and all unused names are fixed as expected!" + "After the transform, we use ``.code`` to generate fixed code and all unused names are fixed as expected! The difflib is used to show only changed part and only import lines are updated as expected." ] }, { @@ -185,8 +202,15 @@ "metadata": {}, "outputs": [], "source": [ + "import difflib\n", "fixed_module = wrapper.module.visit(RemoveUnusedImportTransformer(unused_imports))\n", - "print(fixed_module.code)" + "\n", + "# Use difflib to show the changes to verify unused imports are removed as expected.\n", + "print(\n", + " \"\".join(\n", + " difflib.unified_diff(source.splitlines(1), fixed_module.code.splitlines(1))\n", + " )\n", + ")" ] } ], diff --git a/libcst/metadata/scope_provider.py b/libcst/metadata/scope_provider.py index b9349b91..b4de5367 100644 --- a/libcst/metadata/scope_provider.py +++ b/libcst/metadata/scope_provider.py @@ -39,10 +39,10 @@ class Access: An Access records an access of an assignment. .. note:: - This scope analysis only analyze access via a :class:`~libcst.Name` or a :class:`~libcst.Name` + This scope analysis only analyzes access via a :class:`~libcst.Name` or a :class:`~libcst.Name` node embedded in other node like :class:`~libcst.Call` or :class:`~libcst.Attribute`. - It doesn't support type anontation using :class:`~libcst.SimpleString` literal for forward - reference. E.g. in this example, the ``"Tree"`` isn't parsed as as an access:: + It doesn't support type annontation using :class:`~libcst.SimpleString` literal for forward + references. E.g. in this example, the ``"Tree"`` isn't parsed as as an access:: class Tree: def __new__(cls) -> "Tree": @@ -300,9 +300,9 @@ class Scope(abc.ABC): .. note:: This scope analysis module only analyzes local variable names and it doesn't handle - attribute names; for example, given a.b.c = 1, local variable name ``a`` is recorded + attribute names; for example, given ``a.b.c = 1``, local variable name ``a`` is recorded as an assignment instead of ``c`` or ``a.b.c``. To analyze the assignment/access of - arbitrary object attributes, we leave the the job to type inference metadata provider + arbitrary object attributes, we leave the job to type inference metadata provider coming in the future. """