@@ -7,6 +7,7 @@ use cfg::{CfgExpr, CfgOptions};
77use either:: Either ;
88use hir_expand:: {
99 attrs:: { collect_attrs, Attr , AttrId , RawAttrs } ,
10+ name:: { AsName , Name } ,
1011 HirFileId , InFile ,
1112} ;
1213use itertools:: Itertools ;
@@ -238,6 +239,17 @@ impl Attrs {
238239 } )
239240 }
240241
242+ pub fn doc_exprs ( & self ) -> Vec < DocExpr > {
243+ self . by_key ( "doc" ) . tt_values ( ) . map ( DocExpr :: parse) . collect ( )
244+ }
245+
246+ pub fn doc_aliases ( & self ) -> Vec < SmolStr > {
247+ self . doc_exprs ( )
248+ . into_iter ( )
249+ . flat_map ( |doc_expr| doc_expr. aliases ( ) )
250+ . collect ( )
251+ }
252+
241253 pub fn is_proc_macro ( & self ) -> bool {
242254 self . by_key ( "proc_macro" ) . exists ( )
243255 }
@@ -251,6 +263,106 @@ impl Attrs {
251263 }
252264}
253265
266+ use std:: slice:: Iter as SliceIter ;
267+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , Ord , PartialOrd ) ]
268+ pub enum DocAtom {
269+ /// eg. `#[doc(hidden)]`
270+ Flag ( SmolStr ) ,
271+ /// eg. `#[doc(alias = "x")]`
272+ ///
273+ /// Note that a key can have multiple values that are all considered "active" at the same time.
274+ /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
275+ KeyValue { key : SmolStr , value : SmolStr } ,
276+ }
277+
278+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
279+ // #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
280+ pub enum DocExpr {
281+ Invalid ,
282+ /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
283+ Atom ( DocAtom ) ,
284+ /// eg. `#[doc(alias("x", "y"))]`
285+ Alias ( Vec < SmolStr > ) ,
286+ }
287+
288+ impl From < DocAtom > for DocExpr {
289+ fn from ( atom : DocAtom ) -> Self {
290+ DocExpr :: Atom ( atom)
291+ }
292+ }
293+
294+ impl DocExpr {
295+ pub fn parse < S > ( tt : & tt:: Subtree < S > ) -> DocExpr {
296+ next_doc_expr ( & mut tt. token_trees . iter ( ) ) . unwrap_or ( DocExpr :: Invalid )
297+ }
298+
299+ pub fn aliases ( self ) -> Vec < SmolStr > {
300+ match self {
301+ DocExpr :: Atom ( DocAtom :: KeyValue { key, value } ) if key == "alias" => {
302+ vec ! [ value]
303+ }
304+ DocExpr :: Alias ( aliases) => aliases,
305+ _ => vec ! [ ] ,
306+ }
307+ }
308+ }
309+
310+ fn next_doc_expr < S > ( it : & mut SliceIter < ' _ , tt:: TokenTree < S > > ) -> Option < DocExpr > {
311+ let name = match it. next ( ) {
312+ None => return None ,
313+ Some ( tt:: TokenTree :: Leaf ( tt:: Leaf :: Ident ( ident) ) ) => ident. text . clone ( ) ,
314+ Some ( _) => return Some ( DocExpr :: Invalid ) ,
315+ } ;
316+
317+ // Peek
318+ let ret = match it. as_slice ( ) . first ( ) {
319+ Some ( tt:: TokenTree :: Leaf ( tt:: Leaf :: Punct ( punct) ) ) if punct. char == '=' => {
320+ match it. as_slice ( ) . get ( 1 ) {
321+ Some ( tt:: TokenTree :: Leaf ( tt:: Leaf :: Literal ( literal) ) ) => {
322+ it. next ( ) ;
323+ it. next ( ) ;
324+ // FIXME: escape? raw string?
325+ let value =
326+ SmolStr :: new ( literal. text . trim_start_matches ( '"' ) . trim_end_matches ( '"' ) ) ;
327+ DocAtom :: KeyValue { key : name, value } . into ( )
328+ }
329+ _ => return Some ( DocExpr :: Invalid ) ,
330+ }
331+ }
332+ Some ( tt:: TokenTree :: Subtree ( subtree) ) => {
333+ it. next ( ) ;
334+ let subs = parse_comma_sep ( subtree) ;
335+ match name. as_str ( ) {
336+ "alias" => DocExpr :: Alias ( subs) ,
337+ _ => DocExpr :: Invalid ,
338+ }
339+ }
340+ _ => DocAtom :: Flag ( name) . into ( ) ,
341+ } ;
342+
343+ // Eat comma separator
344+ if let Some ( tt:: TokenTree :: Leaf ( tt:: Leaf :: Punct ( punct) ) ) = it. as_slice ( ) . first ( ) {
345+ if punct. char == ',' {
346+ it. next ( ) ;
347+ }
348+ }
349+ Some ( ret)
350+ }
351+
352+ fn parse_comma_sep < S > ( subtree : & tt:: Subtree < S > ) -> Vec < SmolStr > {
353+ subtree
354+ . token_trees
355+ . iter ( )
356+ . filter_map ( |tt| match tt {
357+ tt:: TokenTree :: Leaf ( tt:: Leaf :: Literal ( lit) ) => {
358+ // FIXME: escape? raw string?
359+ Some ( SmolStr :: new ( lit. text . trim_start_matches ( '"' ) . trim_end_matches ( '"' ) ) )
360+ }
361+ _ => None ,
362+ } )
363+ . collect ( )
364+ }
365+
254366impl AttrsWithOwner {
255367 pub ( crate ) fn attrs_query ( db : & dyn DefDatabase , def : AttrDefId ) -> Self {
256368 // FIXME: this should use `Trace` to avoid duplication in `source_map` below
0 commit comments