1 //! Some lints that are only useful in the compiler or crates that use compiler internals, such as
4 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}
;
5 use rustc_ast
::ast
::{Ident, Item, ItemKind}
;
6 use rustc_data_structures
::fx
::FxHashMap
;
7 use rustc_errors
::Applicability
;
8 use rustc_hir
::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind}
;
9 use rustc_session
::{declare_lint_pass, declare_tool_lint, impl_lint_pass}
;
10 use rustc_span
::hygiene
::{ExpnKind, MacroKind}
;
11 use rustc_span
::symbol
::{sym, Symbol}
;
14 pub rustc
::DEFAULT_HASH_TYPES
,
16 "forbid HashMap and HashSet and suggest the FxHash* variants",
17 report_in_external_macro
: true
20 pub struct DefaultHashTypes
{
21 map
: FxHashMap
<Symbol
, Symbol
>,
24 impl DefaultHashTypes
{
25 // we are allowed to use `HashMap` and `HashSet` as identifiers for implementing the lint itself
26 #[allow(rustc::default_hash_types)]
27 pub fn new() -> Self {
28 let mut map
= FxHashMap
::default();
29 map
.insert(sym
::HashMap
, sym
::FxHashMap
);
30 map
.insert(sym
::HashSet
, sym
::FxHashSet
);
35 impl_lint_pass
!(DefaultHashTypes
=> [DEFAULT_HASH_TYPES
]);
37 impl EarlyLintPass
for DefaultHashTypes
{
38 fn check_ident(&mut self, cx
: &EarlyContext
<'_
>, ident
: Ident
) {
39 if let Some(replace
) = self.map
.get(&ident
.name
) {
40 cx
.struct_span_lint(DEFAULT_HASH_TYPES
, ident
.span
, |lint
| {
41 // FIXME: We can avoid a copy here. Would require us to take String instead of &str.
42 let msg
= format
!("Prefer {} over {}, it has better performance", replace
, ident
);
48 Applicability
::MaybeIncorrect
, // FxHashMap, ... needs another import
51 "a `use rustc_data_structures::fx::{}` may be necessary",
61 pub rustc
::USAGE_OF_TY_TYKIND
,
63 "usage of `ty::TyKind` outside of the `ty::sty` module",
64 report_in_external_macro
: true
68 pub rustc
::TY_PASS_BY_REFERENCE
,
70 "passing `Ty` or `TyCtxt` by reference",
71 report_in_external_macro
: true
75 pub rustc
::USAGE_OF_QUALIFIED_TY
,
77 "using `ty::{Ty,TyCtxt}` instead of importing it",
78 report_in_external_macro
: true
81 declare_lint_pass
!(TyTyKind
=> [
84 USAGE_OF_QUALIFIED_TY
,
87 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for TyTyKind
{
88 fn check_path(&mut self, cx
: &LateContext
<'_
, '_
>, path
: &'tcx Path
<'tcx
>, _
: HirId
) {
89 let segments
= path
.segments
.iter().rev().skip(1).rev();
91 if let Some(last
) = segments
.last() {
92 let span
= path
.span
.with_hi(last
.ident
.span
.hi());
93 if lint_ty_kind_usage(cx
, last
) {
94 cx
.struct_span_lint(USAGE_OF_TY_TYKIND
, span
, |lint
| {
95 lint
.build("usage of `ty::TyKind::<kind>`")
98 "try using ty::<kind> directly",
100 Applicability
::MaybeIncorrect
, // ty maybe needs an import
108 fn check_ty(&mut self, cx
: &LateContext
<'_
, '_
>, ty
: &'tcx Ty
<'tcx
>) {
110 TyKind
::Path(qpath
) => {
111 if let QPath
::Resolved(_
, path
) = qpath
{
112 if let Some(last
) = path
.segments
.iter().last() {
113 if lint_ty_kind_usage(cx
, last
) {
114 cx
.struct_span_lint(USAGE_OF_TY_TYKIND
, path
.span
, |lint
| {
115 lint
.build("usage of `ty::TyKind`")
116 .help("try using `Ty` instead")
120 if ty
.span
.from_expansion() {
123 if let Some(t
) = is_ty_or_ty_ctxt(cx
, ty
) {
124 if path
.segments
.len() > 1 {
125 cx
.struct_span_lint(USAGE_OF_QUALIFIED_TY
, path
.span
, |lint
| {
126 lint
.build(&format
!("usage of qualified `ty::{}`", t
))
129 "try using it unqualified",
131 // The import probably needs to be changed
132 Applicability
::MaybeIncorrect
,
142 TyKind
::Rptr(_
, MutTy { ty: inner_ty, mutbl: Mutability::Not }
) => {
143 if let Some(impl_did
) = cx
.tcx
.impl_of_method(ty
.hir_id
.owner
.to_def_id()) {
144 if cx
.tcx
.impl_trait_ref(impl_did
).is_some() {
148 if let Some(t
) = is_ty_or_ty_ctxt(cx
, &inner_ty
) {
149 cx
.struct_span_lint(TY_PASS_BY_REFERENCE
, ty
.span
, |lint
| {
150 lint
.build(&format
!("passing `{}` by reference", t
))
153 "try passing by value",
155 // Changing type of function argument
156 Applicability
::MaybeIncorrect
,
167 fn lint_ty_kind_usage(cx
: &LateContext
<'_
, '_
>, segment
: &PathSegment
<'_
>) -> bool
{
168 if let Some(res
) = segment
.res
{
169 if let Some(did
) = res
.opt_def_id() {
170 return cx
.tcx
.is_diagnostic_item(sym
::TyKind
, did
);
177 fn is_ty_or_ty_ctxt(cx
: &LateContext
<'_
, '_
>, ty
: &Ty
<'_
>) -> Option
<String
> {
178 if let TyKind
::Path(qpath
) = &ty
.kind
{
179 if let QPath
::Resolved(_
, path
) = qpath
{
180 let did
= path
.res
.opt_def_id()?
;
181 if cx
.tcx
.is_diagnostic_item(sym
::Ty
, did
) {
182 return Some(format
!("Ty{}", gen_args(path
.segments
.last().unwrap())));
183 } else if cx
.tcx
.is_diagnostic_item(sym
::TyCtxt
, did
) {
184 return Some(format
!("TyCtxt{}", gen_args(path
.segments
.last().unwrap())));
192 fn gen_args(segment
: &PathSegment
<'_
>) -> String
{
193 if let Some(args
) = &segment
.args
{
198 if let GenericArg
::Lifetime(lt
) = arg
{
199 Some(lt
.name
.ident().to_string())
204 .collect
::<Vec
<_
>>();
206 if !lifetimes
.is_empty() {
207 return format
!("<{}>", lifetimes
.join(", "));
215 pub rustc
::LINT_PASS_IMPL_WITHOUT_MACRO
,
217 "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
220 declare_lint_pass
!(LintPassImpl
=> [LINT_PASS_IMPL_WITHOUT_MACRO
]);
222 impl EarlyLintPass
for LintPassImpl
{
223 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, item
: &Item
) {
224 if let ItemKind
::Impl { of_trait: Some(lint_pass), .. }
= &item
.kind
{
225 if let Some(last
) = lint_pass
.path
.segments
.last() {
226 if last
.ident
.name
== sym
::LintPass
{
227 let expn_data
= lint_pass
.path
.span
.ctxt().outer_expn_data();
228 let call_site
= expn_data
.call_site
;
229 if expn_data
.kind
!= ExpnKind
::Macro(MacroKind
::Bang
, sym
::impl_lint_pass
)
230 && call_site
.ctxt().outer_expn_data().kind
231 != ExpnKind
::Macro(MacroKind
::Bang
, sym
::declare_lint_pass
)
234 LINT_PASS_IMPL_WITHOUT_MACRO
,
237 lint
.build("implementing `LintPass` by hand")
238 .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")