diff options
| author | Nicholas Nethercote <nnethercote@mozilla.com> | 2019-10-24 06:33:12 +1100 |
|---|---|---|
| committer | Nicholas Nethercote <nnethercote@mozilla.com> | 2019-11-06 23:05:07 +1100 |
| commit | eea6f23a0ed67fd8c6b8e1b02cda3628fee56b2f (patch) | |
| tree | effdb244138f311440b21c9fd52e4028edb575ea /src/libsyntax | |
| parent | 69bc4aba785e071740d2d46f109623b9951aae5d (diff) | |
| download | rust-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.rs | 19 | ||||
| -rw-r--r-- | src/libsyntax/attr/builtin.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/attr/mod.rs | 110 | ||||
| -rw-r--r-- | src/libsyntax/config.rs | 9 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate/check.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/mut_visit.rs | 12 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 22 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser/attr.rs | 7 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser/item.rs | 12 | ||||
| -rw-r--r-- | src/libsyntax/parse/tests.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 21 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 5 |
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) { |
