@@ -10,6 +10,7 @@ use rustc_ast::token::{self, LitKind};
1010use rustc_ast:: tokenstream:: TokenStream ;
1111use rustc_ast:: { ExprKind , GenericArg , Mutability } ;
1212use rustc_expand:: base:: { DummyResult , ExpandResult , ExtCtxt , MacEager , MacroExpanderResult } ;
13+ use rustc_span:: edit_distance:: edit_distance;
1314use rustc_span:: { Ident , Span , Symbol , kw, sym} ;
1415use thin_vec:: thin_vec;
1516
@@ -144,6 +145,12 @@ pub(crate) fn expand_env<'cx>(
144145 if let Some ( msg_from_user) = custom_msg {
145146 cx. dcx ( )
146147 . emit_err ( errors:: EnvNotDefinedWithUserMessage { span, msg_from_user } )
148+ } else if let Some ( suggested_var) = find_similar_cargo_var ( var. as_str ( ) ) {
149+ cx. dcx ( ) . emit_err ( errors:: EnvNotDefined :: CargoEnvVarTypo {
150+ span,
151+ var : * symbol,
152+ suggested_var : Symbol :: intern ( suggested_var) ,
153+ } )
147154 } else if is_cargo_env_var ( var. as_str ( ) ) {
148155 cx. dcx ( ) . emit_err ( errors:: EnvNotDefined :: CargoEnvVar {
149156 span,
@@ -176,3 +183,49 @@ fn is_cargo_env_var(var: &str) -> bool {
176183 || var. starts_with ( "DEP_" )
177184 || matches ! ( var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET" )
178185}
186+
187+ const KNOWN_CARGO_VARS : & [ & str ] = & [
188+ // List of known Cargo environment variables that are set for crates (not build scripts, OUT_DIR etc).
189+ // See: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates
190+ "CARGO_PKG_VERSION" ,
191+ "CARGO_PKG_VERSION_MAJOR" ,
192+ "CARGO_PKG_VERSION_MINOR" ,
193+ "CARGO_PKG_VERSION_PATCH" ,
194+ "CARGO_PKG_VERSION_PRE" ,
195+ "CARGO_PKG_AUTHORS" ,
196+ "CARGO_PKG_NAME" ,
197+ "CARGO_PKG_DESCRIPTION" ,
198+ "CARGO_PKG_HOMEPAGE" ,
199+ "CARGO_PKG_REPOSITORY" ,
200+ "CARGO_PKG_LICENSE" ,
201+ "CARGO_PKG_LICENSE_FILE" ,
202+ "CARGO_PKG_RUST_VERSION" ,
203+ "CARGO_PKG_README" ,
204+ "CARGO_MANIFEST_DIR" ,
205+ "CARGO_MANIFEST_PATH" ,
206+ "CARGO_CRATE_NAME" ,
207+ "CARGO_BIN_NAME" ,
208+ "CARGO_PRIMARY_PACKAGE" ,
209+ ] ;
210+
211+ fn find_similar_cargo_var ( var : & str ) -> Option < & ' static str > {
212+ if !var. starts_with ( "CARGO_" ) {
213+ return None ;
214+ }
215+
216+ let lookup_len = var. chars ( ) . count ( ) ;
217+ let max_dist = std:: cmp:: max ( lookup_len, 3 ) / 3 ;
218+ let mut best_match = None ;
219+ let mut best_distance = usize:: MAX ;
220+
221+ for & known_var in KNOWN_CARGO_VARS {
222+ if let Some ( distance) = edit_distance ( var, known_var, max_dist) {
223+ if distance < best_distance {
224+ best_distance = distance;
225+ best_match = Some ( known_var) ;
226+ }
227+ }
228+ }
229+
230+ best_match
231+ }
0 commit comments