Snap for 10453563 from 5449335803dfea4e815c47fd8c3f46ce7fa4e216 to mainline-art-release

Change-Id: Ied3d394d7bb0506941ccc0b4cf9a0eed6180f56f
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 6895b5b..6f0afbd 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
 {
   "git": {
-    "sha1": "ecc07fb53f45a093811484d4ee1ac791144defd7"
-  }
-}
+    "sha1": "a85f2db274e1367a945a83ed4fe5a7a53d8f4f0e"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..7507077
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: dtolnay
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9ba68d6..748c2b7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,28 +3,58 @@
 on:
   push:
   pull_request:
+  workflow_dispatch:
   schedule: [cron: "40 1 * * *"]
 
+permissions:
+  contents: read
+
+env:
+  RUSTFLAGS: -Dwarnings
+
 jobs:
+  pre_ci:
+    uses: dtolnay/.github/.github/workflows/pre_ci.yml@master
+
   test:
     name: Rust ${{matrix.rust}}
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        rust: [nightly, beta, stable, 1.31.0]
+        rust: [nightly, beta, stable, 1.56.0]
+    timeout-minutes: 45
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - uses: dtolnay/rust-toolchain@master
         with:
           toolchain: ${{matrix.rust}}
+      - name: Enable type layout randomization
+        run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV
+        if: matrix.rust == 'nightly'
       - run: cargo test
 
+  msrv:
+    name: Rust 1.31.0
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
+    runs-on: ubuntu-latest
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v3
+      - uses: dtolnay/[email protected]
+      - run: cargo check
+
   minimal:
     name: Minimal versions
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
     runs-on: ubuntu-latest
+    timeout-minutes: 45
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - uses: dtolnay/rust-toolchain@nightly
       - run: cargo update -Z minimal-versions
       - run: cargo test
@@ -32,7 +62,32 @@
   clippy:
     name: Clippy
     runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    timeout-minutes: 45
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
       - uses: dtolnay/rust-toolchain@clippy
       - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic
+
+  miri:
+    name: Miri
+    needs: pre_ci
+    if: needs.pre_ci.outputs.continue
+    runs-on: ubuntu-latest
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v3
+      - uses: dtolnay/rust-toolchain@miri
+      - run: cargo miri test
+        env:
+          MIRIFLAGS: -Zmiri-strict-provenance
+
+  outdated:
+    name: Outdated
+    runs-on: ubuntu-latest
+    if: github.event_name != 'pull_request'
+    timeout-minutes: 45
+    steps:
+      - uses: actions/checkout@v3
+      - uses: dtolnay/install@cargo-outdated
+      - run: cargo outdated --workspace --exit-code 1
diff --git a/Android.bp b/Android.bp
index ad91d42..d18f027 100644
--- a/Android.bp
+++ b/Android.bp
@@ -41,7 +41,9 @@
     name: "librustversion",
     crate_name: "rustversion",
     cargo_env_compat: true,
-    cargo_pkg_version: "1.0.5",
+    cargo_pkg_version: "1.0.12",
     srcs: ["src/lib.rs"],
     edition: "2018",
+    product_available: true,
+    vendor_available: true,
 }
diff --git a/Cargo.toml b/Cargo.toml
index c72b1b2..073d230 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,28 +3,35 @@
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
 #
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
 
 [package]
 edition = "2018"
+rust-version = "1.31"
 name = "rustversion"
-version = "1.0.5"
+version = "1.0.12"
 authors = ["David Tolnay <[email protected]>"]
 build = "build/build.rs"
 description = "Conditional compilation according to rustc compiler version"
 documentation = "https://docs.rs/rustversion"
 readme = "README.md"
+categories = [
+    "development-tools::build-utils",
+    "no-std",
+]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/dtolnay/rustversion"
+
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
 
 [lib]
 proc-macro = true
+
 [dev-dependencies.trybuild]
