mirror of https://github.com/astral-sh/ruff
[ty] Ignore `__all__` for document and workspace symbol requests
We also ignore names introduced by import statements, which seems to match pylance behavior. Fixes astral-sh/ty#1856
This commit is contained in:
parent
8cc7c993de
commit
0181568fb5
|
|
@ -431,7 +431,11 @@ struct SymbolVisitor<'db> {
|
||||||
/// This is true even when we're inside a function definition
|
/// This is true even when we're inside a function definition
|
||||||
/// that is inside a class.
|
/// that is inside a class.
|
||||||
in_class: bool,
|
in_class: bool,
|
||||||
global_only: bool,
|
/// When enabled, the visitor should only try to extract
|
||||||
|
/// symbols from a module that we believed form the "exported"
|
||||||
|
/// interface for that module. i.e., `__all__` is only respected
|
||||||
|
/// when this is enabled. It's otherwise ignored.
|
||||||
|
exports_only: bool,
|
||||||
/// The origin of an `__all__` variable, if found.
|
/// The origin of an `__all__` variable, if found.
|
||||||
all_origin: Option<DunderAllOrigin>,
|
all_origin: Option<DunderAllOrigin>,
|
||||||
/// A set of names extracted from `__all__`.
|
/// A set of names extracted from `__all__`.
|
||||||
|
|
@ -451,7 +455,7 @@ impl<'db> SymbolVisitor<'db> {
|
||||||
symbol_stack: vec![],
|
symbol_stack: vec![],
|
||||||
in_function: false,
|
in_function: false,
|
||||||
in_class: false,
|
in_class: false,
|
||||||
global_only: false,
|
exports_only: false,
|
||||||
all_origin: None,
|
all_origin: None,
|
||||||
all_names: FxHashSet::default(),
|
all_names: FxHashSet::default(),
|
||||||
all_invalid: false,
|
all_invalid: false,
|
||||||
|
|
@ -460,7 +464,7 @@ impl<'db> SymbolVisitor<'db> {
|
||||||
|
|
||||||
fn globals(db: &'db dyn Db, file: File) -> Self {
|
fn globals(db: &'db dyn Db, file: File) -> Self {
|
||||||
Self {
|
Self {
|
||||||
global_only: true,
|
exports_only: true,
|
||||||
..Self::tree(db, file)
|
..Self::tree(db, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -585,6 +589,11 @@ impl<'db> SymbolVisitor<'db> {
|
||||||
///
|
///
|
||||||
/// If the assignment isn't for `__all__`, then this is a no-op.
|
/// If the assignment isn't for `__all__`, then this is a no-op.
|
||||||
fn add_all_assignment(&mut self, targets: &[ast::Expr], value: Option<&ast::Expr>) {
|
fn add_all_assignment(&mut self, targets: &[ast::Expr], value: Option<&ast::Expr>) {
|
||||||
|
// We don't care about `__all__` unless we're
|
||||||
|
// specifically looking for exported symbols.
|
||||||
|
if !self.exports_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if self.in_function || self.in_class {
|
if self.in_function || self.in_class {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -865,7 +874,7 @@ impl SourceOrderVisitor<'_> for SymbolVisitor<'_> {
|
||||||
import_kind: None,
|
import_kind: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.global_only {
|
if self.exports_only {
|
||||||
self.add_symbol(symbol);
|
self.add_symbol(symbol);
|
||||||
// If global_only, don't walk function bodies
|
// If global_only, don't walk function bodies
|
||||||
return;
|
return;
|
||||||
|
|
@ -894,7 +903,7 @@ impl SourceOrderVisitor<'_> for SymbolVisitor<'_> {
|
||||||
import_kind: None,
|
import_kind: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.global_only {
|
if self.exports_only {
|
||||||
self.add_symbol(symbol);
|
self.add_symbol(symbol);
|
||||||
// If global_only, don't walk class bodies
|
// If global_only, don't walk class bodies
|
||||||
return;
|
return;
|
||||||
|
|
@ -943,6 +952,12 @@ impl SourceOrderVisitor<'_> for SymbolVisitor<'_> {
|
||||||
ast::Stmt::AugAssign(ast::StmtAugAssign {
|
ast::Stmt::AugAssign(ast::StmtAugAssign {
|
||||||
target, op, value, ..
|
target, op, value, ..
|
||||||
}) => {
|
}) => {
|
||||||
|
// We don't care about `__all__` unless we're
|
||||||
|
// specifically looking for exported symbols.
|
||||||
|
if !self.exports_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if self.all_origin.is_none() {
|
if self.all_origin.is_none() {
|
||||||
// We can't update `__all__` if it doesn't already
|
// We can't update `__all__` if it doesn't already
|
||||||
// exist.
|
// exist.
|
||||||
|
|
@ -961,6 +976,12 @@ impl SourceOrderVisitor<'_> for SymbolVisitor<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Stmt::Expr(expr) => {
|
ast::Stmt::Expr(expr) => {
|
||||||
|
// We don't care about `__all__` unless we're
|
||||||
|
// specifically looking for exported symbols.
|
||||||
|
if !self.exports_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if self.all_origin.is_none() {
|
if self.all_origin.is_none() {
|
||||||
// We can't update `__all__` if it doesn't already exist.
|
// We can't update `__all__` if it doesn't already exist.
|
||||||
return;
|
return;
|
||||||
|
|
@ -990,6 +1011,12 @@ impl SourceOrderVisitor<'_> for SymbolVisitor<'_> {
|
||||||
source_order::walk_stmt(self, stmt);
|
source_order::walk_stmt(self, stmt);
|
||||||
}
|
}
|
||||||
ast::Stmt::Import(import) => {
|
ast::Stmt::Import(import) => {
|
||||||
|
// We ignore any names introduced by imports
|
||||||
|
// unless we're specifically looking for the
|
||||||
|
// set of exported symbols.
|
||||||
|
if !self.exports_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// We only consider imports in global scope.
|
// We only consider imports in global scope.
|
||||||
if self.in_function {
|
if self.in_function {
|
||||||
return;
|
return;
|
||||||
|
|
@ -999,6 +1026,12 @@ impl SourceOrderVisitor<'_> for SymbolVisitor<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Stmt::ImportFrom(import_from) => {
|
ast::Stmt::ImportFrom(import_from) => {
|
||||||
|
// We ignore any names introduced by imports
|
||||||
|
// unless we're specifically looking for the
|
||||||
|
// set of exported symbols.
|
||||||
|
if !self.exports_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// We only consider imports in global scope.
|
// We only consider imports in global scope.
|
||||||
if self.in_function {
|
if self.in_function {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,62 @@ class Test:
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_all() {
|
||||||
|
let test = CursorTest::builder()
|
||||||
|
.source(
|
||||||
|
"utils.py",
|
||||||
|
"
|
||||||
|
__all__ = []
|
||||||
|
class Test:
|
||||||
|
def from_path(): ...
|
||||||
|
<CURSOR>",
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_snapshot!(test.workspace_symbols("from"), @r"
|
||||||
|
info[workspace-symbols]: WorkspaceSymbolInfo
|
||||||
|
--> utils.py:4:9
|
||||||
|
|
|
||||||
|
2 | __all__ = []
|
||||||
|
3 | class Test:
|
||||||
|
4 | def from_path(): ...
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
info: Method from_path
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_imports() {
|
||||||
|
let test = CursorTest::builder()
|
||||||
|
.source(
|
||||||
|
"utils.py",
|
||||||
|
"
|
||||||
|
import re
|
||||||
|
import json as json
|
||||||
|
from collections import defaultdict
|
||||||
|
foo = 1
|
||||||
|
<CURSOR>",
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_snapshot!(test.workspace_symbols("foo"), @r"
|
||||||
|
info[workspace-symbols]: WorkspaceSymbolInfo
|
||||||
|
--> utils.py:5:1
|
||||||
|
|
|
||||||
|
3 | import json as json
|
||||||
|
4 | from collections import defaultdict
|
||||||
|
5 | foo = 1
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
info: Variable foo
|
||||||
|
");
|
||||||
|
assert_snapshot!(test.workspace_symbols("re"), @"No symbols found");
|
||||||
|
assert_snapshot!(test.workspace_symbols("json"), @"No symbols found");
|
||||||
|
assert_snapshot!(test.workspace_symbols("default"), @"No symbols found");
|
||||||
|
}
|
||||||
|
|
||||||
impl CursorTest {
|
impl CursorTest {
|
||||||
fn workspace_symbols(&self, query: &str) -> String {
|
fn workspace_symbols(&self, query: &str) -> String {
|
||||||
let symbols = workspace_symbols(&self.db, query);
|
let symbols = workspace_symbols(&self.db, query);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue