20
20
#![ allow( non_camel_case_types) ]
21
21
22
22
use rustc_data_structures:: fx:: FxHashMap ;
23
+ use rustc_hir:: def_id:: DefId ;
24
+ use rustc_hir:: HirId ;
25
+ use rustc_middle:: ty:: TyCtxt ;
26
+ use rustc_session:: lint;
23
27
use rustc_span:: edition:: Edition ;
28
+ use rustc_span:: Span ;
24
29
use std:: borrow:: Cow ;
25
30
use std:: cell:: RefCell ;
26
31
use std:: collections:: VecDeque ;
@@ -192,7 +197,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
192
197
if let Some ( Event :: Start ( Tag :: CodeBlock ( kind) ) ) = event {
193
198
let parse_result = match kind {
194
199
CodeBlockKind :: Fenced ( ref lang) => {
195
- LangString :: parse ( & lang, self . check_error_codes , false )
200
+ LangString :: parse_without_check ( & lang, self . check_error_codes , false )
196
201
}
197
202
CodeBlockKind :: Indented => LangString :: all_false ( ) ,
198
203
} ;
@@ -560,6 +565,7 @@ pub fn find_testable_code<T: test::Tester>(
560
565
tests : & mut T ,
561
566
error_codes : ErrorCodes ,
562
567
enable_per_target_ignores : bool ,
568
+ extra_info : Option < & ExtraInfo < ' _ , ' _ > > ,
563
569
) {
564
570
let mut parser = Parser :: new ( doc) . into_offset_iter ( ) ;
565
571
let mut prev_offset = 0 ;
@@ -573,7 +579,12 @@ pub fn find_testable_code<T: test::Tester>(
573
579
if lang. is_empty ( ) {
574
580
LangString :: all_false ( )
575
581
} else {
576
- LangString :: parse ( lang, error_codes, enable_per_target_ignores)
582
+ LangString :: parse (
583
+ lang,
584
+ error_codes,
585
+ enable_per_target_ignores,
586
+ extra_info,
587
+ )
577
588
}
578
589
}
579
590
CodeBlockKind :: Indented => LangString :: all_false ( ) ,
@@ -615,6 +626,49 @@ pub fn find_testable_code<T: test::Tester>(
615
626
}
616
627
}
617
628
629
+ pub struct ExtraInfo < ' a , ' b > {
630
+ hir_id : Option < HirId > ,
631
+ item_did : Option < DefId > ,
632
+ sp : Span ,
633
+ tcx : & ' a TyCtxt < ' b > ,
634
+ }
635
+
636
+ impl < ' a , ' b > ExtraInfo < ' a , ' b > {
637
+ pub fn new ( tcx : & ' a TyCtxt < ' b > , hir_id : HirId , sp : Span ) -> ExtraInfo < ' a , ' b > {
638
+ ExtraInfo { hir_id : Some ( hir_id) , item_did : None , sp, tcx }
639
+ }
640
+
641
+ pub fn new_did ( tcx : & ' a TyCtxt < ' b > , did : DefId , sp : Span ) -> ExtraInfo < ' a , ' b > {
642
+ ExtraInfo { hir_id : None , item_did : Some ( did) , sp, tcx }
643
+ }
644
+
645
+ fn error_invalid_codeblock_attr ( & self , msg : & str , help : & str ) {
646
+ let hir_id = match ( self . hir_id , self . item_did ) {
647
+ ( Some ( h) , _) => h,
648
+ ( None , Some ( item_did) ) => {
649
+ match self . tcx . hir ( ) . as_local_hir_id ( item_did) {
650
+ Some ( hir_id) => hir_id,
651
+ None => {
652
+ // If non-local, no need to check anything.
653
+ return ;
654
+ }
655
+ }
656
+ }
657
+ ( None , None ) => return ,
658
+ } ;
659
+ self . tcx . struct_span_lint_hir (
660
+ lint:: builtin:: INVALID_CODEBLOCK_ATTRIBUTE ,
661
+ hir_id,
662
+ self . sp ,
663
+ |lint| {
664
+ let mut diag = lint. build ( msg) ;
665
+ diag. help ( help) ;
666
+ diag. emit ( ) ;
667
+ } ,
668
+ ) ;
669
+ }
670
+ }
671
+
618
672
#[ derive( Eq , PartialEq , Clone , Debug ) ]
619
673
pub struct LangString {
620
674
original : String ,
@@ -652,10 +706,19 @@ impl LangString {
652
706
}
653
707
}
654
708
709
+ fn parse_without_check (
710
+ string : & str ,
711
+ allow_error_code_check : ErrorCodes ,
712
+ enable_per_target_ignores : bool ,
713
+ ) -> LangString {
714
+ Self :: parse ( string, allow_error_code_check, enable_per_target_ignores, None )
715
+ }
716
+
655
717
fn parse (
656
718
string : & str ,
657
719
allow_error_code_check : ErrorCodes ,
658
720
enable_per_target_ignores : bool ,
721
+ extra : Option < & ExtraInfo < ' _ , ' _ > > ,
659
722
) -> LangString {
660
723
let allow_error_code_check = allow_error_code_check. as_bool ( ) ;
661
724
let mut seen_rust_tags = false ;
@@ -715,6 +778,53 @@ impl LangString {
715
778
seen_other_tags = true ;
716
779
}
717
780
}
781
+ x if extra. is_some ( ) => {
782
+ let s = x. to_lowercase ( ) ;
783
+ match if s == "compile-fail" || s == "compile_fail" || s == "compilefail" {
784
+ Some ( (
785
+ "compile_fail" ,
786
+ "the code block will either not be tested if not marked as a rust one \
787
+ or won't fail if it compiles successfully",
788
+ ) )
789
+ } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
790
+ Some ( (
791
+ "should_panic" ,
792
+ "the code block will either not be tested if not marked as a rust one \
793
+ or won't fail if it doesn't panic when running",
794
+ ) )
795
+ } else if s == "no-run" || s == "no_run" || s == "norun" {
796
+ Some ( (
797
+ "no_run" ,
798
+ "the code block will either not be tested if not marked as a rust one \
799
+ or will be run (which you might not want)",
800
+ ) )
801
+ } else if s == "allow-fail" || s == "allow_fail" || s == "allowfail" {
802
+ Some ( (
803
+ "allow_fail" ,
804
+ "the code block will either not be tested if not marked as a rust one \
805
+ or will be run (which you might not want)",
806
+ ) )
807
+ } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
808
+ Some ( (
809
+ "test_harness" ,
810
+ "the code block will either not be tested if not marked as a rust one \
811
+ or the code will be wrapped inside a main function",
812
+ ) )
813
+ } else {
814
+ None
815
+ } {
816
+ Some ( ( flag, help) ) => {
817
+ if let Some ( ref extra) = extra {
818
+ extra. error_invalid_codeblock_attr (
819
+ & format ! ( "unknown attribute `{}`. Did you mean `{}`?" , x, flag) ,
820
+ help,
821
+ ) ;
822
+ }
823
+ }
824
+ None => { }
825
+ }
826
+ seen_other_tags = true ;
827
+ }
718
828
_ => seen_other_tags = true ,
719
829
}
720
830
}
@@ -934,7 +1044,7 @@ crate struct RustCodeBlock {
934
1044
935
1045
/// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
936
1046
/// untagged (and assumed to be rust).
937
- crate fn rust_code_blocks ( md : & str ) -> Vec < RustCodeBlock > {
1047
+ crate fn rust_code_blocks ( md : & str , extra_info : & ExtraInfo < ' _ , ' _ > ) -> Vec < RustCodeBlock > {
938
1048
let mut code_blocks = vec ! [ ] ;
939
1049
940
1050
if md. is_empty ( ) {
@@ -951,7 +1061,7 @@ crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
951
1061
let lang_string = if syntax. is_empty ( ) {
952
1062
LangString :: all_false ( )
953
1063
} else {
954
- LangString :: parse ( & * syntax, ErrorCodes :: Yes , false )
1064
+ LangString :: parse ( & * syntax, ErrorCodes :: Yes , false , Some ( extra_info ) )
955
1065
} ;
956
1066
if !lang_string. rust {
957
1067
continue ;
0 commit comments