Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use rustc_errors::{
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};
Expand Down Expand Up @@ -229,20 +228,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(|(symbol, is_incorrect_case)| MisspelledKw {
similar_kw: symbol.to_string(),
is_incorrect_case,
span: lookup.span,
})
}

struct MultiSugg {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,10 @@ passes_missing_panic_handler =
passes_missing_stability_attr =
{$descr} has missing stability attribute
passes_misspelled_feature =
unknown feature `{$misspelled_name}`
.suggestion = 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
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,17 @@ pub(crate) struct UnknownFeature {
pub feature: Symbol,
}

#[derive(Diagnostic)]
#[diag(passes_misspelled_feature, code = E0635)]
pub(crate) struct MisspelledFeature {
#[primary_span]
pub span: Span,
pub misspelled_name: Symbol,
pub actual_name: Symbol,
#[suggestion(style = "verbose", code = "{actual_name}", applicability = "maybe-incorrect")]
pub suggestion: Span,
}

#[derive(Diagnostic)]
#[diag(passes_unknown_feature_alias, code = E0635)]
pub(crate) struct RenamedFeature {
Expand Down
34 changes: 31 additions & 3 deletions compiler/rustc_passes/src/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::num::NonZero;
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};
Expand Down Expand Up @@ -1093,8 +1093,36 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
}
}

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 = tcx
.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);
match suggestion {
Some((actual_name, _)) => {
let misspelled_name = feature;
tcx.dcx().emit_err(errors::MisspelledFeature {
span,
misspelled_name,
actual_name,
suggestion: span,
});
}
None => {
tcx.dcx().emit_err(errors::UnknownFeature { span, feature });
Comment on lines +1114 to +1122
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am not happy of having two error types representing roughly the same error. using only UnknownFeature here means we need to be able to refer to Some<Ident> when creating the suggestion, which is (as far as i can tell) not a thing. i would love to be able to do the following:

#[derive(Diagnostic)]
#[diag(passes_unknown_feature, code = E0635)]
pub(crate) struct UnknownFeature {
    #[primary_span]
    pub span: Span,
    pub feature: Symbol,
    #[suggestion(
        style = "verbose",
        code = "{actual_name}",
        applicability = "maybe-incorrect"
    )]
    pub actual_name: Option<(Span, Symbol)>,
}

i'll try to see how feasible it is to add support for this ^ (or some variation of it).

}
}
}
}

for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustc_data_structures::stable_hasher::{
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)]
Expand Down Expand Up @@ -2843,6 +2844,27 @@ impl Symbol {
// 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 {
Expand Down
14 changes: 13 additions & 1 deletion tests/ui/feature-gates/unknown-feature.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
#![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() {}
32 changes: 28 additions & 4 deletions tests/ui/feature-gates/unknown-feature.stderr
Original file line number Diff line number Diff line change
@@ -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:5: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:10: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`.
Loading