mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-12 23:55:08 +00:00
[syntax-errors] Reimplement PLE0118 (#17135)
Summary -- This PR reimplements [load-before-global-declaration (PLE0118)](https://docs.astral.sh/ruff/rules/load-before-global-declaration/) as a semantic syntax error. I added a `global` method to the `SemanticSyntaxContext` trait to make this very easy, at least in ruff. Does red-knot have something similar? If this approach will also work in red-knot, I think some of the other PLE rules are also compile-time errors in CPython, PLE0117 in particular. 0115 and 0116 also mention `SyntaxError`s in their docs, but I haven't confirmed them in the REPL yet. Test Plan -- Existing linter tests for PLE0118. I think this actually can't be tested very easily in an inline test because the `TestContext` doesn't have a real way to track globals. --------- Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
d45593288f
commit
d382065f8a
5 changed files with 62 additions and 27 deletions
|
@ -12,7 +12,7 @@ use ruff_python_ast::{
|
|||
Expr, ExprContext, IrrefutablePatternKind, Pattern, PythonVersion, Stmt, StmtExpr,
|
||||
StmtImportFrom,
|
||||
};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -397,6 +397,21 @@ impl SemanticSyntaxChecker {
|
|||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
// PLE0118
|
||||
if let Some(stmt) = ctx.global(id) {
|
||||
let start = stmt.start();
|
||||
if expr.start() < start {
|
||||
Self::add_error(
|
||||
ctx,
|
||||
SemanticSyntaxErrorKind::LoadBeforeGlobalDeclaration {
|
||||
name: id.to_string(),
|
||||
start,
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Yield(ast::ExprYield {
|
||||
value: Some(value), ..
|
||||
|
@ -499,6 +514,9 @@ impl Display for SemanticSyntaxError {
|
|||
write!(f, "cannot delete `__debug__` on Python {python_version} (syntax was removed in 3.9)")
|
||||
}
|
||||
},
|
||||
SemanticSyntaxErrorKind::LoadBeforeGlobalDeclaration { name, start: _ } => {
|
||||
write!(f, "name `{name}` is used prior to global declaration")
|
||||
}
|
||||
SemanticSyntaxErrorKind::InvalidStarExpression => {
|
||||
f.write_str("can't use starred expression here")
|
||||
}
|
||||
|
@ -616,6 +634,19 @@ pub enum SemanticSyntaxErrorKind {
|
|||
/// [BPO 45000]: https://github.com/python/cpython/issues/89163
|
||||
WriteToDebug(WriteToDebugKind),
|
||||
|
||||
/// Represents the use of a `global` variable before its `global` declaration.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```python
|
||||
/// counter = 1
|
||||
/// def increment():
|
||||
/// print(f"Adding 1 to {counter}")
|
||||
/// global counter
|
||||
/// counter += 1
|
||||
/// ```
|
||||
LoadBeforeGlobalDeclaration { name: String, start: TextSize },
|
||||
|
||||
/// Represents the use of a starred expression in an invalid location, such as a `return` or
|
||||
/// `yield` statement.
|
||||
///
|
||||
|
@ -758,6 +789,9 @@ pub trait SemanticSyntaxContext {
|
|||
/// The target Python version for detecting backwards-incompatible syntax changes.
|
||||
fn python_version(&self) -> PythonVersion;
|
||||
|
||||
/// Return the [`TextRange`] at which a name is declared as `global` in the current scope.
|
||||
fn global(&self, name: &str) -> Option<TextRange>;
|
||||
|
||||
fn report_semantic_error(&self, error: SemanticSyntaxError);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue