@@ -10,7 +10,10 @@ use rustc_interface::interface;
10
10
use rustc_middle:: hir:: map:: Map ;
11
11
use rustc_middle:: hir:: nested_filter;
12
12
use rustc_middle:: ty:: TyCtxt ;
13
+ use rustc_parse:: maybe_new_parser_from_source_str;
14
+ use rustc_parse:: parser:: attr:: InnerAttrPolicy ;
13
15
use rustc_session:: config:: { self , CrateType , ErrorOutputType } ;
16
+ use rustc_session:: parse:: ParseSess ;
14
17
use rustc_session:: { lint, DiagnosticOutput , Session } ;
15
18
use rustc_span:: edition:: Edition ;
16
19
use rustc_span:: source_map:: SourceMap ;
@@ -493,7 +496,7 @@ crate fn make_test(
493
496
edition : Edition ,
494
497
test_id : Option < & str > ,
495
498
) -> ( String , usize , bool ) {
496
- let ( crate_attrs, everything_else, crates) = partition_source ( s) ;
499
+ let ( crate_attrs, everything_else, crates) = partition_source ( s, edition ) ;
497
500
let everything_else = everything_else. trim ( ) ;
498
501
let mut line_offset = 0 ;
499
502
let mut prog = String :: new ( ) ;
@@ -525,9 +528,7 @@ crate fn make_test(
525
528
rustc_span:: create_session_if_not_set_then ( edition, |_| {
526
529
use rustc_errors:: emitter:: { Emitter , EmitterWriter } ;
527
530
use rustc_errors:: Handler ;
528
- use rustc_parse:: maybe_new_parser_from_source_str;
529
531
use rustc_parse:: parser:: ForceCollect ;
530
- use rustc_session:: parse:: ParseSess ;
531
532
use rustc_span:: source_map:: FilePathMapping ;
532
533
533
534
let filename = FileName :: anon_source_code ( s) ;
@@ -697,8 +698,39 @@ crate fn make_test(
697
698
( prog, line_offset, supports_color)
698
699
}
699
700
700
- // FIXME(aburka): use a real parser to deal with multiline attributes
701
- fn partition_source ( s : & str ) -> ( String , String , String ) {
701
+ fn check_if_attr_is_complete ( source : & str , edition : Edition ) -> bool {
702
+ if source. is_empty ( ) {
703
+ // Empty content so nothing to check in here...
704
+ return true ;
705
+ }
706
+ rustc_span:: create_session_if_not_set_then ( edition, |_| {
707
+ let filename = FileName :: anon_source_code ( source) ;
708
+ let sess = ParseSess :: with_silent_emitter ( None ) ;
709
+ let mut parser = match maybe_new_parser_from_source_str ( & sess, filename, source. to_owned ( ) )
710
+ {
711
+ Ok ( p) => p,
712
+ Err ( _) => {
713
+ debug ! ( "Cannot build a parser to check mod attr so skipping..." ) ;
714
+ return true ;
715
+ }
716
+ } ;
717
+ // If a parsing error happened, it's very likely that the attribute is incomplete.
718
+ if !parser. parse_attribute ( InnerAttrPolicy :: Permitted ) . is_ok ( ) {
719
+ return false ;
720
+ }
721
+ // We now check if there is an unclosed delimiter for the attribute. To do so, we look at
722
+ // the `unclosed_delims` and see if the opening square bracket was closed.
723
+ parser
724
+ . unclosed_delims ( )
725
+ . get ( 0 )
726
+ . map ( |unclosed| {
727
+ unclosed. unclosed_span . map ( |s| s. lo ( ) ) . unwrap_or ( BytePos ( 0 ) ) != BytePos ( 2 )
728
+ } )
729
+ . unwrap_or ( true )
730
+ } )
731
+ }
732
+
733
+ fn partition_source ( s : & str , edition : Edition ) -> ( String , String , String ) {
702
734
#[ derive( Copy , Clone , PartialEq ) ]
703
735
enum PartitionState {
704
736
Attrs ,
@@ -710,15 +742,23 @@ fn partition_source(s: &str) -> (String, String, String) {
710
742
let mut crates = String :: new ( ) ;
711
743
let mut after = String :: new ( ) ;
712
744
745
+ let mut mod_attr_pending = String :: new ( ) ;
746
+
713
747
for line in s. lines ( ) {
714
748
let trimline = line. trim ( ) ;
715
749
716
750
// FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be
717
751
// shunted into "everything else"
718
752
match state {
719
753
PartitionState :: Attrs => {
720
- state = if trimline. starts_with ( "#![" )
721
- || trimline. chars ( ) . all ( |c| c. is_whitespace ( ) )
754
+ state = if trimline. starts_with ( "#![" ) {
755
+ if !check_if_attr_is_complete ( line, edition) {
756
+ mod_attr_pending = line. to_owned ( ) ;
757
+ } else {
758
+ mod_attr_pending. clear ( ) ;
759
+ }
760
+ PartitionState :: Attrs
761
+ } else if trimline. chars ( ) . all ( |c| c. is_whitespace ( ) )
722
762
|| ( trimline. starts_with ( "//" ) && !trimline. starts_with ( "///" ) )
723
763
{
724
764
PartitionState :: Attrs
@@ -727,7 +767,21 @@ fn partition_source(s: &str) -> (String, String, String) {
727
767
{
728
768
PartitionState :: Crates
729
769
} else {
730
- PartitionState :: Other
770
+ // First we check if the previous attribute was "complete"...
771
+ if !mod_attr_pending. is_empty ( ) {
772
+ // If not, then we append the new line into the pending attribute to check
773
+ // if this time it's complete...
774
+ mod_attr_pending. push_str ( line) ;
775
+ if !trimline. is_empty ( ) && check_if_attr_is_complete ( line, edition) {
776
+ // If it's complete, then we can clear the pending content.
777
+ mod_attr_pending. clear ( ) ;
778
+ }
779
+ // In any case, this is considered as `PartitionState::Attrs` so it's
780
+ // prepended before rustdoc's inserts.
781
+ PartitionState :: Attrs
782
+ } else {
783
+ PartitionState :: Other
784
+ }
731
785
} ;
732
786
}
733
787
PartitionState :: Crates => {
0 commit comments