-version = "1.0.35"
+version = "1.0.49"
+features = ["diff"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 75f8ea1..6f5c372 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,20 +1,21 @@
 [package]
 name = "rustversion"
-version = "1.0.5"
+version = "1.0.12"
 authors = ["David Tolnay <[email protected]>"]
+build = "build/build.rs"
+categories = ["development-tools::build-utils", "no-std"]
+description = "Conditional compilation according to rustc compiler version"
+documentation = "https://docs.rs/rustversion"
 edition = "2018"
 license = "MIT OR Apache-2.0"
-description = "Conditional compilation according to rustc compiler version"
 repository = "https://github.com/dtolnay/rustversion"
-documentation = "https://docs.rs/rustversion"
-readme = "README.md"
-build = "build/build.rs"
+rust-version = "1.31"
 
 [lib]
 proc-macro = true
 
 [dev-dependencies]
-trybuild = "1.0.35"
+trybuild = { version = "1.0.49", features = ["diff"] }
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index 16fe87b..1b5ec8b 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -174,28 +174,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 514a21f..4dd08e7 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/rustversion
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "rustversion"
 description: "Conditional compilation according to rustc compiler version"
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/rustversion/rustversion-1.0.5.crate"
+    value: "https://static.crates.io/crates/rustversion/rustversion-1.0.12.crate"
   }
-  version: "1.0.5"
+  version: "1.0.12"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2021
-    month: 5
-    day: 25
+    year: 2023
+    month: 4
+    day: 3
   }
 }
