@@ -103,7 +103,7 @@ fn write_code(
103103) {
104104 // This replace allows to fix how the code source with DOS backline characters is displayed.
105105 let src = src. replace ( "\r \n " , "\n " ) ;
106- let mut closing_tag = "" ;
106+ let mut closing_tags : Vec < & ' static str > = Vec :: new ( ) ;
107107 Classifier :: new (
108108 & src,
109109 edition,
@@ -113,8 +113,12 @@ fn write_code(
113113 . highlight ( & mut |highlight| {
114114 match highlight {
115115 Highlight :: Token { text, class } => string ( out, Escape ( text) , class, & href_context) ,
116- Highlight :: EnterSpan { class } => closing_tag = enter_span ( out, class, & href_context) ,
117- Highlight :: ExitSpan => exit_span ( out, & closing_tag) ,
116+ Highlight :: EnterSpan { class } => {
117+ closing_tags. push ( enter_span ( out, class, & href_context) )
118+ }
119+ Highlight :: ExitSpan => {
120+ exit_span ( out, closing_tags. pop ( ) . expect ( "ExitSpan without EnterSpan" ) )
121+ }
118122 } ;
119123 } ) ;
120124}
@@ -682,8 +686,10 @@ fn enter_span(
682686 klass : Class ,
683687 href_context : & Option < HrefContext < ' _ , ' _ , ' _ > > ,
684688) -> & ' static str {
685- string_without_closing_tag ( out, "" , Some ( klass) , href_context)
686- . expect ( "no closing tag to close wrapper..." )
689+ string_without_closing_tag ( out, "" , Some ( klass) , href_context) . expect (
690+ "internal error: enter_span was called with Some(klass) but did not return a \
691+ closing HTML tag",
692+ )
687693}
688694
689695/// Called at the end of a span of highlighted text.
@@ -718,6 +724,15 @@ fn string<T: Display>(
718724 }
719725}
720726
727+ /// This function writes `text` into `out` with some modifications depending on `klass`:
728+ ///
729+ /// * If `klass` is `None`, `text` is written into `out` with no modification.
730+ /// * If `klass` is `Some` but `klass.get_span()` is `None`, it writes the text wrapped in a
731+ /// `<span>` with the provided `klass`.
732+ /// * If `klass` is `Some` and has a [`rustc_span::Span`], it then tries to generate a link (`<a>`
733+ /// element) by retrieving the link information from the `span_correspondance_map` that was filled
734+ /// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's
735+ /// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]).
721736fn string_without_closing_tag < T : Display > (
722737 out : & mut Buffer ,
723738 text : T ,
@@ -799,42 +814,55 @@ fn string_without_closing_tag<T: Display>(
799814 Some ( "</span>" )
800815}
801816
802- /// This function is to get the external macro path because they are not in the cache used n
817+ /// This function is to get the external macro path because they are not in the cache used in
803818/// `href_with_root_path`.
804819fn generate_macro_def_id_path ( href_context : & HrefContext < ' _ , ' _ , ' _ > , def_id : DefId ) -> String {
805820 let tcx = href_context. context . shared . tcx ;
806821 let crate_name = tcx. crate_name ( def_id. krate ) . to_string ( ) ;
807- let cache = & href_context. context . cache ( ) ;
822+ let cache = href_context. context . cache ( ) ;
808823
809824 let relative = tcx. def_path ( def_id) . data . into_iter ( ) . filter_map ( |elem| {
810825 // extern blocks have an empty name
811826 let s = elem. data . to_string ( ) ;
812827 if !s. is_empty ( ) { Some ( s) } else { None }
813828 } ) ;
814- // Check to see if it is a macro 2.0 or built-in macro
815- let mut path = if matches ! (
816- CStore :: from_tcx( tcx) . load_macro_untracked( def_id, tcx. sess) ,
817- LoadedMacro :: MacroDef ( def, _)
818- if matches!( & def. kind, ast:: ItemKind :: MacroDef ( ast_def)
819- if !ast_def. macro_rules)
820- ) {
829+ // Check to see if it is a macro 2.0 or built-in macro.
830+ // More information in <https://rust-lang.github.io/rfcs/1584-macros.html>.
831+ let is_macro_2 = match CStore :: from_tcx ( tcx) . load_macro_untracked ( def_id, tcx. sess ) {
832+ LoadedMacro :: MacroDef ( def, _) => {
833+ // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0.
834+ matches ! ( & def. kind, ast:: ItemKind :: MacroDef ( ast_def) if !ast_def. macro_rules)
835+ }
836+ _ => false ,
837+ } ;
838+
839+ let mut path = if is_macro_2 {
821840 once ( crate_name. clone ( ) ) . chain ( relative) . collect ( )
822841 } else {
823- vec ! [ crate_name. clone( ) , relative. last( ) . expect ( "relative was empty" ) ]
842+ vec ! [ crate_name. clone( ) , relative. last( ) . unwrap ( ) ]
824843 } ;
844+ if path. len ( ) < 2 {
845+ // The minimum we can have is the crate name followed by the macro name. If shorter, then
846+ // it means that that `relative` was empty, which is an error.
847+ panic ! ( "macro path cannot be empty!" ) ;
848+ }
825849
826- let url_parts = match cache. extern_locations [ & def_id. krate ] {
827- ExternalLocation :: Remote ( ref s) => vec ! [ s. trim_end_matches( '/' ) ] ,
828- ExternalLocation :: Local => vec ! [ href_context. root_path. trim_end_matches( '/' ) , & crate_name] ,
829- ExternalLocation :: Unknown => panic ! ( "unknown crate" ) ,
830- } ;
850+ if let Some ( last) = path. last_mut ( ) {
851+ * last = format ! ( "macro.{}.html" , last) ;
852+ }
831853
832- let last = path. pop ( ) . unwrap ( ) ;
833- let last = format ! ( "macro.{}.html" , last) ;
834- if path. is_empty ( ) {
835- format ! ( "{}/{}" , url_parts. join( "/" ) , last)
836- } else {
837- format ! ( "{}/{}/{}" , url_parts. join( "/" ) , path. join( "/" ) , last)
854+ match cache. extern_locations [ & def_id. krate ] {
855+ ExternalLocation :: Remote ( ref s) => {
856+ // `ExternalLocation::Remote` always end with a `/`.
857+ format ! ( "{}{}" , s, path. join( "/" ) )
858+ }
859+ ExternalLocation :: Local => {
860+ // `href_context.root_path` always end with a `/`.
861+ format ! ( "{}{}/{}" , href_context. root_path, crate_name, path. join( "/" ) )
862+ }
863+ ExternalLocation :: Unknown => {
864+ panic ! ( "crate {} not in cache when linkifying macros" , crate_name)
865+ }
838866 }
839867}
840868
0 commit comments