Skip to content

Commit b28e45e

Browse files
committed
Add lint reporting from/into conversions not portable between 64-bit and 32-bit platforms
1 parent d8e96a3 commit b28e45e

File tree

4 files changed

+168
-2
lines changed

4 files changed

+168
-2
lines changed

src/libcoretest/num/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ fn test_empty() {
117117
macro_rules! test_impl_from {
118118
($fn_name: ident, $Small: ty, $Large: ty) => {
119119
#[test]
120+
#[allow(nonportable_32_64)]
120121
fn $fn_name() {
121122
let small_max = <$Small>::max_value();
122123
let small_min = <$Small>::min_value();

src/librustc_lint/builtin.rs

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ use rustc::hir::def_id::DefId;
3333
use middle::stability;
3434
use rustc::cfg;
3535
use rustc::ty::subst::Substs;
36-
use rustc::ty::{self, Ty, TyCtxt};
37-
use rustc::ty::adjustment;
36+
use rustc::ty::{self, Ty, TyCtxt, TyInt, TyUint};
37+
use rustc::ty::{adjustment, subst};
3838
use rustc::traits::{self, Reveal};
3939
use rustc::hir::map as hir_map;
4040
use util::nodemap::NodeSet;
@@ -1296,3 +1296,103 @@ impl LateLintPass for UnionsWithDropFields {
12961296
}
12971297
}
12981298
}
1299+
1300+
/// Lint for unions that contain fields with possibly non-trivial destructors.
1301+
pub struct NonPortable3264;
1302+
1303+
declare_lint! {
1304+
NONPORTABLE_32_64,
1305+
Warn,
1306+
"conversions not portable between 64-bit and 32-bit platforms"
1307+
}
1308+
1309+
impl LintPass for NonPortable3264 {
1310+
fn get_lints(&self) -> LintArray {
1311+
lint_array!(NONPORTABLE_32_64)
1312+
}
1313+
}
1314+
1315+
fn is_nonportable_conv(src: subst::Kind, dst: subst::Kind) -> bool {
1316+
match (src.as_type(), dst.as_type()) {
1317+
(Some(src), Some(dst)) => {
1318+
use syntax::ast::IntTy::*;
1319+
use syntax::ast::UintTy::*;
1320+
match (&src.sty, &dst.sty) {
1321+
// All conditional impls from libcore/num/mod.rs
1322+
// not including "32" and "64" at the same time.
1323+
(&TyUint(U64), &TyUint(Us)) |
1324+
(&TyUint(Us), &TyUint(U16)) |
1325+
(&TyUint(Us), &TyUint(U32)) |
1326+
(&TyInt(I64), &TyInt(Is)) |
1327+
(&TyInt(Is), &TyInt(I16)) |
1328+
(&TyInt(Is), &TyInt(I32)) |
1329+
(&TyUint(U32), &TyInt(Is)) |
1330+
(&TyUint(Us), &TyInt(I32)) |
1331+
(&TyUint(Us), &TyInt(I64)) => true,
1332+
_ => false,
1333+
}
1334+
}
1335+
_ => false,
1336+
}
1337+
}
1338+
1339+
impl LateLintPass for NonPortable3264 {
1340+
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
1341+
let tcx = cx.tcx;
1342+
let report_lint = |span, src: subst::Kind, dst: subst::Kind| {
1343+
let src_ty = src.as_type().unwrap();
1344+
let dst_ty = dst.as_type().unwrap();
1345+
cx.span_lint(NONPORTABLE_32_64, span,
1346+
&format!("conversion `{}` -> `{}` is not portable \
1347+
between 64-bit and 32-bit platforms", src_ty, dst_ty));
1348+
};
1349+
match e.node {
1350+
hir::ExprMethodCall(name, ..) => {
1351+
if name.node.as_str() == "into" {
1352+
if let Some(callee) = tcx.tables.borrow().method_map
1353+
.get(&ty::MethodCall::expr(e.id)).cloned() {
1354+
if let ty::TyFnDef(def_id, substs, _) = callee.ty.sty {
1355+
let ti = tcx.impl_or_trait_item(def_id);
1356+
if let ty::TraitContainer(trait_def_id) = ti.container() {
1357+
if substs.len() == 2 {
1358+
if tcx.item_name(trait_def_id).as_str() == "Into" {
1359+
if is_nonportable_conv(substs[0], substs[1]) {
1360+
report_lint(name.span, substs[0], substs[1]);
1361+
}
1362+
}
1363+
}
1364+
}
1365+
}
1366+
}
1367+
}
1368+
}
1369+
hir::ExprPath(..) => {
1370+
if let Def::Method(def_id) = tcx.expect_def(e.id) {
1371+
let ti = tcx.impl_or_trait_item(def_id);
1372+
if let ty::MethodTraitItem(ref method) = ti {
1373+
if let ty::TraitContainer(trait_def_id) = ti.container() {
1374+
let substs = tcx.node_id_item_substs(e.id).substs;
1375+
if substs.len() == 2 {
1376+
if method.name.as_str() == "into" {
1377+
if tcx.item_name(trait_def_id).as_str() == "Into" {
1378+
if is_nonportable_conv(substs[0], substs[1]) {
1379+
report_lint(e.span, substs[0], substs[1]);
1380+
}
1381+
}
1382+
}
1383+
if method.name.as_str() == "from" {
1384+
if tcx.item_name(trait_def_id).as_str() == "From" {
1385+
if is_nonportable_conv(substs[1], substs[0]) {
1386+
report_lint(e.span, substs[1], substs[0]);
1387+
}
1388+
}
1389+
}
1390+
}
1391+
}
1392+
}
1393+
}
1394+
}
1395+
_ => {}
1396+
}
1397+
}
1398+
}

src/librustc_lint/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
143143
PluginAsLibrary,
144144
MutableTransmutes,
145145
UnionsWithDropFields,
146+
NonPortable3264,
146147
);
147148

148149
add_builtin_with_new!(sess,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2016 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.
4+
//
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.
10+
11+
// FIXME: make compiletest understand ignore-64bit / ignore-32bit
12+
13+
#![allow(unused)]
14+
#![deny(nonportable_32_64)]
15+
16+
#[cfg(target_pointer_width = "64")]
17+
fn check64() {
18+
let _: usize = 0u64.into();
19+
//~^ ERROR conversion `u64` -> `usize` is not portable between 64-bit and 32-bit platforms
20+
let _: usize = Into::into(0u64);
21+
//~^ ERROR conversion `u64` -> `usize` is not portable between 64-bit and 32-bit platforms
22+
let _: usize = From::from(0u64);
23+
//~^ ERROR conversion `u64` -> `usize` is not portable between 64-bit and 32-bit platforms
24+
25+
let _: isize = 0i64.into();
26+
//~^ ERROR conversion `i64` -> `isize` is not portable between 64-bit and 32-bit platforms
27+
let _: isize = Into::into(0i64);
28+
//~^ ERROR conversion `i64` -> `isize` is not portable between 64-bit and 32-bit platforms
29+
let _: isize = From::from(0i64);
30+
//~^ ERROR conversion `i64` -> `isize` is not portable between 64-bit and 32-bit platforms
31+
32+
let _: isize = 0u32.into();
33+
//~^ ERROR conversion `u32` -> `isize` is not portable between 64-bit and 32-bit platforms
34+
let _: isize = Into::into(0u32);
35+
//~^ ERROR conversion `u32` -> `isize` is not portable between 64-bit and 32-bit platforms
36+
let _: isize = From::from(0u32);
37+
//~^ ERROR conversion `u32` -> `isize` is not portable between 64-bit and 32-bit platforms
38+
}
39+
40+
#[cfg(target_pointer_width = "32")]
41+
fn check32() {
42+
let _: u32 = 0usize.into();
43+
// ERROR conversion `usize` -> `u32` is not portable between 64-bit and 32-bit platforms
44+
let _: u32 = Into::into(0usize);
45+
// ERROR conversion `usize` -> `u32` is not portable between 64-bit and 32-bit platforms
46+
let _: u32 = From::from(0usize);
47+
// ERROR conversion `usize` -> `u32` is not portable between 64-bit and 32-bit platforms
48+
49+
let _: i32 = 0isize.into();
50+
// ERROR conversion `isize` -> `i32` is not portable between 64-bit and 32-bit platforms
51+
let _: i32 = Into::into(0isize);
52+
// ERROR conversion `isize` -> `i32` is not portable between 64-bit and 32-bit platforms
53+
let _: i32 = From::from(0isize);
54+
// ERROR conversion `isize` -> `i32` is not portable between 64-bit and 32-bit platforms
55+
56+
let _: i64 = 0usize.into();
57+
// ERROR conversion `usize` -> `i64` is not portable between 64-bit and 32-bit platforms
58+
let _: i64 = Into::into(0usize);
59+
// ERROR conversion `usize` -> `i64` is not portable between 64-bit and 32-bit platforms
60+
let _: i64 = From::from(0usize);
61+
// ERROR conversion `usize` -> `i64` is not portable between 64-bit and 32-bit platforms
62+
}
63+
64+
fn main() {}

0 commit comments

Comments
 (0)