diff --git a/README.md b/README.md
index 5b97272..2c4be07 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@
 
 [<img alt="github" src="https://img.shields.io/badge/github-dtolnay/rustversion-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/rustversion)
 [<img alt="crates.io" src="https://img.shields.io/crates/v/rustversion.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/rustversion)
-[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-rustversion-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/rustversion)
-[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/rustversion/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/rustversion/actions?query=branch%3Amaster)
+[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-rustversion-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/rustversion)
+[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/rustversion/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/rustversion/actions?query=branch%3Amaster)
 
 This crate provides macros for conditional compilation according to rustc
 compiler version, analogous to [`#[cfg(...)]`][cfg] and
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 684ef48..ed43c73 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,10 +2,19 @@
 {
   "imports": [
     {
+      "path": "external/rust/crates/async-stream"
+    },
+    {
       "path": "external/rust/crates/bitflags"
     },
     {
+      "path": "external/rust/crates/crossbeam-epoch"
+    },
+    {
       "path": "external/rust/crates/crossbeam-utils"
+    },
+    {
+      "path": "external/rust/crates/slab"
     }
   ]
 }
diff --git a/build/build.rs b/build/build.rs
index 1531251..0a45f4c 100644
--- a/build/build.rs
+++ b/build/build.rs
@@ -13,41 +13,63 @@
 use std::process::{self, Command};
 
 fn main() {
+    println!("cargo:rerun-if-changed=build/build.rs");
+
     let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
-    let output = match Command::new(&rustc).arg("--version").output() {
-        Ok(output) => output,
-        Err(e) => {
-            let rustc = rustc.to_string_lossy();
-            eprintln!("Error: failed to run `{} --version`: {}", rustc, e);
-            process::exit(1);
+
+    let mut is_clippy_driver = false;
+    let version = loop {
+        let mut command = Command::new(&rustc);
+        if is_clippy_driver {
+            command.arg("--rustc");
         }
+        command.arg("--version");
+
+        let output = match command.output() {
+            Ok(output) => output,
+            Err(e) => {
+                let rustc = rustc.to_string_lossy();
+                eprintln!("Error: failed to run `{} --version`: {}", rustc, e);
+                process::exit(1);
+            }
+        };
+
+        let string = match String::from_utf8(output.stdout) {
+            Ok(string) => string,
+            Err(e) => {
+                let rustc = rustc.to_string_lossy();
+                eprintln!(
+                    "Error: failed to parse output of `{} --version`: {}",
+                    rustc, e,
+                );
+                process::exit(1);
+            }
+        };
+
+        break match rustc::parse(&string) {
+            rustc::ParseResult::Success(version) => version,
+            rustc::ParseResult::OopsClippy if !is_clippy_driver => {
+                is_clippy_driver = true;
+                continue;
+            }
+            rustc::ParseResult::Unrecognized | rustc::ParseResult::OopsClippy => {
+                eprintln!(
+                    "Error: unexpected output from `rustc --version`: {:?}\n\n\
+                    Please file an issue in https://github.com/dtolnay/rustversion",
+                    string
+                );
+                process::exit(1);
+            }
+        };
     };
 
-    let string = match String::from_utf8(output.stdout) {
-        Ok(string) => string,
-        Err(e) => {
-            let rustc = rustc.to_string_lossy();
-            eprintln!(
-                "Error: failed to parse output of `{} --version`: {}",
-                rustc, e,
-            );
-            process::exit(1);
-        }
-    };
+    if version.minor < 38 {
+        // Prior to 1.38, a #[proc_macro] is not allowed to be named `cfg`.
+        println!("cargo:rustc-cfg=cfg_macro_not_allowed");
+    }
 
-    let version = match rustc::parse(&string) {
-        Some(version) => format!("{:#?}\n", version),
-        None => {
-            eprintln!(
-                "Error: unexpected output from `rustc --version`: {:?}\n\n\
-                 Please file an issue in https://github.com/dtolnay/rustversion",
-                string
-            );
-            process::exit(1);
-        }
-    };
-
+    let version = format!("{:#?}\n", version);
     let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR not set");
-    let out_file = Path::new(&out_dir).join("version.rs");
-    fs::write(out_file, version).expect("failed to write version.rs");
+    let out_file = Path::new(&out_dir).join("version.expr");
+    fs::write(out_file, version).expect("failed to write version.expr");
 }
diff --git a/build/rustc.rs b/build/rustc.rs
index dfc6a05..71a830b 100644
--- a/build/rustc.rs
+++ b/build/rustc.rs
@@ -1,6 +1,12 @@
 use self::Channel::*;
 use std::fmt::{self, Debug};
 
+pub enum ParseResult {
+    Success(Version),
+    OopsClippy,
+    Unrecognized,
+}
+
 #[cfg_attr(test, derive(PartialEq))]
 pub struct Version {
     pub minor: u16,
@@ -23,14 +29,20 @@
     pub day: u8,
 }
 
-pub fn parse(string: &str) -> Option<Version> {
-    let last_line = string.lines().last().unwrap_or(&string);
+pub fn parse(string: &str) -> ParseResult {
+    let last_line = string.lines().last().unwrap_or(string);
     let mut words = last_line.trim().split(' ');
 
-    if words.next()? != "rustc" {
-        return None;
+    match words.next() {
+        Some("rustc") => {}
+        Some(word) if word.starts_with("clippy") => return ParseResult::OopsClippy,
+        Some(_) | None => return ParseResult::Unrecognized,
     }
 
+    parse_words(&mut words).map_or(ParseResult::Unrecognized, ParseResult::Success)
+}
+
+fn parse_words(words: &mut dyn Iterator<Item = &str>) -> Option<Version> {
     let mut version_channel = words.next()?.split('-');
     let version = version_channel.next()?;
     let channel = version_channel.next();
diff --git a/patches/version.diff b/patches/version.diff
index cd19b04..3eb570b 100644
--- a/patches/version.diff
+++ b/patches/version.diff
@@ -1,12 +1,12 @@
 diff --git a/src/lib.rs b/src/lib.rs
-index 172eb89..6c4ef6a 100644
+index 2e8f1b9..e826b25 100644
 --- a/src/lib.rs
 +++ b/src/lib.rs
-@@ -180,7 +180,16 @@ use crate::version::Version;
- use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
- use std::iter::FromIterator;
+@@ -182,7 +182,16 @@ use crate::error::Error;
+ use crate::version::Version;
+ use proc_macro::TokenStream;
  
--const RUSTVERSION: Version = include!(concat!(env!("OUT_DIR"), "/version.rs"));
+-const RUSTVERSION: Version = include!(concat!(env!("OUT_DIR"), "/version.expr"));
 +// ANDROID: Soong is providing the version of rustc via an env variable.
 +const ANDROID_RUSTVERSION: Option<&str> = option_env!("ANDROID_RUST_VERSION");
 +fn rust_version() -> Version {
@@ -20,21 +20,34 @@
  
  #[proc_macro_attribute]
  pub fn stable(args: TokenStream, input: TokenStream) -> TokenStream {
-@@ -241,7 +250,7 @@ fn try_cfg(introducer: &str, args: TokenStream, input: TokenStream) -> Result<To
+@@ -239,7 +248,7 @@ pub fn cfg(input: TokenStream) -> TokenStream {
+         let ref mut args = iter::new(input);
+         let expr = expr::parse(args)?;
+         token::parse_end(args)?;
+-        let boolean = expr.eval(RUSTVERSION);
++        let boolean = expr.eval(rust_version());
+         let ident = Ident::new(&boolean.to_string(), Span::call_site());
+         Ok(TokenStream::from(TokenTree::Ident(ident)))
+     })()
+diff --git a/src/expand.rs b/src/expand.rs
+index 813ba85..3d4e314 100644
+--- a/src/expand.rs
++++ b/src/expand.rs
+@@ -23,7 +23,7 @@ fn try_cfg(introducer: &str, args: TokenStream, input: TokenStream) -> Result<To
      let expr = expr::parse(full_args)?;
      token::parse_end(full_args)?;
  
--    if expr.eval(RUSTVERSION) {
-+    if expr.eval(rust_version()) {
+-    if expr.eval(crate::RUSTVERSION) {
++    if expr.eval(crate::rust_version()) {
          Ok(input)
      } else {
          Ok(TokenStream::new())
-@@ -256,7 +265,7 @@ pub fn attr(args: TokenStream, input: TokenStream) -> TokenStream {
+@@ -31,7 +31,7 @@ fn try_cfg(introducer: &str, args: TokenStream, input: TokenStream) -> Result<To
  }
  
- fn try_attr(args: attr::Args, input: TokenStream) -> Result<TokenStream> {
--    if !args.condition.eval(RUSTVERSION) {
-+    if !args.condition.eval(rust_version()) {
+ pub fn try_attr(args: attr::Args, input: TokenStream) -> Result<TokenStream> {
+-    if !args.condition.eval(crate::RUSTVERSION) {
++    if !args.condition.eval(crate::rust_version()) {
          return Ok(input);
      }
  
diff --git a/src/expand.rs b/src/expand.rs
new file mode 100644
index 0000000..3d4e314
--- /dev/null
+++ b/src/expand.rs
@@ -0,0 +1,72 @@
+use crate::attr::{self, Then};
+use crate::error::{Error, Result};
+use crate::{constfn, expr, iter, token};
+use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
+use std::iter::FromIterator;
+
+pub fn cfg(introducer: &str, args: TokenStream, input: TokenStream) -> TokenStream {
+    try_cfg(introducer, args, input).unwrap_or_else(Error::into_compile_error)
+}
+
+fn try_cfg(introducer: &str, args: TokenStream, input: TokenStream) -> Result<TokenStream> {
+    let introducer = Ident::new(introducer, Span::call_site());
+
+    let mut full_args = TokenStream::from(TokenTree::Ident(introducer));
+    if !args.is_empty() {
+        full_args.extend(std::iter::once(TokenTree::Group(Group::new(
+            Delimiter::Parenthesis,
+            args,
+        ))));
+    }
+
+    let ref mut full_args = iter::new(full_args);
+    let expr = expr::parse(full_args)?;
+    token::parse_end(full_args)?;
+
+    if expr.eval(crate::rust_version()) {
+        Ok(input)
+    } else {
+        Ok(TokenStream::new())
+    }
+}
+
+pub fn try_attr(args: attr::Args, input: TokenStream) -> Result<TokenStream> {
+    if !args.condition.eval(crate::rust_version()) {
+        return Ok(input);
+    }
+
+    match args.then {
+        Then::Const(const_token) => constfn::insert_const(input, const_token),
+        Then::Attribute(then) => {
+            // #[cfg_attr(all(), #then)]
+            Ok(TokenStream::from_iter(
+                vec![
+                    TokenTree::Punct(Punct::new('#', Spacing::Alone)),
+                    TokenTree::Group(Group::new(
+                        Delimiter::Bracket,
+                        TokenStream::from_iter(vec![
+                            TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
+                            TokenTree::Group(Group::new(
+                                Delimiter::Parenthesis,
+                                TokenStream::from_iter(
+                                    vec![
+                                        TokenTree::Ident(Ident::new("all", Span::call_site())),
+                                        TokenTree::Group(Group::new(
+                                            Delimiter::Parenthesis,
+                                            TokenStream::new(),
+                                        )),
+                                        TokenTree::Punct(Punct::new(',', Spacing::Alone)),
+                                    ]
+                                    .into_iter()
+                                    .chain(then),
+                                ),
+                            )),
+                        ]),
+                    )),
+                ]
+                .into_iter()
+                .chain(input),
+            ))
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 6c4ef6a..1cc9e97 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,7 +2,7 @@
 //!
 //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
 //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
-//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
+//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
 //!
 //! <br>
 //!
@@ -145,12 +145,16 @@
 //!
 //! <br>
 
+#![doc(html_root_url = "https://docs.rs/rustversion/1.0.12")]
 #![allow(
     clippy::cast_lossless,
     clippy::cast_possible_truncation,
+    clippy::derive_partial_eq_without_eq,
     clippy::doc_markdown,
     clippy::enum_glob_use,
     clippy::from_iter_instead_of_collect,
+    // https://github.com/rust-lang/rust-clippy/issues/8539
+    clippy::iter_with_drain,
     clippy::module_name_repetitions,
     clippy::must_use_candidate,
     clippy::needless_doctest_main,
@@ -167,6 +171,7 @@
 mod constfn;
 mod date;
 mod error;
+mod expand;
 mod expr;
 mod iter;
 mod release;
@@ -174,11 +179,9 @@
 mod token;
 mod version;
 
-use crate::attr::Then;
-use crate::error::{Error, Result};
+use crate::error::Error;
 use crate::version::Version;
-use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
-use std::iter::FromIterator;
+use proc_macro::TokenStream;
 
 // ANDROID: Soong is providing the version of rustc via an env variable.
 const ANDROID_RUSTVERSION: Option<&str> = option_env!("ANDROID_RUST_VERSION");
@@ -193,114 +196,62 @@
 
 #[proc_macro_attribute]
 pub fn stable(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("stable", args, input)
+    expand::cfg("stable", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn beta(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("beta", args, input)
+    expand::cfg("beta", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn nightly(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("nightly", args, input)
+    expand::cfg("nightly", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn since(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("since", args, input)
+    expand::cfg("since", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn before(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("before", args, input)
+    expand::cfg("before", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn not(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("not", args, input)
+    expand::cfg("not", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn any(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("any", args, input)
+    expand::cfg("any", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn all(args: TokenStream, input: TokenStream) -> TokenStream {
-    cfg("all", args, input)
-}
-
-fn cfg(introducer: &str, args: TokenStream, input: TokenStream) -> TokenStream {
-    try_cfg(introducer, args, input).unwrap_or_else(Error::into_compile_error)
-}
-
-fn try_cfg(introducer: &str, args: TokenStream, input: TokenStream) -> Result<TokenStream> {
-    let introducer = Ident::new(introducer, Span::call_site());
-
-    let mut full_args = TokenStream::from(TokenTree::Ident(introducer));
-    if !args.is_empty() {
-        full_args.extend(std::iter::once(TokenTree::Group(Group::new(
-            Delimiter::Parenthesis,
-            args,
-        ))));
-    }
-
-    let ref mut full_args = iter::new(full_args);
-    let expr = expr::parse(full_args)?;
-    token::parse_end(full_args)?;
-
-    if expr.eval(rust_version()) {
-        Ok(input)
-    } else {
-        Ok(TokenStream::new())
-    }
+    expand::cfg("all", args, input)
 }
 
 #[proc_macro_attribute]
 pub fn attr(args: TokenStream, input: TokenStream) -> TokenStream {
     attr::parse(args)
-        .and_then(|args| try_attr(args, input))
+        .and_then(|args| expand::try_attr(args, input))
         .unwrap_or_else(Error::into_compile_error)
 }
 
-fn try_attr(args: attr::Args, input: TokenStream) -> Result<TokenStream> {
-    if !args.condition.eval(rust_version()) {
-        return Ok(input);
-    }
-
-    match args.then {
-        Then::Const(const_token) => constfn::insert_const(input, const_token),
-        Then::Attribute(then) => {
-            // #[cfg_attr(all(), #then)]
-            Ok(TokenStream::from_iter(
-                vec![
-                    TokenTree::Punct(Punct::new('#', Spacing::Alone)),
-                    TokenTree::Group(Group::new(
-                        Delimiter::Bracket,
-                        TokenStream::from_iter(vec![
-                            TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
-                            TokenTree::Group(Group::new(
-                                Delimiter::Parenthesis,
-                                TokenStream::from_iter(
-                                    vec![
-                                        TokenTree::Ident(Ident::new("all", Span::call_site())),
-                                        TokenTree::Group(Group::new(
-                                            Delimiter::Parenthesis,
-                                            TokenStream::new(),
-                                        )),
-                                        TokenTree::Punct(Punct::new(',', Spacing::Alone)),
-                                    ]
-                                    .into_iter()
-                                    .chain(then),
-                                ),
-                            )),
-                        ]),
-                    )),
-                ]
-                .into_iter()
-                .chain(input),
-            ))
-        }
-    }
+#[cfg(not(cfg_macro_not_allowed))]
+#[proc_macro]
+pub fn cfg(input: TokenStream) -> TokenStream {
+    use proc_macro::{Ident, Span, TokenTree};
+    (|| {
+        let ref mut args = iter::new(input);
+        let expr = expr::parse(args)?;
+        token::parse_end(args)?;
+        let boolean = expr.eval(rust_version());
+        let ident = Ident::new(&boolean.to_string(), Span::call_site());
+        Ok(TokenStream::from(TokenTree::Ident(ident)))
+    })()
+    .unwrap_or_else(Error::into_compile_error)
 }
diff --git a/tests/compiletest.rs b/tests/compiletest.rs
index f9aea23..7974a62 100644
--- a/tests/compiletest.rs
+++ b/tests/compiletest.rs
@@ -1,4 +1,5 @@
 #[rustversion::attr(not(nightly), ignore)]
+#[cfg_attr(miri, ignore)]
 #[test]
 fn ui() {
     let t = trybuild::TestCases::new();
diff --git a/tests/test_const.rs b/tests/test_const.rs
index 1765c95..3c4f7d5 100644
--- a/tests/test_const.rs
+++ b/tests/test_const.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::semicolon_if_nothing_returned)] // https://github.com/rust-lang/rust-clippy/issues/7324
+
 #[rustversion::attr(all(), const)]
 fn _basic() {}
 const _BASIC: () = _basic();
diff --git a/tests/test_parse.rs b/tests/test_parse.rs
index cb39b31..95064ee 100644
--- a/tests/test_parse.rs
+++ b/tests/test_parse.rs
@@ -1,4 +1,8 @@
-#![allow(clippy::enum_glob_use, clippy::must_use_candidate)]
+#![allow(
+    clippy::derive_partial_eq_without_eq,
+    clippy::enum_glob_use,
+    clippy::must_use_candidate
+)]
 
 include!("../build/rustc.rs");
 
@@ -89,6 +93,11 @@
     ];
 
     for (string, expected) in cases {
-        assert_eq!(parse(string).unwrap(), *expected);
+        match parse(string) {
+            ParseResult::Success(version) => assert_eq!(version, *expected),
+            ParseResult::OopsClippy | ParseResult::Unrecognized => {
+                panic!("unrecognized: {:?}", string);
+            }
+        }
     }
 }
diff --git a/tests/ui/bad-bound.stderr b/tests/ui/bad-bound.stderr
index 2c56acb..77956c6 100644
--- a/tests/ui/bad-bound.stderr
+++ b/tests/ui/bad-bound.stderr
@@ -1,11 +1,11 @@
 error: expected rustc release number like 1.31, or nightly date like 2020-02-25
- --> $DIR/bad-bound.rs:1:22
+ --> tests/ui/bad-bound.rs:1:22
   |
 1 | #[rustversion::since(stable)]
   |                      ^^^^^^
 
 error: expected rustc release number like 1.31, or nightly date like 2020-02-25
- --> $DIR/bad-bound.rs:4:26
+ --> tests/ui/bad-bound.rs:4:26
   |
 4 | #[rustversion::any(since(stable))]
   |                          ^^^^^^
diff --git a/tests/ui/bad-date.stderr b/tests/ui/bad-date.stderr
index c523ccc..378b00e 100644
--- a/tests/ui/bad-date.stderr
+++ b/tests/ui/bad-date.stderr
@@ -1,11 +1,11 @@
 error: expected nightly date, like 2020-02-25
- --> $DIR/bad-date.rs:1:24
+ --> tests/ui/bad-date.rs:1:24
   |
 1 | #[rustversion::nightly(stable)]
   |                        ^^^^^^
 
 error: expected nightly date, like 2020-02-25
- --> $DIR/bad-date.rs:4:28
+ --> tests/ui/bad-date.rs:4:28
   |
 4 | #[rustversion::any(nightly(stable))]
   |                            ^^^^^^
diff --git a/tests/ui/bad-not.stderr b/tests/ui/bad-not.stderr
index bfe239b..2b0c699 100644
--- a/tests/ui/bad-not.stderr
+++ b/tests/ui/bad-not.stderr
@@ -1,11 +1,11 @@
 error: expected `(` after `not`
- --> $DIR/bad-not.rs:1:20
+ --> tests/ui/bad-not.rs:1:20
   |
 1 | #[rustversion::any(not)]
   |                    ^^^
 
 error: expected `(`
- --> $DIR/bad-not.rs:4:23
+ --> tests/ui/bad-not.rs:4:23
   |
 4 | #[rustversion::any(not, not)]
   |                       ^
diff --git a/tests/ui/bad-version.stderr b/tests/ui/bad-version.stderr
index 5f7a01f..bf3f144 100644
--- a/tests/ui/bad-version.stderr
+++ b/tests/ui/bad-version.stderr
@@ -1,11 +1,11 @@
 error: expected rustc release number, like 1.31
- --> $DIR/bad-version.rs:1:23
+ --> tests/ui/bad-version.rs:1:23
   |
 1 | #[rustversion::stable(nightly)]
   |                       ^^^^^^^
 
 error: expected rustc release number, like 1.31
- --> $DIR/bad-version.rs:4:27
+ --> tests/ui/bad-version.rs:4:27
   |
 4 | #[rustversion::any(stable(nightly))]
   |                           ^^^^^^^
diff --git a/tests/ui/const-not-fn.stderr b/tests/ui/const-not-fn.stderr
index 7371ff2..d3cb4aa 100644
--- a/tests/ui/const-not-fn.stderr
+++ b/tests/ui/const-not-fn.stderr
@@ -1,5 +1,5 @@
 error: only allowed on a fn item
- --> $DIR/const-not-fn.rs:1:28
+ --> tests/ui/const-not-fn.rs:1:28
   |
 1 | #[rustversion::attr(all(), const)]
   |                            ^^^^^