about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorQuietMisdreavus <grey@quietmisdreavus.net>2017-09-21 22:37:00 -0500
committerQuietMisdreavus <grey@quietmisdreavus.net>2017-11-21 15:46:49 -0600
commitf9f3611f5c2d8b88361cd67d06528ff1ae7876e9 (patch)
treefc3739e05f0d3669da28533415e134b7a42a5138 /src/libsyntax/ext
parentf28df200260c89b2a0bdf942510e0f888c29a70d (diff)
downloadrust-f9f3611f5c2d8b88361cd67d06528ff1ae7876e9.tar.gz
rust-f9f3611f5c2d8b88361cd67d06528ff1ae7876e9.zip
allow loading external files in documentation
Partial implementation of https://github.com/rust-lang/rfcs/pull/1990
(needs error reporting work)

cc #44732
Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/base.rs2
-rw-r--r--src/libsyntax/ext/expand.rs88
2 files changed, 89 insertions, 1 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 0e05cce35e2..6c96692f719 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -665,6 +665,7 @@ pub struct ExtCtxt<'a> {
     pub parse_sess: &'a parse::ParseSess,
     pub ecfg: expand::ExpansionConfig<'a>,
     pub crate_root: Option<&'static str>,
+    pub root_path: PathBuf,
     pub resolver: &'a mut Resolver,
     pub resolve_err_count: usize,
     pub current_expansion: ExpansionData,
@@ -680,6 +681,7 @@ impl<'a> ExtCtxt<'a> {
             parse_sess,
             ecfg,
             crate_root: None,
+            root_path: PathBuf::new(),
             resolver,
             resolve_err_count: 0,
             current_expansion: ExpansionData {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 491dbed01f1..0d1b1c65a29 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -11,7 +11,7 @@
 use ast::{self, Block, Ident, NodeId, PatKind, Path};
 use ast::{MacStmtStyle, StmtKind, ItemKind};
 use attr::{self, HasAttrs};
-use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
+use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute, dummy_spanned};
 use config::{is_test_or_bench, StripUnconfigured};
 use errors::FatalError;
 use ext::base::*;
@@ -35,6 +35,8 @@ use util::small_vector::SmallVector;
 use visit::Visitor;
 
 use std::collections::HashMap;
+use std::fs::File;
+use std::io::Read;
 use std::mem;
 use std::rc::Rc;
 
@@ -223,6 +225,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             directory: self.cx.codemap().span_to_unmapped_path(krate.span),
         };
         module.directory.pop();
+        self.cx.root_path = module.directory.clone();
         self.cx.current_expansion.module = Rc::new(module);
 
         let orig_mod_span = krate.module.inner;
@@ -843,6 +846,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
             feature_gate::check_attribute(attr, self.cx.parse_sess, features);
         }
     }
+
+    fn check_attribute(&mut self, at: &ast::Attribute) {
+        let features = self.cx.ecfg.features.unwrap();
+        feature_gate::check_attribute(at, self.cx.parse_sess, features);
+    }
 }
 
 pub fn find_attr_invoc(attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
@@ -1063,6 +1071,84 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
         }
     }
 
+    fn fold_attribute(&mut self, at: ast::Attribute) -> Option<ast::Attribute> {
+        // turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename",
+        // contents="file contents")]` attributes
+        if !at.check_name("doc") {
+            return noop_fold_attribute(at, self);
+        }
+
+        if let Some(list) = at.meta_item_list() {
+            if !list.iter().any(|it| it.check_name("include")) {
+                return noop_fold_attribute(at, self);
+            }
+
+            let mut items = vec![];
+
+            for it in list {
+                if !it.check_name("include") {
+                    items.push(noop_fold_meta_list_item(it, self));
+                    continue;
+                }
+
+                if let Some(file) = it.value_str() {
+                    let err_count = self.cx.parse_sess.span_diagnostic.err_count();
+                    self.check_attribute(&at);
+                    if self.cx.parse_sess.span_diagnostic.err_count() > err_count {
+                        // avoid loading the file if they haven't enabled the feature
+                        return noop_fold_attribute(at, self);
+                    }
+
+                    let mut buf = vec![];
+                    let filename = self.cx.root_path.join(file.to_string());
+
+                    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))),
+                                dummy_spanned(ast::NestedMetaItemKind::MetaItem(
+                                        attr::mk_name_value_item_str("contents".into(),
+                                                                     (&*src).into()))),
+                            ];
+
+                            items.push(dummy_spanned(ast::NestedMetaItemKind::MetaItem(
+                                        attr::mk_list_item("include".into(), include_info))));
+                        }
+                        Err(_) => {
+                            self.cx.span_warn(at.span,
+                                              &format!("{} wasn't a utf-8 file",
+                                                       filename.display()));
+                        }
+                    }
+                } else {
+                    items.push(noop_fold_meta_list_item(it, self));
+                }
+            }
+
+            let meta = attr::mk_list_item("doc".into(), items);
+            match at.style {
+                ast::AttrStyle::Inner =>
+                    Some(attr::mk_spanned_attr_inner(at.span, at.id, meta)),
+                ast::AttrStyle::Outer =>
+                    Some(attr::mk_spanned_attr_outer(at.span, at.id, meta)),
+            }
+        } else {
+            noop_fold_attribute(at, self)
+        }
+    }
+
     fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId {
         if self.monotonic {
             assert_eq!(id, ast::DUMMY_NODE_ID);