Skip to content

Commit

Permalink
Implement portability lints for 16 and 128 bits
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Oct 27, 2016
1 parent b28e45e commit 2d83bfd
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 61 deletions.
192 changes: 131 additions & 61 deletions src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1297,102 +1297,172 @@ impl LateLintPass for UnionsWithDropFields {
}
}

/// Lint for unions that contain fields with possibly non-trivial destructors.
/// Lints for non-portable integer conversions.
pub struct NonPortable1632;
pub struct NonPortable3264;
pub struct NonPortable64128;

declare_lint! {
NONPORTABLE_16_32,
Allow,
"conversions not portable between 32-bit and 16-bit platforms"
}
declare_lint! {
NONPORTABLE_32_64,
Warn,
"conversions not portable between 64-bit and 32-bit platforms"
}
declare_lint! {
NONPORTABLE_64_128,
Allow,
"conversions not portable between 64-bit and 128-bit platforms"
}

impl LintPass for NonPortable1632 {
fn get_lints(&self) -> LintArray {
lint_array!(NONPORTABLE_16_32)
}
}
impl LintPass for NonPortable3264 {
fn get_lints(&self) -> LintArray {
lint_array!(NONPORTABLE_32_64)
}
}
impl LintPass for NonPortable64128 {
fn get_lints(&self) -> LintArray {
lint_array!(NONPORTABLE_64_128)
}
}

