Repository: Geal/nom
Pull request #677 (2018-02-05 17:58:16)

WIP Add `alloc` feature guard
Related to #676 
I started to work on proposed feature guard, but I encountered some issues.
Some core lib API is not stable yet which causes my troubles with selecting the right impl for `as_bytes` and some char methods.
You can see I just added `unimplemented!()` there just to make things compile on stable. Tests remain untouched. What do You think about it?


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index c78799a..dbc5261 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,8 @@ include = [
 ]
 
 [features]
-std = ["memchr/use_std"]
+alloc = []
+std = ["alloc", "memchr/use_std"]
 nightly = []
 default = ["std"]
 regexp = ["regex"]
diff --git a/src/branch.rs b/src/branch.rs
index 67f185b..1c46c18 100644
--- a/src/branch.rs
+++ b/src/branch.rs
@@ -851,6 +851,7 @@ macro_rules! permutation_iterator (
 
 #[cfg(test)]
 mod tests {
+  #[cfg(feature = "alloc")]
   use std::string::{String, ToString};
   use internal::{Err, Needed, IResult};
   use util::ErrorKind;
@@ -915,21 +916,25 @@ mod tests {
     );
   );
 
+  #[cfg(feature = "alloc")]
   #[derive(Debug, Clone, PartialEq)]
   pub struct ErrorStr(String);
 
+  #[cfg(feature = "alloc")]
   impl From<u32> for ErrorStr {
     fn from(i: u32) -> Self {
       ErrorStr(format!("custom error code: {}", i))
     }
   }
 
+  #[cfg(feature = "alloc")]
   impl<'a> From<&'a str> for ErrorStr {
     fn from(i: &'a str) -> Self {
       ErrorStr(format!("custom error message: {}", i))
     }
   }
 
+  #[cfg(feature = "alloc")]
   #[test]
   fn alt() {
     fn work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
diff --git a/src/lib.rs b/src/lib.rs
index 40cab40..1b538a5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -426,7 +426,7 @@
 //!   assert_eq!(expr(b"2*2/(5-1)+3"), Ok((&b""[..], 4)));
 //! }
 //! ```
-#![cfg_attr(not(feature = "std"), feature(alloc))]
+#![cfg_attr(all(not(feature = "std"), feature = "alloc"), feature(alloc))]
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(feature = "nightly", feature(test))]
 #![cfg_attr(feature = "nightly", feature(const_fn))]
@@ -435,7 +435,7 @@
 
 #![cfg_attr(feature = "cargo-clippy", allow(doc_markdown))]
 
-#[cfg(not(feature = "std"))]
+#[cfg(all(not(feature = "std"), feature = "alloc"))]
 #[macro_use]
 extern crate alloc;
 #[cfg(feature = "regexp")]
@@ -449,8 +449,10 @@ extern crate test;
 
 #[cfg(not(feature = "std"))]
 mod std {
+  #[cfg(feature = "alloc")]
   #[macro_use]
   pub use alloc::{boxed, vec, string};
+
   pub use core::{fmt, cmp, iter, option, result, ops, slice, str, mem, convert};
   pub mod prelude {
     pub use core::prelude as v1;
diff --git a/src/multi.rs b/src/multi.rs
index 3880da6..9e3840d 100644
--- a/src/multi.rs
+++ b/src/multi.rs
@@ -2,6 +2,7 @@
 
 /// `separated_list!(I -> IResult<I,T>, I -> IResult<I,O>) => I -> IResult<I, Vec<O>>`
 /// separated_list(sep, X) returns Vec<X> will return Incomplete if there may be more elements
+#[cfg(feature = "alloc")]
 #[macro_export]
 macro_rules! separated_list(
   ($i:expr, $sep:ident!( $($args:tt)* ), $submac:ident!( $($args2:tt)* )) => (
@@ -381,6 +382,7 @@ macro_rules! many1(
 ///      error_position!(&c[..], ErrorKind::Tag)))));
 /// # }
 /// ```
+#[cfg(feature = "alloc")]
 #[macro_export]
 macro_rules! many_till(
   (__impl $i:expr, $submac1:ident!( $($args1:tt)* ), $submac2:ident!( $($args2:tt)* )) => (
@@ -468,6 +470,7 @@ macro_rules! many_till(
 ///  assert_eq!(multi(&c[..]),Ok((&b"abcdefgh"[..], res2)));
 /// # }
 /// ```
+#[cfg(feature = "alloc")]
 #[macro_export]
 macro_rules! many_m_n(
   ($i:expr, $m:expr, $n: expr, $submac:ident!( $($args:tt)* )) => (
@@ -557,6 +560,7 @@ macro_rules! many_m_n(
 /// # }
 /// ```
 ///
+#[cfg(feature = "alloc")]
 #[macro_export]
 macro_rules! count(
   ($i:expr, $submac:ident!( $($args:tt)* ), $count: expr) => (
diff --git a/src/nom.rs b/src/nom.rs
index 6451a6e..6b28d3c 100644
--- a/src/nom.rs
+++ b/src/nom.rs
@@ -6,6 +6,7 @@
 //! but the macros system makes no promises.
 //!
 
+#[cfg(feature = "alloc")]
 use std::boxed::Box;
 
 #[cfg(feature = "std")]
@@ -18,6 +19,7 @@ use traits::{Compare, CompareResult, Offset, Slice};
 use util::ErrorKind;
 use std::mem::transmute;
 
+#[cfg(feature = "alloc")]
 #[inline]
 pub fn tag_cl<'a, 'b>(rec: &'a [u8]) -> Box<Fn(&'b [u8]) -> IResult<&'b [u8], &'b [u8]> + 'a> {
   Box::new(move |i: &'b [u8]| -> IResult<&'b [u8], &'b [u8]> {
@@ -950,6 +952,7 @@ mod tests {
   use types::{CompleteByteSlice, CompleteStr};
 
   #[test]
+  #[cfg(feature = "alloc")]
   fn tag_closure() {
     let x = tag_cl(&b"abcd"[..]);
     let r = x(&b"abcdabcdefgh"[..]);
diff --git a/src/traits.rs b/src/traits.rs
index 6057174..caa1849 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -101,14 +101,14 @@ pub trait AsBytes {
 impl<'a> AsBytes for &'a str {
   #[inline(always)]
   fn as_bytes(&self) -> &[u8] {
-    str::as_bytes(self)
+    <str as AsBytes>::as_bytes(self)
   }
 }
 
 impl AsBytes for str {
   #[inline(always)]
   fn as_bytes(&self) -> &[u8] {
-    str::as_bytes(self)
+    self.as_ref()
   }
 }
 
@@ -250,10 +250,18 @@ impl AsChar for char {
   fn as_char(self) -> char {
     self
   }
+  #[cfg(feature = "alloc")]
   #[inline]
   fn is_alpha(self) -> bool {
     self.is_alphabetic()
   }
+  #[cfg(not(feature = "alloc"))]
+  #[inline]
+  fn is_alpha(self) -> bool {
+    unimplemented!(
+      "error[E0658]: use of unstable library feature 'core_char_ext': the stable interface is `impl char` in later crate (see issue #32110)"
+    )
+  }
   #[inline]
   fn is_alphanum(self) -> bool {
     self.is_alpha() || self.is_dec_digit()
@@ -283,7 +291,7 @@ impl<'a> AsChar for &'a char {
   }
   #[inline]
   fn is_alpha(self) -> bool {
-    self.is_alphabetic()
+    <char as AsChar>::is_alpha(*self)
   }
   #[inline]
   fn is_alphanum(self) -> bool {
@@ -527,11 +535,11 @@ impl<'a, 'b> Compare<&'b [u8]> for &'a [u8] {
 impl<'a, 'b> Compare<&'b str> for &'a [u8] {
   #[inline(always)]
   fn compare(&self, t: &'b str) -> CompareResult {
-    self.compare(str::as_bytes(t))
+    self.compare(AsBytes::as_bytes(t))
   }
   #[inline(always)]
   fn compare_no_case(&self, t: &'b str) -> CompareResult {
-    self.compare_no_case(str::as_bytes(t))
+    self.compare_no_case(AsBytes::as_bytes(t))
   }
 }
 
@@ -553,6 +561,7 @@ impl<'a, 'b> Compare<&'b str> for &'a str {
   }
 
   //FIXME: this version is too simple and does not use the current locale
+  #[cfg(feature = "alloc")]
   #[inline(always)]
   fn compare_no_case(&self, t: &'b str) -> CompareResult {
     let pos = self
@@ -572,6 +581,12 @@ impl<'a, 'b> Compare<&'b str> for &'a str {
       }
     }
   }
+
+  #[cfg(not(feature = "alloc"))]
+  #[inline(always)]
+  fn compare_no_case(&self, _: &'b str) -> CompareResult {
+    unimplemented!()
+  }
 }
 
 /// look for self in the given input stream
@@ -664,7 +679,7 @@ impl<'a, 'b> FindSubstring<&'b [u8]> for &'a [u8] {
 
 impl<'a, 'b> FindSubstring<&'b str> for &'a [u8] {
   fn find_substring(&self, substr: &'b str) -> Option<usize> {
-    self.find_substring(str::as_bytes(substr))
+    self.find_substring(AsBytes::as_bytes(substr))
   }
 }
 
diff --git a/src/types.rs b/src/types.rs
index 22ec36d..b904d25 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -134,7 +134,7 @@ impl<'a> Offset for CompleteStr<'a> {
 
 impl<'a> AsBytes for CompleteStr<'a> {
   fn as_bytes(&self) -> &[u8] {
-    self.0.as_bytes()
+    AsBytes::as_bytes(self.0)
   }
 }
 
diff --git a/src/util.rs b/src/util.rs
index 94ad684..4348534 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -6,7 +6,9 @@ use verbose_errors::Context;
 #[cfg(feature = "std")]
 use std::collections::HashMap;
 
+#[cfg(feature = "alloc")]
 use std::vec::Vec;
+#[cfg(feature = "alloc")]
 use std::string::ToString;
 
 #[cfg(feature = "std")]
@@ -289,6 +291,7 @@ pub fn code_from_offset<E>(v: &[(ErrorKind<E>, usize, usize)], offset: usize) ->
   }
 }
 
+#[cfg(feature = "alloc")]
 pub fn reset_color(v: &mut Vec<u8>) {
   v.push(0x1B);
   v.push(b'[');
@@ -296,6 +299,7 @@ pub fn reset_color(v: &mut Vec<u8>) {
   v.push(b'm');
 }
 
+#[cfg(feature = "alloc")]
 pub fn write_color(v: &mut Vec<u8>, color: u8) {
   v.push(0x1B);
   v.push(b'[');
diff --git a/src/whitespace.rs b/src/whitespace.rs
index 031acb3..4ca6b40 100644
--- a/src/whitespace.rs
+++ b/src/whitespace.rs
@@ -679,6 +679,7 @@ macro_rules! switch_sep (
 );
 
 #[doc(hidden)]
+#[cfg(feature = "alloc")]
 #[macro_export]
 macro_rules! separated_list_sep (
   ($i:expr, $separator:path, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => (
@@ -895,6 +896,7 @@ macro_rules! ws (
 #[cfg(test)]
 #[allow(dead_code)]
 mod tests {
+  #[cfg(feature = "alloc")]
   use std::string::{String, ToString};
   use internal::{Err, IResult, Needed};
   use super::sp;
@@ -1038,21 +1040,25 @@ mod tests {
     assert_eq!(perm(e), Err(Err::Incomplete(Needed::Size(4))));
   }
 
+  #[cfg(feature = "alloc")]
   #[derive(Debug, Clone, PartialEq)]
   pub struct ErrorStr(String);
 
+  #[cfg(feature = "alloc")]
   impl From<u32> for ErrorStr {
     fn from(i: u32) -> Self {
       ErrorStr(format!("custom error code: {}", i))
     }
   }
 
+  #[cfg(feature = "alloc")]
   impl<'a> From<&'a str> for ErrorStr {
     fn from(i: &'a str) -> Self {
       ErrorStr(format!("custom error message: {}", i))
     }
   }
 
+  #[cfg(feature = "alloc")]
   #[test]
   fn alt() {
     fn work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
@@ -1141,6 +1147,7 @@ mod tests {
 
   // test whitespace parser generation for alt
   named!(space, tag!(" "));
+  #[cfg(feature = "alloc")]
   named!(pipeline_statement<&[u8], ()>,
     ws!(
       do_parse!(
@@ -1160,5 +1167,6 @@ mod tests {
   )
   );
 
+  #[cfg(feature = "alloc")]
   named!(fail<&[u8]>, map!(many_till!(take!(1), ws!(tag!("."))), |(r, _)| r[0]));
 }


Test patch:
diff --git a/tests/custom_errors.rs b/tests/custom_errors.rs
index 566800e..ed83e58 100644
--- a/tests/custom_errors.rs
+++ b/tests/custom_errors.rs
@@ -30,6 +30,7 @@ fn test3(input: &str) -> IResult<&str, &str, CustomError> {
   verify!(input, test1, |s: &str| s.starts_with("abcd"))
 }
 
+#[cfg(feature = "alloc")]
 fn test4(input: &str) -> IResult<&str, Vec<&str>, CustomError> {
   count!(input, test1, 4)
 }
diff --git a/tests/inference.rs b/tests/inference.rs
index 5cd0fe2..da43009 100644
--- a/tests/inference.rs
+++ b/tests/inference.rs
@@ -4,6 +4,7 @@
 #![allow(unused_comparisons)]
 #![allow(unused_doc_comment)]
 #![allow(unused_variables)]
+#![allow(unused_imports)]
 
 #[macro_use]
 extern crate nom;
@@ -15,6 +16,7 @@ use nom::{is_digit, alpha};
 named!(multi<&[u8], () >, fold_many0!( take_while1!( is_digit ), (), |_, _| {}));
 
 // issue #561
+#[cfg(feature = "alloc")]
 named!(value<Vec<Vec<&str>>>,
 	do_parse!(
 		first_line: map_res!(is_not_s!("\n"), std::str::from_utf8) >>
@@ -26,6 +28,7 @@ named!(value<Vec<Vec<&str>>>,
 );
 
 // issue #534
+#[cfg(feature = "alloc")]
 fn wrap_suffix(input: &Option<Vec<&[u8]>>) -> Option<String> {
   if input.is_some() {
     ///
@@ -38,6 +41,7 @@ fn wrap_suffix(input: &Option<Vec<&[u8]>>) -> Option<String> {
   }
 }
 
+#[cfg(feature = "alloc")]
 named!(parse_suffix<&[u8],Option<String>>,do_parse!(
   u: opt!(many1!(alt_complete!(
     tag!("%") | tag!("#")  | tag!("@") | alpha
diff --git a/tests/issues.rs b/tests/issues.rs
index 37f2ac3..7411690 100644
--- a/tests/issues.rs
+++ b/tests/issues.rs
@@ -5,7 +5,9 @@
 #[macro_use]
 extern crate nom;
 
-use nom::{Err, IResult, Needed, space, be_u16, le_u64};
+#[cfg(feature = "alloc")]
+use nom::le_u64;
+use nom::{Err, IResult, Needed, space, be_u16};
 use nom::types::CompleteByteSlice;
 
 #[allow(dead_code)]
@@ -150,7 +152,7 @@ named!(issue_308(&str) -> bool,
         ) >>
         (b) ));
 
-
+#[cfg(feature = "alloc")]
 fn issue_302(input: &[u8]) -> IResult<&[u8], Option<Vec<u64>>> {
   do_parse!(input,
         entries: cond!(true, count!(le_u64, 3)) >>
diff --git a/tests/json.rs b/tests/json.rs
index 0aceeef..2bd5ac7 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -1,3 +1,4 @@
+#![cfg(feature = "alloc")]
 //#![feature(trace_macros)]
 
 #[macro_use]
diff --git a/tests/overflow.rs b/tests/overflow.rs
index b60420c..8229b06 100644
--- a/tests/overflow.rs
+++ b/tests/overflow.rs
@@ -41,6 +41,7 @@ fn overflow_incomplete_tuple() {
 }
 
 #[test]
+#[cfg(feature = "alloc")]
 fn overflow_incomplete_length_bytes() {
   named!(multi<&[u8], Vec<&[u8]> >, many0!( length_bytes!(be_u64) ) );
 
@@ -50,6 +51,7 @@ fn overflow_incomplete_length_bytes() {
 }
 
 #[test]
+#[cfg(feature = "alloc")]
 fn overflow_incomplete_many0() {
   named!(multi<&[u8], Vec<&[u8]> >, many0!( length_bytes!(be_u64) ) );
 
@@ -69,6 +71,7 @@ fn overflow_incomplete_many1() {
 }
 
 #[test]
+#[cfg(feature = "alloc")]
 fn overflow_incomplete_many_till() {
   named!(multi<&[u8], (Vec<&[u8]>, &[u8]) >, many_till!( length_bytes!(be_u64), tag!("abc") ) );
 
@@ -78,6 +81,7 @@ fn overflow_incomplete_many_till() {
 }
 
 #[test]
+#[cfg(feature = "alloc")]
 fn overflow_incomplete_many_m_n() {
   named!(multi<&[u8], Vec<&[u8]> >, many_m_n!(2, 4, length_bytes!(be_u64) ) );
 
@@ -87,6 +91,7 @@ fn overflow_incomplete_many_m_n() {
 }
 
 #[test]
+#[cfg(feature = "alloc")]
 fn overflow_incomplete_count() {
   named!(counter<&[u8], Vec<&[u8]> >, count!( length_bytes!(be_u64), 2 ) );
 
@@ -103,6 +108,7 @@ fn overflow_incomplete_count_fixed() {
 }
 
 #[test]
+#[cfg(feature = "alloc")]
 fn overflow_incomplete_length_count() {
   named!(multi<&[u8], Vec<&[u8]> >, length_count!( be_u8, length_bytes!(be_u64) ) );
 
@@ -111,6 +117,7 @@ fn overflow_incomplete_length_count() {
 }
 
 #[test]
+#[cfg(feature = "alloc")]
 fn overflow_incomplete_length_data() {
   named!(multi<&[u8], Vec<&[u8]> >, many0!( length_data!(be_u64) ) );