diff options
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/items_after_statements.rs')
-rw-r--r-- | src/tools/clippy/clippy_lints/src/items_after_statements.rs | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs new file mode 100644 index 000000000..46d439b44 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs @@ -0,0 +1,88 @@ +//! lint when items are used after statements + +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Block, ItemKind, StmtKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for items declared after some statement in a block. + /// + /// ### Why is this bad? + /// Items live for the entire scope they are declared + /// in. But statements are processed in order. This might cause confusion as + /// it's hard to figure out which item is meant in a statement. + /// + /// ### Example + /// ```rust + /// fn foo() { + /// println!("cake"); + /// } + /// + /// fn main() { + /// foo(); // prints "foo" + /// fn foo() { + /// println!("foo"); + /// } + /// foo(); // prints "foo" + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// fn foo() { + /// println!("cake"); + /// } + /// + /// fn main() { + /// fn foo() { + /// println!("foo"); + /// } + /// foo(); // prints "foo" + /// foo(); // prints "foo" + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub ITEMS_AFTER_STATEMENTS, + pedantic, + "blocks where an item comes after a statement" +} + +declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); + +impl EarlyLintPass for ItemsAfterStatements { + fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { + if in_external_macro(cx.sess(), item.span) { + return; + } + + // skip initial items and trailing semicolons + let stmts = item + .stmts + .iter() + .map(|stmt| &stmt.kind) + .skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty)); + + // lint on all further items + for stmt in stmts { + if let StmtKind::Item(ref it) = *stmt { + if in_external_macro(cx.sess(), it.span) { + return; + } + if let ItemKind::MacroDef(..) = it.kind { + // do not lint `macro_rules`, but continue processing further statements + continue; + } + span_lint( + cx, + ITEMS_AFTER_STATEMENTS, + it.span, + "adding items after statements is confusing, since items exist from the \ + start of the scope", + ); + } + } + } +} |