From d2036565691acee93a4ee58462ac30bb30d288a0 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 7 Jun 2021 07:29:05 -0400 Subject: [PATCH] Fix users ability to override the default nonstandard macro braces --- clippy_lints/src/nonstandard_macro_braces.rs | 74 ++++++++++++------- .../nonstandard_macro_braces/clippy.toml | 6 +- .../conf_nonstandard_macro_braces.rs | 2 + .../conf_nonstandard_macro_braces.stderr | 15 +++- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index ca10cd1416ee..4ba8a278526b 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,9 +1,12 @@ -use std::fmt; +use std::{ + fmt, + hash::{Hash, Hasher}, +}; 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_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -32,12 +35,6 @@ declare_clippy_lint! { "check consistent use of braces in macro" } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct MacroMatcher { - name: String, - braces: (String, String), -} - const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")]; /// The (name, (open brace, close brace), source snippet) @@ -45,7 +42,7 @@ type MacroInfo<'a> = (&'a str, &'a (String, String), String); #[derive(Clone, Debug, Default)] pub struct MacroBraces { - macro_braces: FxHashSet, + macro_braces: FxHashMap, done: FxHashSet, } @@ -87,7 +84,7 @@ impl EarlyLintPass for MacroBraces { fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option> { if_chain! { if in_macro(span); - if let Some(MacroMatcher { name, braces }) = find_matching_macro(span, &this.macro_braces); + if let Some((name, braces)) = find_matching_macro(span, &this.macro_braces); 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)); @@ -123,21 +120,16 @@ fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), nam ); } -fn find_matching_macro(span: Span, braces: &FxHashSet) -> Option<&MacroMatcher> { - braces.iter().find(|mac| is_direct_expn_of(span, &mac.name).is_some()) -} - -macro_rules! macro_matcher { - (name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => { - MacroMatcher { - name: $name.to_owned(), - braces: ($open.to_owned(), $close.to_owned()), - } - }; +fn find_matching_macro( + span: Span, + braces: &FxHashMap, +) -> Option<(&String, &(String, String))> { + braces + .iter() + .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some()) } -pub(crate) use macro_matcher; -fn macro_braces(conf: FxHashSet) -> FxHashSet { +fn macro_braces(conf: FxHashSet) -> FxHashMap { let mut braces = vec![ macro_matcher!( name: "print", @@ -159,6 +151,10 @@ fn macro_braces(conf: FxHashSet) -> FxHashSet { name: "write", braces: ("(", ")"), ), + macro_matcher!( + name: "writeln", + braces: ("(", ")"), + ), macro_matcher!( name: "format", braces: ("(", ")"), @@ -173,12 +169,40 @@ fn macro_braces(conf: FxHashSet) -> FxHashSet { ), ] .into_iter() - .collect::>(); + .collect::>(); // We want users items to override any existing items - braces.extend(conf); + for it in conf { + braces.insert(it.name, it.braces); + } braces } +macro_rules! macro_matcher { + (name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => { + ($name.to_owned(), ($open.to_owned(), $close.to_owned())) + }; +} +pub(crate) use macro_matcher; + +#[derive(Clone, Debug)] +pub struct MacroMatcher { + name: String, + braces: (String, String), +} + +impl Hash for MacroMatcher { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl PartialEq for MacroMatcher { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} +impl Eq for MacroMatcher {} + impl<'de> Deserialize<'de> for MacroMatcher { fn deserialize(deser: D) -> Result where diff --git a/tests/ui-toml/nonstandard_macro_braces/clippy.toml b/tests/ui-toml/nonstandard_macro_braces/clippy.toml index ba5859e6396a..e3f8a90a326f 100644 --- a/tests/ui-toml/nonstandard_macro_braces/clippy.toml +++ b/tests/ui-toml/nonstandard_macro_braces/clippy.toml @@ -1 +1,5 @@ -standard-macro-braces = [ { name = "quote", brace = "{" }, { name = "quote::quote", brace = "{" } ] +standard-macro-braces = [ + { name = "quote", brace = "{" }, + { name = "quote::quote", brace = "{" }, + { name = "eprint", brace = "[" }, +] diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index 2590d9f4e831..9d2b80b0ebc0 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -31,4 +31,6 @@ fn main() { let _ = vec! [0 ,0 ,0]; let _ = format!("fds{}fds", 10); let _ = test2!["{}{}{}", 1, 2, 3]; + + eprint!("test if user config overrides defaults"); } diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index c869fc628c9e..ba68c9da849d 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -67,5 +67,18 @@ LL | let _ = test!(); | ------- in this macro invocation = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 5 previous errors +error: use of irregular braces for `eprint!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:35:5 + | +LL | eprint!("test if user config overrides defaults"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider writing `eprint!["test if user config overrides defaults"];` + --> $DIR/conf_nonstandard_macro_braces.rs:35:5 + | +LL | eprint!("test if user config overrides defaults"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `eprint` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index ef0fdd8accd4..f179479cbec4 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `standard-macro-braces`, `cargo-ignore-publish`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `third-party` at line 5 column 1 error: aborting due to previous error