Skip to content

Commit

Permalink
Fix users ability to override the default nonstandard macro braces
Browse files Browse the repository at this point in the history
  • Loading branch information
DevinR528 committed Jun 10, 2021
1 parent a18e9d4 commit d203656
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 28 deletions.
74 changes: 49 additions & 25 deletions clippy_lints/src/nonstandard_macro_braces.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -32,20 +35,14 @@ 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)
type MacroInfo<'a> = (&'a str, &'a (String, String), String);

#[derive(Clone, Debug, Default)]
pub struct MacroBraces {
macro_braces: FxHashSet<MacroMatcher>,
macro_braces: FxHashMap<String, (String, String)>,
done: FxHashSet<Span>,
}

Expand Down Expand Up @@ -87,7 +84,7 @@ impl EarlyLintPass for MacroBraces {
fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option<MacroInfo<'a>> {
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));
Expand Down Expand Up @@ -123,21 +120,16 @@ fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), nam
);
}

fn find_matching_macro(span: Span, braces: &FxHashSet<MacroMatcher>) -> 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<String, (String, String)>,
) -> 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<MacroMatcher>) -> FxHashSet<MacroMatcher> {
fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
let mut braces = vec![
macro_matcher!(
name: "print",
Expand All @@ -159,6 +151,10 @@ fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashSet<MacroMatcher> {
name: "write",
braces: ("(", ")"),
),
macro_matcher!(
name: "writeln",
braces: ("(", ")"),
),
macro_matcher!(
name: "format",
braces: ("(", ")"),
Expand All @@ -173,12 +169,40 @@ fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashSet<MacroMatcher> {
),
]
.into_iter()
.collect::<FxHashSet<_>>();
.collect::<FxHashMap<_, _>>();
// 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<H: Hasher>(&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<D>(deser: D) -> Result<Self, D::Error>
where
Expand Down
6 changes: 5 additions & 1 deletion tests/ui-toml/nonstandard_macro_braces/clippy.toml
Original file line number Diff line number Diff line change
@@ -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 = "[" },
]
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

2 changes: 1 addition & 1 deletion tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit d203656

Please sign in to comment.