2020#![ allow( non_camel_case_types) ]
2121
2222use 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;
2327use rustc_span:: edition:: Edition ;
28+ use rustc_span:: Span ;
2429use std:: borrow:: Cow ;
2530use std:: cell:: RefCell ;
2631use std:: collections:: VecDeque ;
@@ -192,7 +197,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
192197 if let Some ( Event :: Start ( Tag :: CodeBlock ( kind) ) ) = event {
193198 let parse_result = match kind {
194199 CodeBlockKind :: Fenced ( ref lang) => {
195- LangString :: parse ( & lang, self . check_error_codes , false )
200+ LangString :: parse_without_check ( & lang, self . check_error_codes , false )
196201 }
197202 CodeBlockKind :: Indented => LangString :: all_false ( ) ,
198203 } ;
@@ -560,6 +565,7 @@ pub fn find_testable_code<T: test::Tester>(
560565 tests : & mut T ,
561566 error_codes : ErrorCodes ,
562567 enable_per_target_ignores : bool ,
568+ extra_info : Option < & ExtraInfo < ' _ , ' _ > > ,
563569) {
564570 let mut parser = Parser :: new ( doc) . into_offset_iter ( ) ;
565571 let mut prev_offset = 0 ;
@@ -573,7 +579,12 @@ pub fn find_testable_code<T: test::Tester>(
573579 if lang. is_empty ( ) {
574580 LangString :: all_false ( )
575581 } 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+ )
577588 }
578589 }
579590 CodeBlockKind :: Indented => LangString :: all_false ( ) ,
@@ -615,6 +626,49 @@ pub fn find_testable_code<T: test::Tester>(
615626 }
616627}
617628
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+
618672#[ derive( Eq , PartialEq , Clone , Debug ) ]
619673pub struct LangString {
620674 original : String ,
@@ -652,10 +706,19 @@ impl LangString {
652706 }
653707 }
654708
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+
655717 fn parse (
656718 string : & str ,
657719 allow_error_code_check : ErrorCodes ,
658720 enable_per_target_ignores : bool ,
721+ extra : Option < & ExtraInfo < ' _ , ' _ > > ,
659722 ) -> LangString {
660723 let allow_error_code_check = allow_error_code_check. as_bool ( ) ;
661724 let mut seen_rust_tags = false ;
@@ -715,6 +778,53 @@ impl LangString {
715778 seen_other_tags = true ;
716779 }
717780 }
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+ }
718828 _ => seen_other_tags = true ,
719829 }
720830 }
@@ -934,7 +1044,7 @@ crate struct RustCodeBlock {
9341044
9351045/// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
9361046/// 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 > {
9381048 let mut code_blocks = vec ! [ ] ;
9391049
9401050 if md. is_empty ( ) {
@@ -951,7 +1061,7 @@ crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
9511061 let lang_string = if syntax. is_empty ( ) {
9521062 LangString :: all_false ( )
9531063 } else {
954- LangString :: parse ( & * syntax, ErrorCodes :: Yes , false )
1064+ LangString :: parse ( & * syntax, ErrorCodes :: Yes , false , Some ( extra_info ) )
9551065 } ;
9561066 if !lang_string. rust {
9571067 continue ;
0 commit comments