Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions crates/ruff_db/src/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use std::sync::Arc;

use arc_swap::ArcSwapOption;
use get_size2::GetSize;
use ruff_python_ast::{AnyRootNodeRef, ModModule, NodeIndex};
use ruff_python_parser::{ParseOptions, Parsed, parse_unchecked};
use ruff_python_ast::{AnyRootNodeRef, ModExpression, ModModule, NodeIndex, StringLiteral};
use ruff_python_parser::{
ParseError, ParseOptions, Parsed, parse_string_annotation, parse_unchecked,
};

use crate::Db;
use crate::files::File;
Expand Down Expand Up @@ -41,6 +43,18 @@ pub fn parsed_module_impl(db: &dyn Db, file: File) -> Parsed<ModModule> {
.expect("PySourceType always parses into a module")
}

pub fn parsed_string_annotation(
source: &str,
string: &StringLiteral,
) -> Result<Parsed<ModExpression>, ParseError> {
let expr = parse_string_annotation(source, string)?;

// We need the sub-ast of the string annotation to be indexed
indexed::ensure_indexed(&expr);

Ok(expr)
}

/// A wrapper around a parsed module.
///
/// This type manages instances of the module AST. A particular instance of the AST
Expand Down Expand Up @@ -170,12 +184,21 @@ mod indexed {
pub parsed: Parsed<ModModule>,
}

pub fn ensure_indexed(parsed: &Parsed<ModExpression>) {
let mut visitor = Visitor {
nodes: Some(Vec::new()),
index: 0,
};

AnyNodeRef::from(parsed.syntax()).visit_source_order(&mut visitor);
}

