|
1 | 1 | use rustc::hir::def::{Res, DefKind}; |
2 | 2 | use rustc::hir::def_id::DefId; |
| 3 | +use rustc::hir::HirVec; |
3 | 4 | use rustc::lint; |
4 | 5 | use rustc::ty::{self, Ty}; |
| 6 | +use rustc::ty::subst::Subst; |
5 | 7 | use rustc::ty::adjustment; |
| 8 | +use rustc::mir::interpret::{GlobalId, ConstValue}; |
6 | 9 | use rustc_data_structures::fx::FxHashMap; |
7 | 10 | use lint::{LateContext, EarlyContext, LintContext, LintArray}; |
8 | 11 | use lint::{LintPass, EarlyLintPass, LateLintPass}; |
@@ -151,8 +154,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { |
151 | 154 | let descr_pre = &format!("{}boxed ", descr_pre); |
152 | 155 | check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural) |
153 | 156 | } |
154 | | - ty::Adt(def, _) => { |
155 | | - check_must_use_def(cx, def.did, span, descr_pre, descr_post) |
| 157 | + ty::Adt(def, subst) => { |
| 158 | + // Check the type itself for `#[must_use]` annotations. |
| 159 | + let mut has_emitted = check_must_use_def( |
| 160 | + cx, def.did, span, descr_pre, descr_post); |
| 161 | + // Check any fields of the type for `#[must_use]` annotations. |
| 162 | + // We ignore ADTs with more than one variant for simplicity and to avoid |
| 163 | + // false positives. |
| 164 | + // Unions are also ignored (though in theory, we could lint if every field of |
| 165 | + // a union was `#[must_use]`). |
| 166 | + if def.variants.len() == 1 && !def.is_union() { |
| 167 | + let fields = match &expr.node { |
| 168 | + hir::ExprKind::Struct(_, fields, _) => { |
| 169 | + fields.iter().map(|f| &*f.expr).collect() |
| 170 | + } |
| 171 | + hir::ExprKind::Call(_, args) => args.iter().collect(), |
| 172 | + _ => HirVec::new(), |
| 173 | + }; |
| 174 | + |
| 175 | + for variant in &def.variants { |
| 176 | + for (i, field) in variant.fields.iter().enumerate() { |
| 177 | + let descr_post |
| 178 | + = &format!(" in field `{}`", field.ident.as_str()); |
| 179 | + let ty = cx.tcx.type_of(field.did).subst(cx.tcx, subst); |
| 180 | + let (expr, span) = if let Some(&field) = fields.get(i) { |
| 181 | + (field, field.span) |
| 182 | + } else { |
| 183 | + (expr, span) |
| 184 | + }; |
| 185 | + has_emitted |= check_must_use_ty( |
| 186 | + cx, ty, expr, span, descr_pre, descr_post, plural); |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | + has_emitted |
156 | 191 | } |
157 | 192 | ty::Opaque(def, _) => { |
158 | 193 | let mut has_emitted = false; |
@@ -202,24 +237,42 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { |
202 | 237 | for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() { |
203 | 238 | let descr_post = &format!(" in tuple element {}", i); |
204 | 239 | let span = *spans.get(i).unwrap_or(&span); |
205 | | - if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural) { |
206 | | - has_emitted = true; |
207 | | - } |
| 240 | + has_emitted |= check_must_use_ty( |
| 241 | + cx, ty, expr, span, descr_pre, descr_post, plural); |
208 | 242 | } |
209 | 243 | has_emitted |
210 | 244 | } |
211 | | - ty::Array(ty, len) => match len.assert_usize(cx.tcx) { |
212 | | - // If the array is definitely non-empty, we can do `#[must_use]` checking. |
213 | | - Some(n) if n != 0 => { |
214 | | - let descr_pre = &format!( |
215 | | - "{}array{} of ", |
216 | | - descr_pre, |
217 | | - plural_suffix, |
218 | | - ); |
219 | | - check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, true) |
| 245 | + ty::Array(ty, mut len) => { |
| 246 | + // Try to evaluate the length if it's unevaluated. |
| 247 | + if let ConstValue::Unevaluated(def_id, substs) = len.val { |
| 248 | + let instance = ty::Instance::resolve( |
| 249 | + cx.tcx.global_tcx(), |
| 250 | + cx.param_env, |
| 251 | + def_id, |
| 252 | + substs, |
| 253 | + ).unwrap(); |
| 254 | + let global_id = GlobalId { |
| 255 | + instance, |
| 256 | + promoted: None |
| 257 | + }; |
| 258 | + if let Ok(ct) = cx.tcx.const_eval(cx.param_env.and(global_id)) { |
| 259 | + len = ct; |
| 260 | + } |
| 261 | + } |
| 262 | + |
| 263 | + match len.assert_usize(cx.tcx) { |
| 264 | + // If the array is definitely non-empty, we can do `#[must_use]` checking. |
| 265 | + Some(n) if n != 0 => { |
| 266 | + let descr_pre = &format!( |
| 267 | + "{}array{} of ", |
| 268 | + descr_pre, |
| 269 | + plural_suffix, |
| 270 | + ); |
| 271 | + check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, true) |
| 272 | + } |
| 273 | + // Otherwise, we don't lint, to avoid false positives. |
| 274 | + _ => false, |
220 | 275 | } |
221 | | - // Otherwise, we don't lint, to avoid false positives. |
222 | | - _ => false, |
223 | 276 | } |
224 | 277 | _ => false, |
225 | 278 | } |
|
0 commit comments