Auto merge of #1381 - emilio:integer-eval, r=nox

ir: Handle overflowing integer constant evaluation properly.

Fixes #1380
diff --git a/Cargo.lock b/Cargo.lock
index d642aeb..e510cc8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,7 +23,7 @@
 
 [[package]]
 name = "bindgen"
-version = "0.39.0"
+version = "0.40.0"
 dependencies = [
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index 9e7d326..61dbe3d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,7 @@
 repository = "https://github.com/rust-lang-nursery/rust-bindgen"
 documentation = "https://docs.rs/bindgen"
 homepage = "https://rust-lang-nursery.github.io/rust-bindgen/"
-version = "0.39.0"
+version = "0.40.0"
 build = "build.rs"
 
 include = [
diff --git a/src/clang.rs b/src/clang.rs
index bac29ee..78bbd07 100644
--- a/src/clang.rs
+++ b/src/clang.rs
@@ -12,7 +12,7 @@
 use std::fmt;
 use std::hash::Hash;
 use std::hash::Hasher;
-use std::os::raw::{c_char, c_int, c_uint, c_ulong};
+use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_longlong, c_ulonglong};
 
 /// A cursor into the Clang AST, pointing to an AST node.
 ///
@@ -1786,13 +1786,34 @@
     }
 
     /// Try to get back the result as an integer.
-    pub fn as_int(&self) -> Option<i32> {
-        match self.kind() {
-            CXEval_Int => {
-                Some(unsafe { clang_EvalResult_getAsInt(self.x) } as i32)
-            }
-            _ => None,
+    pub fn as_int(&self) -> Option<i64> {
+        if self.kind() != CXEval_Int {
+            return None;
         }
+
+        if !clang_EvalResult_isUnsignedInt::is_loaded() {
+            // FIXME(emilio): There's no way to detect underflow here, and clang
+            // will just happily give us a value.
+            return Some(unsafe { clang_EvalResult_getAsInt(self.x) } as i64)
+        }
+
+        if unsafe { clang_EvalResult_isUnsignedInt(self.x) } != 0 {
+            let value = unsafe { clang_EvalResult_getAsUnsigned(self.x) };
+            if value > i64::max_value() as c_ulonglong {
+                return None;
+            }
+
+            return Some(value as i64)
+        }
+
+        let value = unsafe { clang_EvalResult_getAsLongLong(self.x) };
+        if value > i64::max_value() as c_longlong {
+            return None;
+        }
+        if value < i64::min_value() as c_longlong {
+            return None;
+        }
+        Some(value as i64)
     }
 
     /// Evaluates the expression as a literal string, that may or may not be
diff --git a/src/ir/var.rs b/src/ir/var.rs
index 25ac56d..78455c9 100644
--- a/src/ir/var.rs
+++ b/src/ir/var.rs
@@ -265,8 +265,7 @@
 
                     let mut val = cursor
                         .evaluate()
-                        .and_then(|v| v.as_int())
-                        .map(|val| val as i64);
+                        .and_then(|v| v.as_int());
                     if val.is_none() || !kind.signedness_matches(val.unwrap()) {
                         let tu = ctx.translation_unit();
                         val = get_integer_literal_from_cursor(&cursor, tu);
diff --git a/tests/expectations/tests/derive-debug-bitfield-core.rs b/tests/expectations/tests/derive-debug-bitfield-core.rs
index 11522bc..8c47fce 100644
--- a/tests/expectations/tests/derive-debug-bitfield-core.rs
+++ b/tests/expectations/tests/derive-debug-bitfield-core.rs
@@ -7,6 +7,8 @@
     non_upper_case_globals
 )]
 
+extern crate core;
+
 #[repr(C)]
 #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
 pub struct __BindgenBitfieldUnit<Storage, Align>
diff --git a/tests/expectations/tests/error-E0600-cannot-apply-unary-negation-to-u32.rs b/tests/expectations/tests/error-E0600-cannot-apply-unary-negation-to-u32.rs
deleted file mode 100644
index 9613a7f..0000000
--- a/tests/expectations/tests/error-E0600-cannot-apply-unary-negation-to-u32.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-/* automatically generated by rust-bindgen */
-
-
-#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-
-
-pub const a: u32 = 18446744073709551611;
diff --git a/tests/expectations/tests/libclang-3.8/constant-evaluate.rs b/tests/expectations/tests/libclang-3.8/constant-evaluate.rs
index 8faddfe..7df4e91 100644
--- a/tests/expectations/tests/libclang-3.8/constant-evaluate.rs
+++ b/tests/expectations/tests/libclang-3.8/constant-evaluate.rs
@@ -16,6 +16,10 @@
     pub static mut k_expr: EasyToOverflow;
 }
 extern "C" {
+    #[link_name = "\u{1}wow"]
+    pub static mut wow: EasyToOverflow;
+}
+extern "C" {
     #[link_name = "\u{1}BAZ"]
     pub static mut BAZ: ::std::os::raw::c_longlong;
 }
