about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorNicholas Nethercote <nnethercote@mozilla.com>2019-10-24 06:33:12 +1100
committerNicholas Nethercote <nnethercote@mozilla.com>2019-11-06 23:05:07 +1100
commiteea6f23a0ed67fd8c6b8e1b02cda3628fee56b2f (patch)
treeeffdb244138f311440b21c9fd52e4028edb575ea /src/libsyntax
parent69bc4aba785e071740d2d46f109623b9951aae5d (diff)
downloadrust-eea6f23a0ed67fd8c6b8e1b02cda3628fee56b2f.tar.gz
rust-eea6f23a0ed67fd8c6b8e1b02cda3628fee56b2f.zip
Make doc comments cheaper with `AttrKind`.
`AttrKind` is a new type with two variants, `Normal` and `DocComment`. It's a
big performance win (over 10% in some cases) because `DocComment` lets doc
comments (which are common) be represented very cheaply.

`Attribute` gets some new helper methods to ease the transition:
- `has_name()`: check if the attribute name matches a single `Symbol`; for
  `DocComment` variants it succeeds if the symbol is `sym::doc`.
- `is_doc_comment()`: check if it has a `DocComment` kind.
- `{get,unwrap}_normal_item()`: extract the item from a `Normal` variant;
  panic otherwise.

Fixes #60935.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs19
-rw-r--r--src/libsyntax/attr/builtin.rs8
-rw-r--r--src/libsyntax/attr/mod.rs110
-rw-r--r--src/libsyntax/config.rs9
-rw-r--r--src/libsyntax/feature_gate/check.rs3
-rw-r--r--src/libsyntax/mut_visit.rs12
-rw-r--r--src/libsyntax/parse/mod.rs22
-rw-r--r--src/libsyntax/parse/parser/attr.rs7
-rw-r--r--src/libsyntax/parse/parser/item.rs12
-rw-r--r--src/libsyntax/parse/tests.rs2
-rw-r--r--src/libsyntax/print/pprust.rs21
-rw-r--r--src/libsyntax/visit.rs5
12 files changed, 153 insertions, 77 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index df1fb5d97d7..2392b809150 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -2190,18 +2190,31 @@ pub struct AttrItem {
 }
 
 /// Metadata associated with an item.
-/// Doc-comments are promoted to attributes that have `is_sugared_doc = true`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Attribute {
-    pub item: AttrItem,
+    pub kind: AttrKind,
     pub id: AttrId,
     /// Denotes if the attribute decorates the following construct (outer)
     /// or the construct this attribute is contained within (inner).
     pub style: AttrStyle,
-    pub is_sugared_doc: bool,
     pub span: Span,
 }
 
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+pub enum AttrKind {
+    /// A normal attribute.
+    Normal(AttrItem),
+
+    /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
+    /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
+    /// variant (which is much less compact and thus more expensive).
+    ///
+    /// Note: `self.has_name(sym::doc)` and `self.check_name(sym::doc)` succeed
+    /// for this variant, but this may change in the future.
+    /// ```
+    DocComment(Symbol),
+}
+
 /// `TraitRef`s appear in impls.
 ///
 /// Resolution maps each `TraitRef`'s `ref_id` to its defining trait; that's all
diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs
index e77d9ef326a..787d69f5e99 100644
--- a/src/libsyntax/attr/builtin.rs
+++ b/src/libsyntax/attr/builtin.rs
@@ -228,7 +228,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
             sym::stable,
             sym::rustc_promotable,
             sym::rustc_allow_const_fn_ptr,
-        ].iter().any(|&s| attr.item.path == s) {
+        ].iter().any(|&s| attr.has_name(s)) {
             continue // not a stability level
         }
 
@@ -236,10 +236,10 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
 
         let meta = attr.meta();
 
