@@ -39,7 +39,7 @@ use rustc_abi::ReprOptions;
3939use rustc_hash:: FxHashSet ;
4040use smallvec:: SmallVec ;
4141use syntax:: {
42- AstNode , AstToken , NodeOrToken , SmolStr , SyntaxNode , SyntaxToken , T ,
42+ AstNode , AstToken , NodeOrToken , SmolStr , SourceFile , SyntaxNode , SyntaxToken , T ,
4343 ast:: { self , AttrDocCommentIter , HasAttrs , IsString , TokenTreeChildren } ,
4444} ;
4545use tt:: { TextRange , TextSize } ;
@@ -292,35 +292,69 @@ bitflags::bitflags! {
292292 }
293293}
294294
295+ pub fn parse_extra_crate_attrs ( db : & dyn DefDatabase , krate : Crate ) -> Option < SourceFile > {
296+ let crate_data = krate. data ( db) ;
297+ let crate_attrs = & crate_data. crate_attrs ;
298+ if crate_attrs. is_empty ( ) {
299+ return None ;
300+ }
301+ // All attributes are already enclosed in `#![]`.
302+ let combined = crate_attrs. concat ( ) ;
303+ let p = SourceFile :: parse ( & combined, crate_data. edition ) ;
304+
305+ let errs = p. errors ( ) ;
306+ if !errs. is_empty ( ) {
307+ let base_msg = "Failed to parse extra crate-level attribute" ;
308+ let crate_name =
309+ krate. extra_data ( db) . display_name . as_ref ( ) . map_or ( "{unknown}" , |name| name. as_str ( ) ) ;
310+ let mut errs = errs. iter ( ) . peekable ( ) ;
311+ let mut offset = TextSize :: from ( 0 ) ;
312+ for raw_attr in crate_attrs {
313+ let attr_end = offset + TextSize :: of ( & * * raw_attr) ;
314+ if errs. peeking_take_while ( |e| e. range ( ) . start ( ) < attr_end) . count ( ) > 0 {
315+ tracing:: error!( "{base_msg} {raw_attr} for crate {crate_name}" ) ;
316+ }
317+ offset = attr_end
318+ }
319+ return None ;
320+ }
321+
322+ Some ( p. tree ( ) )
323+ }
324+
295325fn attrs_source (
296326 db : & dyn DefDatabase ,
297327 owner : AttrDefId ,
298- ) -> ( InFile < ast:: AnyHasAttrs > , Option < InFile < ast:: Module > > , Crate ) {
328+ ) -> ( InFile < ast:: AnyHasAttrs > , Option < InFile < ast:: Module > > , Option < SourceFile > , Crate ) {
299329 let ( owner, krate) = match owner {
300330 AttrDefId :: ModuleId ( id) => {
301331 let def_map = id. def_map ( db) ;
302- let ( definition, declaration) = match def_map[ id] . origin {
332+ let krate = def_map. krate ( ) ;
333+ let ( definition, declaration, extra_crate_attrs) = match def_map[ id] . origin {
303334 ModuleOrigin :: CrateRoot { definition } => {
304- let file = db. parse ( definition) . tree ( ) ;
305- ( InFile :: new ( definition. into ( ) , ast:: AnyHasAttrs :: from ( file) ) , None )
335+ let definition_source = db. parse ( definition) . tree ( ) ;
336+ let definition = InFile :: new ( definition. into ( ) , definition_source. into ( ) ) ;
337+ let extra_crate_attrs = parse_extra_crate_attrs ( db, krate) ;
338+ ( definition, None , extra_crate_attrs)
306339 }
307340 ModuleOrigin :: File { declaration, declaration_tree_id, definition, .. } => {
341+ let definition_source = db. parse ( definition) . tree ( ) ;
342+ let definition = InFile :: new ( definition. into ( ) , definition_source. into ( ) ) ;
308343 let declaration = InFile :: new ( declaration_tree_id. file_id ( ) , declaration) ;
309344 let declaration = declaration. with_value ( declaration. to_node ( db) ) ;
310- let definition_source = db. parse ( definition) . tree ( ) ;
311- ( InFile :: new ( definition. into ( ) , definition_source. into ( ) ) , Some ( declaration) )
345+ ( definition, Some ( declaration) , None )
312346 }
313347 ModuleOrigin :: Inline { definition_tree_id, definition } => {
314348 let definition = InFile :: new ( definition_tree_id. file_id ( ) , definition) ;
315349 let definition = definition. with_value ( definition. to_node ( db) . into ( ) ) ;
316- ( definition, None )
350+ ( definition, None , None )
317351 }
318352 ModuleOrigin :: BlockExpr { block, .. } => {
319353 let definition = block. to_node ( db) ;
320- ( block. with_value ( definition. into ( ) ) , None )
354+ ( block. with_value ( definition. into ( ) ) , None , None )
321355 }
322356 } ;
323- return ( definition, declaration, def_map . krate ( ) ) ;
357+ return ( definition, declaration, extra_crate_attrs , krate) ;
324358 }
325359 AttrDefId :: AdtId ( AdtId :: StructId ( it) ) => attrs_from_ast_id_loc ( db, it) ,
326360 AttrDefId :: AdtId ( AdtId :: UnionId ( it) ) => attrs_from_ast_id_loc ( db, it) ,
@@ -339,22 +373,23 @@ fn attrs_source(
339373 AttrDefId :: ExternCrateId ( it) => attrs_from_ast_id_loc ( db, it) ,
340374 AttrDefId :: UseId ( it) => attrs_from_ast_id_loc ( db, it) ,
341375 } ;
342- ( owner, None , krate)
376+ ( owner, None , None , krate)
343377}
344378
345379fn collect_attrs < BreakValue > (
346380 db : & dyn DefDatabase ,
347381 owner : AttrDefId ,
348382 mut callback : impl FnMut ( Meta ) -> ControlFlow < BreakValue > ,
349383) -> Option < BreakValue > {
350- let ( source, outer_mod_decl, krate) = attrs_source ( db, owner) ;
384+ let ( source, outer_mod_decl, extra_crate_attrs, krate) = attrs_source ( db, owner) ;
385+ let extra_attrs = extra_crate_attrs
386+ . into_iter ( )
387+ . flat_map ( |src| src. attrs ( ) )
388+ . chain ( outer_mod_decl. into_iter ( ) . flat_map ( |it| it. value . attrs ( ) ) ) ;
351389
352390 let mut cfg_options = None ;
353391 expand_cfg_attr (
354- outer_mod_decl
355- . into_iter ( )
356- . flat_map ( |it| it. value . attrs ( ) )
357- . chain ( ast:: attrs_including_inner ( & source. value ) ) ,
392+ extra_attrs. chain ( ast:: attrs_including_inner ( & source. value ) ) ,
358393 || cfg_options. get_or_insert_with ( || krate. cfg_options ( db) ) ,
359394 move |meta, _, _, _| callback ( meta) ,
360395 )
@@ -1013,10 +1048,12 @@ impl AttrFlags {
10131048 pub fn doc_html_root_url ( db : & dyn DefDatabase , krate : Crate ) -> Option < SmolStr > {
10141049 let root_file_id = krate. root_file_id ( db) ;
10151050 let syntax = db. parse ( root_file_id) . tree ( ) ;
1051+ let extra_crate_attrs =
1052+ parse_extra_crate_attrs ( db, krate) . into_iter ( ) . flat_map ( |src| src. attrs ( ) ) ;
10161053
10171054 let mut cfg_options = None ;
10181055 expand_cfg_attr (
1019- syntax. attrs ( ) ,
1056+ extra_crate_attrs . chain ( syntax. attrs ( ) ) ,
10201057 || cfg_options. get_or_insert ( krate. cfg_options ( db) ) ,
10211058 |attr, _, _, _| {
10221059 if let Meta :: TokenTree { path, tt } = attr
@@ -1231,8 +1268,11 @@ impl AttrFlags {
12311268 // We LRU this query because it is only used by IDE.
12321269 #[ salsa:: tracked( returns( ref) , lru = 250 ) ]
12331270 pub fn docs ( db : & dyn DefDatabase , owner : AttrDefId ) -> Option < Box < Docs > > {
1234- let ( source, outer_mod_decl, krate) = attrs_source ( db, owner) ;
1271+ let ( source, outer_mod_decl, _extra_crate_attrs , krate) = attrs_source ( db, owner) ;
12351272 let inner_attrs_node = source. value . inner_attributes_node ( ) ;
1273+ // Note: we don't have to pass down `_extra_crate_attrs` here, since `extract_docs`
1274+ // does not handle crate-level attributes related to docs.
1275+ // See: https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#at-the-crate-level
12361276 extract_docs ( & || krate. cfg_options ( db) , source, outer_mod_decl, inner_attrs_node)
12371277 }
12381278
@@ -1480,8 +1520,9 @@ mod tests {
14801520 use test_fixture:: WithFixture ;
14811521 use tt:: { TextRange , TextSize } ;
14821522
1483- use crate :: attrs:: IsInnerDoc ;
1484- use crate :: { attrs:: Docs , test_db:: TestDB } ;
1523+ use crate :: AttrDefId ;
1524+ use crate :: attrs:: { AttrFlags , Docs , IsInnerDoc } ;
1525+ use crate :: test_db:: TestDB ;
14851526
14861527 #[ test]
14871528 fn docs ( ) {
@@ -1617,4 +1658,15 @@ mod tests {
16171658 Some ( ( in_file( range( 263 , 265 ) ) , IsInnerDoc :: Yes ) )
16181659 ) ;
16191660 }
1661+
1662+ #[ test]
1663+ fn crate_attrs ( ) {
1664+ let fixture = r#"
1665+ //- /lib.rs crate:foo crate-attr:no_std crate-attr:cfg(target_arch="x86")
1666+ "# ;
1667+ let ( db, file_id) = TestDB :: with_single_file ( fixture) ;
1668+ let module = db. module_for_file ( file_id. file_id ( & db) ) ;
1669+ let attrs = AttrFlags :: query ( & db, AttrDefId :: ModuleId ( module) ) ;
1670+ assert ! ( attrs. contains( AttrFlags :: IS_NO_STD | AttrFlags :: HAS_CFG ) ) ;
1671+ }
16201672}
0 commit comments