about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2022-07-10 20:58:54 -0700
committerMichael Howell <michael@notriddle.com>2022-07-10 20:58:54 -0700
commit6c44357e14465443acc3be46560271c3c8f656fe (patch)
tree159ea0c282e3c1b21841cf90096e36f205c64a66
parentf893495e3da91dc319d37861b803eed9d6c8c7c7 (diff)
downloadrust-6c44357e14465443acc3be46560271c3c8f656fe.tar.gz
rust-6c44357e14465443acc3be46560271c3c8f656fe.zip
fix(doctest): treat fatal parse errors as incomplete attributes
Fixes #99089
-rw-r--r--src/librustdoc/doctest.rs73
-rw-r--r--src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs10
-rw-r--r--src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout6
3 files changed, 66 insertions, 23 deletions
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 509c4253f0f..39ec6a60856 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -726,31 +726,58 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
         // Empty content so nothing to check in here...
         return true;
     }
-    rustc_span::create_session_if_not_set_then(edition, |_| {
-        let filename = FileName::anon_source_code(source);
-        let sess = ParseSess::with_silent_emitter(None);
-        let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source.to_owned())
-        {
-            Ok(p) => p,
-            Err(_) => {
-                debug!("Cannot build a parser to check mod attr so skipping...");
-                return true;
+    rustc_driver::catch_fatal_errors(|| {
+        rustc_span::create_session_if_not_set_then(edition, |_| {
+            use rustc_errors::emitter::EmitterWriter;
+            use rustc_errors::Handler;
+            use rustc_span::source_map::FilePathMapping;
+
+            let filename = FileName::anon_source_code(source);
+            // Any errors in parsing should also appear when the doctest is compiled for real, so just
+            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
+            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let fallback_bundle =
+                rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+
+            let emitter = EmitterWriter::new(
+                box io::sink(),
+                None,
+                None,
+                fallback_bundle,
+                false,
+                false,
+                false,
+                None,
+                false,
+            );
+
+            let handler = Handler::with_emitter(false, None, box emitter);
+            let sess = ParseSess::with_span_handler(handler, sm);
+            let mut parser =
+                match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) {
+                    Ok(p) => p,
+                    Err(_) => {
+                        debug!("Cannot build a parser to check mod attr so skipping...");
+                        return true;
+                    }
+                };
+            // If a parsing error happened, it's very likely that the attribute is incomplete.
+            if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) {
+                e.cancel();
+                return false;
             }
-        };
-        // If a parsing error happened, it's very likely that the attribute is incomplete.
-        if parser.parse_attribute(InnerAttrPolicy::Permitted).is_err() {
-            return false;
-        }
-        // We now check if there is an unclosed delimiter for the attribute. To do so, we look at
-        // the `unclosed_delims` and see if the opening square bracket was closed.
-        parser
-            .unclosed_delims()
-            .get(0)
-            .map(|unclosed| {
-                unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2)
-            })
-            .unwrap_or(true)
+            // We now check if there is an unclosed delimiter for the attribute. To do so, we look at
+            // the `unclosed_delims` and see if the opening square bracket was closed.
+            parser
+                .unclosed_delims()
+                .get(0)
+                .map(|unclosed| {
+                    unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2)
+                })
+                .unwrap_or(true)
+        })
     })
+    .unwrap_or(false)
 }
 
 fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
diff --git a/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs
new file mode 100644
index 00000000000..a30472ac56b
--- /dev/null
+++ b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs
@@ -0,0 +1,10 @@
+// compile-flags:--test --test-args=--test-threads=1
+// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
+// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
+// check-pass
+
+/// ```
+/// #![deprecated(since = "5.2", note = "foo was rarely used. \
+///    Users should instead use bar")]
+/// ```
+pub fn f() {}
diff --git a/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout
new file mode 100644
index 00000000000..07a4f657dea
--- /dev/null
+++ b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout
@@ -0,0 +1,6 @@
+
+running 1 test
+test $DIR/doctest-multiline-crate-attribute.rs - f (line 6) ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+