diff --git a/tests/expectations/tests/libclang-3.8/error-E0600-cannot-apply-unary-negation-to-u32.rs b/tests/expectations/tests/libclang-3.8/error-E0600-cannot-apply-unary-negation-to-u32.rs
new file mode 100644
index 0000000..68eab7a
--- /dev/null
+++ b/tests/expectations/tests/libclang-3.8/error-E0600-cannot-apply-unary-negation-to-u32.rs
@@ -0,0 +1,10 @@
+/* automatically generated by rust-bindgen */
+
+#![allow(
+    dead_code,
+    non_snake_case,
+    non_camel_case_types,
+    non_upper_case_globals
+)]
+
+pub const a: u32 = 18446744073709551611;
diff --git a/tests/expectations/tests/libclang-3.9/constant-evaluate.rs b/tests/expectations/tests/libclang-3.9/constant-evaluate.rs
index 159dba7..69501c6 100644
--- a/tests/expectations/tests/libclang-3.9/constant-evaluate.rs
+++ b/tests/expectations/tests/libclang-3.9/constant-evaluate.rs
@@ -15,6 +15,10 @@
 pub type EasyToOverflow = ::std::os::raw::c_ulonglong;
 pub const k: EasyToOverflow = 2147483648;
 pub const k_expr: EasyToOverflow = 0;
+extern "C" {
+    #[link_name = "\u{1}wow"]
+    pub static mut wow: EasyToOverflow;
+}
 pub const BAZ: ::std::os::raw::c_longlong = 24;
 pub const fuzz: f64 = 51.0;
 pub const BAZZ: ::std::os::raw::c_char = 53;
diff --git a/tests/expectations/tests/libclang-3.9/error-E0600-cannot-apply-unary-negation-to-u32.rs b/tests/expectations/tests/libclang-3.9/error-E0600-cannot-apply-unary-negation-to-u32.rs
new file mode 100644
index 0000000..68eab7a
--- /dev/null
+++ b/tests/expectations/tests/libclang-3.9/error-E0600-cannot-apply-unary-negation-to-u32.rs
@@ -0,0 +1,10 @@
+/* automatically generated by rust-bindgen */
+
+#![allow(
+    dead_code,
+    non_snake_case,
+    non_camel_case_types,
+    non_upper_case_globals
+)]
+
+pub const a: u32 = 18446744073709551611;
diff --git a/tests/expectations/tests/libclang-4/constant-evaluate.rs b/tests/expectations/tests/libclang-4/constant-evaluate.rs
index 045710f..07df811 100644
--- a/tests/expectations/tests/libclang-4/constant-evaluate.rs
+++ b/tests/expectations/tests/libclang-4/constant-evaluate.rs
@@ -1,6 +1,11 @@
 /* automatically generated by rust-bindgen */
 
-#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#![allow(
+    dead_code,
+    non_snake_case,
+    non_camel_case_types,
+    non_upper_case_globals
+)]
 
 pub const foo: _bindgen_ty_1 = _bindgen_ty_1::foo;
 pub const bar: _bindgen_ty_1 = _bindgen_ty_1::bar;
@@ -12,7 +17,8 @@
 }
 pub type EasyToOverflow = ::std::os::raw::c_ulonglong;
 pub const k: EasyToOverflow = 2147483648;
