1
0
forked from VimPlug/jedi

Fix some issues with Definition.parent()

This commit is contained in:
Dave Halter
2019-12-22 15:37:53 +01:00
parent 0202d4ed0a
commit 22c3beffd0
2 changed files with 51 additions and 6 deletions

View File

@@ -7,6 +7,8 @@ import re
import sys import sys
import warnings import warnings
from parso.python.tree import search_ancestor
from jedi import settings from jedi import settings
from jedi import debug from jedi import debug
from jedi.inference.utils import unite from jedi.inference.utils import unite
@@ -368,12 +370,28 @@ class BaseDefinition(object):
if not self._name.is_value_name: if not self._name.is_value_name:
return None return None
context = self._name.parent_context if self.type in ('function', 'class', 'param') and self._name.tree_name is not None:
# Since the parent_context doesn't really match what the user
# thinks of that the parent is here, we do these cases separately.
# The reason for this is the following:
# - class: Nested classes parent_context is always the
# parent_context of the most outer one.
# - function: Functions in classes have the module as
# parent_context.
# - param: The parent_context of a param is not its function but
# e.g. the outer class or module.
cls_or_func_node = self._name.tree_name.get_definition()
parent = search_ancestor(cls_or_func_node, 'funcdef', 'classdef', 'file_input')
context = self._get_module_context().create_value(parent).as_context()
else:
context = self._name.parent_context
if context is None: if context is None:
return None return None
while context.name is None: while context.name is None:
# Happens for comprehension contexts # Happens for comprehension contexts
context = context.parent_context context = context.parent_context
return Definition(self._inference_state, context.name) return Definition(self._inference_state, context.name)
def __repr__(self): def __repr__(self):

View File

@@ -276,7 +276,7 @@ def test_parent_on_function(Script):
assert parent.type == 'module' assert parent.type == 'module'
def test_parent_on_completion(Script): def test_parent_on_completion_and_else(Script):
script = Script(dedent('''\ script = Script(dedent('''\
class Foo(): class Foo():
def bar(name): name def bar(name): name
@@ -287,14 +287,19 @@ def test_parent_on_completion(Script):
assert parent.name == 'Foo' assert parent.name == 'Foo'
assert parent.type == 'class' assert parent.type == 'class'
param, = script.goto(line=2) param, name, = [d for d in script.names(all_scopes=True, references=True)
parent = param.parent() if d.name == 'name']
parent = name.parent()
assert parent.name == 'bar'
assert parent.type == 'function'
parent = name.parent().parent()
assert parent.name == 'Foo' assert parent.name == 'Foo'
assert parent.type == 'class' assert parent.type == 'class'
name, = [d for d in script.names(all_scopes=True, references=True)
if d.name == 'name' and d.type == 'statement']
parent = param.parent() parent = param.parent()
assert parent.name == 'bar'
assert parent.type == 'function'
parent = param.parent().parent()
assert parent.name == 'Foo' assert parent.name == 'Foo'
assert parent.type == 'class' assert parent.type == 'class'
@@ -303,6 +308,28 @@ def test_parent_on_completion(Script):
assert parent.type == 'class' assert parent.type == 'class'
def test_parent_on_closure(Script):
script = Script(dedent('''\
class Foo():
def bar(name):
def inner(): foo
return inner'''))
names = script.names(all_scopes=True, references=True)
inner_func, inner_reference = filter(lambda d: d.name == 'inner', names)
foo, = filter(lambda d: d.name == 'foo', names)
assert foo.parent().name == 'inner'
assert foo.parent().parent().name == 'bar'
assert foo.parent().parent().parent().name == 'Foo'
assert foo.parent().parent().parent().parent().name == ''
assert inner_func.parent().name == 'bar'
assert inner_func.parent().parent().name == 'Foo'
assert inner_reference.parent().name == 'bar'
assert inner_reference.parent().parent().name == 'Foo'
def test_parent_on_comprehension(Script): def test_parent_on_comprehension(Script):
ns = Script('''\ ns = Script('''\
def spam(): def spam():