mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-28 20:16:58 +03:00
Rollup merge of #149659 - scrabsha:push-vtrtnooqlvvv, r=jdonszelmann
Look for typos when reporting an unknown nightly feature
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
pluralize,
|
||||
};
|
||||
use rustc_session::errors::ExprParenthesesNeeded;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::used_keywords;
|
||||
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym};
|
||||
@@ -222,6 +221,8 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
style = "verbose"
|
||||
)]
|
||||
struct MisspelledKw {
|
||||
// We use a String here because `Symbol::into_diag_arg` calls `Symbol::to_ident_string`, which
|
||||
// prefix the keyword with a `r#` because it aims to print the symbol as an identifier.
|
||||
similar_kw: String,
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@@ -229,20 +230,15 @@ struct MisspelledKw {
|
||||
}
|
||||
|
||||
/// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`.
|
||||
///
|
||||
/// This is a specialized version of [`Symbol::find_similar`] that constructs an error when a
|
||||
/// candidate is found.
|
||||
fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> {
|
||||
let lowercase = lookup.name.as_str().to_lowercase();
|
||||
let lowercase_sym = Symbol::intern(&lowercase);
|
||||
if candidates.contains(&lowercase_sym) {
|
||||
Some(MisspelledKw { similar_kw: lowercase, span: lookup.span, is_incorrect_case: true })
|
||||
} else if let Some(similar_sym) = find_best_match_for_name(candidates, lookup.name, None) {
|
||||
Some(MisspelledKw {
|
||||
similar_kw: similar_sym.to_string(),
|
||||
span: lookup.span,
|
||||
is_incorrect_case: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
lookup.name.find_similar(candidates).map(|(similar_kw, is_incorrect_case)| MisspelledKw {
|
||||
similar_kw: similar_kw.to_string(),
|
||||
is_incorrect_case,
|
||||
span: lookup.span,
|
||||
})
|
||||
}
|
||||
|
||||
struct MultiSugg {
|
||||
|
||||
@@ -421,6 +421,8 @@ passes_missing_panic_handler =
|
||||
passes_missing_stability_attr =
|
||||
{$descr} has missing stability attribute
|
||||
|
||||
passes_misspelled_feature = there is a feature with a similar name: `{$actual_name}`
|
||||
|
||||
passes_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`
|
||||
.label = `{$no_mangle_attr}` is ignored
|
||||
.note = `{$export_name_attr}` takes precedence
|
||||
|
||||
@@ -1183,6 +1183,21 @@ pub(crate) struct UnknownFeature {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub feature: Symbol,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: Option<MisspelledFeature>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(
|
||||
passes_misspelled_feature,
|
||||
style = "verbose",
|
||||
code = "{actual_name}",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub(crate) struct MisspelledFeature {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub actual_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use rustc_ast_lowering::stability::extern_abi_stability;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
|
||||
use rustc_feature::{EnabledLangFeature, EnabledLibFeature};
|
||||
use rustc_feature::{EnabledLangFeature, EnabledLibFeature, UNSTABLE_LANG_FEATURES};
|
||||
use rustc_hir::attrs::{AttributeKind, DeprecatedSince};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
|
||||
@@ -1062,11 +1062,13 @@ fn check_features<'tcx>(
|
||||
// no unknown features, because the collection also does feature attribute validation.
|
||||
let local_defined_features = tcx.lib_features(LOCAL_CRATE);
|
||||
if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
|
||||
let crates = tcx.crates(());
|
||||
|
||||
// Loading the implications of all crates is unavoidable to be able to emit the partial
|
||||
// stabilization diagnostic, but it can be avoided when there are no
|
||||
// `remaining_lib_features`.
|
||||
let mut all_implications = remaining_implications.clone();
|
||||
for &cnum in tcx.crates(()) {
|
||||
for &cnum in crates {
|
||||
all_implications
|
||||
.extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
|
||||
}
|
||||
@@ -1079,7 +1081,7 @@ fn check_features<'tcx>(
|
||||
&all_implications,
|
||||
);
|
||||
|
||||
for &cnum in tcx.crates(()) {
|
||||
for &cnum in crates {
|
||||
if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
|
||||
break;
|
||||
}
|
||||
@@ -1091,10 +1093,26 @@ fn check_features<'tcx>(
|
||||
&all_implications,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (feature, span) in remaining_lib_features {
|
||||
tcx.dcx().emit_err(errors::UnknownFeature { span, feature });
|
||||
if !remaining_lib_features.is_empty() {
|
||||
let lang_features =
|
||||
UNSTABLE_LANG_FEATURES.iter().map(|feature| feature.name).collect::<Vec<_>>();
|
||||
let lib_features = crates
|
||||
.into_iter()
|
||||
.flat_map(|&cnum| {
|
||||
tcx.lib_features(cnum).stability.keys().copied().into_sorted_stable_ord()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let valid_feature_names = [lang_features, lib_features].concat();
|
||||
|
||||
for (feature, span) in remaining_lib_features {
|
||||
let suggestion = feature
|
||||
.find_similar(&valid_feature_names)
|
||||
.map(|(actual_name, _)| errors::MisspelledFeature { span, actual_name });
|
||||
tcx.dcx().emit_err(errors::UnknownFeature { span, feature, suggestion });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, symbols};
|
||||
|
||||
use crate::edit_distance::find_best_match_for_name;
|
||||
use crate::{DUMMY_SP, Edition, Span, with_session_globals};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -2843,6 +2844,27 @@ pub fn to_ident_string(self) -> String {
|
||||
// Avoid creating an empty identifier, because that asserts in debug builds.
|
||||
if self == sym::empty { String::new() } else { Ident::with_dummy_span(self).to_string() }
|
||||
}
|
||||
|
||||
/// Checks if `self` is similar to any symbol in `candidates`.
|
||||
///
|
||||
/// The returned boolean represents whether the candidate is the same symbol with a different
|
||||
/// casing.
|
||||
///
|
||||
/// All the candidates are assumed to be lowercase.
|
||||
pub fn find_similar(
|
||||
self,
|
||||
candidates: &[Symbol],
|
||||
) -> Option<(Symbol, /* is incorrect case */ bool)> {
|
||||
let lowercase = self.as_str().to_lowercase();
|
||||
let lowercase_sym = Symbol::intern(&lowercase);
|
||||
if candidates.contains(&lowercase_sym) {
|
||||
Some((lowercase_sym, true))
|
||||
} else if let Some(similar_sym) = find_best_match_for_name(candidates, self, None) {
|
||||
Some((similar_sym, false))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Symbol {
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#![feature(unknown_rust_feature)] //~ ERROR unknown feature
|
||||
#![feature(
|
||||
unknown_rust_feature,
|
||||
//~^ ERROR unknown feature
|
||||
|
||||
// Typo for lang feature
|
||||
associated_types_default,
|
||||
//~^ ERROR unknown feature
|
||||
//~| HELP there is a feature with a similar name
|
||||
|
||||
// Typo for lib feature
|
||||
core_intrnisics,
|
||||
//~^ ERROR unknown feature
|
||||
//~| HELP there is a feature with a similar name
|
||||
)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
@@ -1,9 +1,33 @@
|
||||
error[E0635]: unknown feature `unknown_rust_feature`
|
||||
--> $DIR/unknown-feature.rs:1:12
|
||||
--> $DIR/unknown-feature.rs:2:5
|
||||
|
|
||||
LL | #![feature(unknown_rust_feature)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
LL | unknown_rust_feature,
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error[E0635]: unknown feature `associated_types_default`
|
||||
--> $DIR/unknown-feature.rs:6:5
|
||||
|
|
||||
LL | associated_types_default,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: there is a feature with a similar name: `associated_type_defaults`
|
||||
|
|
||||
LL - associated_types_default,
|
||||
LL + associated_type_defaults,
|
||||
|
|
||||
|
||||
error[E0635]: unknown feature `core_intrnisics`
|
||||
--> $DIR/unknown-feature.rs:11:5
|
||||
|
|
||||
LL | core_intrnisics,
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: there is a feature with a similar name: `core_intrinsics`
|
||||
|
|
||||
LL - core_intrnisics,
|
||||
LL + core_intrinsics,
|
||||
|
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0635`.
|
||||
|
||||
Reference in New Issue
Block a user