about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-04-02 23:39:25 +0000
committerbors <bors@rust-lang.org>2022-04-02 23:39:25 +0000
commitc1550e3f8c57b0ebb0edb3a7ea64727e668f669c (patch)
tree9dd00bf58606e2c3c98f2b2d18cd41fca19d92e3
parent76d770ac21d9521db6a92a48c7b3d5b2cc535941 (diff)
parent732ed2adc816eb600eac33a4cfcd2095c43be4f7 (diff)
downloadrust-c1550e3f8c57b0ebb0edb3a7ea64727e668f669c.tar.gz
rust-c1550e3f8c57b0ebb0edb3a7ea64727e668f669c.zip
Auto merge of #95590 - GuillaumeGomez:multi-line-attr-handling-doctest, r=notriddle
Fix multiline attributes handling in doctests

Fixes #55713.

I needed to have access to the `unclosed_delims` field in order to check that the attribute was completely parsed and didn't have missing parts, so I created a getter for it.

r? `@notriddle`
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs4
-rw-r--r--src/librustdoc/doctest.rs70
-rw-r--r--src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.rs12
-rw-r--r--src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.stdout6
4 files changed, 84 insertions, 8 deletions
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 8f64d6d732f..0bbbd3a32c4 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -210,6 +210,10 @@ impl<'a> Parser<'a> {
         self.unclosed_delims.extend(snapshot.unclosed_delims.clone());
     }
 
+    pub fn unclosed_delims(&self) -> &[UnmatchedBrace] {
+        &self.unclosed_delims
+    }
+
     /// Create a snapshot of the `Parser`.
     pub(super) fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {
         let mut snapshot = self.clone();
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 41efda980a2..c4201e22212 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -10,7 +10,10 @@ use rustc_interface::interface;
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
+use rustc_parse::maybe_new_parser_from_source_str;
+use rustc_parse::parser::attr::InnerAttrPolicy;
 use rustc_session::config::{self, CrateType, ErrorOutputType};
+use rustc_session::parse::ParseSess;
 use rustc_session::{lint, DiagnosticOutput, Session};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::SourceMap;
@@ -493,7 +496,7 @@ crate fn make_test(
     edition: Edition,
     test_id: Option<&str>,
 ) -> (String, usize, bool) {
-    let (crate_attrs, everything_else, crates) = partition_source(s);
+    let (crate_attrs, everything_else, crates) = partition_source(s, edition);
     let everything_else = everything_else.trim();
     let mut line_offset = 0;
     let mut prog = String::new();
@@ -525,9 +528,7 @@ crate fn make_test(
         rustc_span::create_session_if_not_set_then(edition, |_| {
             use rustc_errors::emitter::{Emitter, EmitterWriter};
             use rustc_errors::Handler;
-            use rustc_parse::maybe_new_parser_from_source_str;
             use rustc_parse::parser::ForceCollect;
-            use rustc_session::parse::ParseSess;
             use rustc_span::source_map::FilePathMapping;
 
             let filename = FileName::anon_source_code(s);
@@ -697,8 +698,39 @@ crate fn make_test(
     (prog, line_offset, supports_color)
 }
 
-// FIXME(aburka): use a real parser to deal with multiline attributes
-fn partition_source(s: &str) -> (String, String, String) {
+fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
+    if source.is_empty() {
+        // 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;
+            }
+        };
+        // If a parsing error happened, it's very likely that the attribute is incomplete.
+        if !parser.parse_attribute(InnerAttrPolicy::Permitted).is_ok() {
+            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)
+    })
+}
+
+fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
     #[derive(Copy, Clone, PartialEq)]
     enum PartitionState {
         Attrs,
@@ -710,6 +742,8 @@ fn partition_source(s: &str) -> (String, String, String) {
     let mut crates = String::new();
     let mut after = String::new();
 
+    let mut mod_attr_pending = String::new();
+
     for line in s.lines() {
         let trimline = line.trim();
 
@@ -717,8 +751,14 @@ fn partition_source(s: &str) -> (String, String, String) {
         // shunted into "everything else"
         match state {
             PartitionState::Attrs => {
-                state = if trimline.starts_with("#![")
-                    || trimline.chars().all(|c| c.is_whitespace())
+                state = if trimline.starts_with("#![") {
+                    if !check_if_attr_is_complete(line, edition) {
+                        mod_attr_pending = line.to_owned();
+                    } else {
+                        mod_attr_pending.clear();
+                    }
+                    PartitionState::Attrs
+                } else if trimline.chars().all(|c| c.is_whitespace())
                     || (trimline.starts_with("//") && !trimline.starts_with("///"))
                 {
                     PartitionState::Attrs
@@ -727,7 +767,21 @@ fn partition_source(s: &str) -> (String, String, String) {
                 {
                     PartitionState::Crates
                 } else {
-                    PartitionState::Other
+                    // First we check if the previous attribute was "complete"...
+                    if !mod_attr_pending.is_empty() {
+                        // If not, then we append the new line into the pending attribute to check
+                        // if this time it's complete...
+                        mod_attr_pending.push_str(line);
+                        if !trimline.is_empty() && check_if_attr_is_complete(line, edition) {
+                            // If it's complete, then we can clear the pending content.
+                            mod_attr_pending.clear();
+                        }
+                        // In any case, this is considered as `PartitionState::Attrs` so it's
+                        // prepended before rustdoc's inserts.
+                        PartitionState::Attrs
+                    } else {
+                        PartitionState::Other
+                    }
                 };
             }
             PartitionState::Crates => {
diff --git a/src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.rs b/src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.rs
new file mode 100644
index 00000000000..b2a8133c90e
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.rs
@@ -0,0 +1,12 @@
+// compile-flags:--test
+// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
+// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
+// check-pass
+
+/// ```
+/// # #![cfg_attr(not(dox), deny(missing_abi,
+/// # non_ascii_idents))]
+///
+/// pub struct Bar;
+/// ```
+pub struct Bar;
diff --git a/src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.stdout b/src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.stdout
new file mode 100644
index 00000000000..bf3521e4f91
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-comment-multi-line-cfg-attr.stdout
@@ -0,0 +1,6 @@
+
+running 1 test
+test $DIR/doc-comment-multi-line-cfg-attr.rs - Bar (line 6) ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+