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}
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::def
::Res
;
9 GenericArg
, HirId
, Item
, ItemKind
, MutTy
, Mutability
, Node
, Path
, PathSegment
, QPath
, Ty
,
13 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
14 use rustc_span
::hygiene
::{ExpnKind, MacroKind}
;
15 use rustc_span
::symbol
::{kw, sym, Symbol}
;
18 pub rustc
::DEFAULT_HASH_TYPES
,
20 "forbid HashMap and HashSet and suggest the FxHash* variants",
21 report_in_external_macro
: true
24 declare_lint_pass
!(DefaultHashTypes
=> [DEFAULT_HASH_TYPES
]);
26 impl LateLintPass
<'_
> for DefaultHashTypes
{
27 fn check_path(&mut self, cx
: &LateContext
<'_
>, path
: &Path
<'_
>, hir_id
: HirId
) {
28 let def_id
= match path
.res
{
29 Res
::Def(rustc_hir
::def
::DefKind
::Struct
, id
) => id
,
32 if matches
!(cx
.tcx
.hir().get(hir_id
), Node
::Item(Item { kind: ItemKind::Use(..), .. }
)) {
33 // don't lint imports, only actual usages
36 let replace
= if cx
.tcx
.is_diagnostic_item(sym
::hashmap_type
, def_id
) {
38 } else if cx
.tcx
.is_diagnostic_item(sym
::hashset_type
, def_id
) {
43 cx
.struct_span_lint(DEFAULT_HASH_TYPES
, path
.span
, |lint
| {
45 "prefer `{}` over `{}`, it has better performance",
47 cx
.tcx
.item_name(def_id
)
50 .note(&format
!("a `use rustc_data_structures::fx::{}` may be necessary", replace
))
57 pub rustc
::USAGE_OF_TY_TYKIND
,
59 "usage of `ty::TyKind` outside of the `ty::sty` module",
60 report_in_external_macro
: true
64 pub rustc
::TY_PASS_BY_REFERENCE
,
66 "passing `Ty` or `TyCtxt` by reference",
67 report_in_external_macro
: true
71 pub rustc
::USAGE_OF_QUALIFIED_TY
,
73 "using `ty::{Ty,TyCtxt}` instead of importing it",
74 report_in_external_macro
: true
77 declare_lint_pass
!(TyTyKind
=> [
80 USAGE_OF_QUALIFIED_TY
,
83 impl<'tcx
> LateLintPass
<'tcx
> for TyTyKind
{
84 fn check_path(&mut self, cx
: &LateContext
<'_
>, path
: &'tcx Path
<'tcx
>, _
: HirId
) {
85 let segments
= path
.segments
.iter().rev().skip(1).rev();
87 if let Some(last
) = segments
.last() {
88 let span
= path
.span
.with_hi(last
.ident
.span
.hi());
89 if lint_ty_kind_usage(cx
, last
) {
90 cx
.struct_span_lint(USAGE_OF_TY_TYKIND
, span
, |lint
| {
91 lint
.build("usage of `ty::TyKind::<kind>`")
94 "try using ty::<kind> directly",
96 Applicability
::MaybeIncorrect
, // ty maybe needs an import
104 fn check_ty(&mut self, cx
: &LateContext
<'_
>, ty
: &'tcx Ty
<'tcx
>) {
106 TyKind
::Path(qpath
) => {
107 if let QPath
::Resolved(_
, path
) = qpath
{
108 if let Some(last
) = path
.segments
.iter().last() {
109 if lint_ty_kind_usage(cx
, last
) {
110 cx
.struct_span_lint(USAGE_OF_TY_TYKIND
, path
.span
, |lint
| {
111 lint
.build("usage of `ty::TyKind`")
112 .help("try using `Ty` instead")
116 if ty
.span
.from_expansion() {
119 if let Some(t
) = is_ty_or_ty_ctxt(cx
, ty
) {
120 if path
.segments
.len() > 1 {
121 cx
.struct_span_lint(USAGE_OF_QUALIFIED_TY
, path
.span
, |lint
| {
122 lint
.build(&format
!("usage of qualified `ty::{}`", t
))
125 "try using it unqualified",
127 // The import probably needs to be changed
128 Applicability
::MaybeIncorrect
,
138 TyKind
::Rptr(_
, MutTy { ty: inner_ty, mutbl: Mutability::Not }
) => {
139 if let Some(impl_did
) = cx
.tcx
.impl_of_method(ty
.hir_id
.owner
.to_def_id()) {
140 if cx
.tcx
.impl_trait_ref(impl_did
).is_some() {
144 if let Some(t
) = is_ty_or_ty_ctxt(cx
, &inner_ty
) {
145 cx
.struct_span_lint(TY_PASS_BY_REFERENCE
, ty
.span
, |lint
| {
146 lint
.build(&format
!("passing `{}` by reference", t
))
149 "try passing by value",
151 // Changing type of function argument
152 Applicability
::MaybeIncorrect
,
163 fn lint_ty_kind_usage(cx
: &LateContext
<'_
>, segment
: &PathSegment
<'_
>) -> bool
{
164 if let Some(res
) = segment
.res
{
165 if let Some(did
) = res
.opt_def_id() {
166 return cx
.tcx
.is_diagnostic_item(sym
::TyKind
, did
);
173 fn is_ty_or_ty_ctxt(cx
: &LateContext
<'_
>, ty
: &Ty
<'_
>) -> Option
<String
> {
174 if let TyKind
::Path(qpath
) = &ty
.kind
{
175 if let QPath
::Resolved(_
, path
) = qpath
{
177 Res
::Def(_
, did
) => {
178 if cx
.tcx
.is_diagnostic_item(sym
::Ty
, did
) {
179 return Some(format
!("Ty{}", gen_args(path
.segments
.last().unwrap())));
180 } else if cx
.tcx
.is_diagnostic_item(sym
::TyCtxt
, did
) {
181 return Some(format
!("TyCtxt{}", gen_args(path
.segments
.last().unwrap())));
184 // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
185 Res
::SelfTy(None
, Some((did
, _
))) => {
186 if let ty
::Adt(adt
, substs
) = cx
.tcx
.type_of(did
).kind() {
187 if cx
.tcx
.is_diagnostic_item(sym
::Ty
, adt
.did
) {
188 // NOTE: This path is currently unreachable as `Ty<'tcx>` is
189 // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>`
190 // is not actually allowed.
192 // I(@lcnr) still kept this branch in so we don't miss this
193 // if we ever change it in the future.
194 return Some(format
!("Ty<{}>", substs
[0]));
195 } else if cx
.tcx
.is_diagnostic_item(sym
::TyCtxt
, adt
.did
) {
196 return Some(format
!("TyCtxt<{}>", substs
[0]));
208 fn gen_args(segment
: &PathSegment
<'_
>) -> String
{
209 if let Some(args
) = &segment
.args
{
214 if let GenericArg
::Lifetime(lt
) = arg
{
215 Some(lt
.name
.ident().to_string())
220 .collect
::<Vec
<_
>>();
222 if !lifetimes
.is_empty() {
223 return format
!("<{}>", lifetimes
.join(", "));
231 pub rustc
::LINT_PASS_IMPL_WITHOUT_MACRO
,
233 "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
236 declare_lint_pass
!(LintPassImpl
=> [LINT_PASS_IMPL_WITHOUT_MACRO
]);
238 impl EarlyLintPass
for LintPassImpl
{
239 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, item
: &ast
::Item
) {
240 if let ast
::ItemKind
::Impl(box ast
::ImplKind { of_trait: Some(lint_pass), .. }
) = &item
.kind
242 if let Some(last
) = lint_pass
.path
.segments
.last() {
243 if last
.ident
.name
== sym
::LintPass
{
244 let expn_data
= lint_pass
.path
.span
.ctxt().outer_expn_data();
245 let call_site
= expn_data
.call_site
;
246 if expn_data
.kind
!= ExpnKind
::Macro(MacroKind
::Bang
, sym
::impl_lint_pass
)
247 && call_site
.ctxt().outer_expn_data().kind
248 != ExpnKind
::Macro(MacroKind
::Bang
, sym
::declare_lint_pass
)
251 LINT_PASS_IMPL_WITHOUT_MACRO
,
254 lint
.build("implementing `LintPass` by hand")
255 .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
267 pub rustc
::EXISTING_DOC_KEYWORD
,
269 "Check that documented keywords in std and core actually exist",
270 report_in_external_macro
: true
273 declare_lint_pass
!(ExistingDocKeyword
=> [EXISTING_DOC_KEYWORD
]);
275 fn is_doc_keyword(s
: Symbol
) -> bool
{
279 impl<'tcx
> LateLintPass
<'tcx
> for ExistingDocKeyword
{
280 fn check_item(&mut self, cx
: &LateContext
<'_
>, item
: &rustc_hir
::Item
<'_
>) {
281 for attr
in cx
.tcx
.hir().attrs(item
.hir_id()) {
282 if !attr
.has_name(sym
::doc
) {
285 if let Some(list
) = attr
.meta_item_list() {
287 if nested
.has_name(sym
::keyword
) {
290 .expect("#[doc(keyword = \"...\")] expected a value!");
291 if is_doc_keyword(v
) {
294 cx
.struct_span_lint(EXISTING_DOC_KEYWORD
, attr
.span
, |lint
| {
296 "Found non-existing keyword `{}` used in \
297 `#[doc(keyword = \"...\")]`",
300 .help("only existing keywords are allowed in core/std")