@@ -9,6 +9,7 @@ use dtoa;
99use failure:: { err_msg, format_err, Fail } ;
1010use indexmap:: indexmap;
1111use indexmap:: { IndexMap , IndexSet } ;
12+ use itertools:: Itertools ;
1213use log:: { error, info, trace, warn} ;
1314use proc_macro2:: { Punct , Spacing :: * , Span , TokenStream , TokenTree } ;
1415use syn:: spanned:: Spanned as _;
@@ -492,11 +493,13 @@ pub fn translate_failure(tcfg: &TranspilerConfig, msg: &str) {
492493 }
493494}
494495
496+ type DeclMap = IndexMap < String , String > ;
497+
495498pub fn translate (
496499 ast_context : TypedAstContext ,
497500 tcfg : & TranspilerConfig ,
498501 main_file : & Path ,
499- ) -> ( String , PragmaVec , CrateSet ) {
502+ ) -> ( String , Option < DeclMap > , PragmaVec , CrateSet ) {
500503 let mut t = Translation :: new ( ast_context, tcfg, main_file) ;
501504 let ctx = ExprContext {
502505 used : true ,
@@ -513,6 +516,8 @@ pub fn translate(
513516 {
514517 t. locate_comments ( ) ;
515518
519+ let decl_source_ranges = t. ast_context . top_decl_locs ( ) ;
520+
516521 // Headers often pull in declarations that are unused;
517522 // we simplify the translator output by omitting those.
518523 t. ast_context
@@ -762,6 +767,80 @@ pub fn translate(
762767 } )
763768 . collect :: < HashMap < _ , _ > > ( ) ;
764769
770+ // Generate a map from Rust items to the source code of their C declarations.
771+ let decl_map = if tcfg. emit_c_decl_map {
772+ let mut path_to_c_source_range: HashMap < & Ident , ( SrcLoc , SrcLoc ) > = Default :: default ( ) ;
773+ for ( decl, source_range) in decl_source_ranges {
774+ match converted_decls. get ( & decl) {
775+ Some ( ConvertedDecl :: ForeignItem ( item) ) => {
776+ path_to_c_source_range
777+ . insert ( foreign_item_ident_vis ( & * item) . unwrap ( ) . 0 , source_range) ;
778+ }
779+ Some ( ConvertedDecl :: Item ( item) ) => {
780+ path_to_c_source_range. insert ( item_ident ( & * item) . unwrap ( ) , source_range) ;
781+ }
782+ Some ( ConvertedDecl :: Items ( items) ) => {
783+ for item in items {
784+ path_to_c_source_range
785+ . insert ( item_ident ( & * item) . unwrap ( ) , source_range) ;
786+ }
787+ }
788+ Some ( ConvertedDecl :: NoItem ) => { }
789+ None => eprintln ! ( "failed to convert top-level decl {decl:?}!" ) ,
790+ }
791+ }
792+
793+ let file_content =
794+ std:: fs:: read ( & t. ast_context . get_file_path ( t. main_file ) . unwrap ( ) ) . unwrap ( ) ;
795+ let line_end_offsets = //memchr::memchr_iter(file_content, '\n')
796+ file_content. iter ( ) . positions ( |c| * c == b'\n' )
797+ . collect :: < Vec < _ > > ( ) ;
798+
799+ /// Convert a source location line/column into a byte offset, given the positions of each newline in the file.
800+ fn src_loc_to_byte_offset ( line_end_offsets : & [ usize ] , loc : SrcLoc ) -> usize {
801+ let line_offset = line_end_offsets
802+ . get ( loc. line as usize - 2 ) // end of the previous line; for line < 1, index out of bounds
803+ . map ( |x| x + 1 ) // increment end of the prev line to find start of this one
804+ . unwrap_or ( 0 ) ; // if we indexed out of bounds, start at byte 0
805+ line_offset + ( loc. column as usize ) . saturating_sub ( 1 )
806+ }
807+
808+ // Slice into the source file, fixing up the ends to account for Clang AST quirks.
809+ let slice_decl_with_fixups = |begin, end| -> & [ u8 ] {
810+ let mut begin_offset = src_loc_to_byte_offset ( & line_end_offsets, begin) ;
811+ let mut end_offset = src_loc_to_byte_offset ( & line_end_offsets, end) ;
812+ const VT : u8 = 11 ;
813+ /* Skip whitespace and any trailing semicolons after the previous decl. */
814+ while let Some ( b'\t' | b'\n' | & VT | b'\r' | b' ' | b';' ) =
815+ file_content. get ( begin_offset)
816+ {
817+ begin_offset += 1 ;
818+ }
819+
820+ // Extend to include a single trailing semicolon if this decl is not a block
821+ // (e.g., a variable declaration).
822+ if file_content. get ( end_offset - 1 ) != Some ( & b'}' )
823+ && file_content. get ( end_offset) == Some ( & b';' )
824+ {
825+ end_offset += 1 ;
826+ }
827+
828+ & file_content[ begin_offset..end_offset]
829+ } ;
830+
831+ let item_path_to_c_source: IndexMap < _ , _ > = path_to_c_source_range
832+ . into_iter ( )
833+ . map ( |( ident, ( begin, end) ) | {
834+ let path = ident. to_string ( ) ;
835+ let c_src = std:: str:: from_utf8 ( slice_decl_with_fixups ( begin, end) ) . unwrap ( ) ;
836+ ( path, c_src. to_owned ( ) )
837+ } )
838+ . collect ( ) ;
839+ Some ( item_path_to_c_source)
840+ } else {
841+ None
842+ } ;
843+
765844 t. ast_context . sort_top_decls_for_emitting ( ) ;
766845
767846 for top_id in & t. ast_context . c_decls_top {
@@ -926,7 +1005,7 @@ pub fn translate(
9261005 . copied ( )
9271006 . collect ( ) ;
9281007
929- ( translation, pragmas, crates)
1008+ ( translation, decl_map , pragmas, crates)
9301009 }
9311010}
9321011
0 commit comments