-pub const k_expr: EasyToOverflow = 0;
+pub const k_expr: EasyToOverflow = 1152921504606846976;
+pub const wow: EasyToOverflow = 2147483648;
 pub const BAZ: ::std::os::raw::c_longlong = 24;
 pub const fuzz: f64 = 51.0;
 pub const BAZZ: ::std::os::raw::c_char = 53;
diff --git a/tests/expectations/tests/libclang-4/error-E0600-cannot-apply-unary-negation-to-u32.rs b/tests/expectations/tests/libclang-4/error-E0600-cannot-apply-unary-negation-to-u32.rs
new file mode 100644
index 0000000..7ad2fc1
--- /dev/null
+++ b/tests/expectations/tests/libclang-4/error-E0600-cannot-apply-unary-negation-to-u32.rs
@@ -0,0 +1,10 @@
+/* automatically generated by rust-bindgen */
+
+#![allow(
+    dead_code,
+    non_snake_case,
+    non_camel_case_types,
+    non_upper_case_globals
+)]
+
+pub const a: u32 = 4294967291;
diff --git a/tests/expectations/tests/libclang-5/constant-evaluate.rs b/tests/expectations/tests/libclang-5/constant-evaluate.rs
index 045710f..07df811 100644
--- a/tests/expectations/tests/libclang-5/constant-evaluate.rs
+++ b/tests/expectations/tests/libclang-5/constant-evaluate.rs
@@ -1,6 +1,11 @@
 /* automatically generated by rust-bindgen */
 
-#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#![allow(
+    dead_code,
+    non_snake_case,
+    non_camel_case_types,
+    non_upper_case_globals
+)]
 
 pub const foo: _bindgen_ty_1 = _bindgen_ty_1::foo;
 pub const bar: _bindgen_ty_1 = _bindgen_ty_1::bar;
@@ -12,7 +17,8 @@
 }
 pub type EasyToOverflow = ::std::os::raw::c_ulonglong;
 pub const k: EasyToOverflow = 2147483648;
-pub const k_expr: EasyToOverflow = 0;
+pub const k_expr: EasyToOverflow = 1152921504606846976;
+pub const wow: EasyToOverflow = 2147483648;
 pub const BAZ: ::std::os::raw::c_longlong = 24;
 pub const fuzz: f64 = 51.0;
 pub const BAZZ: ::std::os::raw::c_char = 53;
diff --git a/tests/expectations/tests/libclang-5/error-E0600-cannot-apply-unary-negation-to-u32.rs b/tests/expectations/tests/libclang-5/error-E0600-cannot-apply-unary-negation-to-u32.rs
new file mode 100644
index 0000000..7ad2fc1
--- /dev/null
+++ b/tests/expectations/tests/libclang-5/error-E0600-cannot-apply-unary-negation-to-u32.rs
@@ -0,0 +1,10 @@
+/* automatically generated by rust-bindgen */
+
+#![allow(
+    dead_code,
+    non_snake_case,
+    non_camel_case_types,
+    non_upper_case_globals
+)]
+
+pub const a: u32 = 4294967291;
diff --git a/tests/headers/constant-evaluate.h b/tests/headers/constant-evaluate.h
index e8d0a5b..812553e 100644
--- a/tests/headers/constant-evaluate.h
+++ b/tests/headers/constant-evaluate.h
@@ -10,6 +10,7 @@
 const EasyToOverflow k = 0x80000000;
 
 const EasyToOverflow k_expr = 1ULL << 60;
+const EasyToOverflow wow = 1ULL << 31;
 
 const long long BAZ = (1 << foo) | bar;
 const double fuzz = (1 + 50.0f);
diff --git a/tests/headers/derive-debug-bitfield-core.hpp b/tests/headers/derive-debug-bitfield-core.hpp
index 7a5694e..5d78e74 100644
--- a/tests/headers/derive-debug-bitfield-core.hpp
+++ b/tests/headers/derive-debug-bitfield-core.hpp
@@ -1,4 +1,4 @@
-// bindgen-flags: --impl-debug --use-core
+// bindgen-flags: --impl-debug --use-core --raw-line "extern crate core;"
 
 class C {
   bool a: 1;