[ty] Fix completion in decorators with missing declaration (#22177)

This commit is contained in:
Micha Reiser
2025-12-25 16:05:47 +01:00
committed by GitHub
parent dd3a985109
commit 014abe1ee1
6 changed files with 248 additions and 61 deletions

View File

@@ -477,6 +477,17 @@ impl<'src> Parser<'src> {
}
}
pub(super) fn parse_missing_name(&mut self) -> ast::ExprName {
let identifier = self.parse_missing_identifier();
ast::ExprName {
range: identifier.range,
id: identifier.id,
ctx: ExprContext::Invalid,
node_index: AtomicNodeIndex::NONE,
}
}
/// Parses an identifier.
///
/// For an invalid identifier, the `id` field will be an empty string.
@@ -524,16 +535,20 @@ impl<'src> Parser<'src> {
node_index: AtomicNodeIndex::NONE,
}
} else {
self.add_error(
ParseErrorType::OtherError("Expected an identifier".into()),
range,
);
self.parse_missing_identifier()
}
}
ast::Identifier {
id: Name::empty(),
range: self.missing_node_range(),
node_index: AtomicNodeIndex::NONE,
}
fn parse_missing_identifier(&mut self) -> ast::Identifier {
self.add_error(
ParseErrorType::OtherError("Expected an identifier".into()),
self.current_token_range(),
);
ast::Identifier {
id: Name::empty(),
range: self.missing_node_range(),
node_index: AtomicNodeIndex::NONE,
}
}

View File

@@ -2782,13 +2782,20 @@ impl<'src> Parser<'src> {
// def foo(): ...
// @@
// def foo(): ...
// @test
// @
// class Test
while self.at(TokenKind::At) {
progress.assert_progressing(self);
let decorator_start = self.node_start();
self.bump(TokenKind::At);
let parsed_expr = self.parse_named_expression_or_higher(ExpressionContext::default());
let parsed_expr = if self.at(TokenKind::Def) || self.at(TokenKind::Class) {
Expr::Name(self.parse_missing_name()).into()
} else {
self.parse_named_expression_or_higher(ExpressionContext::default())
};
if self.options.target_version < PythonVersion::PY39 {
// test_ok decorator_expression_dotted_ident_py38
@@ -2914,21 +2921,27 @@ impl<'src> Parser<'src> {
self.current_token_range(),
);
// TODO(dhruvmanila): It seems that this recovery drops all the parsed
// decorators. Maybe we could convert them into statement expression
// with a flag indicating that this expression is part of a decorator.
// It's only possible to keep them if it's a function or class definition.
// We could possibly keep them if there's indentation error:
//
// ```python
// @decorator
// @decorator
// def foo(): ...
// ```
//
// Or, parse it as a binary expression where the left side is missing.
// We would need to convert each decorator into a binary expression.
self.parse_statement()
let range = self.node_range(start);
ast::StmtFunctionDef {
node_index: AtomicNodeIndex::default(),
range,
is_async: false,
decorator_list: decorators,
name: ast::Identifier {
id: Name::empty(),
range: self.missing_node_range(),
node_index: AtomicNodeIndex::NONE,
},
type_params: None,
parameters: Box::new(ast::Parameters {
range: self.missing_node_range(),
..ast::Parameters::default()
}),
returns: None,
body: vec![],
}
.into()
}
}
}