Skip to content

Commit a172553

Browse files
committed
[ty] Pull types on synthesized Python files created by mdtest
1 parent 0c20010 commit a172553

File tree

8 files changed

+155
-132
lines changed

8 files changed

+155
-132
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ty_project/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ tracing = { workspace = true }
3939

4040
[dev-dependencies]
4141
ruff_db = { workspace = true, features = ["testing"] }
42+
ty_project = { workspace = true, features = ["testing"] }
43+
4244
glob = { workspace = true }
4345
insta = { workspace = true, features = ["redactions", "ron"] }
4446

@@ -48,6 +50,7 @@ deflate = ["ty_vendored/deflate"]
4850
schemars = ["dep:schemars", "ruff_db/schemars", "ty_python_semantic/schemars"]
4951
zstd = ["ty_vendored/zstd"]
5052
format = ["ruff_python_formatter"]
53+
testing = []
5154

5255
[lints]
5356
workspace = true

crates/ty_project/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use ty_python_semantic::types::check_types;
2727
use ty_python_semantic::{add_inferred_python_version_hint_to_diagnostic, register_lints};
2828

2929
pub mod combine;
30+
#[cfg(feature = "testing")]
31+
pub mod pull_types_visitor;
3032

3133
mod db;
3234
mod files;
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use ruff_db::{files::File, parsed::parsed_module};
2+
use ty_python_semantic::{SemanticModel, HasType, Db};
3+
use ruff_python_ast::{self as ast, visitor::source_order, visitor::source_order::SourceOrderVisitor};
4+
5+
pub fn pull_types(db: &dyn Db, file: File) {
6+
let mut visitor = PullTypesVisitor::new(db, file);
7+
8+
let ast = parsed_module(db, file).load(db);
9+
10+
visitor.visit_body(ast.suite());
11+
}
12+
13+
struct PullTypesVisitor<'db> {
14+
model: SemanticModel<'db>,
15+
}
16+
17+
impl<'db> PullTypesVisitor<'db> {
18+
fn new(db: &'db dyn Db, file: File) -> Self {
19+
Self {
20+
model: SemanticModel::new(db, file),
21+
}
22+
}
23+
24+
fn visit_target(&mut self, target: &ast::Expr) {
25+
match target {
26+
ast::Expr::List(ast::ExprList { elts, .. }) | ast::Expr::Tuple(ast::ExprTuple { elts, .. }) => {
27+
for element in elts {
28+
self.visit_target(element);
29+
}
30+
}
31+
_ => self.visit_expr(target),
32+
}
33+
}
34+
}
35+
36+
impl SourceOrderVisitor<'_> for PullTypesVisitor<'_> {
37+
fn visit_stmt(&mut self, stmt: &ast::Stmt) {
38+
match stmt {
39+
ast::Stmt::FunctionDef(function) => {
40+
let _ty = function.inferred_type(&self.model);
41+
}
42+
ast::Stmt::ClassDef(class) => {
43+
let _ty = class.inferred_type(&self.model);
44+
}
45+
ast::Stmt::Assign(assign) => {
46+
for target in &assign.targets {
47+
self.visit_target(target);
48+
}
49+
self.visit_expr(&assign.value);
50+
return;
51+
}
52+
ast::Stmt::For(for_stmt) => {
53+
self.visit_target(&for_stmt.target);
54+
self.visit_expr(&for_stmt.iter);
55+
self.visit_body(&for_stmt.body);
56+
self.visit_body(&for_stmt.orelse);
57+
return;
58+
}
59+
ast::Stmt::With(with_stmt) => {
60+
for item in &with_stmt.items {
61+
if let Some(target) = &item.optional_vars {
62+
self.visit_target(target);
63+
}
64+
self.visit_expr(&item.context_expr);
65+
}
66+
67+
self.visit_body(&with_stmt.body);
68+
return;
69+
}
70+
ast::Stmt::AnnAssign(_)
71+
| ast::Stmt::Return(_)
72+
| ast::Stmt::Delete(_)
73+
| ast::Stmt::AugAssign(_)
74+
| ast::Stmt::TypeAlias(_)
75+
| ast::Stmt::While(_)
76+
| ast::Stmt::If(_)
77+
| ast::Stmt::Match(_)
78+
| ast::Stmt::Raise(_)
79+
| ast::Stmt::Try(_)
80+
| ast::Stmt::Assert(_)
81+
| ast::Stmt::Import(_)
82+
| ast::Stmt::ImportFrom(_)
83+
| ast::Stmt::Global(_)
84+
| ast::Stmt::Nonlocal(_)
85+
| ast::Stmt::Expr(_)
86+
| ast::Stmt::Pass(_)
87+
| ast::Stmt::Break(_)
88+
| ast::Stmt::Continue(_)
89+
| ast::Stmt::IpyEscapeCommand(_) => {}
90+
}
91+
92+
source_order::walk_stmt(self, stmt);
93+
}
94+
95+
fn visit_expr(&mut self, expr: &ast::Expr) {
96+
let _ty = expr.inferred_type(&self.model);
97+
98+
source_order::walk_expr(self, expr);
99+
}
100+
101+
fn visit_comprehension(&mut self, comprehension: &ast::Comprehension) {
102+
self.visit_expr(&comprehension.iter);
103+
self.visit_target(&comprehension.target);
104+
for if_expr in &comprehension.ifs {
105+
self.visit_expr(if_expr);
106+
}
107+
}
108+
109+
fn visit_parameter(&mut self, parameter: &ast::Parameter) {
110+
let _ty = parameter.inferred_type(&self.model);
111+
112+
source_order::walk_parameter(self, parameter);
113+
}
114+
115+
fn visit_parameter_with_default(&mut self, parameter_with_default: &ast::ParameterWithDefault) {
116+
let _ty = parameter_with_default.inferred_type(&self.model);
117+
118+
source_order::walk_parameter_with_default(self, parameter_with_default);
119+
}
120+
121+
fn visit_alias(&mut self, alias: &ast::Alias) {
122+
let _ty = alias.inferred_type(&self.model);
123+
124+
source_order::walk_alias(self, alias);
125+
}
126+
}

