@@ -3,17 +3,18 @@ use std::convert::identity;
33use rustc_ast as ast;
44use rustc_ast:: token:: DocFragmentKind ;
55use rustc_ast:: { AttrItemKind , AttrStyle , NodeId , Safety } ;
6- use rustc_errors:: DiagCtxtHandle ;
6+ use rustc_errors:: { DiagCtxtHandle , StashKey } ;
77use rustc_feature:: { AttributeTemplate , Features } ;
88use rustc_hir:: attrs:: AttributeKind ;
99use rustc_hir:: lints:: AttributeLintKind ;
1010use rustc_hir:: { AttrArgs , AttrItem , AttrPath , Attribute , HashIgnoredAttrId , Target } ;
1111use rustc_session:: Session ;
1212use rustc_session:: lint:: { BuiltinLintDiag , LintId } ;
13- use rustc_span:: { DUMMY_SP , Span , Symbol , sym} ;
13+ use rustc_span:: { BytePos , DUMMY_SP , Span , Symbol , sym} ;
1414
1515use crate :: context:: { AcceptContext , FinalizeContext , FinalizeFn , SharedContext , Stage } ;
1616use crate :: early_parsed:: { EARLY_PARSED_ATTRIBUTES , EarlyParsedState } ;
17+ use crate :: errors:: { InvalidAttrAtCrateLevel , ItemFollowingInnerAttr } ;
1718use crate :: parser:: { ArgParser , PathParser , RefPathParser } ;
1819use crate :: session_diagnostics:: ParsedDescription ;
1920use crate :: { Early , Late , OmitDoc , ShouldEmit } ;
@@ -51,6 +52,7 @@ impl<'sess> AttributeParser<'sess, Early> {
5152 sess : & ' sess Session ,
5253 attrs : & [ ast:: Attribute ] ,
5354 sym : Symbol ,
55+ target : Target ,
5456 target_span : Span ,
5557 target_node_id : NodeId ,
5658 features : Option < & ' sess Features > ,
@@ -59,6 +61,7 @@ impl<'sess> AttributeParser<'sess, Early> {
5961 sess,
6062 attrs,
6163 sym,
64+ target,
6265 target_span,
6366 target_node_id,
6467 features,
@@ -72,6 +75,7 @@ impl<'sess> AttributeParser<'sess, Early> {
7275 sess : & ' sess Session ,
7376 attrs : & [ ast:: Attribute ] ,
7477 sym : Symbol ,
78+ target : Target ,
7579 target_span : Span ,
7680 target_node_id : NodeId ,
7781 features : Option < & ' sess Features > ,
@@ -81,7 +85,7 @@ impl<'sess> AttributeParser<'sess, Early> {
8185 sess,
8286 attrs,
8387 Some ( sym) ,
84- Target :: Crate , // Does not matter, we're not going to emit errors anyways
88+ target ,
8589 target_span,
8690 target_node_id,
8791 features,
@@ -302,7 +306,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
302306 kind : DocFragmentKind :: Sugared ( * comment_kind) ,
303307 span : attr_span,
304308 comment : * symbol,
305- } ) )
309+ } ) ) ;
306310 }
307311 ast:: AttrKind :: Normal ( n) => {
308312 attr_paths. push ( PathParser ( & n. item . path ) ) ;
@@ -405,15 +409,23 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
405409 // "attribute {path} wasn't parsed and isn't a know tool attribute",
406410 // );
407411
408- attributes . push ( Attribute :: Unparsed ( Box :: new ( AttrItem {
412+ let attr = AttrItem {
409413 path : attr_path. clone ( ) ,
410414 args : self
411415 . lower_attr_args ( n. item . args . unparsed_ref ( ) . unwrap ( ) , lower_span) ,
412416 id : HashIgnoredAttrId { attr_id : attr. id } ,
413417 style : attr. style ,
414418 span : attr_span,
415- } ) ) ) ;
416- }
419+ } ;
420+
421+ if !matches ! ( self . stage. should_emit( ) , ShouldEmit :: Nothing )
422+ && target == Target :: Crate
423+ {
424+ self . check_invalid_crate_level_attr_item ( & attr) ;
425+ }
426+
427+ attributes. push ( Attribute :: Unparsed ( Box :: new ( attr) ) ) ;
428+ } ;
417429 }
418430 }
419431 }
@@ -477,4 +489,96 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
477489 }
478490 }
479491 }
492+
493+ // FIXME: Fix "Cannot determine resolution" error and remove built-in macros
494+ // from this check.
495+ fn check_invalid_crate_level_attr_item ( & self , attr : & AttrItem ) {
496+ // Check for builtin attributes at the crate level
497+ // which were unsuccessfully resolved due to cannot determine
498+ // resolution for the attribute macro error.
499+ const ATTRS_TO_CHECK : & [ Symbol ] =
500+ & [ sym:: derive, sym:: test, sym:: test_case, sym:: global_allocator, sym:: bench] ;
501+
502+ // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day.
503+ if let Some ( name) = ATTRS_TO_CHECK . iter ( ) . find ( |attr_to_check| matches ! ( attr. path. segments. as_ref( ) , [ segment] if segment == * attr_to_check) ) {
504+ let span = attr. span ;
505+ let name = * name;
506+
507+ let item = self . first_line_of_next_item ( span) . map ( |span| ItemFollowingInnerAttr { span } ) ;
508+
509+ let err = self . dcx ( ) . create_err ( InvalidAttrAtCrateLevel {
510+ span,
511+ sugg_span : self
512+ . sess
513+ . source_map ( )
514+ . span_to_snippet ( attr. span )
515+ . ok ( )
516+ . filter ( |src| src. starts_with ( "#![" ) )
517+ . map ( |_| {
518+ span
519+ . with_lo ( span. lo ( ) + BytePos ( 1 ) )
520+ . with_hi ( span. lo ( ) + BytePos ( 2 ) )
521+ } ) ,
522+ name,
523+ item,
524+ } ) ;
525+
526+ self . dcx ( ) . try_steal_replace_and_emit_err (
527+ attr. path . span ,
528+ StashKey :: UndeterminedMacroResolution ,
529+ err,
530+ ) ;
531+ }
532+ }
533+
534+ pub ( crate ) fn first_line_of_next_item ( & self , span : Span ) -> Option < Span > {
535+ // We can't exactly call `tcx.hir_free_items()` here because it's too early and querying
536+ // this would create a circular dependency. Instead, we resort to getting the original
537+ // source code that follows `span` and find the next item from here.
538+
539+ self . sess ( )
540+ . source_map ( )
541+ . span_to_source ( span, |content, _, span_end| {
542+ let mut source = & content[ span_end..] ;
543+ let initial_source_len = source. len ( ) ;
544+ let span = try {
545+ loop {
546+ let first = source. chars ( ) . next ( ) ?;
547+
548+ if first. is_whitespace ( ) {
549+ let split_idx = source. find ( |c : char | !c. is_whitespace ( ) ) ?;
550+ source = & source[ split_idx..] ;
551+ } else if source. starts_with ( "//" ) {
552+ let line_idx = source. find ( '\n' ) ?;
553+ source = & source[ line_idx + '\n' . len_utf8 ( ) ..] ;
554+ } else if source. starts_with ( "/*" ) {
555+ // FIXME: support nested comments.
556+ let close_idx = source. find ( "*/" ) ?;
557+ source = & source[ close_idx + "*/" . len ( ) ..] ;
558+ } else if first == '#' {
559+ // FIXME: properly find the end of the attributes in order to accurately
560+ // skip them. This version just consumes the source code until the next
561+ // `]`.
562+ let close_idx = source. find ( ']' ) ?;
563+ source = & source[ close_idx + ']' . len_utf8 ( ) ..] ;
564+ } else {
565+ let lo = span_end + initial_source_len - source. len ( ) ;
566+ let last_line = source. split ( '\n' ) . next ( ) . map ( |s| s. trim_end ( ) ) ?;
567+
568+ let hi = lo + last_line. len ( ) ;
569+ let lo = BytePos ( lo as u32 ) ;
570+ let hi = BytePos ( hi as u32 ) ;
571+ let next_item_span = Span :: new ( lo, hi, span. ctxt ( ) , None ) ;
572+
573+ break next_item_span;
574+ }
575+ }
576+ } ;
577+
578+
579+ Ok ( span)
580+ } )
581+ . ok ( )
582+ . flatten ( )
583+ }
480584}
0 commit comments