use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
-use rustc_errors::Applicability;
+use rustc_errors::{fluent, Applicability};
use rustc_hir::def::Res;
use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
_ => return,
};
cx.struct_span_lint(DEFAULT_HASH_TYPES, path.span, |lint| {
- let msg = format!(
- "prefer `{}` over `{}`, it has better performance",
- replace,
- cx.tcx.item_name(def_id)
- );
- lint.build(&msg)
- .note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace))
+ lint.build(fluent::lint::default_hash_types)
+ .set_arg("preferred", replace)
+ .set_arg("used", cx.tcx.item_name(def_id))
+ .note(fluent::lint::note)
.emit();
});
}
cx: &LateContext<'tcx>,
expr: &Expr<'_>,
) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> {
- // FIXME(rustdoc): Lints which use this function use typecheck results which can cause
- // `rustdoc` to error if there are resolution failures.
- //
- // As internal lints are currently always run if there are `unstable_options`, they are added
- // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query.
- // Crate lints run outside of a query so rustdoc currently doesn't disable them.
- //
- // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only
- // run internal lints if the user is explicitly opting in or figure out a different way to
- // avoid running lints for rustdoc.
- if cx.tcx.sess.opts.actually_rustdoc {
- return None;
- }
-
match expr.kind {
ExprKind::MethodCall(segment, _, _)
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
cx.struct_span_lint(POTENTIAL_QUERY_INSTABILITY, span, |lint| {
- let msg = format!(
- "using `{}` can result in unstable query results",
- cx.tcx.item_name(def_id)
- );
- lint.build(&msg)
- .note("if you believe this case to be fine, allow this lint and add a comment explaining your rationale")
+ lint.build(fluent::lint::query_instability)
+ .set_arg("query", cx.tcx.item_name(def_id))
+ .note(fluent::lint::note)
.emit();
})
}
segment.args.map_or(segment.ident.span, |a| a.span_ext).hi()
);
cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
- lint.build("usage of `ty::TyKind::<kind>`")
+ lint.build(fluent::lint::tykind_kind)
.span_suggestion(
span,
- "try using `ty::<kind>` directly",
+ fluent::lint::suggestion,
"ty",
Applicability::MaybeIncorrect, // ty maybe needs an import
)
if let QPath::TypeRelative(qpath_ty, ..) = qpath
&& qpath_ty.hir_id == ty.hir_id
{
- lint.build("usage of `ty::TyKind::<kind>`")
+ lint.build(fluent::lint::tykind_kind)
.span_suggestion(
path.span,
- "try using `ty::<kind>` directly",
+ fluent::lint::suggestion,
"ty",
Applicability::MaybeIncorrect, // ty maybe needs an import
)
if let QPath::TypeRelative(qpath_ty, ..) = qpath
&& qpath_ty.hir_id == ty.hir_id
{
- lint.build("usage of `ty::TyKind::<kind>`")
+ lint.build(fluent::lint::tykind_kind)
.span_suggestion(
path.span,
- "try using `ty::<kind>` directly",
+ fluent::lint::suggestion,
"ty",
Applicability::MaybeIncorrect, // ty maybe needs an import
)
if let QPath::TypeRelative(qpath_ty, ..) = qpath
&& qpath_ty.hir_id == ty.hir_id
{
- lint.build("usage of `ty::TyKind::<kind>`")
+ lint.build(fluent::lint::tykind_kind)
.span_suggestion(
path.span,
- "try using `ty::<kind>` directly",
+ fluent::lint::suggestion,
"ty",
Applicability::MaybeIncorrect, // ty maybe needs an import
)
}
_ => {}
}
- lint.build("usage of `ty::TyKind`").help("try using `Ty` instead").emit();
+ lint.build(fluent::lint::tykind).help(fluent::lint::help).emit();
})
} else if !ty.span.from_expansion() && let Some(t) = is_ty_or_ty_ctxt(cx, &path) {
if path.segments.len() > 1 {
cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
- lint.build(&format!("usage of qualified `ty::{}`", t))
+ lint.build(fluent::lint::ty_qualified)
+ .set_arg("ty", t.clone())
.span_suggestion(
path.span,
- "try importing it and using it unqualified",
+ fluent::lint::suggestion,
t,
// The import probably needs to be changed
Applicability::MaybeIncorrect,
LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span,
|lint| {
- lint.build("implementing `LintPass` by hand")
- .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
+ lint.build(fluent::lint::lintpass_by_hand)
+ .help(fluent::lint::help)
.emit();
},
)
return;
}
cx.struct_span_lint(EXISTING_DOC_KEYWORD, attr.span, |lint| {
- lint.build(&format!(
- "Found non-existing keyword `{}` used in \
- `#[doc(keyword = \"...\")]`",
- v,
- ))
- .help("only existing keywords are allowed in core/std")
- .emit();
+ lint.build(fluent::lint::non_existant_doc_keyword)
+ .set_arg("keyword", v)
+ .help(fluent::lint::help)
+ .emit();
});
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let Some((span, def_id, substs)) = typeck_results_of_method_fn(cx, expr) else { return };
debug!(?span, ?def_id, ?substs);
- if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) &&
- !cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_diagnostics)
- {
+ let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs)
+ .ok()
+ .and_then(|inst| inst)
+ .map(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics))
+ .unwrap_or(false);
+ if !has_attr {
return;
}
let Impl { of_trait: Some(of_trait), .. } = impl_ &&
let Some(def_id) = of_trait.trait_def_id() &&
let Some(name) = cx.tcx.get_diagnostic_name(def_id) &&
- matches!(name, sym::SessionDiagnostic | sym::AddSubdiagnostic)
+ matches!(name, sym::SessionDiagnostic | sym::AddSubdiagnostic | sym::DecorateLint)
{
found_impl = true;
break;
debug!(?found_impl);
if !found_impl {
cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
- lint.build("diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls")
- .emit();
+ lint.build(fluent::lint::diag_out_of_impl).emit();
})
}
debug!(?found_diagnostic_message);
if !found_diagnostic_message {
cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
- lint.build("diagnostics should be created using translatable messages").emit();
+ lint.build(fluent::lint::untranslatable_diag).emit();
})
}
}
}
+
+declare_tool_lint! {
+ pub rustc::BAD_OPT_ACCESS,
+ Deny,
+ "prevent using options by field access when there is a wrapper function",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]);
+
+impl LateLintPass<'_> for BadOptAccess {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ let ExprKind::Field(base, target) = expr.kind else { return };
+ let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return };
+ // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be
+ // avoided.
+ if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) {
+ return;
+ }
+
+ for field in adt_def.all_fields() {
+ if field.name == target.name &&
+ let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) &&
+ let Some(items) = attr.meta_item_list() &&
+ let Some(item) = items.first() &&
+ let Some(literal) = item.literal() &&
+ let ast::LitKind::Str(val, _) = literal.kind
+ {
+ cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| {
+ lint.build(val.as_str()).emit(); }
+ );
+ }
+ }
+ }
+}