@@ -20,8 +20,9 @@ use rustc_ast_pretty::pprust;
2020use rustc_errors:: { Diag , EmissionGuarantee , FatalError , PResult , pluralize} ;
2121pub use rustc_lexer:: UNICODE_VERSION ;
2222use rustc_session:: parse:: ParseSess ;
23+ use rustc_span:: edit_distance:: find_best_match_for_name;
2324use rustc_span:: source_map:: SourceMap ;
24- use rustc_span:: { FileName , SourceFile , Span } ;
25+ use rustc_span:: { FileName , SourceFile , Span , Symbol } ;
2526
2627pub const MACRO_ARGUMENTS : Option < & str > = Some ( "macro arguments" ) ;
2728
@@ -105,6 +106,8 @@ pub fn new_parser_from_source_str(
105106/// dropped.
106107///
107108/// If a span is given, that is used on an error as the source of the problem.
109+ ///
110+ /// Error messages are tailored to the specific error kind.
108111pub fn new_parser_from_file < ' a > (
109112 psess : & ' a ParseSess ,
110113 path : & Path ,
@@ -113,8 +116,43 @@ pub fn new_parser_from_file<'a>(
113116) -> Result < Parser < ' a > , Vec < Diag < ' a > > > {
114117 let sm = psess. source_map ( ) ;
115118 let source_file = sm. load_file ( path) . unwrap_or_else ( |e| {
116- let msg = format ! ( "couldn't read `{}`: {}" , path. display( ) , e) ;
119+ use std:: io:: ErrorKind ;
120+
121+ let msg = match e. kind ( ) {
122+ ErrorKind :: NotFound => format ! ( "couldn't find file `{}`" , path. display( ) ) ,
123+ ErrorKind :: PermissionDenied => {
124+ format ! ( "permission denied when opening file `{}`" , path. display( ) )
125+ }
126+ ErrorKind :: IsADirectory => format ! ( "`{}` is a directory" , path. display( ) ) ,
127+ _ => format ! ( "couldn't read `{}`: {}" , path. display( ) , e) ,
128+ } ;
129+
117130 let mut err = psess. dcx ( ) . struct_fatal ( msg) ;
131+
132+ if e. kind ( ) == ErrorKind :: NotFound {
133+ if let Some ( file_name) = path. file_name ( ) . and_then ( |n| n. to_str ( ) ) {
134+ let parent =
135+ path. parent ( ) . filter ( |p| !p. as_os_str ( ) . is_empty ( ) ) . unwrap_or ( Path :: new ( "." ) ) ;
136+ if let Ok ( entries) = std:: fs:: read_dir ( parent) {
137+ let candidates: Vec < Symbol > = entries
138+ . flatten ( )
139+ . filter_map ( |entry| entry. file_name ( ) . to_str ( ) . map ( Symbol :: intern) )
140+ . collect ( ) ;
141+ let lookup = Symbol :: intern ( file_name) ;
142+ if let Some ( suggestion) = find_best_match_for_name ( & candidates, lookup, None ) {
143+ let suggested_path = if parent == Path :: new ( "." ) {
144+ suggestion. as_str ( ) . to_string ( )
145+ } else {
146+ parent. join ( suggestion. as_str ( ) ) . display ( ) . to_string ( )
147+ } ;
148+ err. help ( format ! (
149+ "you might have meant to open `{}`: `rustc {}`" ,
150+ suggested_path, suggested_path,
151+ ) ) ;
152+ }
153+ }
154+ }
155+ }
118156 if let Ok ( contents) = std:: fs:: read ( path)
119157 && let Err ( utf8err) = std:: str:: from_utf8 ( & contents)
120158 {
0 commit comments