crates/ty_project/tests/check.rs

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
use anyhow::{Context, anyhow};
22
use ruff_db::files::{File, system_path_to_file};
3-
use ruff_db::parsed::parsed_module;
43
use ruff_db::system::{SystemPath, SystemPathBuf, TestSystem};
5-
use ruff_python_ast::visitor::source_order;
6-
use ruff_python_ast::visitor::source_order::SourceOrderVisitor;
7-
use ruff_python_ast::{
8-
self as ast, Alias, Comprehension, Expr, Parameter, ParameterWithDefault, Stmt,
9-
};
4+
use ty_project::pull_types_visitor::pull_types;
105
use ty_project::{ProjectDatabase, ProjectMetadata};
11-
use ty_python_semantic::{HasType, SemanticModel};
126

137
fn setup_db(project_root: &SystemPath, system: TestSystem) -> anyhow::Result<ProjectDatabase> {
148
let project = ProjectMetadata::discover(project_root, &system)?;
@@ -172,129 +166,6 @@ fn run_corpus_tests(pattern: &str) -> anyhow::Result<()> {
172166
Ok(())
173167
}
174168

175-
fn pull_types(db: &ProjectDatabase, file: File) {
176-
let mut visitor = PullTypesVisitor::new(db, file);
177-
178-
let ast = parsed_module(db, file).load(db);
179-
180-
visitor.visit_body(ast.suite());
181-
}
182-
183-
struct PullTypesVisitor<'db> {
184-
model: SemanticModel<'db>,
185-
}
186-
187-
impl<'db> PullTypesVisitor<'db> {
188-
fn new(db: &'db ProjectDatabase, file: File) -> Self {
189-
Self {
190-
model: SemanticModel::new(db, file),
191-
}
192-
}
193-
194-
fn visit_target(&mut self, target: &Expr) {
195-
match target {
196-
Expr::List(ast::ExprList { elts, .. }) | Expr::Tuple(ast::ExprTuple { elts, .. }) => {
197-
for element in elts {
198-
self.visit_target(element);
199-
}
200-
}
201-
_ => self.visit_expr(target),
202-
}
203-
}
204-
}
205-
206-
impl SourceOrderVisitor<'_> for PullTypesVisitor<'_> {
207-
fn visit_stmt(&mut self, stmt: &Stmt) {
208-
match stmt {
209-
Stmt::FunctionDef(function) => {
210-
let _ty = function.inferred_type(&self.model);
211-
}
212-
Stmt::ClassDef(class) => {
213-
let _ty = class.inferred_type(&self.model);
214-
}
215-
Stmt::Assign(assign) => {
216-
for target in &assign.targets {
217-
self.visit_target(target);
218-
}
219-
self.visit_expr(&assign.value);
220-
return;
221-
}
222-
Stmt::For(for_stmt) => {
223-
self.visit_target(&for_stmt.target);
224-
self.visit_expr(&for_stmt.iter);
225-
self.visit_body(&for_stmt.body);
226-
self.visit_body(&for_stmt.orelse);
227-
return;
228-
}
229-
Stmt::With(with_stmt) => {
230-
for item in &with_stmt.items {
231-
if let Some(target) = &item.optional_vars {
232-
self.visit_target(target);
233-
}
234-
self.visit_expr(&item.context_expr);
235-
}
236-
237-
self.visit_body(&with_stmt.body);
238-
return;
239-
}
240-
Stmt::AnnAssign(_)
241-
| Stmt::Return(_)
242-
| Stmt::Delete(_)
243-
| Stmt::AugAssign(_)
244-
| Stmt::TypeAlias(_)
245-
| Stmt::While(_)
246-
| Stmt::If(_)
247-
| Stmt::Match(_)
248-
| Stmt::Raise(_)
249-
| Stmt::Try(_)
250-
| Stmt::Assert(_)
251-
| Stmt::Import(_)
252-
| Stmt::ImportFrom(_)
253-
| Stmt::Global(_)
254-
| Stmt::Nonlocal(_)
255-
| Stmt::Expr(_)
256-
| Stmt::Pass(_)
257-
| Stmt::Break(_)
258-
| Stmt::Continue(_)
259-
| Stmt::IpyEscapeCommand(_) => {}
260-
}
261-
262-
source_order::walk_stmt(self, stmt);
263-
}
264-
265-
fn visit_expr(&mut self, expr: &Expr) {
266-
let _ty = expr.inferred_type(&self.model);
267-
268-
source_order::walk_expr(self, expr);
269-
}
270-
271-
fn visit_comprehension(&mut self, comprehension: &Comprehension) {
272-
self.visit_expr(&comprehension.iter);
273-
self.visit_target(&comprehension.target);
274-
for if_expr in &comprehension.ifs {
275-
self.visit_expr(if_expr);
276-
}
277-
}
278-
279-
fn visit_parameter(&mut self, parameter: &Parameter) {
280-
let _ty = parameter.inferred_type(&self.model);
281-
282-
source_order::walk_parameter(self, parameter);
283-
}
284-
285-
fn visit_parameter_with_default(&mut self, parameter_with_default: &ParameterWithDefault) {
286-
let _ty = parameter_with_default.inferred_type(&self.model);
287-
288-
source_order::walk_parameter_with_default(self, parameter_with_default);
289-
}
290-
291-
fn visit_alias(&mut self, alias: &Alias) {
292-
let _ty = alias.inferred_type(&self.model);
293-
294-
source_order::walk_alias(self, alias);
295-
}
296-
}
297-
298169
/// Whether or not the .py/.pyi version of this file is expected to fail
299170
#[rustfmt::skip]
300171
const KNOWN_FAILURES: &[(&str, bool, bool)] = &[

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,10 @@ impl<'db> TypeInference<'db> {
470470
#[track_caller]
471471
pub(crate) fn expression_type(&self, expression: ScopedExpressionId) -> Type<'db> {
472472
self.try_expression_type(expression).expect(
473-
"expression should belong to this TypeInference region and \
474-
TypeInferenceBuilder should have inferred a type for it",
473+
"Failed to retrieve the inferred type for an `ast::Expr` node \
474+
passed to `TypeInference::expression_type()`. The `TypeInferenceBuilder` \
475+
should infer and store types for all `ast::Expr` nodes in any `TypeInference` \
476+
region it analyzes.",
475477
)
476478
}
477479

crates/ty_test/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ ruff_python_trivia = { workspace = true }
1818
ruff_source_file = { workspace = true }
1919
ruff_text_size = { workspace = true }
2020
ruff_python_ast = { workspace = true }
21+
ty_project = { workspace = true, features = ["testing"] }
2122
ty_python_semantic = { workspace = true, features = ["serde"] }
2223
ty_vendored = { workspace = true }
2324

crates/ty_test/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use ruff_db::testing::{setup_logging, setup_logging_with_filter};
1717
use ruff_source_file::{LineIndex, OneIndexed};
1818
use std::backtrace::BacktraceStatus;
1919
use std::fmt::Write;
20+
use ty_project::pull_types_visitor::pull_types;
2021
use ty_python_semantic::types::check_types;
2122
use ty_python_semantic::{
2223
Program, ProgramSettings, PythonPath, PythonPlatform, PythonVersionSource,
@@ -294,6 +295,10 @@ fn run_test(
294295
let failures: Failures = test_files
295296
.into_iter()
296297
.filter_map(|test_file| {
298+
if !KNOWN_PULL_TYPES_FAILURES.contains(&&*relative_fixture_path.as_str().replace('\\', "/")) {
299+
pull_types(db, test_file.file);
300+
}
301+
297302
let parsed = parsed_module(db, test_file.file).load(db);
298303

299304
let mut diagnostics: Vec<Diagnostic> = parsed
@@ -350,6 +355,7 @@ fn run_test(
350355
});
351356
}
352357
};
358+
353359
diagnostics.extend(type_diagnostics.into_iter().cloned());
354360
diagnostics.sort_by(|left, right|left.rendering_sort_key(db).cmp(&right.rendering_sort_key(db)));
355361

@@ -363,6 +369,7 @@ fn run_test(
363369
if test.should_snapshot_diagnostics() {
364370
snapshot_diagnostics.extend(diagnostics);
365371
}
372+
366373
failure
367374
})
368375
.collect();
@@ -462,3 +469,12 @@ fn create_diagnostic_snapshot(
462469
}
463470
snapshot
464471
}
472+
473+
const KNOWN_PULL_TYPES_FAILURES: &[&str] = &[
474+
"crates/ty_python_semantic/resources/mdtest/annotations/any.md",
475+
"crates/ty_python_semantic/resources/mdtest/annotations/callable.md",
476+
"crates/ty_python_semantic/resources/mdtest/annotations/unsupported_special_forms.md",
477+
"crates/ty_python_semantic/resources/mdtest/type_api.md",
478+
"crates/ty_python_semantic/resources/mdtest/type_qualifiers/classvar.md",
479+
"crates/ty_python_semantic/resources/mdtest/type_qualifiers/final.md",
480+
];

0 commit comments

Comments
 (0)