Extract macros into separate submodule
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a58642c..29043b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,10 @@
+# v0.4.4 (2019-11-13)
+* Fix `abort_if_dirty` + warnings bug
+* Allow trailing commas in macros
+
 # v0.4.2 (2019-11-7)
 * FINALLY fixed `__pme__suggestions not found` bug
 
-
 # v0.4.1 (2019-11-7) YANKED
 * Fixed `__pme__suggestions not found` bug
 * Documentation improvements, links checked
diff --git a/proc-macro-error/src/lib.rs b/proc-macro-error/src/lib.rs
index c714369..5fb0223 100644
--- a/proc-macro-error/src/lib.rs
+++ b/proc-macro-error/src/lib.rs
@@ -194,6 +194,8 @@
 
 pub mod dummy;
 
+mod macros;
+
 #[cfg(not(any(pme_nightly, nightly_fmt)))]
 #[path = "stable.rs"]
 mod imp;
@@ -202,218 +204,6 @@
 #[path = "nightly.rs"]
 mod imp;
 
-// FIXME: this can be greatly simplified via $()?
-// as soon as MRSV hits 1.32
-
-/// Build [`Diagnostic`](struct.Diagnostic.html) instance from provided arguments.
-///
-/// # Syntax
-///
-/// See [the guide](index.html#guide).
-///
-#[macro_export]
-macro_rules! diagnostic {
-    // from alias
-    ($err:expr) => { $crate::Diagnostic::from($err) };
-
-    // span, message, help
-    ($span:expr, $level:expr, $fmt:expr, $($args:expr),+ ; $($rest:tt)+) => {{
-        let diag = $crate::Diagnostic::spanned(
-            $span.into(),
-            $level,
-            format!($fmt, $($args),*)
-        );
-        $crate::__pme__suggestions!(diag $($rest)*);
-        diag
-    }};
-
-    ($span:expr, $level:expr, $msg:expr ; $($rest:tt)+) => {{
-        let diag = $crate::Diagnostic::spanned($span.into(), $level, $msg.to_string());
-        $crate::__pme__suggestions!(diag $($rest)*);
-        diag
-    }};
-
-    // span, message, no help
-    ($span:expr, $level:expr, $fmt:expr, $($args:expr),+) => {{
-        $crate::Diagnostic::spanned(
-            $span.into(),
-            $level,
-            format!($fmt, $($args),*)
-        )
-    }};
-
-    ($span:expr, $level:expr, $msg:expr) => {{
-        $crate::Diagnostic::spanned($span.into(), $level, $msg.to_string())
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! __pme__suggestions {
-    ($var:ident) => ();
-
-    ($var:ident $help:ident =? $msg:expr) => {
-        let $var = if let Some(msg) = $msg {
-            $var.suggestion(stringify!($help), msg.to_string())
-        } else {
-            $var
-        };
-    };
-    ($var:ident $help:ident =? $span:expr => $msg:expr) => {
-        let $var = if let Some(msg) = $msg {
-            $var.span_suggestion($span.into(), stringify!($help), msg.to_string())
-        } else {
-            $var
-        };
-    };
-
-    ($var:ident $help:ident =? $msg:expr ; $($rest:tt)*) => {
-        $crate::__pme__suggestions!($var $help =? $msg);
-        $crate::__pme__suggestions!($var $($rest)*);
-    };
-    ($var:ident $help:ident =? $span:expr => $msg:expr ; $($rest:tt)*) => {
-        $crate::__pme__suggestions!($var $help =? $span => $msg);
-        $crate::__pme__suggestions!($var $($rest)*);
-    };
-
-    ($var:ident $help:ident = $msg:expr) => {
-        let $var = $var.suggestion(stringify!($help), $msg.to_string());
-    };
-    ($var:ident $help:ident = $fmt:expr, $($args:expr),*) => {
-        let $var = $var.suggestion(
-            stringify!($help),
-            format!($fmt, $($args),*)
-        );
-    };
-    ($var:ident $help:ident = $span:expr => $msg:expr) => {
-        let $var = $var.span_suggestion($span.into(), stringify!($help), $msg.to_string());
-    };
-    ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),*) => {
-        let $var = $var.span_suggestion(
-            $span.into(),
-            stringify!($help),
-            format!($fmt, $($args),*)
-        );
-    };
-
-    ($var:ident $help:ident = $msg:expr ; $($rest:tt)*) => {
-        $crate::__pme__suggestions!($var $help = $msg);
-        $crate::__pme__suggestions!($var $($rest)*);
-    };
-    ($var:ident $help:ident = $fmt:expr, $($args:expr),* ; $($rest:tt)*) => {
-        $crate::__pme__suggestions!($var $help = $fmt, $($args),*);
-        $crate::__pme__suggestions!($var $($rest)*);
-    };
-    ($var:ident $help:ident = $span:expr => $msg:expr ; $($rest:tt)*) => {
-        $crate::__pme__suggestions!($var $help = $span => $msg);
-        $crate::__pme__suggestions!($var $($rest)*);
-    };
-    ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),* ; $($rest:tt)*) => {
-        $crate::__pme__suggestions!($var $help = $span => $fmt, $($args),*);
-        $crate::__pme__suggestions!($var $($rest)*);
-    };
-}
-
-/// Abort proc-macro execution right now and display the error.
-///
-/// # Syntax
-///
-/// See [the guide](index.html#guide).
-#[macro_export]
-macro_rules! abort {
-    ($err:expr) => {
-        $crate::diagnostic!($err).abort()
-    };
-
-    ($span:expr, $($tts:tt)*) => {{
-        $crate::diagnostic!($span, $crate::Level::Error, $($tts)*).abort()
-    }};
-}
-
-/// Shortcut for `abort!(Span::call_site(), msg...)`. This macro
-/// is still preferable over plain panic, panics are not for error reporting.
-///
-/// # Syntax
-///
-/// See [the guide](index.html#guide).
-///
-#[macro_export]
-macro_rules! abort_call_site {
-    ($($tts:tt)*) => {
-        $crate::diagnostic!(
-            $crate::proc_macro2::Span::call_site(),
-            $crate::Level::Error,
-            $($tts)*
-        ).abort()
-    };
-}
-
-/// Emit an error while not aborting the proc-macro right away.
-///
-/// # Syntax
-///
-/// See [the guide](index.html#guide).
-///
-#[macro_export]
-macro_rules! emit_error {
-    ($err:expr) => {
-        $crate::diagnostic!($err).emit()
-    };
-
-    ($span:expr, $($tts:tt)*) => {
-        $crate::diagnostic!($span, $crate::Level::Error, $($tts)*).emit()
-    };
-}
-
-/// Shortcut for `emit_error!(Span::call_site(), ...)`. This macro
-/// is still preferable over plain panic, panics are not for error reporting..
-///
-/// # Syntax
-///
-/// See [the guide](index.html#guide).
-///
-#[macro_export]
-macro_rules! emit_call_site_error {
-    ($($tts:tt)*) => {
-        $crate::diagnostic!(
-            $crate::proc_macro2::Span()::call_site(),
-            $crate::Level::Error,
-            $($tts)*
-        ).emit()
-    };
-}
-
-/// Emit a warning. Warnings are not errors and compilation won't fail because of them.
-///
-/// **Does nothing on stable**
-///
-/// # Syntax
-///
-/// See [the guide](index.html#guide).
-///
-#[macro_export]
-macro_rules! emit_warning {
-    ($span:expr, $($tts:tt)*) => {
-        $crate::diagnostic!($span, $crate::Level::Warning, $($tts)*).emit()
-    };
-}
-
-/// Shortcut for `emit_warning!(Span::call_site(), ...)`.
-///
-/// **Does nothing on stable**
-///
-/// # Syntax
-///
-/// See [the guide](index.html#guide).
-///
-#[macro_export]
-macro_rules! emit_call_site_warning {
-    ($($tts:tt)*) => {{
-        let span = $crate::proc_macro2::Span()::call_site();
-        $crate::diagnostic!(span, $crate::Level::Warning, $($tts)*).emit()
-    }};
-}
-
 /// Represents a diagnostic level
 ///
 /// # Warnings
