diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index cc662806e85f..644b0ca5b8f9 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -2,7 +2,7 @@ use crate::utils::{match_type, paths, return_ty, span_lint};
 use itertools::Itertools;
 use rustc::hir;
 use rustc::impl_lint_pass;
-use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
+use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintPass};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_session::declare_tool_lint;
 use rustc_span::source_map::{BytePos, MultiSpan, Span};
@@ -153,7 +153,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown {
         let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
         match item.kind {
             hir::ItemKind::Fn(ref sig, ..) => {
-                lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers);
+                if !in_external_macro(cx.tcx.sess, item.span) {
+                    lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers);
+                }
             },
             hir::ItemKind::Impl(_, _, _, _, ref trait_ref, ..) => {
                 self.in_trait_impl = trait_ref.is_some();
@@ -171,13 +173,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown {
     fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
         let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
         if let hir::TraitItemKind::Method(ref sig, ..) = item.kind {
-            lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers);
+            if !in_external_macro(cx.tcx.sess, item.span) {
+                lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers);
+            }
         }
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
         let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
-        if self.in_trait_impl {
+        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
             return;
         }
         if let hir::ImplItemKind::Method(ref sig, ..) = item.kind {
diff --git a/tests/ui/auxiliary/doc_unsafe_macros.rs b/tests/ui/auxiliary/doc_unsafe_macros.rs
new file mode 100644
index 000000000000..869672d1eda5
--- /dev/null
+++ b/tests/ui/auxiliary/doc_unsafe_macros.rs
@@ -0,0 +1,8 @@
+#[macro_export]
+macro_rules! undocd_unsafe {
+    () => {
+        pub unsafe fn oy_vey() {
+            unimplemented!();
+        }
+    };
+}
diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs
index 98dbe2d4f549..c44f3c62a98e 100644
--- a/tests/ui/doc_unsafe.rs
+++ b/tests/ui/doc_unsafe.rs
@@ -1,3 +1,8 @@
+// aux-build:doc_unsafe_macros.rs
+
+#[macro_use]
+extern crate doc_unsafe_macros;
+
 /// This is not sufficiently documented
 pub unsafe fn destroy_the_planet() {
     unimplemented!();
@@ -63,6 +68,26 @@ impl Struct {
     }
 }
 
+macro_rules! very_unsafe {
+    () => {
+        pub unsafe fn whee() {
+            unimplemented!()
+        }
+
+        /// # Safety
+        ///
+        /// Please keep the seat belt fastened
+        pub unsafe fn drive() {
+            whee()
+        }
+    };
+}
+
+very_unsafe!();
+
+// we don't lint code from external macros
+undocd_unsafe!();
+
 #[allow(clippy::let_unit_value)]
 fn main() {
     unsafe {
@@ -71,5 +96,6 @@ fn main() {
         let mut universe = ();
         apocalypse(&mut universe);
         private_mod::only_crate_wide_accessible();
+        drive();
     }
 }
diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr
index 4689430684d3..9c8666419ff0 100644
--- a/tests/ui/doc_unsafe.stderr
+++ b/tests/ui/doc_unsafe.stderr
@@ -1,5 +1,5 @@
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:2:1
+  --> $DIR/doc_unsafe.rs:7:1
    |
 LL | / pub unsafe fn destroy_the_planet() {
 LL | |     unimplemented!();
@@ -9,7 +9,7 @@ LL | | }
    = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
 
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:25:5
+  --> $DIR/doc_unsafe.rs:30:5
    |
 LL | /     pub unsafe fn republished() {
 LL | |         unimplemented!();
@@ -17,18 +17,29 @@ LL | |     }
    | |_____^
 
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:33:5
+  --> $DIR/doc_unsafe.rs:38:5
    |
 LL |     unsafe fn woefully_underdocumented(self);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:52:5
+  --> $DIR/doc_unsafe.rs:57:5
    |
 LL | /     pub unsafe fn more_undocumented_unsafe() -> Self {
 LL | |         unimplemented!();
 LL | |     }
    | |_____^
 
-error: aborting due to 4 previous errors
+error: unsafe function's docs miss `# Safety` section
+  --> $DIR/doc_unsafe.rs:73:9
+   |
+LL | /         pub unsafe fn whee() {
+LL | |             unimplemented!()
+LL | |         }
+   | |_________^
+...
+LL |   very_unsafe!();
+   |   --------------- in this macro invocation
+
+error: aborting due to 5 previous errors