Upgrade rust/crates/const_fn to 0.4.3

Test: make
Change-Id: I6d1557fc976b54f172237ac5968413751b9fe1d8
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 6c4b0a1..3602ab1 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "d306b29df53ab27a825e4213fc25f7e7aba493a6"
+    "sha1": "8684f7ec1965df5434d5eec38b340f10030fce49"
   }
 }
diff --git a/.editorconfig b/.editorconfig
index c93ffc7..933a9a4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,3 +14,6 @@
 
 [*.{json,yml,md}]
 indent_size = 2
+
+[*.sh]
+switch_case_indent = true
diff --git a/.gitattributes b/.gitattributes
index 45bca84..7e4a4f0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,2 @@
-[attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4
-
 * text=auto eol=lf
-*.rs rust
+*.rs text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 63e3ab4..affe118 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,9 +6,8 @@
     branches:
       - master
       - staging
-      - trying
   schedule:
-    - cron: '00 01 * * *'
+    - cron: '0 1 * * *'
 
 env:
   RUSTFLAGS: -Dwarnings
@@ -37,49 +36,30 @@
     steps:
       - uses: actions/checkout@v2
       - name: Install Rust
-        run: |
-          . ./ci/install-rust.sh ${{ matrix.rust }}
-      - name: Install cargo-hack
-        if: matrix.rust == 'nightly'
-        run: |
-          cargo install cargo-hack
-      - name: cargo test
-        run: |
-          cargo test --all
-      - name: cargo check (minimal versions)
-        if: matrix.rust == 'nightly'
-        run: |
-          . ./ci/check-minimal-versions.sh
+        run: ci/install-rust.sh ${{ matrix.rust }}
+      - if: matrix.rust == 'nightly'
+        run: cargo install cargo-hack
+      - run: cargo test --all
+      - if: matrix.rust == 'nightly'
+        run: bash scripts/check-minimal-versions.sh
 
   clippy:
     name: clippy
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - name: Install Rust
-        run: |
-          . ./ci/install-rust.sh
-      - name: Install clippy
-        run: |
-          . ./ci/install-component.sh clippy
-      - name: cargo clippy
-        run: |
-          cargo clippy --all --all-features --all-targets
+      - name: Install Rust and Clippy
+        run: ci/install-component.sh clippy
+      - run: cargo clippy --all --all-features --all-targets
 
   rustfmt:
     name: rustfmt
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - name: Install Rust
-        run: |
-          . ./ci/install-rust.sh
-      - name: Install rustfmt
-        run: |
-          . ./ci/install-component.sh rustfmt
-      - name: cargo fmt --check
-        run: |
-          cargo fmt --all -- --check
+      - name: Install Rust and Rustfmt
+        run: ci/install-component.sh rustfmt
+      - run: cargo fmt --all -- --check
 
   rustdoc:
     name: rustdoc
@@ -89,11 +69,15 @@
     steps:
       - uses: actions/checkout@v2
       - name: Install Rust
-        run: |
-          . ./ci/install-rust.sh
-      - name: cargo doc
-        run: |
-          cargo doc --no-deps --all --all-features
+        run: ci/install-rust.sh
+      - run: cargo doc --no-deps --all --all-features
+
+  shellcheck:
+    name: shellcheck
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - run: shellcheck **/*.sh
 
   # These jobs don't actually test anything, but they're used to tell bors the
   # build completed, as there is no practical way to detect when a workflow is
@@ -109,6 +93,7 @@
       - clippy
       - rustfmt
       - rustdoc
+      - shellcheck
     runs-on: ubuntu-latest
     steps:
       - name: Mark the job as a success
@@ -121,6 +106,7 @@
       - clippy
       - rustfmt
       - rustdoc
+      - shellcheck
     runs-on: ubuntu-latest
     steps:
       - name: Mark the job as a failure
diff --git a/.gitignore b/.gitignore
index 214d7a8..ad0f684 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@
 
 # For platform and editor specific settings, it is recommended to add to
 # a global .gitignore file.
-# Refs: https://help.github.com/en/articles/ignoring-files#create-a-global-gitignore
+# Refs: https://docs.github.com/en/free-pro-team@latest/github/using-git/ignoring-files#configuring-ignored-files-for-all-repositories-on-your-computer
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 94890d8..00b86b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,10 +6,18 @@
 
 ## [Unreleased]
 
+## [0.4.3] - 2020-11-02
+
+* [`const_fn` no longer fails to compile if unable to determine rustc version. Instead, it now displays a warning.](https://github.com/taiki-e/const_fn/pull/31)
+
+* [`const_fn` no longer relies on debug print format.](https://github.com/taiki-e/const_fn/pull/30)
+
 ## [0.4.2] - 2020-08-31
 
 * [Improve error messages when failed to parse version information.](https://github.com/taiki-e/const_fn/pull/26)
 
+* [Fix compile failure with cargo installed by yum.](https://github.com/taiki-e/const_fn/pull/26)
+
 ## [0.4.1] - 2020-08-25
 
 * [Fix compile failure with non-cargo build systems.](https://github.com/taiki-e/const_fn/pull/23)
@@ -98,7 +106,8 @@
 
 Initial release
 
-[Unreleased]: https://github.com/taiki-e/const_fn/compare/v0.4.2...HEAD
+[Unreleased]: https://github.com/taiki-e/const_fn/compare/v0.4.3...HEAD
+[0.4.3]: https://github.com/taiki-e/const_fn/compare/v0.4.2...v0.4.3
 [0.4.2]: https://github.com/taiki-e/const_fn/compare/v0.4.1...v0.4.2
 [0.4.1]: https://github.com/taiki-e/const_fn/compare/v0.4.0...v0.4.1
 [0.4.0]: https://github.com/taiki-e/const_fn/compare/v0.3.1...v0.4.0
diff --git a/Cargo.toml b/Cargo.toml
index 312f02b..f27ba56 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
 [package]
 edition = "2018"
 name = "const_fn"
-version = "0.4.2"
+version = "0.4.3"
 authors = ["Taiki Endo <te316e89@gmail.com>"]
 description = "An attribute for easy generation of const functions with conditional compilations.\n"
 homepage = "https://github.com/taiki-e/const_fn"
@@ -28,5 +28,3 @@
 
 [lib]
 proc-macro = true
-
-[dependencies]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index fe9e6f0..73c690a 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "const_fn"
-version = "0.4.2"
+version = "0.4.3"
 authors = ["Taiki Endo <te316e89@gmail.com>"]
 edition = "2018"
 license = "Apache-2.0 OR MIT"
@@ -22,5 +22,3 @@
 
 [lib]
 proc-macro = true
-
-[dependencies]
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index d645695..f433b1a 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -175,28 +175,3 @@
       of your accepting any such warranty or additional liability.
 
    END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/METADATA b/METADATA
index d499a12..63bbaa8 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/const_fn/const_fn-0.4.2.crate"
+    value: "https://static.crates.io/crates/const_fn/const_fn-0.4.3.crate"
   }
-  version: "0.4.2"
+  version: "0.4.3"
   license_type: NOTICE
   last_upgrade_date {
     year: 2020
-    month: 10
-    day: 21
+    month: 11
+    day: 2
   }
 }
diff --git a/build.rs b/build.rs
index 758b390..f9e80a5 100644
--- a/build.rs
+++ b/build.rs
@@ -1,3 +1,6 @@
+#![forbid(unsafe_code)]
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+
 use std::{
     env, fs,
     path::{Path, PathBuf},
@@ -9,70 +12,90 @@
 // opening a GitHub issue if your build environment requires some way to enable
 // these cfgs other than by executing our build script.
 fn main() {
-    println!("cargo:rustc-cfg=const_fn_has_build_script");
-
     let rustc = env::var_os("RUSTC").map_or_else(|| "rustc".into(), PathBuf::from);
     let version = match Version::from_rustc(&rustc) {
-        Ok(version) => format!("{:#?}\n", version),
-        Err(e) => panic!("{}", e),
+        Ok(version) => version.print(),
+        Err(e) => {
+            println!(
+                "cargo:warning={}: unable to determine rustc version: {}",
+                env!("CARGO_PKG_NAME"),
+                e
+            );
+            return;
+        }
     };
 
     let out_dir = env::var_os("OUT_DIR").map(PathBuf::from).expect("OUT_DIR not set");
     let out_file = out_dir.join("version.rs");
     fs::write(out_file, version).expect("failed to write version.rs");
+
+    // Mark as build script has been run successfully.
+    println!("cargo:rustc-cfg=const_fn_has_build_script");
 }
 
-#[derive(Debug)]
 struct Version {
-    minor: u16,
-    patch: u16,
+    minor: u32,
     nightly: bool,
 }
 
-// Based on https://github.com/cuviper/autocfg/blob/1.0.1/src/version.rs
-//
-// Using our own parser instead of the existing crates to generate better errors.
 impl Version {
-    // from the verbose version output
-    fn from_vv(vv: &str) -> Option<Self> {
+    // Based on https://github.com/cuviper/autocfg/blob/1.0.1/src/version.rs#L25-L59
+    //
+    // TODO: use autocfg if https://github.com/cuviper/autocfg/issues/28 merged
+    // or https://github.com/taiki-e/const_fn/issues/27 rejected.
+    fn from_rustc(rustc: &Path) -> Result<Self, String> {
+        let output =
+            Command::new(rustc).args(&["--version", "--verbose"]).output().map_err(|e| {
+                format!("could not execute `{} --version --verbose`: {}", rustc.display(), e)
+            })?;
+        if !output.status.success() {
+            return Err(format!(
+                "process didn't exit successfully: `{} --version --verbose`",
+                rustc.display()
+            ));
+        }
+        let output = str::from_utf8(&output.stdout).map_err(|e| {
+            format!("failed to parse output of `{} --version --verbose`: {}", rustc.display(), e)
+        })?;
+
         // Find the release line in the verbose version output.
-        let release = vv
+        let release = output
             .lines()
             .find(|line| line.starts_with("release: "))
-            .map(|line| &line["release: ".len()..])?;
+            .map(|line| &line["release: ".len()..])
+            .ok_or_else(|| {
+                format!(
+                    "could not find rustc release from output of `{} --version --verbose`: {}",
+                    rustc.display(),
+                    output
+                )
+            })?;
 
         // Split the version and channel info.
         let mut version_channel = release.split('-');
         let version = version_channel.next().unwrap();
         let channel = version_channel.next();
 
-        // Split the version into semver components.
-        let mut digits = version.splitn(3, '.');
-        let major = digits.next()?;
-        if major != "1" {
-            return None;
-        }
-        let minor = digits.next()?.parse().ok()?;
-        let patch = digits.next().unwrap_or("0").parse().ok()?;
-
-        let nightly = channel.map_or(false, |c| c == "dev" || c == "nightly");
-        Some(Version { minor, patch, nightly })
-    }
-
-    fn from_rustc(rustc: &Path) -> Result<Self, String> {
-        let output =
-            Command::new(rustc).args(&["--version", "--verbose"]).output().map_err(|e| {
-                format!("failed to run `{} --version --verbose`: {}", rustc.display(), e)
-            })?;
-        if !output.status.success() {
-            return Err("could not execute rustc".to_string());
-        }
-        let output = str::from_utf8(&output.stdout).map_err(|e| {
-            format!("failed to parse output of `{} --version --verbose`: {}", rustc.display(), e)
+        let minor = (|| {
+            // Split the version into semver components.
+            let mut digits = version.splitn(3, '.');
+            let major = digits.next()?;
+            if major != "1" {
+                return None;
+            }
+            let minor = digits.next()?.parse().ok()?;
+            let _patch = digits.next()?;
+            Some(minor)
+        })()
+        .ok_or_else(|| {
+            format!("unexpected output from `{} --version --verbose`: {}", rustc.display(), output)
         })?;
 
-        Self::from_vv(output).ok_or_else(|| {
-            format!("unexpected output from `{} --version --verbose`: {}", rustc.display(), output)
-        })
+        let nightly = channel.map_or(false, |c| c == "dev" || c == "nightly");
+        Ok(Self { minor, nightly })
+    }
+
+    fn print(&self) -> String {
+        format!("Version {{ minor: {}, nightly: {} }}\n", self.minor, self.nightly)
     }
 }
diff --git a/ci/check-minimal-versions.sh b/ci/check-minimal-versions.sh
deleted file mode 100644
index 6152c15..0000000
--- a/ci/check-minimal-versions.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-# Check all public crates with minimal version dependencies.
-#
-# Note that this script modifies Cargo.toml and Cargo.lock while this script is
-# running, and it is an error if there are any unstaged changes.
-#
-# Refs:
-# * minimal versions: https://github.com/rust-lang/cargo/issues/5657
-# * features 2.0: https://github.com/rust-lang/cargo/issues/8088
-
-set -euo pipefail
-
-# This script modifies Cargo.toml and Cargo.lock, so make sure there are no
-# unstaged changes.
-git diff --exit-code
-
-# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
-# from determining minimal versions based on dev-dependencies.
-cargo hack --remove-dev-deps --workspace
-
-# Update Cargo.lock to minimal version dependencies.
-cargo update -Zminimal-versions
-# Run check for all public members of the workspace.
-cargo hack check --workspace --all-features --ignore-private -Zfeatures=all
-
-# Restore original Cargo.toml and Cargo.lock.
-git checkout .
diff --git a/ci/install-component.sh b/ci/install-component.sh
old mode 100644
new mode 100755
index 9aaa5ce..dbba7b1
--- a/ci/install-component.sh
+++ b/ci/install-component.sh
@@ -1,22 +1,27 @@
 #!/bin/bash
 
+# Install nightly Rust with a given component.
+#
+# If the component is unavailable on the latest nightly,
+# use the latest toolchain with the component available.
+#
+# When using stable Rust, this script is basically unnecessary as almost components available.
+#
+# Refs: https://github.com/rust-lang/rustup-components-history#the-web-part
+
 set -euo pipefail
 
-component="${1}"
+package="${1:?}"
+target="${2:-x86_64-unknown-linux-gnu}"
 
-if ! rustup component add "${component}" 2>/dev/null; then
-    # If the component is unavailable on the latest nightly,
-    # use the latest toolchain with the component available.
-    # Refs: https://github.com/rust-lang/rustup-components-history#the-web-part
-    target=$(curl -sSf "https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/${component}")
-    echo "'${component}' is unavailable on the default toolchain, use the toolchain 'nightly-${target}' instead"
+date=$(curl -sSf https://rust-lang.github.io/rustup-components-history/"${target}"/"${package}")
 
-    . ci/install-rust.sh "nightly-${target}"
+# shellcheck disable=1090
+"$(cd "$(dirname "${0}")" && pwd)"/install-rust.sh nightly-"${date}"
 
-    rustup component add "${component}"
-fi
+rustup component add "${package}"
 
-case "${component}" in
-    rustfmt) "${component}" -V ;;
-    *) cargo "${component}" -V ;;
+case "${package}" in
+    rustfmt) "${package}" -V ;;
+    *) cargo "${package}" -V ;;
 esac
diff --git a/ci/install-rust.sh b/ci/install-rust.sh
old mode 100644
new mode 100755
index b6625b6..92c5877
--- a/ci/install-rust.sh
+++ b/ci/install-rust.sh
@@ -4,8 +4,7 @@
 
 toolchain="${1:-nightly}"
 
-rustup set profile minimal
-rustup update "${toolchain}" --no-self-update
+rustup toolchain install "${toolchain}" --no-self-update --profile minimal
 rustup default "${toolchain}"
 
 rustup -V
diff --git a/out/version.rs b/out/version.rs
index a24fecb..be0b2d7 100644
--- a/out/version.rs
+++ b/out/version.rs
@@ -1,5 +1 @@
-Version {
-    minor: 46,
-    patch: 0,
-    nightly: true,
-}
+Version { minor: 46, nightly: true }
diff --git a/scripts/README.md b/scripts/README.md
new file mode 100644
index 0000000..671a9b1
--- /dev/null
+++ b/scripts/README.md
@@ -0,0 +1,3 @@
+This directory contains scripts used by the developers of this project.
+
+Some scripts in this directory are also used in CI, but scripts intended to be used only in CI and scripts not intended to be used directly by the developers are basically in the `ci` directory.
diff --git a/scripts/check-minimal-versions.sh b/scripts/check-minimal-versions.sh
new file mode 100644
index 0000000..7d16e93
--- /dev/null
+++ b/scripts/check-minimal-versions.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+# Check all public crates with minimal version dependencies.
+#
+# Usage:
+#    bash scripts/check-minimal-versions.sh [check|test]
+#
+# Note:
+# - This script modifies Cargo.toml and Cargo.lock while running
+# - This script exits with 1 if there are any unstaged changes
+# - This script requires nightly Rust and cargo-hack
+#
+# Refs: https://github.com/rust-lang/cargo/issues/5657
+
+set -euo pipefail
+
+cd "$(cd "$(dirname "${0}")" && pwd)"/..
+
+# Decide Rust toolchain.
+# If the `CI` environment variable is not set to `true`, then nightly is used by default.
+if [[ "${1:-none}" == "+"* ]]; then
+    toolchain="${1}"
+    shift
+elif [[ "${CI:-false}" != "true" ]]; then
+    cargo +nightly -V >/dev/null || exit 1
+    toolchain="+nightly"
+fi
+# This script requires nightly Rust and cargo-hack
+if [[ "${toolchain:-+nightly}" != "+nightly"* ]] || ! cargo hack -V &>/dev/null; then
+    echo "error: check-minimal-versions.sh requires nightly Rust and cargo-hack"
+    exit 1
+fi
+
+subcmd="${1:-check}"
+case "${subcmd}" in
+    check | test) ;;
+    *)
+        echo "error: invalid argument \`${1}\`"
+        exit 1
+        ;;
+esac
+
+# This script modifies Cargo.toml and Cargo.lock, so make sure there are no
+# unstaged changes.
+git diff --exit-code
+# Restore original Cargo.toml and Cargo.lock on exit.
+trap 'git checkout .' EXIT
+
+if [[ "${subcmd}" == "check" ]]; then
+    # Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
+    # from determining minimal versions based on dev-dependencies.
+    cargo hack --remove-dev-deps --workspace
+fi
+
+# Update Cargo.lock to minimal version dependencies.
+cargo ${toolchain:-} update -Zminimal-versions
+# Run check for all public members of the workspace.
+cargo ${toolchain:-} hack "${subcmd}" --workspace --all-features --ignore-private -Zfeatures=all
diff --git a/src/ast.rs b/src/ast.rs
index b9acee5..b156493 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -1,7 +1,7 @@
 use proc_macro::{Delimiter, Literal, Span, TokenStream, TokenTree};
-use std::iter::Peekable;
 
 use crate::{
+    iter::TokenIter,
     to_tokens::ToTokens,
     utils::{parse_as_empty, tt_span},
     Result,
@@ -15,7 +15,7 @@
 }
 
 pub(crate) fn parse_input(input: TokenStream) -> Result<Func> {
-    let mut input = input.into_iter().peekable();
+    let mut input = TokenIter::new(input);
 
     let attrs = parse_attrs(&mut input)?;
     let sig = parse_signature(&mut input);
@@ -64,19 +64,19 @@
     }
 }
 
-fn parse_signature(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Vec<TokenTree> {
+fn parse_signature(input: &mut TokenIter) -> Vec<TokenTree> {
     let mut sig = Vec::new();
     loop {
         match input.peek() {
             Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => break,
             None => break,
-            _ => sig.push(input.next().unwrap()),
+            Some(_) => sig.push(input.next().unwrap()),
         }
     }
     sig
 }
 
-fn parse_attrs(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Result<Vec<Attribute>> {
+fn parse_attrs(input: &mut TokenIter) -> Result<Vec<Attribute>> {
     let mut attrs = Vec::new();
     loop {
         let pound_token = match input.peek() {
@@ -109,16 +109,16 @@
 }
 
 pub(crate) struct LitStr {
-    token: Literal,
+    pub(crate) token: Literal,
     value: String,
 }
 
 impl LitStr {
-    pub(crate) fn new(token: &Literal) -> Result<Self> {
+    pub(crate) fn new(token: Literal) -> Result<Self> {
         let value = token.to_string();
         // unlike `syn::LitStr`, only accepts `"..."`
         if value.starts_with('"') && value.ends_with('"') {
-            Ok(Self { token: token.clone(), value })
+            Ok(Self { token, value })
         } else {
             Err(error!(token.span(), "expected string literal"))
         }
diff --git a/src/error.rs b/src/error.rs
index bd3ea92..b572d5a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,5 +1,5 @@
 use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
-use std::{fmt, iter::FromIterator};
+use std::iter::FromIterator;
 
 pub(crate) struct Error {
     span: Span,
@@ -7,8 +7,8 @@
 }
 
 impl Error {
-    pub(crate) fn new(span: Span, msg: impl fmt::Display) -> Self {
-        Error { span, msg: msg.to_string() }
+    pub(crate) fn new(span: Span, msg: impl Into<String>) -> Self {
+        Self { span, msg: msg.into() }
     }
 
     // https://github.com/dtolnay/syn/blob/1.0.39/src/error.rs#L218-L237
diff --git a/src/iter.rs b/src/iter.rs
new file mode 100644
index 0000000..f998dce
--- /dev/null
+++ b/src/iter.rs
@@ -0,0 +1,39 @@
+// Based on https://github.com/dtolnay/proc-macro-hack/blob/0.5.18/src/iter.rs
+
+use proc_macro::{token_stream, Delimiter, TokenStream, TokenTree};
+
+pub(crate) struct TokenIter {
+    stack: Vec<token_stream::IntoIter>,
+    peeked: Option<TokenTree>,
+}
+
+impl TokenIter {
+    pub(crate) fn new(tokens: TokenStream) -> Self {
+        Self { stack: vec![tokens.into_iter()], peeked: None }
+    }
+
+    pub(crate) fn peek(&mut self) -> Option<&TokenTree> {
+        self.peeked = self.next();
+        self.peeked.as_ref()
+    }
+}
+
+impl Iterator for TokenIter {
+    type Item = TokenTree;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(tt) = self.peeked.take() {
+            return Some(tt);
+        }
+        loop {
+            let top = self.stack.last_mut()?;
+            match top.next() {
+                None => drop(self.stack.pop()),
+                Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::None => {
+                    self.stack.push(group.stream().into_iter());
+                }
+                Some(tt) => return Some(tt),
+            }
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index e453950..1f818f8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -39,16 +39,23 @@
 //! You can manually define declarative macros with similar functionality (see [`if_rust_version`](https://github.com/ogoffart/if_rust_version#examples)), or [you can define the same function twice with different cfg](https://github.com/crossbeam-rs/crossbeam/blob/0b6ea5f69fde8768c1cfac0d3601e0b4325d7997/crossbeam-epoch/src/atomic.rs#L340-L372).
 //! (Note: the former approach requires more macros to be defined depending on the number of version requirements, the latter approach requires more functions to be maintained manually)
 
-#![doc(html_root_url = "https://docs.rs/const_fn/0.4.2")]
+#![doc(html_root_url = "https://docs.rs/const_fn/0.4.3")]
 #![doc(test(
     no_crate_inject,
     attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
 ))]
 #![forbid(unsafe_code)]
-#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
+#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
 #![warn(clippy::all, clippy::default_trait_access)]
-// mem::take and #[non_exhaustive] requires Rust 1.40
-#![allow(clippy::mem_replace_with_default, clippy::manual_non_exhaustive)]
+// mem::take, #[non_exhaustive], and Option::{as_deref, as_deref_mut} require Rust 1.40,
+// matches! requires Rust 1.42, str::{strip_prefix, strip_suffix} requires Rust 1.45
+#![allow(
+    clippy::mem_replace_with_default,
+    clippy::manual_non_exhaustive,
+    clippy::option_as_ref_deref,
+    clippy::match_like_matches_macro,
+    clippy::manual_strip
+)]
 
 // older compilers require explicit `extern crate`.
 #[allow(unused_extern_crates)]
@@ -59,12 +66,14 @@
 
 mod ast;
 mod error;
+mod iter;
 mod to_tokens;
 
 use proc_macro::{Delimiter, TokenStream, TokenTree};
 use std::str::FromStr;
 
 use crate::{
+    ast::{Func, LitStr},
     error::Error,
     to_tokens::ToTokens,
     utils::{cfg_attrs, parse_as_empty, tt_span},
@@ -77,25 +86,29 @@
 #[proc_macro_attribute]
 pub fn const_fn(args: TokenStream, input: TokenStream) -> TokenStream {
     let arg = match parse_arg(args) {
-        Ok(a) => a,
+        Ok(arg) => arg,
         Err(e) => return e.to_compile_error(),
     };
-    let mut func = match ast::parse_input(input) {
-        Ok(i) => i,
+    let func = match ast::parse_input(input) {
+        Ok(func) => func,
         Err(e) => return e.to_compile_error(),
     };
 
+    expand(arg, func)
+}
+
+fn expand(arg: Arg, mut func: Func) -> TokenStream {
     match arg {
-        Arg::Cfg(c) => {
-            let (mut tokens, cfg_not) = cfg_attrs(c);
+        Arg::Cfg(cfg) => {
+            let (mut tokens, cfg_not) = cfg_attrs(cfg);
             tokens.extend(func.to_token_stream());
             tokens.extend(cfg_not);
             func.print_const = false;
             tokens.extend(func.to_token_stream());
             tokens
         }
-        Arg::Feature(f) => {
-            let (mut tokens, cfg_not) = cfg_attrs(f);
+        Arg::Feature(feat) => {
+            let (mut tokens, cfg_not) = cfg_attrs(feat);
             tokens.extend(func.to_token_stream());
             tokens.extend(cfg_not);
             func.print_const = false;
@@ -103,15 +116,13 @@
             tokens
         }
         Arg::Version(req) => {
-            if req.major > 1 || VERSION.as_ref().map_or(true, |v| req.minor > v.minor) {
+            if req.major > 1 || req.minor > VERSION.minor {
                 func.print_const = false;
             }
             func.to_token_stream()
         }
         Arg::Nightly => {
-            if VERSION.as_ref().map_or(true, |v| !v.nightly) {
-                func.print_const = false;
-            }
+            func.print_const = VERSION.nightly;
             func.to_token_stream()
         }
     }
@@ -129,12 +140,12 @@
 }
 
 fn parse_arg(tokens: TokenStream) -> Result<Arg> {
-    let tokens2 = tokens.clone();
     let mut iter = tokens.into_iter();
 
     let next = iter.next();
-    match &next {
-        Some(TokenTree::Ident(i)) => match i.to_string().as_str() {
+    let next_span = tt_span(next.as_ref());
+    match next {
+        Some(TokenTree::Ident(i)) => match &*i.to_string() {
             "nightly" => {
                 parse_as_empty(iter)?;
                 return Ok(Arg::Nightly);
@@ -149,13 +160,19 @@
                 };
             }
             "feature" => {
-                return match iter.next().as_ref() {
-                    Some(TokenTree::Punct(p)) if p.as_char() == '=' => match iter.next().as_ref() {
-                        Some(TokenTree::Literal(l)) if l.to_string().starts_with('"') => {
+                let next = iter.next();
+                return match next.as_ref() {
+                    Some(TokenTree::Punct(p)) if p.as_char() == '=' => match iter.next() {
+                        Some(TokenTree::Literal(l)) => {
+                            let l = LitStr::new(l)?;
                             parse_as_empty(iter)?;
-                            Ok(Arg::Feature(tokens2))
+                            Ok(Arg::Feature(
+                                vec![TokenTree::Ident(i), next.unwrap(), l.token.into()]
+                                    .into_iter()
+                                    .collect(),
+                            ))
                         }
-                        tt => Err(error!(tt_span(tt), "expected `=`")),
+                        tt => Err(error!(tt_span(tt.as_ref()), "expected string literal")),
                     },
                     tt => Err(error!(tt_span(tt), "expected `=`")),
                 };
@@ -163,7 +180,7 @@
             _ => {}
         },
         Some(TokenTree::Literal(l)) => {
-            if let Ok(l) = ast::LitStr::new(l) {
+            if let Ok(l) = LitStr::new(l) {
                 parse_as_empty(iter)?;
                 return match l.value().parse::<VersionReq>() {
                     Ok(req) => Ok(Arg::Version(req)),
@@ -174,15 +191,12 @@
         _ => {}
     }
 
-    Err(error!(
-        tt_span(next.as_ref()),
-        "expected one of: `nightly`, `cfg`, `feature`, string literal"
-    ))
+    Err(error!(next_span, "expected one of: `nightly`, `cfg`, `feature`, string literal"))
 }
 
 struct VersionReq {
-    major: u16,
-    minor: u16,
+    major: u32,
+    minor: u32,
 }
 
 impl FromStr for VersionReq {
@@ -193,29 +207,28 @@
         let major = pieces
             .next()
             .ok_or("need to specify the major version")?
-            .parse::<u16>()
+            .parse::<u32>()
             .map_err(|e| e.to_string())?;
         let minor = pieces
             .next()
             .ok_or("need to specify the minor version")?
-            .parse::<u16>()
+            .parse::<u32>()
             .map_err(|e| e.to_string())?;
         if let Some(s) = pieces.next() {
-            Err(format!("unexpected input: {}", s))
+            Err(format!("unexpected input: .{}", s))
         } else {
             Ok(Self { major, minor })
         }
     }
 }
 
-#[derive(Debug)]
 struct Version {
-    minor: u16,
-    patch: u16,
+    minor: u32,
     nightly: bool,
 }
 
 #[cfg(const_fn_has_build_script)]
-const VERSION: Option<Version> = Some(include!(concat!(env!("OUT_DIR"), "/version.rs")));
+const VERSION: Version = include!(concat!(env!("OUT_DIR"), "/version.rs"));
+// If build script has not run or unable to determine version, it is considered as Rust 1.0.
 #[cfg(not(const_fn_has_build_script))]
-const VERSION: Option<Version> = None;
+const VERSION: Version = Version { minor: 0, nightly: false };
diff --git a/src/to_tokens.rs b/src/to_tokens.rs
index 6cc51fd..771c7f6 100644
--- a/src/to_tokens.rs
+++ b/src/to_tokens.rs
@@ -1,4 +1,4 @@
-use proc_macro::*;
+use proc_macro::{Ident, Literal, TokenStream, TokenTree};
 use std::iter;
 
 pub(crate) trait ToTokens {
diff --git a/src/utils.rs b/src/utils.rs
index 78be0a9..e557ea5 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,4 +1,4 @@
-use proc_macro::*;
+use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
 use std::iter::FromIterator;
 
 use crate::Result;