fn is_nonportable_conv(src: subst::Kind, dst: subst::Kind) -> bool {
match (src.as_type(), dst.as_type()) {
(Some(src), Some(dst)) => {
use syntax::ast::IntTy::*;
use syntax::ast::UintTy::*;
match (&src.sty, &dst.sty) {
// All conditional impls from libcore/num/mod.rs
// not including "32" and "64" at the same time.
(&TyUint(U64), &TyUint(Us)) |
(&TyUint(Us), &TyUint(U16)) |
(&TyUint(Us), &TyUint(U32)) |
(&TyInt(I64), &TyInt(Is)) |
(&TyInt(Is), &TyInt(I16)) |
(&TyInt(Is), &TyInt(I32)) |
(&TyUint(U32), &TyInt(Is)) |
(&TyUint(Us), &TyInt(I32)) |
(&TyUint(Us), &TyInt(I64)) => true,
_ => false,
}
mod pred {
use super::*;
use syntax::ast::IntTy::*;
use syntax::ast::UintTy::*;

pub fn is_nonportable_conv_32_64(src: &ty::TypeVariants, dst: &ty::TypeVariants) -> bool {
match (src, dst) {
// All conditional impls from libcore/num/mod.rs
// not including "32" and "64" at the same time.
(&TyUint(U64), &TyUint(Us)) |
(&TyUint(Us), &TyUint(U16)) |
(&TyUint(Us), &TyUint(U32)) |
(&TyInt(I64), &TyInt(Is)) |
(&TyInt(Is), &TyInt(I16)) |
(&TyInt(Is), &TyInt(I32)) |
(&TyUint(U32), &TyInt(Is)) |
(&TyUint(Us), &TyInt(I32)) |
(&TyUint(Us), &TyInt(I64)) => true,
_ => false,
}
}

pub fn is_nonportable_conv_16_32(src: &ty::TypeVariants, dst: &ty::TypeVariants) -> bool {
match (src, dst) {
// All conditional impls from libcore/num/mod.rs
// not including "16" and "32" at the same time.
(&TyUint(U32), &TyUint(Us)) |
(&TyUint(Us), &TyUint(U16)) |
(&TyInt(I32), &TyInt(Is)) |
(&TyInt(Is), &TyInt(I16)) |
(&TyUint(U16), &TyInt(Is)) |
(&TyUint(Us), &TyInt(I32)) => true,
_ => false,
}
}

pub fn is_nonportable_conv_64_128(src: &ty::TypeVariants, dst: &ty::TypeVariants) -> bool {
match (src, dst) {
// All conditional impls from libcore/num/mod.rs
// not including "64" and potentially "128" at the same time.
(&TyUint(Us), &TyUint(U64)) |
(&TyInt(Is), &TyInt(I64)) => true,
_ => false,
}
}
}

fn is_nonportable_conv<Pred>(src: subst::Kind, dst: subst::Kind, pred: Pred) -> bool
where Pred: Fn(&ty::TypeVariants, &ty::TypeVariants) -> bool
{
match (src.as_type(), dst.as_type()) {
(Some(src), Some(dst)) => pred(&src.sty, &dst.sty),
_ => false,
}
}

impl LateLintPass for NonPortable3264 {
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
let tcx = cx.tcx;
let report_lint = |span, src: subst::Kind, dst: subst::Kind| {
let src_ty = src.as_type().unwrap();
let dst_ty = dst.as_type().unwrap();
cx.span_lint(NONPORTABLE_32_64, span,
&format!("conversion `{}` -> `{}` is not portable \
between 64-bit and 32-bit platforms", src_ty, dst_ty));
};
match e.node {
hir::ExprMethodCall(name, ..) => {
if name.node.as_str() == "into" {
if let Some(callee) = tcx.tables.borrow().method_map
.get(&ty::MethodCall::expr(e.id)).cloned() {
if let ty::TyFnDef(def_id, substs, _) = callee.ty.sty {
let ti = tcx.impl_or_trait_item(def_id);
if let ty::TraitContainer(trait_def_id) = ti.container() {
if substs.len() == 2 {
if tcx.item_name(trait_def_id).as_str() == "Into" {
if is_nonportable_conv(substs[0], substs[1]) {
report_lint(name.span, substs[0], substs[1]);
}
fn nonportable_check_expr<Pred>(cx: &LateContext, e: &hir::Expr, w1: u8, w2: u8,
lint: &'static Lint, pred: Pred)
where Pred: Fn(&ty::TypeVariants, &ty::TypeVariants) -> bool
{
let tcx = cx.tcx;
let report_lint = |span, src: subst::Kind, dst: subst::Kind| {
let src_ty = src.as_type().unwrap();
let dst_ty = dst.as_type().unwrap();
cx.span_lint(lint, span,
&format!("conversion `{}` -> `{}` is not portable \
between {}-bit and {}-bit platforms", src_ty, dst_ty, w1, w2));
};
match e.node {
hir::ExprMethodCall(name, ..) => {
if name.node.as_str() == "into" {
if let Some(callee) = tcx.tables.borrow().method_map
.get(&ty::MethodCall::expr(e.id)).cloned() {
if let ty::TyFnDef(def_id, substs, _) = callee.ty.sty {
let ti = tcx.impl_or_trait_item(def_id);
if let ty::TraitContainer(trait_def_id) = ti.container() {
if substs.len() == 2 {
if tcx.item_name(trait_def_id).as_str() == "Into" {
if is_nonportable_conv(substs[0], substs[1], pred) {
report_lint(name.span, substs[0], substs[1]);
}
}
}
}
}
}
}
hir::ExprPath(..) => {
if let Def::Method(def_id) = tcx.expect_def(e.id) {
let ti = tcx.impl_or_trait_item(def_id);
if let ty::MethodTraitItem(ref method) = ti {
if let ty::TraitContainer(trait_def_id) = ti.container() {
let substs = tcx.node_id_item_substs(e.id).substs;
if substs.len() == 2 {
if method.name.as_str() == "into" {
if tcx.item_name(trait_def_id).as_str() == "Into" {
if is_nonportable_conv(substs[0], substs[1]) {
report_lint(e.span, substs[0], substs[1]);
}
}
hir::ExprPath(..) => {
if let Def::Method(def_id) = tcx.expect_def(e.id) {
let ti = tcx.impl_or_trait_item(def_id);
if let ty::MethodTraitItem(ref method) = ti {
if let ty::TraitContainer(trait_def_id) = ti.container() {
let substs = tcx.node_id_item_substs(e.id).substs;
if substs.len() == 2 {
if method.name.as_str() == "into" {
if tcx.item_name(trait_def_id).as_str() == "Into" {
if is_nonportable_conv(substs[0], substs[1], pred) {
report_lint(e.span, substs[0], substs[1]);
}
}
if method.name.as_str() == "from" {
if tcx.item_name(trait_def_id).as_str() == "From" {
if is_nonportable_conv(substs[1], substs[0]) {
report_lint(e.span, substs[1], substs[0]);
}
} else if method.name.as_str() == "from" {
if tcx.item_name(trait_def_id).as_str() == "From" {
if is_nonportable_conv(substs[1], substs[0], pred) {
report_lint(e.span, substs[1], substs[0]);
}
}
}
}
}
}
}
_ => {}
}
_ => {}
}
}

impl LateLintPass for NonPortable1632 {
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
nonportable_check_expr(cx, e, 32, 16, NONPORTABLE_16_32, pred::is_nonportable_conv_16_32)
}
}
impl LateLintPass for NonPortable3264 {
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
nonportable_check_expr(cx, e, 64, 32, NONPORTABLE_32_64, pred::is_nonportable_conv_32_64)
}
}
impl LateLintPass for NonPortable64128 {
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
nonportable_check_expr(cx, e, 64, 128, NONPORTABLE_64_128, pred::is_nonportable_conv_64_128)
}
}
3 changes: 3 additions & 0 deletions src/librustc_lint/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(dotdot_in_tuple_patterns)]
#![feature(item_like_imports)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]
Expand Down Expand Up @@ -143,7 +144,9 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
PluginAsLibrary,
MutableTransmutes,
UnionsWithDropFields,
NonPortable1632,
NonPortable3264,
NonPortable64128,
);

add_builtin_with_new!(sess,
Expand Down
37 changes: 37 additions & 0 deletions src/test/compile-fail/lint-nonportable-16-32.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(unused)]
#![deny(nonportable_16_32)]

fn check() {
let _: usize = 0u32.into();
//~^ ERROR conversion `u32` -> `usize` is not portable between 32-bit and 16-bit platforms
let _: usize = Into::into(0u32);
//~^ ERROR conversion `u32` -> `usize` is not portable between 32-bit and 16-bit platforms
let _: usize = From::from(0u32);
//~^ ERROR conversion `u32` -> `usize` is not portable between 32-bit and 16-bit platforms

let _: isize = 0i32.into();
//~^ ERROR conversion `i32` -> `isize` is not portable between 32-bit and 16-bit platforms
let _: isize = Into::into(0i32);
//~^ ERROR conversion `i32` -> `isize` is not portable between 32-bit and 16-bit platforms
let _: isize = From::from(0i32);
//~^ ERROR conversion `i32` -> `isize` is not portable between 32-bit and 16-bit platforms

let _: isize = 0u16.into();
//~^ ERROR conversion `u16` -> `isize` is not portable between 32-bit and 16-bit platforms
let _: isize = Into::into(0u16);
//~^ ERROR conversion `u16` -> `isize` is not portable between 32-bit and 16-bit platforms
let _: isize = From::from(0u16);
//~^ ERROR conversion `u16` -> `isize` is not portable between 32-bit and 16-bit platforms
}

fn main() {}
30 changes: 30 additions & 0 deletions src/test/compile-fail/lint-nonportable-64-128.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(unused)]
#![deny(nonportable_64_128)]

fn check() {
let _: u64 = 0usize.into();
//~^ ERROR conversion `usize` -> `u64` is not portable between 64-bit and 128-bit platforms
let _: u64 = Into::into(0usize);
//~^ ERROR conversion `usize` -> `u64` is not portable between 64-bit and 128-bit platforms
let _: u64 = From::from(0usize);
//~^ ERROR conversion `usize` -> `u64` is not portable between 64-bit and 128-bit platforms

let _: i64 = 0isize.into();
//~^ ERROR conversion `isize` -> `i64` is not portable between 64-bit and 128-bit platforms
let _: i64 = Into::into(0isize);
//~^ ERROR conversion `isize` -> `i64` is not portable between 64-bit and 128-bit platforms
let _: i64 = From::from(0isize);
//~^ ERROR conversion `isize` -> `i64` is not portable between 64-bit and 128-bit platforms
}

fn main() {}

0 comments on commit 2d83bfd

Please sign in to comment.