-        if attr.item.path == sym::rustc_promotable {
+        if attr.has_name(sym::rustc_promotable) {
             promotable = true;
         }
-        if attr.item.path == sym::rustc_allow_const_fn_ptr {
+        if attr.has_name(sym::rustc_allow_const_fn_ptr) {
             allow_const_fn_ptr = true;
         }
         // attributes with data
@@ -778,7 +778,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
 
     let mut acc = Vec::new();
     let diagnostic = &sess.span_diagnostic;
-    if attr.item.path == sym::repr {
+    if attr.has_name(sym::repr) {
         if let Some(items) = attr.meta_item_list() {
             mark_used(attr);
             for item in items {
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 0c46c501be9..c663995eb8f 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -9,7 +9,7 @@ pub use StabilityLevel::*;
 pub use crate::ast::Attribute;
 
 use crate::ast;
-use crate::ast::{AttrItem, AttrId, AttrStyle, Name, Ident, Path, PathSegment};
+use crate::ast::{AttrItem, AttrId, AttrKind, AttrStyle, Name, Ident, Path, PathSegment};
 use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem};
 use crate::ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam};
 use crate::mut_visit::visit_clobber;
@@ -145,12 +145,17 @@ impl NestedMetaItem {
 }
 
 impl Attribute {
+    pub fn has_name(&self, name: Symbol) -> bool {
+        match self.kind {
+            AttrKind::Normal(ref item) => item.path == name,
+            AttrKind::DocComment(_) => name == sym::doc,
+        }
+    }
+
     /// Returns `true` if the attribute's path matches the argument. If it matches, then the
     /// attribute is marked as used.
-    ///
-    /// To check the attribute name without marking it used, use the `path` field directly.
     pub fn check_name(&self, name: Symbol) -> bool {
-        let matches = self.item.path == name;
+        let matches = self.has_name(name);
         if matches {
             mark_used(self);
         }
@@ -159,10 +164,15 @@ impl Attribute {
 
     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
     pub fn ident(&self) -> Option<Ident> {
-        if self.item.path.segments.len() == 1 {
-            Some(self.item.path.segments[0].ident)
-        } else {
-            None
+        match self.kind {
+            AttrKind::Normal(ref item) => {
+                if item.path.segments.len() == 1 {
+                    Some(item.path.segments[0].ident)
+                } else {
+                    None
+                }
+            }
+            AttrKind::DocComment(_) => Some(Ident::new(sym::doc, self.span)),
         }
     }
     pub fn name_or_empty(&self) -> Symbol {
@@ -170,18 +180,32 @@ impl Attribute {
     }
 
     pub fn value_str(&self) -> Option<Symbol> {
-        self.meta().and_then(|meta| meta.value_str())
+        match self.kind {
+            AttrKind::Normal(ref item) => {
+                item.meta(self.span).and_then(|meta| meta.value_str())
+            }
+            AttrKind::DocComment(comment) => Some(comment),
+        }
     }
 
     pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
-        match self.meta() {
-            Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
-            _ => None
+        match self.kind {
+            AttrKind::Normal(ref item) => {
+                match item.meta(self.span) {
+                    Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
+                    _ => None
+                }
+            }
+            AttrKind::DocComment(_) => None,
         }
     }
 
     pub fn is_word(&self) -> bool {
-        self.item.tokens.is_empty()
+        if let AttrKind::Normal(item) = &self.kind {
+            item.tokens.is_empty()
+        } else {
+            false
+        }
     }
 
     pub fn is_meta_item_list(&self) -> bool {
@@ -275,17 +299,49 @@ impl AttrItem {
 }
 
 impl Attribute {
+    pub fn is_doc_comment(&self) -> bool {
+        match self.kind {
+            AttrKind::Normal(_) => false,
+            AttrKind::DocComment(_) => true,
+        }
+    }
+
+    pub fn get_normal_item(&self) -> &AttrItem {
+        match self.kind {
+            AttrKind::Normal(ref item) => item,
+            AttrKind::DocComment(_) => panic!("unexpected sugared doc"),
+        }
+    }
+
+    pub fn unwrap_normal_item(self) -> AttrItem {
+        match self.kind {
+            AttrKind::Normal(item) => item,
+            AttrKind::DocComment(_) => panic!("unexpected sugared doc"),
+        }
+    }
+
     /// Extracts the MetaItem from inside this Attribute.
     pub fn meta(&self) -> Option<MetaItem> {
-        self.item.meta(self.span)
+        match self.kind {
+            AttrKind::Normal(ref item) => item.meta(self.span),
+            AttrKind::DocComment(comment) =>
+                Some(mk_name_value_item_str(Ident::new(sym::doc, self.span), comment, self.span)),
+        }
     }
 
     pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
-        Ok(MetaItem {
-            path: self.item.path.clone(),
-            kind: parse::parse_in_attr(sess, self, |p| p.parse_meta_item_kind())?,
-            span: self.span,
-        })
+        match self.kind {
+            AttrKind::Normal(ref item) => {
+                Ok(MetaItem {
+                    path: item.path.clone(),
+                    kind: parse::parse_in_attr(sess, self, |parser| parser.parse_meta_item_kind())?,
+                    span: self.span,
+                })
+            }
+            AttrKind::DocComment(comment) => {
+                Ok(mk_name_value_item_str(Ident::new(sym::doc, self.span), comment, self.span))
+            }
+        }
     }
 }
 
@@ -327,10 +383,9 @@ crate fn mk_attr_id() -> AttrId {
 
 pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
     Attribute {
-        item: AttrItem { path, tokens },
+        kind: AttrKind::Normal(AttrItem { path, tokens }),
         id: mk_attr_id(),
         style,
-        is_sugared_doc: false,
         span,
     }
 }
@@ -345,18 +400,11 @@ pub fn mk_attr_outer(item: MetaItem) -> Attribute {
     mk_attr(AttrStyle::Outer, item.path, item.kind.tokens(item.span), item.span)
 }
 
-pub fn mk_sugared_doc_attr(text: Symbol, span: Span) -> Attribute {
-    let style = doc_comment_style(&text.as_str());
-    let lit_kind = LitKind::Str(text, ast::StrStyle::Cooked);
-    let lit = Lit::from_lit_kind(lit_kind, span);
+pub fn mk_doc_comment(comment: Symbol, span: Span) -> Attribute {
     Attribute {
-        item: AttrItem {
-            path: Path::from_ident(Ident::with_dummy_span(sym::doc).with_span_pos(span)),
-            tokens: MetaItemKind::NameValue(lit).tokens(span),
-        },
+        kind: AttrKind::DocComment(comment),
         id: mk_attr_id(),
-        style,
-        is_sugared_doc: true,
+        style: doc_comment_style(&comment.as_str()),
         span,
     }
 }
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 682c8f71dc8..5f89ed36e2a 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -93,10 +93,10 @@ impl<'a> StripUnconfigured<'a> {
     /// is in the original source file. Gives a compiler error if the syntax of
     /// the attribute is incorrect.
     fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
-        if attr.item.path != sym::cfg_attr {
+        if !attr.has_name(sym::cfg_attr) {
             return vec![attr];
         }
-        if attr.item.tokens.is_empty() {
+        if attr.get_normal_item().tokens.is_empty() {
             self.sess.span_diagnostic
                 .struct_span_err(
                     attr.span,
@@ -136,10 +136,9 @@ impl<'a> StripUnconfigured<'a> {
             //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
             expanded_attrs.into_iter()
             .flat_map(|(item, span)| self.process_cfg_attr(ast::Attribute {
-                item,
+                kind: ast::AttrKind::Normal(item),
                 id: attr::mk_attr_id(),
                 style: attr.style,
-                is_sugared_doc: false,
                 span,
             }))
             .collect()
@@ -212,7 +211,7 @@ impl<'a> StripUnconfigured<'a> {
                                       GateIssue::Language,
                                       EXPLAIN_STMT_ATTR_SYNTAX);
 
-            if attr.is_sugared_doc {
+            if attr.is_doc_comment() {
                 err.help("`///` is for documentation comments. For a plain comment, use `//`.");
             }
 
diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs
index c19ed774507..b7e75ff3a7e 100644
--- a/src/libsyntax/feature_gate/check.rs
+++ b/src/libsyntax/feature_gate/check.rs
@@ -329,7 +329,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
             Some((name, _, template, _)) if name != sym::rustc_dummy =>
                 check_builtin_attribute(self.parse_sess, attr, name, template),
-            _ => if let Some(TokenTree::Token(token)) = attr.item.tokens.trees().next() {
+            _ => if let Some(TokenTree::Token(token)) =
+                    attr.get_normal_item().tokens.trees().next() {
                 if token == token::Eq {
                     // All key-value attributes are restricted to meta-item syntax.
                     attr.parse_meta(self.parse_sess).map_err(|mut err| err.emit()).ok();
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 60ee17d09b7..7261601e144 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -550,10 +550,14 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
 }
 
 pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
-    let Attribute { item: AttrItem { path, tokens }, id: _, style: _, is_sugared_doc: _, span }
-        = attr;
-    vis.visit_path(path);
-    vis.visit_tts(tokens);
+    let Attribute { kind, id: _, style: _, span } = attr;
+    match kind {
+        AttrKind::Normal(AttrItem { path, tokens }) => {
+            vis.visit_path(path);
+            vis.visit_tts(tokens);
+        }
+        AttrKind::DocComment(_) => {}
+    }
     vis.visit_span(span);
 }
 
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 6cfa0dfad82..b688dce87c1 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -287,7 +287,7 @@ pub fn parse_in_attr<'a, T>(
 ) -> PResult<'a, T> {
     let mut parser = Parser::new(
         sess,
-        attr.item.tokens.clone(),
+        attr.get_normal_item().tokens.clone(),
         None,
         false,
         false,
@@ -393,18 +393,22 @@ fn prepend_attrs(
 
         let source = pprust::attribute_to_string(attr);
         let macro_filename = FileName::macro_expansion_source_code(&source);
-        if attr.is_sugared_doc {
-            let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
-            builder.push(stream);
-            continue
-        }
+
+        let item = match attr.kind {
+            ast::AttrKind::Normal(ref item) => item,
+            ast::AttrKind::DocComment(_) => {
+                let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
+                builder.push(stream);
+                continue
+            }
+        };
 
         // synthesize # [ $path $tokens ] manually here
         let mut brackets = tokenstream::TokenStreamBuilder::new();
 
         // For simple paths, push the identifier directly
-        if attr.item.path.segments.len() == 1 && attr.item.path.segments[0].args.is_none() {
-            let ident = attr.item.path.segments[0].ident;
+        if item.path.segments.len() == 1 && item.path.segments[0].args.is_none() {
+            let ident = item.path.segments[0].ident;
             let token = token::Ident(ident.name, ident.as_str().starts_with("r#"));
             brackets.push(tokenstream::TokenTree::token(token, ident.span));
 
@@ -415,7 +419,7 @@ fn prepend_attrs(
             brackets.push(stream);
         }
 
-        brackets.push(attr.item.tokens.clone());
+        brackets.push(item.tokens.clone());
 
         // The span we list here for `#` and for `[ ... ]` are both wrong in
         // that it encompasses more than each token, but it hopefully is "good
diff --git a/src/libsyntax/parse/parser/attr.rs b/src/libsyntax/parse/parser/attr.rs
index 188a144cac9..1c292661f24 100644
--- a/src/libsyntax/parse/parser/attr.rs
+++ b/src/libsyntax/parse/parser/attr.rs
@@ -43,7 +43,7 @@ impl<'a> Parser<'a> {
                     just_parsed_doc_comment = false;
                 }
                 token::DocComment(s) => {
-                    let attr = attr::mk_sugared_doc_attr(s, self.token.span);
+                    let attr = attr::mk_doc_comment(s, self.token.span);
                     if attr.style != ast::AttrStyle::Outer {
                         let mut err = self.fatal("expected outer doc comment");
                         err.note("inner doc comments like this (starting with \
@@ -150,10 +150,9 @@ impl<'a> Parser<'a> {
         };
 
         Ok(ast::Attribute {
-            item,
+            kind: ast::AttrKind::Normal(item),
             id: attr::mk_attr_id(),
             style,
-            is_sugared_doc: false,
             span,
         })
     }
@@ -229,7 +228,7 @@ impl<'a> Parser<'a> {
                 }
                 token::DocComment(s) => {
                     // We need to get the position of this token before we bump.
-                    let attr = attr::mk_sugared_doc_attr(s, self.token.span);
+                    let attr = attr::mk_doc_comment(s, self.token.span);
                     if attr.style == ast::AttrStyle::Inner {
                         attrs.push(attr);
                         self.bump();
diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs
index 5b60e7e6dba..cc6235c6fc7 100644
--- a/src/libsyntax/parse/parser/item.rs
+++ b/src/libsyntax/parse/parser/item.rs
@@ -3,8 +3,8 @@ use super::diagnostics::{Error, dummy_arg, ConsumeClosingDelim};
 
 use crate::maybe_whole;
 use crate::ptr::P;
-use crate::ast::{self, DUMMY_NODE_ID, Ident, Attribute, AttrStyle, AnonConst, Item, ItemKind};
-use crate::ast::{ImplItem, ImplItemKind, TraitItem, TraitItemKind, UseTree, UseTreeKind};
+use crate::ast::{self, DUMMY_NODE_ID, Ident, Attribute, AttrKind, AttrStyle, AnonConst, Item};
+use crate::ast::{ItemKind, ImplItem, ImplItemKind, TraitItem, TraitItemKind, UseTree, UseTreeKind};
 use crate::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness};
 use crate::ast::{Visibility, VisibilityKind, Mutability, FnHeader, ForeignItem, ForeignItemKind};
 use crate::ast::{Ty, TyKind, Generics, GenericBounds, TraitRef, EnumDef, VariantData, StructField};
@@ -483,12 +483,14 @@ impl<'a> Parser<'a> {
     /// Emits an expected-item-after-attributes error.
     fn expected_item_err(&mut self, attrs: &[Attribute]) -> PResult<'a,  ()> {
         let message = match attrs.last() {
-            Some(&Attribute { is_sugared_doc: true, .. }) => "expected item after doc comment",
-            _ => "expected item after attributes",
+            Some(&Attribute { kind: AttrKind::DocComment(_), .. }) =>
+                "expected item after doc comment",
+            _ =>
+                "expected item after attributes",
         };
 
         let mut err = self.diagnostic().struct_span_err(self.prev_span, message);
-        if attrs.last().unwrap().is_sugared_doc {
+        if attrs.last().unwrap().is_doc_comment() {
             err.span_label(self.prev_span, "this doc comment doesn't document anything");
         }
         Err(err)
diff --git a/src/libsyntax/parse/tests.rs b/src/libsyntax/parse/tests.rs
index 3bdb9227b4e..169eb954efa 100644
--- a/src/libsyntax/parse/tests.rs
+++ b/src/libsyntax/parse/tests.rs
@@ -246,7 +246,7 @@ let mut fflags: c_int = wb();
         let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string();
         let item = parse_item_from_source_str(name_2, source, &sess)
             .unwrap().unwrap();
-        let docs = item.attrs.iter().filter(|a| a.path == sym::doc)
+        let docs = item.attrs.iter().filter(|a| a.has_name(sym::doc))
                     .map(|a| a.value_str().unwrap().to_string()).collect::<Vec<_>>();
         let b: &[_] = &["/// doc comment".to_string(), "/// line 2".to_string()];
         assert_eq!(&docs[..], b);
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 74ab5c79019..c8afe8a1ff4 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -622,16 +622,19 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             self.hardbreak_if_not_bol();
         }
         self.maybe_print_comment(attr.span.lo());
-        if attr.is_sugared_doc {
-            self.word(attr.value_str().unwrap().to_string());
-            self.hardbreak()
-        } else {
-            match attr.style {
-                ast::AttrStyle::Inner => self.word("#!["),
-                ast::AttrStyle::Outer => self.word("#["),
+        match attr.kind {
+            ast::AttrKind::Normal(ref item) => {
+                match attr.style {
+                    ast::AttrStyle::Inner => self.word("#!["),
+                    ast::AttrStyle::Outer => self.word("#["),
+                }
+                self.print_attr_item(&item, attr.span);
+                self.word("]");
+            }
+            ast::AttrKind::DocComment(comment) => {
+                self.word(comment.to_string());
+                self.hardbreak()
             }
-            self.print_attr_item(&attr.item, attr.span);
-            self.word("]");
         }
     }
 
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 64393a295de..117787d08c7 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -846,7 +846,10 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
 }
 
 pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
-    visitor.visit_tts(attr.item.tokens.clone());
+    match attr.kind {
+        AttrKind::Normal(ref item) => visitor.visit_tts(item.tokens.clone()),
+        AttrKind::DocComment(_) => {}
+    }
 }
 
 pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) {