Skip to content

Commit

Permalink
fix(doctest): treat fatal parse errors as incomplete attributes
Browse files Browse the repository at this point in the history
Fixes #99089
  • Loading branch information
notriddle committed Jul 11, 2022
1 parent f893495 commit 6c44357
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 23 deletions.
73 changes: 50 additions & 23 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs
Original file line number Diff line number Diff line change
@@ -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() {}
6 changes: 6 additions & 0 deletions src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 6c44357

Please sign in to comment.