impl IndexedModule {
/// Create a new [`IndexedModule`] from the given AST.
#[allow(clippy::unnecessary_cast)]
pub fn new(parsed: Parsed<ModModule>) -> Arc<Self> {
let mut visitor = Visitor {
nodes: Vec::new(),
nodes: Some(Vec::new()),
index: 0,
};

Expand All @@ -186,7 +209,7 @@ mod indexed {

AnyNodeRef::from(inner.parsed.syntax()).visit_source_order(&mut visitor);

let index: Box<[AnyRootNodeRef<'_>]> = visitor.nodes.into_boxed_slice();
let index: Box<[AnyRootNodeRef<'_>]> = visitor.nodes.unwrap().into_boxed_slice();

// SAFETY: We cast from `Box<[AnyRootNodeRef<'_>]>` to `Box<[AnyRootNodeRef<'static>]>`,
// faking the 'static lifetime to create the self-referential struct. The node references
Expand Down Expand Up @@ -215,7 +238,7 @@ mod indexed {
/// A visitor that collects nodes in source order.
pub struct Visitor<'a> {
pub index: u32,
pub nodes: Vec<AnyRootNodeRef<'a>>,
pub nodes: Option<Vec<AnyRootNodeRef<'a>>>,
}

impl<'a> Visitor<'a> {
Expand All @@ -225,7 +248,9 @@ mod indexed {
AnyRootNodeRef<'a>: From<&'a T>,
{
node.node_index().set(NodeIndex::from(self.index));
self.nodes.push(AnyRootNodeRef::from(node));
if let Some(nodes) = &mut self.nodes {
nodes.push(AnyRootNodeRef::from(node));
}
self.index += 1;
}
}
Expand Down
12 changes: 6 additions & 6 deletions crates/ty/docs/rules.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 5 additions & 12 deletions crates/ty_ide/src/goto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,18 +313,11 @@ impl GotoTarget<'_> {
subrange,
..
} => {
let (subast, _submodel) = model.enter_string_annotation(string_expr)?;
let submod = subast.syntax();
let subnode = covering_node(submod.into(), *subrange).node();

// The type checker knows the type of the full annotation but nothing else
if AnyNodeRef::from(&*submod.body) == subnode {
string_expr.inferred_type(model)
} else {
// TODO: force the typechecker to tell us its secrets
// (it computes but then immediately discards these types)
return None;
}
let (subast, submodel) = model.enter_string_annotation(string_expr)?;
let subexpr = covering_node(subast.syntax().into(), *subrange)
.node()
.as_expr_ref()?;
subexpr.inferred_type(&submodel)
}
GotoTarget::BinOp { expression, .. } => {
let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?;
Expand Down
80 changes: 76 additions & 4 deletions crates/ty_ide/src/goto_type_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,25 @@ mod tests {
"#,
);

assert_snapshot!(test.goto_type_definition(), @"No goto target found");
assert_snapshot!(test.goto_type_definition(), @r#"
info[goto-type-definition]: Type definition
--> main.py:4:7
|
2 | a: "None | MyClass" = 1
3 |
4 | class MyClass:
| ^^^^^^^
5 | """some docs"""
|
info: Source
--> main.py:2:12
|
2 | a: "None | MyClass" = 1
| ^^^^^^^
3 |
4 | class MyClass:
|
"#);
}

#[test]
Expand Down Expand Up @@ -851,7 +869,25 @@ mod tests {
"#,
);

assert_snapshot!(test.goto_type_definition(), @"No goto target found");
assert_snapshot!(test.goto_type_definition(), @r#"
info[goto-type-definition]: Type definition
--> main.py:4:7
|
2 | a: "None | MyClass" = 1
3 |
4 | class MyClass:
| ^^^^^^^
5 | """some docs"""
|
info: Source
--> main.py:2:12
|
2 | a: "None | MyClass" = 1
| ^^^^^^^
3 |
4 | class MyClass:
|
"#);
}

#[test]
Expand Down Expand Up @@ -947,7 +983,25 @@ mod tests {
"#,
);

assert_snapshot!(test.goto_type_definition(), @"No goto target found");
assert_snapshot!(test.goto_type_definition(), @r#"
info[goto-type-definition]: Type definition
--> main.py:4:7
|
2 | a: "MyClass | No" = 1
3 |
4 | class MyClass:
| ^^^^^^^
5 | """some docs"""
|
info: Source
--> main.py:2:5
|
2 | a: "MyClass | No" = 1
| ^^^^^^^
3 |
4 | class MyClass:
|
"#);
}

#[test]
Expand All @@ -961,7 +1015,25 @@ mod tests {
"#,
);

assert_snapshot!(test.goto_type_definition(), @"No goto target found");
assert_snapshot!(test.goto_type_definition(), @r#"
info[goto-type-definition]: Type definition
--> stdlib/ty_extensions.pyi:20:1
|
19 | # Types
20 | Unknown = object()
| ^^^^^^^
21 | AlwaysTruthy = object()
22 | AlwaysFalsy = object()
|
info: Source
--> main.py:2:15
|
2 | a: "MyClass | No" = 1
| ^^
3 |
4 | class MyClass:
|
"#);
}

#[test]
Expand Down
38 changes: 37 additions & 1 deletion crates/ty_ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,9 +953,15 @@ mod tests {
);

assert_snapshot!(test.hover(), @r#"
MyClass
---------------------------------------------
some docs

---------------------------------------------
```python
MyClass
```
---
some docs
---------------------------------------------
info[hover]: Hovered content is
Expand Down Expand Up @@ -998,9 +1004,15 @@ mod tests {
);

assert_snapshot!(test.hover(), @r#"
MyClass
---------------------------------------------
some docs

---------------------------------------------
```python
MyClass
```
---
some docs
---------------------------------------------
info[hover]: Hovered content is
Expand Down Expand Up @@ -1056,9 +1068,15 @@ mod tests {
);

assert_snapshot!(test.hover(), @r#"
MyClass
---------------------------------------------
some docs

---------------------------------------------
```python
MyClass
```
---
some docs
---------------------------------------------
info[hover]: Hovered content is
Expand Down Expand Up @@ -1086,7 +1104,25 @@ mod tests {
"#,
);

assert_snapshot!(test.hover(), @"Hover provided no content");
assert_snapshot!(test.hover(), @r#"
Unknown
---------------------------------------------
```python
Unknown
```
---------------------------------------------
info[hover]: Hovered content is
--> main.py:2:15
|
2 | a: "MyClass | No" = 1
| ^-
| ||
| |Cursor offset
| source
3 |
4 | class MyClass:
|
"#);
}

#[test]
Expand Down
25 changes: 20 additions & 5 deletions crates/ty_python_semantic/src/semantic_index/ast_ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,29 +116,44 @@ pub(crate) mod node_key {
use crate::node_key::NodeKey;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update, get_size2::GetSize)]
pub(crate) struct ExpressionNodeKey(NodeKey);
pub(crate) struct ExpressionNodeKey(NodeKey, Option<NodeKey>);

impl From<(NodeKey, &ast::Expr)> for ExpressionNodeKey {
fn from(value: (NodeKey, &ast::Expr)) -> Self {
Self(value.0, Some(NodeKey::from_node(value.1)))
}
}

impl From<(ast::ExprRef<'_>, ast::ExprRef<'_>)> for ExpressionNodeKey {
fn from(value: (ast::ExprRef<'_>, ast::ExprRef<'_>)) -> Self {
Self(
NodeKey::from_node(value.0),
Some(NodeKey::from_node(value.1)),
)
}
}

impl From<ast::ExprRef<'_>> for ExpressionNodeKey {
fn from(value: ast::ExprRef<'_>) -> Self {
Self(NodeKey::from_node(value))
Self(NodeKey::from_node(value), None)
}
}

impl From<&ast::Expr> for ExpressionNodeKey {
fn from(value: &ast::Expr) -> Self {
Self(NodeKey::from_node(value))
Self(NodeKey::from_node(value), None)
}
}

impl From<&ast::ExprCall> for ExpressionNodeKey {
fn from(value: &ast::ExprCall) -> Self {
Self(NodeKey::from_node(value))
Self(NodeKey::from_node(value), None)
}
}

impl From<&ast::Identifier> for ExpressionNodeKey {
fn from(value: &ast::Identifier) -> Self {
Self(NodeKey::from_node(value))
Self(NodeKey::from_node(value), None)
}
}
}
Loading