Skip to content

Commit

Permalink
Add macro_braces lint to check for irregular brace use in certain macros
Browse files Browse the repository at this point in the history
  • Loading branch information
DevinR528 committed May 31, 2021
1 parent 5cdba7d commit a891cdf
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2695,6 +2695,7 @@ Released 2018-09-13
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`unconventional_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#unconventional_macro_braces
[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
Expand Down
4 changes: 4 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ mod transmute;
mod transmuting_null;
mod try_err;
mod types;
mod unconventional_macro_braces;
mod undropped_manually_drops;
mod unicode;
mod unit_return_expecting_ord;
Expand Down Expand Up @@ -932,6 +933,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
types::REDUNDANT_ALLOCATION,
types::TYPE_COMPLEXITY,
types::VEC_BOX,
unconventional_macro_braces::UNCONVENTIONAL_MACRO_BRACES,
undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
unicode::INVISIBLE_CHARACTERS,
unicode::NON_ASCII_LITERAL,
Expand Down Expand Up @@ -1769,6 +1771,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(strings::STRING_LIT_AS_BYTES),
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(unconventional_macro_braces::UNCONVENTIONAL_MACRO_BRACES),
LintId::of(use_self::USE_SELF),
]);

Expand Down Expand Up @@ -2032,6 +2035,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
single_char_binding_names_threshold,
});
store.register_early_pass(|| box unconventional_macro_braces::MacroBraces::default());
store.register_late_pass(|| box macro_use::MacroUseImports::default());
store.register_late_pass(|| box map_identity::MapIdentity);
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
Expand Down
146 changes: 146 additions & 0 deletions clippy_lints/src/unconventional_macro_braces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;

declare_clippy_lint! {
/// **What it does:** Checks that common macros are used with consistent bracing.
///
/// **Why is this bad?** This is mostly a consistency lint although using () or []
/// doesn't give you a semicolon in item position, which can be unexpected.
///
/// **Known problems:**
/// None
///
/// **Example:**
///
/// ```rust
/// vec!{1, 2, 3};
/// ```
/// Use instead:
/// ```rust
/// vec![1, 2, 3];
/// ```
pub UNCONVENTIONAL_MACRO_BRACES,
nursery,
"check consistent use of braces in macro"
}

#[derive(Clone, Copy, Debug)]
struct MacroMatcher {
name: &'static str,
braces: (&'static str, &'static str),
}

const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];

const MACROS_TO_BRACES: &[MacroMatcher] = &[
MacroMatcher {
name: "print",
braces: ("(", ")"),
},
MacroMatcher {
name: "println",
braces: ("(", ")"),
},
MacroMatcher {
name: "eprint",
braces: ("(", ")"),
},
MacroMatcher {
name: "eprintln",
braces: ("(", ")"),
},
MacroMatcher {
name: "write",
braces: ("(", ")"),
},
MacroMatcher {
name: "format",
braces: ("(", ")"),
},
MacroMatcher {
name: "format_args",
braces: ("(", ")"),
},
MacroMatcher {
name: "vec",
braces: ("[", "]"),
},
MacroMatcher {
name: "quote",
braces: ("{", "}"),
},
];

type MacroInfo = (&'static str, (&'static str, &'static str), String);

#[derive(Clone, Debug, Default)]
pub struct MacroBraces {
done: Vec<&'static str>,
}

impl_lint_pass!(MacroBraces => [UNCONVENTIONAL_MACRO_BRACES]);

impl EarlyLintPass for MacroBraces {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, &self.done) {
emit_help(cx, snip, braces, name, item.span);
self.done.push(name);
}
}

fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, &self.done) {
emit_help(cx, snip, braces, name, stmt.span);
self.done.push(name);
}
}

fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, &self.done) {
emit_help(cx, snip, braces, name, expr.span);
self.done.push(name);
}
}
}

fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, done: &[&str]) -> Option<MacroInfo> {
if_chain! {
if in_macro(span);
if let Some(MacroMatcher { name, braces }) = find_matching_macro(span);
if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
let c = snip.replace(" ", ""); // make formatting consistent
if !c.starts_with(&format!("{}!{}", name, braces.0));
if !done.contains(&name);
then {
Some((name, braces, snip))
} else {
None
}
}
}

fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: (&str, &str), name: &str, span: Span) {
let mut help = snip;
for b in BRACES.iter().filter(|b| b.0 != braces.0) {
help = help.replace(b.0, braces.0).replace(b.1, braces.1);
}
span_lint_and_help(
cx,
UNCONVENTIONAL_MACRO_BRACES,
span,
&format!("use of irregular braces for `{}!` macro", name),
Some(span.ctxt().outer_expn_data().call_site),
&format!("consider writing `{}`", help),
);
}

fn find_matching_macro(span: Span) -> Option<MacroMatcher> {
MACROS_TO_BRACES
.iter()
.copied()
.find(|mac| is_direct_expn_of(span, mac.name).is_some())
}
7 changes: 7 additions & 0 deletions tests/ui/unconventional_macro_braces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![warn(clippy::unconventional_macro_braces)]

#[rustfmt::skip]
fn main() {
let x = vec! {1, 2, 3};
let y = format!["ugh {} stop being such a good compiler", "hello"];
}
29 changes: 29 additions & 0 deletions tests/ui/unconventional_macro_braces.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error: use of irregular braces for `vec!` macro
--> $DIR/unconventional_macro_braces.rs:5:13
|
LL | let x = vec! {1, 2, 3};
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::unconventional-macro-braces` implied by `-D warnings`
help: consider writing `vec! [1, 2, 3]`
--> $DIR/unconventional_macro_braces.rs:5:13
|
LL | let x = vec! {1, 2, 3};
| ^^^^^^^^^^^^^^
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)

error: use of irregular braces for `format!` macro
--> $DIR/unconventional_macro_braces.rs:6:13
|
LL | let y = format!["ugh {} stop being such a good compiler", "hello"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider writing `format!("ugh () stop being such a good compiler", "hello")`
--> $DIR/unconventional_macro_braces.rs:6:13
|
LL | let y = format!["ugh {} stop being such a good compiler", "hello"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

0 comments on commit a891cdf

Please sign in to comment.