1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use rustc
::hir
::def_id
::DefId
;
13 use rustc
::ty
::adjustment
;
14 use lint
::{LateContext, EarlyContext, LintContext, LintArray}
;
15 use lint
::{LintPass, EarlyLintPass, LateLintPass}
;
19 use syntax
::feature_gate
::{BUILTIN_ATTRIBUTES, AttributeType}
;
20 use syntax
::print
::pprust
;
21 use syntax
::symbol
::keywords
;
22 use syntax
::util
::parser
;
30 "unused result of a type flagged as #[must_use]"
36 "unused result of an expression in a statement"
39 #[derive(Copy, Clone)]
40 pub struct UnusedResults
;
42 impl LintPass
for UnusedResults
{
43 fn get_lints(&self) -> LintArray
{
44 lint_array
!(UNUSED_MUST_USE
, UNUSED_RESULTS
)
48 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for UnusedResults
{
49 fn check_stmt(&mut self, cx
: &LateContext
, s
: &hir
::Stmt
) {
50 let expr
= match s
.node
{
51 hir
::StmtSemi(ref expr
, _
) => &**expr
,
55 if let hir
::ExprRet(..) = expr
.node
{
59 let t
= cx
.tables
.expr_ty(&expr
);
60 let ty_warned
= match t
.sty
{
61 ty
::TyTuple(ref tys
, _
) if tys
.is_empty() => return,
62 ty
::TyNever
=> return,
63 ty
::TyAdt(def
, _
) => {
64 if def
.variants
.is_empty() {
67 check_must_use(cx
, def
.did
, s
.span
, "")
73 let mut fn_warned
= false;
74 let mut op_warned
= false;
75 if cx
.tcx
.sess
.features
.borrow().fn_must_use
{
76 let maybe_def
= match expr
.node
{
77 hir
::ExprCall(ref callee
, _
) => {
79 hir
::ExprPath(ref qpath
) => {
80 Some(cx
.tables
.qpath_def(qpath
, callee
.hir_id
))
85 hir
::ExprMethodCall(..) => {
86 cx
.tables
.type_dependent_defs().get(expr
.hir_id
).cloned()
90 if let Some(def
) = maybe_def
{
91 let def_id
= def
.def_id();
92 fn_warned
= check_must_use(cx
, def_id
, s
.span
, "return value of ");
95 if let hir
::ExprBinary(bin_op
, ..) = expr
.node
{
97 // Hardcoding the comparison operators here seemed more
98 // expedient than the refactoring that would be needed to
99 // look up the `#[must_use]` attribute which does exist on
100 // the comparison trait methods
101 hir
::BiEq
| hir
::BiLt
| hir
::BiLe
| hir
::BiNe
| hir
::BiGe
| hir
::BiGt
=> {
102 let msg
= "unused comparison which must be used";
103 cx
.span_lint(UNUSED_MUST_USE
, expr
.span
, msg
);
111 if !(ty_warned
|| fn_warned
|| op_warned
) {
112 cx
.span_lint(UNUSED_RESULTS
, s
.span
, "unused result");
115 fn check_must_use(cx
: &LateContext
, def_id
: DefId
, sp
: Span
, describe_path
: &str) -> bool
{
116 for attr
in cx
.tcx
.get_attrs(def_id
).iter() {
117 if attr
.check_name("must_use") {
118 let mut msg
= format
!("unused {}`{}` which must be used",
119 describe_path
, cx
.tcx
.item_path_str(def_id
));
120 // check for #[must_use="..."]
121 if let Some(s
) = attr
.value_str() {
123 msg
.push_str(&s
.as_str());
125 cx
.span_lint(UNUSED_MUST_USE
, sp
, &msg
);
137 "path statements with no effect"
140 #[derive(Copy, Clone)]
141 pub struct PathStatements
;
143 impl LintPass
for PathStatements
{
144 fn get_lints(&self) -> LintArray
{
145 lint_array
!(PATH_STATEMENTS
)
149 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for PathStatements
{
150 fn check_stmt(&mut self, cx
: &LateContext
, s
: &hir
::Stmt
) {
151 if let hir
::StmtSemi(ref expr
, _
) = s
.node
{
152 if let hir
::ExprPath(_
) = expr
.node
{
153 cx
.span_lint(PATH_STATEMENTS
, s
.span
, "path statement with no effect");
160 pub UNUSED_ATTRIBUTES
,
162 "detects attributes that were not used by the compiler"
165 #[derive(Copy, Clone)]
166 pub struct UnusedAttributes
;
168 impl LintPass
for UnusedAttributes
{
169 fn get_lints(&self) -> LintArray
{
170 lint_array
!(UNUSED_ATTRIBUTES
)
174 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for UnusedAttributes
{
175 fn check_attribute(&mut self, cx
: &LateContext
, attr
: &ast
::Attribute
) {
176 debug
!("checking attribute: {:?}", attr
);
177 let name
= unwrap_or
!(attr
.name(), return);
179 // Note that check_name() marks the attribute as used if it matches.
180 for &(ref name
, ty
, _
) in BUILTIN_ATTRIBUTES
{
182 AttributeType
::Whitelisted
if attr
.check_name(name
) => {
183 debug
!("{:?} is Whitelisted", name
);
190 let plugin_attributes
= cx
.sess().plugin_attributes
.borrow_mut();
191 for &(ref name
, ty
) in plugin_attributes
.iter() {
192 if ty
== AttributeType
::Whitelisted
&& attr
.check_name(&name
) {
193 debug
!("{:?} (plugin attr) is whitelisted with ty {:?}", name
, ty
);
198 if !attr
::is_used(attr
) {
199 debug
!("Emitting warning for: {:?}", attr
);
200 cx
.span_lint(UNUSED_ATTRIBUTES
, attr
.span
, "unused attribute");
201 // Is it a builtin attribute that must be used at the crate level?
202 let known_crate
= BUILTIN_ATTRIBUTES
.iter()
203 .find(|&&(builtin
, ty
, _
)| name
== builtin
&& ty
== AttributeType
::CrateLevel
)
206 // Has a plugin registered this attribute as one which must be used at
208 let plugin_crate
= plugin_attributes
.iter()
209 .find(|&&(ref x
, t
)| name
== &**x
&& AttributeType
::CrateLevel
== t
)
211 if known_crate
|| plugin_crate
{
212 let msg
= match attr
.style
{
213 ast
::AttrStyle
::Outer
=> {
214 "crate-level attribute should be an inner attribute: add an exclamation \
217 ast
::AttrStyle
::Inner
=> "crate-level attribute should be in the root module",
219 cx
.span_lint(UNUSED_ATTRIBUTES
, attr
.span
, msg
);
222 debug
!("Attr was used: {:?}", attr
);
228 pub(super) UNUSED_PARENS
,
230 "`if`, `match`, `while` and `return` do not need parentheses"
233 #[derive(Copy, Clone)]
234 pub struct UnusedParens
;
237 fn check_unused_parens_core(&self,
241 struct_lit_needs_parens
: bool
) {
242 if let ast
::ExprKind
::Paren(ref inner
) = value
.node
{
243 let necessary
= struct_lit_needs_parens
&&
244 parser
::contains_exterior_struct_lit(&inner
);
246 let span_msg
= format
!("unnecessary parentheses around {}", msg
);
247 let mut err
= cx
.struct_span_lint(UNUSED_PARENS
,
250 // Remove exactly one pair of parentheses (rather than naïvely
251 // stripping all paren characters)
252 let mut ate_left_paren
= false;
253 let mut ate_right_paren
= false;
254 let parens_removed
= pprust
::expr_to_string(value
)
261 ate_left_paren
= true;
269 ate_right_paren
= true;
276 err
.span_suggestion_short(value
.span
,
277 "remove these parentheses",
285 impl LintPass
for UnusedParens
{
286 fn get_lints(&self) -> LintArray
{
287 lint_array
!(UNUSED_PARENS
)
291 impl EarlyLintPass
for UnusedParens
{
292 fn check_expr(&mut self, cx
: &EarlyContext
, e
: &ast
::Expr
) {
293 use syntax
::ast
::ExprKind
::*;
294 let (value
, msg
, struct_lit_needs_parens
) = match e
.node
{
295 If(ref cond
, ..) => (cond
, "`if` condition", true),
296 While(ref cond
, ..) => (cond
, "`while` condition", true),
297 IfLet(_
, ref cond
, ..) => (cond
, "`if let` head expression", true),
298 WhileLet(_
, ref cond
, ..) => (cond
, "`while let` head expression", true),
299 ForLoop(_
, ref cond
, ..) => (cond
, "`for` head expression", true),
300 Match(ref head
, _
) => (head
, "`match` head expression", true),
301 Ret(Some(ref value
)) => (value
, "`return` value", false),
302 Assign(_
, ref value
) => (value
, "assigned value", false),
303 AssignOp(.., ref value
) => (value
, "assigned value", false),
304 InPlace(_
, ref value
) => (value
, "emplacement value", false),
305 // either function/method call, or something this lint doesn't care about
306 ref call_or_other
=> {
309 match *call_or_other
{
310 Call(_
, ref args
) => {
311 call_kind
= "function";
312 args_to_check
= &args
[..];
314 MethodCall(_
, ref args
) => {
315 call_kind
= "method";
316 // first "argument" is self (which sometimes needs parens)
317 args_to_check
= &args
[1..];
319 // actual catch-all arm
322 // Don't lint if this is a nested macro expansion: otherwise, the lint could
323 // trigger in situations that macro authors shouldn't have to care about, e.g.,
324 // when a parenthesized token tree matched in one macro expansion is matched as
325 // an expression in another and used as a fn/method argument (Issue #47775)
326 if e
.span
.ctxt().outer().expn_info()
327 .map_or(false, |info
| info
.call_site
.ctxt().outer()
328 .expn_info().is_some()) {
331 let msg
= format
!("{} argument", call_kind
);
332 for arg
in args_to_check
{
333 self.check_unused_parens_core(cx
, arg
, &msg
, false);
338 self.check_unused_parens_core(cx
, &value
, msg
, struct_lit_needs_parens
);
341 fn check_stmt(&mut self, cx
: &EarlyContext
, s
: &ast
::Stmt
) {
342 let (value
, msg
) = match s
.node
{
343 ast
::StmtKind
::Local(ref local
) => {
345 Some(ref value
) => (value
, "assigned value"),
351 self.check_unused_parens_core(cx
, &value
, msg
, false);
356 UNUSED_IMPORT_BRACES
,
358 "unnecessary braces around an imported item"
361 #[derive(Copy, Clone)]
362 pub struct UnusedImportBraces
;
364 impl UnusedImportBraces
{
365 fn check_use_tree(&self, cx
: &EarlyContext
, use_tree
: &ast
::UseTree
, item
: &ast
::Item
) {
366 if let ast
::UseTreeKind
::Nested(ref items
) = use_tree
.kind
{
367 // Recursively check nested UseTrees
368 for &(ref tree
, _
) in items
{
369 self.check_use_tree(cx
, tree
, item
);
372 // Trigger the lint only if there is one nested item
373 if items
.len() != 1 {
377 // Trigger the lint if the nested item is a non-self single item
379 match items
[0].0.kind
{
380 ast
::UseTreeKind
::Simple(ident
) => {
381 if ident
.name
== keywords
::SelfValue
.name() {
387 ast
::UseTreeKind
::Glob
=> {
388 node_ident
= ast
::Ident
::from_str("*");
390 ast
::UseTreeKind
::Nested(_
) => {
395 let msg
= format
!("braces around {} is unnecessary", node_ident
.name
);
396 cx
.span_lint(UNUSED_IMPORT_BRACES
, item
.span
, &msg
);
401 impl LintPass
for UnusedImportBraces
{
402 fn get_lints(&self) -> LintArray
{
403 lint_array
!(UNUSED_IMPORT_BRACES
)
407 impl EarlyLintPass
for UnusedImportBraces
{
408 fn check_item(&mut self, cx
: &EarlyContext
, item
: &ast
::Item
) {
409 if let ast
::ItemKind
::Use(ref use_tree
) = item
.node
{
410 self.check_use_tree(cx
, use_tree
, item
);
416 pub(super) UNUSED_ALLOCATION
,
418 "detects unnecessary allocations that can be eliminated"
421 #[derive(Copy, Clone)]
422 pub struct UnusedAllocation
;
424 impl LintPass
for UnusedAllocation
{
425 fn get_lints(&self) -> LintArray
{
426 lint_array
!(UNUSED_ALLOCATION
)
430 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for UnusedAllocation
{
431 fn check_expr(&mut self, cx
: &LateContext
, e
: &hir
::Expr
) {
433 hir
::ExprBox(_
) => {}
437 for adj
in cx
.tables
.expr_adjustments(e
) {
438 if let adjustment
::Adjust
::Borrow(adjustment
::AutoBorrow
::Ref(_
, m
)) = adj
.kind
{
440 adjustment
::AutoBorrowMutability
::Immutable
=>
441 "unnecessary allocation, use & instead",
442 adjustment
::AutoBorrowMutability
::Mutable { .. }
=>
443 "unnecessary allocation, use &mut instead"
445 cx
.span_lint(UNUSED_ALLOCATION
, e
.span
, msg
);