Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ The expressions in these string annotations aren't valid expressions in this con
shouldn't panic.

```py
# Regression test for https://github.com/astral-sh/ty/issues/1865
# error: [fstring-type-annotation]
stringified_fstring_with_conditional: "f'{1 if 1 else 1}'"
# error: [fstring-type-annotation]
stringified_fstring_with_boolean_expression: "f'{1 or 2}'"
# error: [fstring-type-annotation]
stringified_fstring_with_generator_expression: "f'{(i for i in range(5))}'"
# error: [fstring-type-annotation]
stringified_fstring_with_list_comprehension: "f'{[i for i in range(5)]}'"
# error: [fstring-type-annotation]
stringified_fstring_with_dict_comprehension: "f'{ {i: i for i in range(5)} }'"
# error: [fstring-type-annotation]
stringified_fstring_with_set_comprehension: "f'{ {i for i in range(5)} }'"

a: "1 or 2"
b: "(x := 1)"
# error: [invalid-type-form]
Expand Down
5 changes: 5 additions & 0 deletions crates/ty_python_semantic/src/semantic_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,11 @@ impl<'db> SemanticIndex<'db> {
self.scopes_by_node[&node.node_key()]
}

/// Returns the id of the scope that `node` creates, if it exists.
pub(crate) fn try_node_scope(&self, node: NodeWithScopeRef) -> Option<FileScopeId> {
self.scopes_by_node.get(&node.node_key()).copied()
}

/// Checks if there is an import of `__future__.annotations` in the global scope, which affects
/// the logic for type inference.
pub(super) fn has_future_annotations(&self) -> bool {
Expand Down
38 changes: 25 additions & 13 deletions crates/ty_python_semantic/src/types/infer/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7926,7 +7926,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let Some(first_comprehension) = comprehensions_iter.next() else {
unreachable!("Comprehension must contain at least one generator");
};
self.infer_standalone_expression(&first_comprehension.iter, TypeContext::default());
self.infer_maybe_standalone_expression(&first_comprehension.iter, TypeContext::default());

if first_comprehension.is_async {
EvaluationMode::Async
Expand All @@ -7946,9 +7946,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {

let evaluation_mode = self.infer_first_comprehension_iter(generators);

let scope_id = self
let Some(scope_id) = self
.index
.node_scope(NodeWithScopeRef::GeneratorExpression(generator));
.try_node_scope(NodeWithScopeRef::GeneratorExpression(generator))
else {
return Type::unknown();
};
let scope = scope_id.to_scope_id(self.db(), self.file());
let inference = infer_scope_types(self.db(), scope);
let yield_type = inference.expression_type(elt.as_ref());
Expand Down Expand Up @@ -8021,9 +8024,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {

self.infer_first_comprehension_iter(generators);

let scope_id = self
let Some(scope_id) = self
.index
.node_scope(NodeWithScopeRef::ListComprehension(listcomp));
.try_node_scope(NodeWithScopeRef::ListComprehension(listcomp))
else {
return Type::unknown();
};
let scope = scope_id.to_scope_id(self.db(), self.file());
let inference = infer_scope_types(self.db(), scope);
let element_type = inference.expression_type(elt.as_ref());
Expand All @@ -8046,9 +8052,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {

self.infer_first_comprehension_iter(generators);

let scope_id = self
let Some(scope_id) = self
.index
.node_scope(NodeWithScopeRef::DictComprehension(dictcomp));
.try_node_scope(NodeWithScopeRef::DictComprehension(dictcomp))
else {
return Type::unknown();
};
let scope = scope_id.to_scope_id(self.db(), self.file());
let inference = infer_scope_types(self.db(), scope);
let key_type = inference.expression_type(key.as_ref());
Expand All @@ -8071,9 +8080,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {

self.infer_first_comprehension_iter(generators);

let scope_id = self
let Some(scope_id) = self
.index
.node_scope(NodeWithScopeRef::SetComprehension(setcomp));
.try_node_scope(NodeWithScopeRef::SetComprehension(setcomp))
else {
return Type::unknown();
};
let scope = scope_id.to_scope_id(self.db(), self.file());
let inference = infer_scope_types(self.db(), scope);
let element_type = inference.expression_type(elt.as_ref());
Expand Down Expand Up @@ -8165,14 +8177,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
builder.module(),
)
} else {
builder.infer_standalone_expression(iter, tcx)
builder.infer_maybe_standalone_expression(iter, tcx)
}
.iterate(builder.db())
.homogeneous_element_type(builder.db())
});

for expr in ifs {
self.infer_standalone_expression(expr, TypeContext::default());
self.infer_maybe_standalone_expression(expr, TypeContext::default());
}
}

Expand Down Expand Up @@ -8278,7 +8290,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
orelse,
} = if_expression;

let test_ty = self.infer_standalone_expression(test, TypeContext::default());
let test_ty = self.infer_maybe_standalone_expression(test, TypeContext::default());
let body_ty = self.infer_expression(body, tcx);
let orelse_ty = self.infer_expression(orelse, tcx);

Expand Down Expand Up @@ -10341,7 +10353,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let ty = if index == values.len() - 1 {
builder.infer_expression(value, TypeContext::default())
} else {
builder.infer_standalone_expression(value, TypeContext::default())
builder.infer_maybe_standalone_expression(value, TypeContext::default())
};

(ty, value.range())
Expand Down
Loading