From 2ce368dd5891fab11b4d7db4a5524b0b925cba80 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Thu, 7 Dec 2017 10:43:14 -0600 Subject: [PATCH 1/3] turn external_doc errors into a proper lint --- src/librustc_lint/builtin.rs | 48 +++++++++++++++++++++++++++++++ src/librustc_lint/lib.rs | 1 + src/librustdoc/clean/mod.rs | 44 +++++++++++++++++++---------- src/libsyntax/ext/expand.rs | 55 +++++++++++++++++++++--------------- 4 files changed, 111 insertions(+), 37 deletions(-) diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 07874a8cc69dd..6210fca57ff2c 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -296,6 +296,54 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { } } +declare_lint! { + EXTERNAL_DOC_ERROR, + Warn, + "errors that occur when loading external documentation" +} + +pub struct ExternalDocError; + +impl LintPass for ExternalDocError { + fn get_lints(&self) -> LintArray { + lint_array!(EXTERNAL_DOC_ERROR) + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExternalDocError { + fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) { + //in libsyntax, failures to read a file transform a #[doc(include = "filename")] into a + //#[doc(include(file="filename", error="error message"))], so we need to pick that up here + + if !attr.check_name("doc") { + return; + } + + if let Some(list) = attr.meta_item_list() { + for it in list { + if !it.check_name("include") { + continue; + } + + if let Some(inner_list) = it.meta_item_list() { + let mut error: Option = None; + + for it in inner_list { + if it.check_name("error") { + error = it.value_str().map(|s| s.to_string()); + break; + } + } + + if let Some(error) = error { + cx.span_lint(EXTERNAL_DOC_ERROR, attr.span(), &error); + } + } + } + } + } +} + declare_lint! { MISSING_DOCS, Allow, diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 8b41dd62742ce..b656ae83bc3b4 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -139,6 +139,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { MutableTransmutes, UnionsWithDropFields, UnreachablePub, + ExternalDocError, ); add_builtin_with_new!(sess, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index be7bd3d5510ef..0449022796e1d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -666,17 +666,18 @@ impl Attributes { /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from /// its expansion. fn extract_include(mi: &ast::MetaItem) - -> Option<(String, String)> + -> Result, String> { - mi.meta_item_list().and_then(|list| { + mi.meta_item_list().map_or(Ok(None), |list| { for meta in list { if meta.check_name("include") { // the actual compiled `#[doc(include="filename")]` gets expanded to // `#[doc(include(file="filename", contents="file contents")]` so we need to // look for that instead - return meta.meta_item_list().and_then(|list| { + return meta.meta_item_list().map_or(Ok(None), |list| { let mut filename: Option = None; let mut contents: Option = None; + let mut error: Option = None; for it in list { if it.check_name("file") { @@ -687,19 +688,25 @@ impl Attributes { if let Some(docs) = it.value_str() { contents = Some(docs.to_string()); } + } else if it.check_name("error") { + if let Some(err) = it.value_str() { + error = Some(err.to_string()); + } } } - if let (Some(filename), Some(contents)) = (filename, contents) { - Some((filename, contents)) + if let Some(error) = error { + Err(error) + } else if let (Some(filename), Some(contents)) = (filename, contents) { + Ok(Some((filename, contents))) } else { - None + Ok(None) } }); } } - None + Ok(None) }) } @@ -751,14 +758,21 @@ impl Attributes { Err(e) => diagnostic.span_err(e.span, e.msg), } return None; - } else if let Some((filename, contents)) = Attributes::extract_include(&mi) - { - let line = doc_line; - doc_line += contents.lines().count(); - doc_strings.push(DocFragment::Include(line, - attr.span, - filename, - contents)); + } else { + match Attributes::extract_include(&mi) { + Ok(Some((filename, contents))) => { + let line = doc_line; + doc_line += contents.lines().count(); + doc_strings.push(DocFragment::Include(line, + attr.span, + filename, + contents)); + } + Err(e) => { + diagnostic.span_err(attr.span(), &e); + } + Ok(None) => {} + } } } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0d1b1c65a2934..4504a82deb3fa 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1101,37 +1101,48 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let mut buf = vec![]; let filename = self.cx.root_path.join(file.to_string()); + let mut include_info = vec![ + dummy_spanned(ast::NestedMetaItemKind::MetaItem( + attr::mk_name_value_item_str("file".into(), + file))), + ]; match File::open(&filename).and_then(|mut f| f.read_to_end(&mut buf)) { Ok(..) => {} Err(e) => { - self.cx.span_warn(at.span, - &format!("couldn't read {}: {}", - filename.display(), - e)); - } - } - - match String::from_utf8(buf) { - Ok(src) => { - let include_info = vec![ - dummy_spanned(ast::NestedMetaItemKind::MetaItem( - attr::mk_name_value_item_str("file".into(), - file))), + include_info.push( dummy_spanned(ast::NestedMetaItemKind::MetaItem( - attr::mk_name_value_item_str("contents".into(), - (&*src).into()))), - ]; + attr::mk_name_value_item_str("error".into(), + (&*format!("couldn't read {}: {}", + filename.display(), + e)).into())))); - items.push(dummy_spanned(ast::NestedMetaItemKind::MetaItem( - attr::mk_list_item("include".into(), include_info)))); + //make sure the buffer is empty so we can properly skip the next match + buf.clear(); } - Err(_) => { - self.cx.span_warn(at.span, - &format!("{} wasn't a utf-8 file", - filename.display())); + } + + if !buf.is_empty() { + match String::from_utf8(buf) { + Ok(src) => { + include_info.push( + dummy_spanned(ast::NestedMetaItemKind::MetaItem( + attr::mk_name_value_item_str("contents".into(), + (&*src).into())))); + } + Err(_) => { + include_info.push( + dummy_spanned(ast::NestedMetaItemKind::MetaItem( + attr::mk_name_value_item_str( + "error".into(), + (&*format!("{} wasn't a utf-8 file", + filename.display())).into())))); + } } } + + items.push(dummy_spanned(ast::NestedMetaItemKind::MetaItem( + attr::mk_list_item("include".into(), include_info)))); } else { items.push(noop_fold_meta_list_item(it, self)); } From 7445b0179535d7bd4c1df0a6cfccf03668aef9cc Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Thu, 7 Dec 2017 10:43:30 -0600 Subject: [PATCH 2/3] add a test for the external_doc_error lint --- src/test/compile-fail/lint-external-doc-error.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/test/compile-fail/lint-external-doc-error.rs diff --git a/src/test/compile-fail/lint-external-doc-error.rs b/src/test/compile-fail/lint-external-doc-error.rs new file mode 100644 index 0000000000000..6c2ba4cb3f4af --- /dev/null +++ b/src/test/compile-fail/lint-external-doc-error.rs @@ -0,0 +1,15 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(external_doc)] +#![deny(external_doc_error)] + +#[doc(include = "not-a-file.md")] //~ ERROR +pub struct SomeStruct; From 7bbb04cb92bb2c342de2af92123d20bc09d8f666 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Thu, 7 Dec 2017 13:23:11 -0600 Subject: [PATCH 3/3] fix lint-external-doc-error test --- src/test/compile-fail/lint-external-doc-error.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/lint-external-doc-error.rs b/src/test/compile-fail/lint-external-doc-error.rs index 6c2ba4cb3f4af..f407121e694a6 100644 --- a/src/test/compile-fail/lint-external-doc-error.rs +++ b/src/test/compile-fail/lint-external-doc-error.rs @@ -11,5 +11,7 @@ #![feature(external_doc)] #![deny(external_doc_error)] -#[doc(include = "not-a-file.md")] //~ ERROR +#[doc(include = "not-a-file.md")] //~ ERROR: couldn't read pub struct SomeStruct; + +fn main() {}