diff --git a/proc-macro-error/src/macros.rs b/proc-macro-error/src/macros.rs
new file mode 100644
index 0000000..117612c
--- /dev/null
+++ b/proc-macro-error/src/macros.rs
@@ -0,0 +1,257 @@
+// FIXME: this can be greatly simplified via $()?
+// as soon as MRSV hits 1.32
+
+/// Build [`Diagnostic`](struct.Diagnostic.html) instance from provided arguments.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! diagnostic {
+    // from alias
+    ($err:expr) => { $crate::Diagnostic::from($err) };
+
+    // span, message, help
+    ($span:expr, $level:expr, $fmt:expr, $($args:expr),+ ; $($rest:tt)+) => {{
+        let diag = $crate::Diagnostic::spanned(
+            $span.into(),
+            $level,
+            format!($fmt, $($args),*)
+        );
+        $crate::__pme__suggestions!(diag $($rest)*);
+        diag
+    }};
+
+    ($span:expr, $level:expr, $msg:expr ; $($rest:tt)+) => {{
+        let diag = $crate::Diagnostic::spanned($span.into(), $level, $msg.to_string());
+        $crate::__pme__suggestions!(diag $($rest)*);
+        diag
+    }};
+
+    // span, message, no help
+    ($span:expr, $level:expr, $fmt:expr, $($args:expr),+) => {{
+        $crate::Diagnostic::spanned(
+            $span.into(),
+            $level,
+            format!($fmt, $($args),*)
+        )
+    }};
+
+    ($span:expr, $level:expr, $msg:expr) => {{
+        $crate::Diagnostic::spanned($span.into(), $level, $msg.to_string())
+    }};
+
+
+    // trailing commas
+
+    ($span:expr, $level:expr, $fmt:expr, $($args:expr),+, ; $($rest:tt)+) => {
+        $crate::diagnostic!($span, $level, $fmt, $($args),* ; $($rest)*)
+    };
+    ($span:expr, $level:expr, $msg:expr, ; $($rest:tt)+) => {
+        $crate::diagnostic!($span, $level, $msg ; $($rest)*)
+    };
+    ($span:expr, $level:expr, $fmt:expr, $($args:expr),+,) => {
+        $crate::diagnostic!($span, $level, $fmt, $($args),*)
+    };
+    ($span:expr, $level:expr, $msg:expr,) => {
+        $crate::diagnostic!($span, $level, $msg)
+    };
+    // ($err:expr,) => { $crate::diagnostic!($err) };
+}
+
+/// Abort proc-macro execution right now and display the error.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+#[macro_export]
+macro_rules! abort {
+    ($err:expr) => {
+        $crate::diagnostic!($err).abort()
+    };
+
+    ($span:expr, $($tts:tt)*) => {
+        $crate::diagnostic!($span, $crate::Level::Error, $($tts)*).abort()
+    };
+}
+
+/// Shortcut for `abort!(Span::call_site(), msg...)`. This macro
+/// is still preferable over plain panic, panics are not for error reporting.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! abort_call_site {
+    ($($tts:tt)*) => {
+        $crate::diagnostic!(
+            $crate::proc_macro2::Span::call_site(),
+            $crate::Level::Error,
+            $($tts)*
+        ).abort()
+    };
+}
+
+/// Emit an error while not aborting the proc-macro right away.
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_error {
+    ($err:expr) => {
+        $crate::diagnostic!($err).emit()
+    };
+
+    ($span:expr, $($tts:tt)*) => {{
+        let level = $crate::Level::Error;
+        $crate::diagnostic!($span, level, $($tts)*).emit()
+    }};
+}
+
+/// Shortcut for `emit_error!(Span::call_site(), ...)`. This macro
+/// is still preferable over plain panic, panics are not for error reporting..
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_call_site_error {
+    ($($tts:tt)*) => {
+        $crate::diagnostic!(
+            $crate::proc_macro2::Span()::call_site(),
+            $crate::Level::Error,
+            $($tts)*
+        ).emit()
+    };
+}
+
+/// Emit a warning. Warnings are not errors and compilation won't fail because of them.
+///
+/// **Does nothing on stable**
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_warning {
+    ($span:expr, $($tts:tt)*) => {
+        $crate::diagnostic!($span, $crate::Level::Warning, $($tts)*).emit()
+    };
+}
+
+/// Shortcut for `emit_warning!(Span::call_site(), ...)`.
+///
+/// **Does nothing on stable**
+///
+/// # Syntax
+///
+/// See [the guide](index.html#guide).
+///
+#[macro_export]
+macro_rules! emit_call_site_warning {
+    ($($tts:tt)*) => {{
+        let span = $crate::proc_macro2::Span()::call_site();
+        $crate::diagnostic!(span, $crate::Level::Warning, $($tts)*).emit()
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __pme__suggestions {
+    ($var:ident) => ();
+
+    ($var:ident $help:ident =? $msg:expr) => {
+        let $var = if let Some(msg) = $msg {
+            $var.suggestion(stringify!($help), msg.to_string())
+        } else {
+            $var
+        };
+    };
+    ($var:ident $help:ident =? $span:expr => $msg:expr) => {
+        let $var = if let Some(msg) = $msg {
+            $var.span_suggestion($span.into(), stringify!($help), msg.to_string())
+        } else {
+            $var
+        };
+    };
+
+    ($var:ident $help:ident =? $msg:expr ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help =? $msg);
+        $crate::__pme__suggestions!($var $($rest)*);
+    };
+    ($var:ident $help:ident =? $span:expr => $msg:expr ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help =? $span => $msg);
+        $crate::__pme__suggestions!($var $($rest)*);
+    };
+
+
+    ($var:ident $help:ident = $msg:expr) => {
+        let $var = $var.suggestion(stringify!($help), $msg.to_string());
+    };
+    ($var:ident $help:ident = $fmt:expr, $($args:expr),+) => {
+        let $var = $var.suggestion(
+            stringify!($help),
+            format!($fmt, $($args),*)
+        );
+    };
+    ($var:ident $help:ident = $span:expr => $msg:expr) => {
+        let $var = $var.span_suggestion($span.into(), stringify!($help), $msg.to_string());
+    };
+    ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),+) => {
+        let $var = $var.span_suggestion(
+            $span.into(),
+            stringify!($help),
+            format!($fmt, $($args),*)
+        );
+    };
+
+    ($var:ident $help:ident = $msg:expr ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $msg);
+        $crate::__pme__suggestions!($var $($rest)*);
+    };
+    ($var:ident $help:ident = $fmt:expr, $($args:expr),+ ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $fmt, $($args),*);
+        $crate::__pme__suggestions!($var $($rest)*);
+    };
+    ($var:ident $help:ident = $span:expr => $msg:expr ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $span => $msg);
+        $crate::__pme__suggestions!($var $($rest)*);
+    };
+    ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),+ ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $span => $fmt, $($args),*);
+        $crate::__pme__suggestions!($var $($rest)*);
+    };
+
+    // trailing commas
+
+    ($var:ident $help:ident = $msg:expr,) => {
+        $crate::__pme__suggestions!($var $help = $msg)
+    };
+    ($var:ident $help:ident = $fmt:expr, $($args:expr),+,) => {
+        $crate::__pme__suggestions!($var $help = $fmt, $($args)*)
+    };
+    ($var:ident $help:ident = $span:expr => $msg:expr,) => {
+        $crate::__pme__suggestions!($var $help = $span => $msg)
+    };
+    ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),*,) => {
+        $crate::__pme__suggestions!($var $help = $span => $fmt, $($args)*)
+    };
+    ($var:ident $help:ident = $msg:expr, ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $msg; $($rest)*)
+    };
+    ($var:ident $help:ident = $fmt:expr, $($args:expr),+, ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $fmt, $($args),*; $($rest)*)
+    };
+    ($var:ident $help:ident = $span:expr => $msg:expr, ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $span => $msg; $($rest)*)
+    };
+    ($var:ident $help:ident = $span:expr => $fmt:expr, $($args:expr),+, ; $($rest:tt)*) => {
+        $crate::__pme__suggestions!($var $help = $span => $fmt, $($args),*; $($rest)*)
+    };
+}
diff --git a/proc-macro-error/src/stable.rs b/proc-macro-error/src/stable.rs
index 1547764..07042d3 100644
--- a/proc-macro-error/src/stable.rs
+++ b/proc-macro-error/src/stable.rs
@@ -1,25 +1,26 @@
-use std::cell::RefCell;

