Use top-level semantic detection for E402 (#6526)

## Summary

Noticed in https://github.com/astral-sh/ruff/pull/6378. Given `import h;
import i`, we don't consider `import i` to be a "top-level" import for
E402 purposes, which is wrong. Similarly, we _do_ consider `import k` to
be a "top-level" import in:

```python
if __name__ == "__main__":
    import j; \
import k
```

Using the semantic detection, rather than relying on newline position,
fixes both cases.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-08-12 14:52:44 -04:00 committed by GitHub
parent c03e2acadb
commit b49c80f8c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 28 additions and 19 deletions

View File

@ -30,3 +30,10 @@ def foo() -> None:
if __name__ == "__main__":
import g
import h; import i
if __name__ == "__main__":
import j; \
import k

View File

@ -520,11 +520,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pycodestyle::rules::multiple_imports_on_one_line(checker, stmt, names);
}
if checker.enabled(Rule::ModuleImportNotAtTopOfFile) {
pycodestyle::rules::module_import_not_at_top_of_file(
checker,
stmt,
checker.locator,
);
pycodestyle::rules::module_import_not_at_top_of_file(checker, stmt);
}
if checker.enabled(Rule::GlobalStatement) {
for name in names {
@ -689,11 +685,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
let module = module.as_deref();
let level = level.map(|level| level.to_u32());
if checker.enabled(Rule::ModuleImportNotAtTopOfFile) {
pycodestyle::rules::module_import_not_at_top_of_file(
checker,
stmt,
checker.locator,
);
pycodestyle::rules::module_import_not_at_top_of_file(checker, stmt);
}
if checker.enabled(Rule::GlobalStatement) {
for name in names {

View File

@ -1,8 +1,6 @@
use ruff_python_ast::{Alias, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_source_file::Locator;
use ruff_python_ast::{Alias, Ranged, Stmt};
use crate::checkers::ast::Checker;
@ -81,12 +79,8 @@ pub(crate) fn multiple_imports_on_one_line(checker: &mut Checker, stmt: &Stmt, n
}
/// E402
pub(crate) fn module_import_not_at_top_of_file(
checker: &mut Checker,
stmt: &Stmt,
locator: &Locator,
) {
if checker.semantic().seen_import_boundary() && locator.is_at_start_of_line(stmt.start()) {
pub(crate) fn module_import_not_at_top_of_file(checker: &mut Checker, stmt: &Stmt) {
if checker.semantic().seen_import_boundary() && checker.semantic().at_top_level() {
checker
.diagnostics
.push(Diagnostic::new(ModuleImportNotAtTopOfFile, stmt.range()));

View File

@ -9,4 +9,20 @@ E402.py:24:1: E402 Module level import not at top of file
| ^^^^^^^^ E402
|
E402.py:34:1: E402 Module level import not at top of file
|
32 | import g
33 |
34 | import h; import i
| ^^^^^^^^ E402
|
E402.py:34:11: E402 Module level import not at top of file
|
32 | import g
33 |
34 | import h; import i
| ^^^^^^^^ E402
|