1
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
1
2
use rustc_middle:: ty:: TyCtxt ;
2
3
use rustc_span:: { edition:: Edition , Symbol } ;
3
4
5
+ use std:: fs;
6
+ use std:: io:: Write ;
7
+ use std:: path:: Path ;
8
+
4
9
use crate :: clean;
5
10
use crate :: config:: RenderOptions ;
6
11
use crate :: error:: Error ;
7
12
use crate :: formats:: cache:: Cache ;
13
+ use crate :: html:: render:: print_item:: item_path;
8
14
9
15
/// Allows for different backends to rustdoc to be used with the `run_format()` function. Each
10
16
/// backend renderer has hooks for initialization, documenting an item, entering and exiting a
@@ -26,6 +32,7 @@ crate trait FormatRenderer<'tcx>: Sized {
26
32
edition : Edition ,
27
33
cache : Cache ,
28
34
tcx : TyCtxt < ' tcx > ,
35
+ case_insensitive_conflicts : Option < FxHashSet < String > > ,
29
36
) -> Result < ( Self , clean:: Crate ) , Error > ;
30
37
31
38
/// Make a new renderer to render a child of the item currently being rendered.
@@ -52,6 +59,71 @@ crate trait FormatRenderer<'tcx>: Sized {
52
59
fn cache ( & self ) -> & Cache ;
53
60
}
54
61
62
+ fn handle_module ( dst : & Path , item : & clean:: Item , paths_map : & mut FxHashMap < String , usize > ) {
63
+ // modules are special because they add a namespace. We also need to
64
+ // recurse into the items of the module as well.
65
+ let name = item. name . as_ref ( ) . unwrap ( ) . to_string ( ) ;
66
+ if name. is_empty ( ) {
67
+ panic ! ( "Unexpected module with empty name" ) ;
68
+ }
69
+ let module = match * item. kind {
70
+ clean:: StrippedItem ( box clean:: ModuleItem ( ref m) ) | clean:: ModuleItem ( ref m) => m,
71
+ _ => unreachable ! ( ) ,
72
+ } ;
73
+ let mod_path = dst. join ( & name) ;
74
+ for it in & module. items {
75
+ if it. is_mod ( ) {
76
+ handle_module ( & mod_path, it, paths_map) ;
77
+ } else if it. name . is_some ( ) && !it. is_extern_crate ( ) {
78
+ let name = it. name . as_ref ( ) . unwrap ( ) ;
79
+ let item_type = it. type_ ( ) ;
80
+ let file_name = & item_path ( item_type, & name. as_str ( ) ) ;
81
+ let insensitive_path = mod_path. join ( file_name) . display ( ) . to_string ( ) ;
82
+
83
+ let entry = paths_map. entry ( insensitive_path. to_lowercase ( ) ) . or_insert ( 0 ) ;
84
+ * entry += 1 ;
85
+ }
86
+ }
87
+ }
88
+
89
+ fn build_case_insensitive_map (
90
+ krate : & clean:: Crate ,
91
+ options : & RenderOptions ,
92
+ ) -> Option < FxHashSet < String > > {
93
+ let mut paths_map: FxHashMap < String , usize > = FxHashMap :: default ( ) ;
94
+
95
+ handle_module ( & options. output , & krate. module , & mut paths_map) ;
96
+ Some ( paths_map. into_iter ( ) . filter ( |( _, count) | * count > 1 ) . map ( |( path, _) | path) . collect ( ) )
97
+ }
98
+
99
+ fn check_if_case_insensitive ( dst : & Path ) -> bool {
100
+ fn create_and_write ( dst : & Path , content : & str ) {
101
+ if let Ok ( mut f) = fs:: OpenOptions :: new ( ) . write ( true ) . create ( true ) . truncate ( true ) . open ( dst)
102
+ {
103
+ // Ignoring potential errors.
104
+ let _ = f. write ( content. as_bytes ( ) ) ;
105
+ }
106
+ }
107
+ fn compare_content ( dst : & Path , content : & str ) -> bool {
108
+ fs:: read_to_string ( dst) . unwrap_or_else ( |_| String :: new ( ) ) . as_str ( ) == content
109
+ }
110
+
111
+ let path1 = dst. join ( "___a.tmp" ) ;
112
+ let content1 = "a" ;
113
+ let path2 = dst. join ( "___A.tmp" ) ;
114
+ let content2 = "A" ;
115
+
116
+ create_and_write ( & path1, content1) ;
117
+ create_and_write ( & path1, content2) ;
118
+
119
+ let res = compare_content ( & path1, content1) && compare_content ( & path2, content2) ;
120
+ // We ignore the errors when removing the files.
121
+ let _ = fs:: remove_file ( & path1) ;
122
+ let _ = fs:: remove_file ( & path2) ;
123
+
124
+ res
125
+ }
126
+
55
127
/// Main method for rendering a crate.
56
128
crate fn run_format < ' tcx , T : FormatRenderer < ' tcx > > (
57
129
krate : clean:: Crate ,
@@ -63,9 +135,16 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>(
63
135
) -> Result < ( ) , Error > {
64
136
let prof = & tcx. sess . prof ;
65
137
138
+ let case_insensitive_conflicts =
139
+ if options. generate_case_insensitive || check_if_case_insensitive ( & options. output ) {
140
+ build_case_insensitive_map ( & krate, & options)
141
+ } else {
142
+ None
143
+ } ;
144
+
66
145
let ( mut format_renderer, krate) = prof
67
146
. extra_verbose_generic_activity ( "create_renderer" , T :: descr ( ) )
68
- . run ( || T :: init ( krate, options, edition, cache, tcx) ) ?;
147
+ . run ( || T :: init ( krate, options, edition, cache, tcx, case_insensitive_conflicts ) ) ?;
69
148
70
149
// Render the crate documentation
71
150
let crate_name = krate. name ;
0 commit comments