-

-use crate::{abort_now, check_correctness, Diagnostic};

-

-pub fn abort_if_dirty() {

-    check_correctness();

-    ERR_STORAGE.with(|storage| {

-        if !storage.borrow().is_empty() {

-            abort_now()

-        }

-    });

-}

-

-/// Clear the global error storage, returning the errors contained.

-pub(crate) fn cleanup() -> Vec<Diagnostic> {

-    ERR_STORAGE.with(|storage| storage.replace(Vec::new()))

-}

-

-pub(crate) fn emit_diagnostic(diag: Diagnostic) {

-    ERR_STORAGE.with(|storage| storage.borrow_mut().push(diag));

-}

-

-thread_local! {

-    static ERR_STORAGE: RefCell<Vec<Diagnostic>> = RefCell::new(Vec::new());

-}

+use std::cell::RefCell;
+
+use crate::{abort_now, check_correctness, Diagnostic, Level};
+
+pub fn abort_if_dirty() {
+    check_correctness();
+    ERR_STORAGE.with(|storage| {
+        if !storage.borrow().is_empty() {
+            abort_now()
+        }
+    });
+}
+
+pub(crate) fn cleanup() -> Vec<Diagnostic> {
+    ERR_STORAGE.with(|storage| storage.replace(Vec::new()))
+}
+
+pub(crate) fn emit_diagnostic(diag: Diagnostic) {
+    if diag.level == Level::Error {
+        ERR_STORAGE.with(|storage| storage.borrow_mut().push(diag));
+    }
+}
+
+thread_local! {
+    static ERR_STORAGE: RefCell<Vec<Diagnostic>> = RefCell::new(Vec::new());
+}