Repository: rust-lang/rustfmt (rust)

Issue:
skip_macro_invocations: Cannot configure from `rustfmt.toml`
`skip_macro_invocations` option doesn't parse as [documented](https://rust-lang.github.io/rustfmt/?version=master&search=macro#skip_macro_invocations). 

Writing `skip_macro_invocations=["*", "println"]` in `rustfmt.toml` leads to this error:

```
Error: Decoding config file failed:
unknown variant `*`, expected `Name` or `All`
in `skip_macro_invocations`

Please check your config file.
```

It seems that parsing rules are not what is actually designed (`*` or some name), but more like tagged serde enum. These config files work:

```toml
[[skip_macro_invocations]]
Name="aboba"
```

```toml
skip_macro_invocations = ["All"]
```

Pull request description:
handle skip_macro_invocations from config file
fixes #5816

We need to keep the `FromStr` impl for `MacroSelector` (e.g. to support command line config args), so seems reasonable to just have the serde deserializer use that impl too.

I'm not sure whether there's a better/simpler approach to achieve that (e.g. an item-level serde attribute), but adding our own `Deserialize` impl seems trivial enough

Patch:
diff --git a/src/config/macro_names.rs b/src/config/macro_names.rs
index 26ad78d6..61658f0a 100644
--- a/src/config/macro_names.rs
+++ b/src/config/macro_names.rs
@@ -3,7 +3,7 @@
 use itertools::Itertools;
 use std::{fmt, str};
 
-use serde::{Deserialize, Serialize};
+use serde::{Deserialize, Deserializer, Serialize};
 use serde_json as json;
 use thiserror::Error;
 
@@ -30,12 +30,22 @@ impl From<MacroName> for String {
 }
 
 /// Defines a selector to match against a macro.
-#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize)]
 pub enum MacroSelector {
     Name(MacroName),
     All,
 }
 
+impl<'de> Deserialize<'de> for MacroSelector {
+    fn deserialize<D>(de: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let s = String::deserialize(de)?;
+        std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom)
+    }
+}
+
 impl fmt::Display for MacroSelector {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {


Test patch:
diff --git a/tests/config/issue-5816.toml b/tests/config/issue-5816.toml
new file mode 100644
index 00000000..00375746
--- /dev/null
+++ b/tests/config/issue-5816.toml
@@ -0,0 +1,1 @@
+skip_macro_invocations=["*", "println"]
diff --git a/tests/source/skip_macro_invocations/config_file.rs b/tests/source/skip_macro_invocations/config_file.rs
new file mode 100644
index 00000000..e0f5ddf5
--- /dev/null
+++ b/tests/source/skip_macro_invocations/config_file.rs
@@ -0,0 +1,9 @@
+// rustfmt-unstable: true
+// rustfmt-config: issue-5816.toml
+
+fn main() {
+    println!(             "Hello, world!");
+    let     x    =    
+7
+;
+}
diff --git a/tests/target/skip_macro_invocations/config_file.rs b/tests/target/skip_macro_invocations/config_file.rs
new file mode 100644
index 00000000..008e28db
--- /dev/null
+++ b/tests/target/skip_macro_invocations/config_file.rs
@@ -0,0 +1,7 @@
+// rustfmt-unstable: true
+// rustfmt-config: issue-5816.toml
+
+fn main() {
+    println!(             "Hello, world!");
+    let x = 7;
+}