mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:45:24 +00:00
[red-knot] add maybe-undefined lint rule (#12414)
Add a lint rule to detect if a name is definitely or possibly undefined at a given usage. If I create the file `undef/main.py` with contents: ```python x = int def foo(): z return x if flag: y = x y ``` And then run `cargo run --bin red_knot -- --current-directory ../ruff-examples/undef`, I get the output: ``` Name 'z' used when not defined. Name 'flag' used when not defined. Name 'y' used when possibly not defined. ``` If I modify the file to add `y = 0` at the top, red-knot re-checks it and I get the new output: ``` Name 'z' used when not defined. Name 'flag' used when not defined. ``` Note that `int` is not flagged, since it's a builtin, and `return x` in the function scope is not flagged, since it refers to the global `x`.
This commit is contained in:
parent
2a8f95c437
commit
f22c8ab811
9 changed files with 199 additions and 10 deletions
|
@ -49,7 +49,6 @@ pub(crate) mod tests {
|
|||
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_db::{Db as SourceDb, Jar as SourceJar, Upcast};
|
||||
use ruff_python_trivia::textwrap;
|
||||
|
||||
use super::{Db, Jar};
|
||||
|
||||
|
@ -91,12 +90,6 @@ pub(crate) mod tests {
|
|||
pub(crate) fn clear_salsa_events(&mut self) {
|
||||
self.take_salsa_events();
|
||||
}
|
||||
|
||||
/// Write auto-dedented text to a file.
|
||||
pub(crate) fn write_dedented(&mut self, path: &str, content: &str) -> anyhow::Result<()> {
|
||||
self.write_file(path, textwrap::dedent(content))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DbWithTestSystem for TestDb {
|
||||
|
|
|
@ -239,6 +239,12 @@ pub struct UnionType<'db> {
|
|||
elements: FxOrderSet<Type<'db>>,
|
||||
}
|
||||
|
||||
impl<'db> UnionType<'db> {
|
||||
pub fn contains(&self, db: &'db dyn Db, ty: Type<'db>) -> bool {
|
||||
self.elements(db).contains(&ty)
|
||||
}
|
||||
}
|
||||
|
||||
struct UnionTypeBuilder<'db> {
|
||||
elements: FxOrderSet<Type<'db>>,
|
||||
db: &'db dyn Db,
|
||||
|
|
|
@ -314,6 +314,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
ast::Stmt::For(for_statement) => self.infer_for_statement(for_statement),
|
||||
ast::Stmt::Import(import) => self.infer_import_statement(import),
|
||||
ast::Stmt::ImportFrom(import) => self.infer_import_from_statement(import),
|
||||
ast::Stmt::Return(ret) => self.infer_return_statement(ret),
|
||||
ast::Stmt::Break(_) | ast::Stmt::Continue(_) | ast::Stmt::Pass(_) => {
|
||||
// No-op
|
||||
}
|
||||
|
@ -551,6 +552,12 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
self.types.definitions.insert(definition, ty);
|
||||
}
|
||||
|
||||
fn infer_return_statement(&mut self, ret: &ast::StmtReturn) {
|
||||
if let Some(value) = &ret.value {
|
||||
self.infer_expression(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn module_ty_from_name(&self, name: &ast::Identifier) -> Type<'db> {
|
||||
let module_name = ModuleName::new(&name.id);
|
||||
let module =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue