summaryrefslogtreecommitdiffstats
path: root/third_party/rust/extend
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/rust/extend
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/extend')
-rw-r--r--third_party/rust/extend/.cargo-checksum.json1
-rw-r--r--third_party/rust/extend/CHANGELOG.md79
-rw-r--r--third_party/rust/extend/Cargo.toml48
-rw-r--r--third_party/rust/extend/LICENSE19
-rw-r--r--third_party/rust/extend/README.md32
-rw-r--r--third_party/rust/extend/deny.toml47
-rw-r--r--third_party/rust/extend/rustfmt.toml1
-rw-r--r--third_party/rust/extend/src/lib.rs620
-rw-r--r--third_party/rust/extend/tests/compile_fail/double_vis.rs14
-rw-r--r--third_party/rust/extend/tests/compile_fail/double_vis.stderr5
-rw-r--r--third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.rs14
-rw-r--r--third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.stderr10
-rw-r--r--third_party/rust/extend/tests/compile_pass/associated_constants.rs10
-rw-r--r--third_party/rust/extend/tests/compile_pass/async_trait.rs25
-rw-r--r--third_party/rust/extend/tests/compile_pass/changing_extension_trait_name.rs10
-rw-r--r--third_party/rust/extend/tests/compile_pass/complex_trait_name.rs16
-rw-r--r--third_party/rust/extend/tests/compile_pass/double_ext_on_same_type.rs17
-rw-r--r--third_party/rust/extend/tests/compile_pass/extension_on_complex_types.rs58
-rw-r--r--third_party/rust/extend/tests/compile_pass/generics.rs15
-rw-r--r--third_party/rust/extend/tests/compile_pass/hello_world.rs20
-rw-r--r--third_party/rust/extend/tests/compile_pass/issue_2.rs33
-rw-r--r--third_party/rust/extend/tests/compile_pass/more_than_one_extension.rs13
-rw-r--r--third_party/rust/extend/tests/compile_pass/multiple_config.rs13
-rw-r--r--third_party/rust/extend/tests/compile_pass/multiple_generic_params.rs11
-rw-r--r--third_party/rust/extend/tests/compile_pass/pub_impl.rs15
-rw-r--r--third_party/rust/extend/tests/compile_pass/ref_and_ref_mut.rs13
-rw-r--r--third_party/rust/extend/tests/compile_pass/sized.rs36
-rw-r--r--third_party/rust/extend/tests/compile_pass/super_trait.rs16
-rw-r--r--third_party/rust/extend/tests/compile_pass/visibility_config.rs15
29 files changed, 1226 insertions, 0 deletions
diff --git a/third_party/rust/extend/.cargo-checksum.json b/third_party/rust/extend/.cargo-checksum.json
new file mode 100644
index 0000000000..3ef7391275
--- /dev/null
+++ b/third_party/rust/extend/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"910cd2057d356b18cd4f21791bc23956734a83b0f58701ec0d0d03405f5c21df","Cargo.toml":"928b8b97607b3589b85a6d4d00fb1cf64b78bee6354c303d8f49fbfa2ca5bdae","LICENSE":"db0efd0bb80126f32214f478434536d07d86a30658fb209d8b543b7261492a2b","README.md":"0872b2b15ec22732a09b121a31cdd8f8cfc80566f671566e4a7fb6b52a742a2e","deny.toml":"60448e9313f1883a7adf30713fcca789c454e77228105926024ce3abe9293100","rustfmt.toml":"9bbb759b3914c49c8060bcdeeb7786f4aec083d30bcfe236bbd48d8388d06103","src/lib.rs":"85bd23c064c78dcd141cb2fecca6fff2ee7e1dfb4cf19b74ab97ef30d51f6f03","tests/compile_fail/double_vis.rs":"524e8bc996f5a45de3d15f0c741d4f3414004e9c636c52de0f011785cc535109","tests/compile_fail/double_vis.stderr":"3748e67e6f41bd5624946ad7cdc4e2b30a9d0b5e76bc27c19ecc9d62da733978","tests/compile_fail/supertraits_are_actually_included.rs":"c03980316c4e71ceb7e80ab7011f00b6f1f8f89906670e9823267a4118f72853","tests/compile_fail/supertraits_are_actually_included.stderr":"835e79bb1bceb48e9db5f0a410c57af51c856e17917ef72f65e3ae2af79d97ec","tests/compile_pass/associated_constants.rs":"f3e56405650ddd31c33e932846553e88fe7181585e0f09a7414d50d521b0f103","tests/compile_pass/async_trait.rs":"9138801d67dbe20ed87fdb3f48bb968f0bfa11fe2d27414d5ea5ea7a6e44bb35","tests/compile_pass/changing_extension_trait_name.rs":"046e7a66151ce05c3515b4bea36be72f83b98f3bdb06fa6f065ad6fc373e19f2","tests/compile_pass/complex_trait_name.rs":"5a8c3588c26df07973739fbb85619d0de480cc8c992611dfc2dbcb9ab3c6e2b6","tests/compile_pass/double_ext_on_same_type.rs":"0e4d16fe9059f0325e4ad1337d53738876633b0533f9a2d4e9714ddada96eb4d","tests/compile_pass/extension_on_complex_types.rs":"88bdf979e1f399d7f9824165730b33884bc9c46347d246f8cc12e0e2039642a1","tests/compile_pass/generics.rs":"367f089e8010d1c98ccc975b03acb6cf03810793e1198105f75c006eafd52bb8","tests/compile_pass/hello_world.rs":"1961cfa634143974f0d64b26f817ac3148375faa5cf5864d63f0127d3af8099e","tests/compile_pass/issue_2.rs":"a14d5e2179b74ff71c5357e8a4f1a9ac228a711196edd40c12719565c8dfaa21","tests/compile_pass/more_than_one_extension.rs":"1d7486e4e9e4095d7e7f42aac878d836f021c49fbe26c31cb13fe6a0415a8558","tests/compile_pass/multiple_config.rs":"dc3b06e4fafbb7236b5bc49f9a5e9693241c07118da8886dd0752b895ff27425","tests/compile_pass/multiple_generic_params.rs":"2ac052ea7b818b6d45ee8070cf6ec18953419473f711375cafbe7283244b5743","tests/compile_pass/pub_impl.rs":"8dfcba21fbbc45efcf85b66fca2d187022569e72d4b10b57047eddda67a1725b","tests/compile_pass/ref_and_ref_mut.rs":"d1086a23809cbd8f87487677073238c3657a8a98fafc63a688eb9d622bf2eb11","tests/compile_pass/sized.rs":"baceaaabcf368b3c72e6d27fea72aadde11d97d9d153c35df5e2d89fb3da4e3e","tests/compile_pass/super_trait.rs":"07448e1fe2b9018125ccaabf7db1e2a244788519bb42117f8e437e48358eb2f0","tests/compile_pass/visibility_config.rs":"64846014a63327661fb250cdb726cce532ed82b1cea5cb83308526bcba22b4be"},"package":"5c5216e387a76eebaaf11f6d871ec8a4aae0b25f05456ee21f228e024b1b3610"} \ No newline at end of file
diff --git a/third_party/rust/extend/CHANGELOG.md b/third_party/rust/extend/CHANGELOG.md
new file mode 100644
index 0000000000..f17149a2eb
--- /dev/null
+++ b/third_party/rust/extend/CHANGELOG.md
@@ -0,0 +1,79 @@
+# Change Log
+
+All user visible changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/), as described
+for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md)
+
+## Unreleased
+
+None.
+
+### Breaking changes
+
+None.
+
+## 1.1.2 - 2021-09-02
+
+- Fix using `pub impl` with `#[async_trait]`.
+
+## 1.1.1 - 2021-06-12
+
+- Fix name collision for extensions on `&T` and `&mut T`. The generated traits
+ now get different names.
+
+## 1.1.0 - 2021-06-12
+
+- Support setting visibility of the generated trait directly on the `impl`
+ block. For example: `pub impl i32 { ... }`.
+- Add `#[ext_sized]` for adding `Sized` supertrait.
+
+## 1.0.1 - 2021-02-14
+
+- Update maintenance status.
+
+## 1.0.0 - 2021-01-30
+
+- Support extensions on bare functions types (things like `fn(i32) -> bool`).
+- Support extensions on trait objects (things like `dyn Send + Sync`).
+
+## 0.3.0 - 2020-08-31
+
+- Add async-trait compatibility.
+
+### Breaking changes
+
+- Other attributes put on the `impl` would previously only be included on the generated trait. They're now included on both the trait and the implementation.
+
+## 0.2.1 - 2020-08-29
+
+- Fix documentation link in Cargo.toml.
+- Use more correct repository URL in Cargo.toml.
+
+## 0.2.0 - 2020-08-29
+
+- Handle unnamed extensions on the same generic type with different type parameters. For example `Option<i32>` and `Option<String>`. Previously we would generate the same name of both hidden traits which wouldn't compile.
+- Support associated constants in extension impls.
+
+### Breaking changes
+
+- Generated traits are no longer sealed and the `sealed` argument previously supported by `#[ext]` has been removed. Making the traits sealed lead to lots of complexity that we didn't think brought much value.
+
+## 0.1.1 - 2020-02-22
+
+- Add support for specifying supertraits of the generated trait [#4](https://github.com/davidpdrsn/extend/pull/4).
+
+## 0.1.0
+
+- Support adding extensions to the ["never type"](https://doc.rust-lang.org/std/primitive.never.html).
+
+### Breaking changes
+
+- Simplify names of traits generates for complex types.
+
+## 0.0.2
+
+- Move "trybuild" to dev-dependency.
+
+## 0.0.1
+
+Initial release.
diff --git a/third_party/rust/extend/Cargo.toml b/third_party/rust/extend/Cargo.toml
new file mode 100644
index 0000000000..dcc2ebae15
--- /dev/null
+++ b/third_party/rust/extend/Cargo.toml
@@ -0,0 +1,48 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# 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
+#
+# 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)
+
+[package]
+edition = "2018"
+name = "extend"
+version = "1.1.2"
+authors = ["David Pedersen <david.pdrsn@gmail.com>"]
+description = "Create extensions for types you don't own with extension traits but without the boilerplate."
+homepage = "https://github.com/davidpdrsn/extend"
+documentation = "https://docs.rs/extend"
+readme = "README.md"
+keywords = ["extension", "trait"]
+categories = ["rust-patterns"]
+license = "MIT"
+repository = "https://github.com/davidpdrsn/extend.git"
+
+[lib]
+path = "src/lib.rs"
+proc-macro = true
+[dependencies.proc-macro-error]
+version = "1"
+
+[dependencies.proc-macro2]
+version = "1"
+
+[dependencies.quote]
+version = "1"
+
+[dependencies.syn]
+version = "1"
+features = ["full", "extra-traits", "visit"]
+[dev-dependencies.async-trait]
+version = "0.1.40"
+
+[dev-dependencies.trybuild]
+version = "1.0.17"
+[badges.maintenance]
+status = "passively-maintained"
diff --git a/third_party/rust/extend/LICENSE b/third_party/rust/extend/LICENSE
new file mode 100644
index 0000000000..0cf5bc7301
--- /dev/null
+++ b/third_party/rust/extend/LICENSE
@@ -0,0 +1,19 @@
+MIT License Copyright (c) 2020 David Pedersen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/extend/README.md b/third_party/rust/extend/README.md
new file mode 100644
index 0000000000..ffa19e951a
--- /dev/null
+++ b/third_party/rust/extend/README.md
@@ -0,0 +1,32 @@
+# extend
+
+[![Crates.io](https://img.shields.io/crates/v/extend.svg)](https://crates.io/crates/extend)
+[![Docs](https://docs.rs/extend/badge.svg)](https://docs.rs/extend)
+[![dependency status](https://deps.rs/repo/github/davidpdrsn/extend/status.svg)](https://deps.rs/repo/github/davidpdrsn/extend)
+[![Build status](https://github.com/davidpdrsn/extend/workflows/CI/badge.svg)](https://github.com/davidpdrsn/extend/actions)
+![maintenance-status](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen.svg)
+
+Create extensions for types you don't own with [extension traits] but without the boilerplate.
+
+Example:
+
+```rust
+use extend::ext;
+
+#[ext]
+impl<T: Ord> Vec<T> {
+ fn sorted(mut self) -> Self {
+ self.sort();
+ self
+ }
+}
+
+fn main() {
+ assert_eq!(
+ vec![1, 2, 3],
+ vec![2, 3, 1].sorted(),
+ );
+}
+```
+
+[extension traits]: https://dev.to/matsimitsu/extending-existing-functionality-in-rust-with-traits-in-rust-3622
diff --git a/third_party/rust/extend/deny.toml b/third_party/rust/extend/deny.toml
new file mode 100644
index 0000000000..57816c7a0d
--- /dev/null
+++ b/third_party/rust/extend/deny.toml
@@ -0,0 +1,47 @@
+targets = []
+
+[advisories]
+db-path = "~/.cargo/advisory-db"
+db-urls = ["https://github.com/rustsec/advisory-db"]
+vulnerability = "deny"
+unmaintained = "warn"
+yanked = "warn"
+notice = "warn"
+ignore = []
+
+[licenses]
+unlicensed = "deny"
+allow = [
+ "MIT",
+ "Apache-2.0",
+]
+deny = []
+copyleft = "warn"
+allow-osi-fsf-free = "neither"
+default = "deny"
+confidence-threshold = 0.8
+exceptions = []
+
+[licenses.private]
+ignore = false
+registries = []
+
+[bans]
+multiple-versions = "deny"
+wildcards = "allow"
+highlight = "all"
+allow = []
+deny = []
+skip = []
+skip-tree = []
+
+[sources]
+unknown-registry = "warn"
+unknown-git = "warn"
+allow-registry = ["https://github.com/rust-lang/crates.io-index"]
+allow-git = []
+
+[sources.allow-org]
+github = []
+gitlab = []
+bitbucket = []
diff --git a/third_party/rust/extend/rustfmt.toml b/third_party/rust/extend/rustfmt.toml
new file mode 100644
index 0000000000..7d2cf549dc
--- /dev/null
+++ b/third_party/rust/extend/rustfmt.toml
@@ -0,0 +1 @@
+merge_imports = true
diff --git a/third_party/rust/extend/src/lib.rs b/third_party/rust/extend/src/lib.rs
new file mode 100644
index 0000000000..9b8188a4a0
--- /dev/null
+++ b/third_party/rust/extend/src/lib.rs
@@ -0,0 +1,620 @@
+//! Create extensions for types you don't own with [extension traits] but without the boilerplate.
+//!
+//! Example:
+//!
+//! ```rust
+//! use extend::ext;
+//!
+//! #[ext]
+//! impl<T: Ord> Vec<T> {
+//! fn sorted(mut self) -> Self {
+//! self.sort();
+//! self
+//! }
+//! }
+//!
+//! fn main() {
+//! assert_eq!(
+//! vec![1, 2, 3],
+//! vec![2, 3, 1].sorted(),
+//! );
+//! }
+//! ```
+//!
+//! # How does it work?
+//!
+//! Under the hood it generates a trait with methods in your `impl` and implements those for the
+//! type you specify. The code shown above expands roughly to:
+//!
+//! ```rust
+//! trait VecExt<T: Ord> {
+//! fn sorted(self) -> Self;
+//! }
+//!
+//! impl<T: Ord> VecExt<T> for Vec<T> {
+//! fn sorted(mut self) -> Self {
+//! self.sort();
+//! self
+//! }
+//! }
+//! ```
+//!
+//! # Supported items
+//!
+//! Extensions can contain methods or associated constants:
+//!
+//! ```rust
+//! use extend::ext;
+//!
+//! #[ext]
+//! impl String {
+//! const CONSTANT: &'static str = "FOO";
+//!
+//! fn method() {
+//! // ...
+//! # todo!()
+//! }
+//! }
+//! ```
+//!
+//! # Configuration
+//!
+//! You can configure:
+//!
+//! - The visibility of the trait. Use `pub impl ...` to generate `pub trait ...`. The default
+//! visibility is private.
+//! - The name of the generated extension trait. Example: `#[ext(name = MyExt)]`. By default we
+//! generate a name based on what you extend.
+//! - Which supertraits the generated extension trait should have. Default is no supertraits.
+//! Example: `#[ext(supertraits = Default + Clone)]`.
+//!
+//! More examples:
+//!
+//! ```rust
+//! use extend::ext;
+//!
+//! #[ext(name = SortedVecExt)]
+//! impl<T: Ord> Vec<T> {
+//! fn sorted(mut self) -> Self {
+//! self.sort();
+//! self
+//! }
+//! }
+//!
+//! #[ext]
+//! pub(crate) impl i32 {
+//! fn double(self) -> i32 {
+//! self * 2
+//! }
+//! }
+//!
+//! #[ext(name = ResultSafeUnwrapExt)]
+//! pub impl<T> Result<T, std::convert::Infallible> {
+//! fn safe_unwrap(self) -> T {
+//! match self {
+//! Ok(t) => t,
+//! Err(_) => unreachable!(),
+//! }
+//! }
+//! }
+//!
+//! #[ext(supertraits = Default + Clone)]
+//! impl String {
+//! fn my_length(self) -> usize {
+//! self.len()
+//! }
+//! }
+//! ```
+//!
+//! For backwards compatibility you can also declare the visibility as the first argument to `#[ext]`:
+//!
+//! ```
+//! use extend::ext;
+//!
+//! #[ext(pub)]
+//! impl i32 {
+//! fn double(self) -> i32 {
+//! self * 2
+//! }
+//! }
+//! ```
+//!
+//! # async-trait compatibility
+//!
+//! Async extensions are supported via [async-trait](https://crates.io/crates/async-trait).
+//!
+//! Be aware that you need to add `#[async_trait]` _below_ `#[ext]`. Otherwise the `ext` macro
+//! cannot see the `#[async_trait]` attribute and pass it along in the generated code.
+//!
+//! Example:
+//!
+//! ```
+//! use extend::ext;
+//! use async_trait::async_trait;
+//!
+//! #[ext]
+//! #[async_trait]
+//! impl String {
+//! async fn read_file() -> String {
+//! // ...
+//! # todo!()
+//! }
+//! }
+//! ```
+//!
+//! # Other attributes
+//!
+//! Other attributes provided _below_ `#[ext]` will be passed along to both the generated trait and
+//! the implementation. See [async-trait compatibility](#async-trait-compatibility) above for an
+//! example.
+//!
+//! [extension traits]: https://dev.to/matsimitsu/extending-existing-functionality-in-rust-with-traits-in-rust-3622
+
+#![doc(html_root_url = "https://docs.rs/extend/1.1.2")]
+#![allow(clippy::let_and_return)]
+#![deny(
+ unused_variables,
+ mutable_borrow_reservation_conflict,
+ dead_code,
+ unused_must_use,
+ unused_imports
+)]
+
+use proc_macro2::TokenStream;
+use proc_macro_error::*;
+use quote::{format_ident, quote, ToTokens};
+use syn::{
+ parse::{self, Parse, ParseStream},
+ parse_macro_input, parse_quote,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ token::{Add, Semi},
+ Ident, ImplItem, ItemImpl, Token, TraitItemConst, TraitItemMethod, Type, TypeArray, TypeBareFn,
+ TypeGroup, TypeNever, TypeParamBound, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice,
+ TypeTraitObject, TypeTuple, Visibility,
+};
+
+#[derive(Debug)]
+struct Input {
+ item_impl: ItemImpl,
+ vis: Option<Visibility>,
+}
+
+impl Parse for Input {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let mut attributes = Vec::new();
+ if input.peek(syn::Token![#]) {
+ attributes.extend(syn::Attribute::parse_outer(input)?);
+ }
+
+ let vis = input
+ .parse::<Visibility>()
+ .ok()
+ .filter(|vis| vis != &Visibility::Inherited);
+
+ let mut item_impl = input.parse::<ItemImpl>()?;
+ item_impl.attrs.extend(attributes);
+
+ Ok(Self { item_impl, vis })
+ }
+}
+
+/// See crate docs for more info.
+#[proc_macro_attribute]
+#[proc_macro_error]
+#[allow(clippy::unneeded_field_pattern)]
+pub fn ext(
+ attr: proc_macro::TokenStream,
+ item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ let item = parse_macro_input!(item as Input);
+ let config = parse_macro_input!(attr as Config);
+ go(item, config)
+}
+
+/// Like [`ext`](macro@crate::ext) but always add `Sized` as a supertrait.
+///
+/// This is provided as a convenience for generating extension traits that require `Self: Sized`
+/// such as:
+///
+/// ```
+/// use extend::ext_sized;
+///
+/// #[ext_sized]
+/// impl i32 {
+/// fn requires_sized(self) -> Option<Self> {
+/// Some(self)
+/// }
+/// }
+/// ```
+#[proc_macro_attribute]
+#[proc_macro_error]
+#[allow(clippy::unneeded_field_pattern)]
+pub fn ext_sized(
+ attr: proc_macro::TokenStream,
+ item: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ let item = parse_macro_input!(item as Input);
+ let mut config: Config = parse_macro_input!(attr as Config);
+
+ config.supertraits = if let Some(supertraits) = config.supertraits.take() {
+ Some(parse_quote!(#supertraits + Sized))
+ } else {
+ Some(parse_quote!(Sized))
+ };
+
+ go(item, config)
+}
+
+fn go(item: Input, mut config: Config) -> proc_macro::TokenStream {
+ if let Some(vis) = item.vis {
+ if config.visibility != Visibility::Inherited {
+ abort!(
+ config.visibility.span(),
+ "Cannot set visibility on `#[ext]` and `impl` block"
+ );
+ }
+
+ config.visibility = vis;
+ }
+
+ let ItemImpl {
+ attrs,
+ unsafety,
+ generics,
+ trait_,
+ self_ty,
+ items,
+ // What is defaultness?
+ defaultness: _,
+ impl_token: _,
+ brace_token: _,
+ } = item.item_impl;
+
+ if let Some((_, path, _)) = trait_ {
+ abort!(path.span(), "Trait impls cannot be used for #[ext]");
+ }
+
+ let self_ty = parse_self_ty(&self_ty);
+
+ let ext_trait_name = config
+ .ext_trait_name
+ .unwrap_or_else(|| ext_trait_name(&self_ty));
+
+ let MethodsAndConsts {
+ trait_methods,
+ trait_consts,
+ } = extract_allowed_items(&items);
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let visibility = &config.visibility;
+
+ let mut all_supertraits = Vec::<TypeParamBound>::new();
+
+ if let Some(supertraits_from_config) = config.supertraits {
+ all_supertraits.extend(supertraits_from_config);
+ }
+
+ let supertraits_quoted = if all_supertraits.is_empty() {
+ quote! {}
+ } else {
+ let supertraits_quoted = punctuated_from_iter::<_, _, Add>(all_supertraits);
+ quote! { : #supertraits_quoted }
+ };
+
+ let code = (quote! {
+ #[allow(non_camel_case_types)]
+ #(#attrs)*
+ #visibility
+ #unsafety
+ trait #ext_trait_name #impl_generics #supertraits_quoted #where_clause {
+ #(
+ #trait_consts
+ )*
+
+ #(
+ #[allow(
+ patterns_in_fns_without_body,
+ clippy::inline_fn_without_body,
+ unused_attributes
+ )]
+ #trait_methods
+ )*
+ }
+
+ #(#attrs)*
+ impl #impl_generics #ext_trait_name #ty_generics for #self_ty #where_clause {
+ #(#items)*
+ }
+ })
+ .into();
+
+ code
+}
+
+#[derive(Debug, Clone)]
+enum ExtType<'a> {
+ Array(&'a TypeArray),
+ Group(&'a TypeGroup),
+ Never(&'a TypeNever),
+ Paren(&'a TypeParen),
+ Path(&'a TypePath),
+ Ptr(&'a TypePtr),
+ Reference(&'a TypeReference),
+ Slice(&'a TypeSlice),
+ Tuple(&'a TypeTuple),
+ BareFn(&'a TypeBareFn),
+ TraitObject(&'a TypeTraitObject),
+}
+
+#[allow(clippy::wildcard_in_or_patterns)]
+fn parse_self_ty(self_ty: &Type) -> ExtType {
+ match self_ty {
+ Type::Array(inner) => ExtType::Array(inner),
+ Type::Group(inner) => ExtType::Group(inner),
+ Type::Never(inner) => ExtType::Never(inner),
+ Type::Paren(inner) => ExtType::Paren(inner),
+ Type::Path(inner) => ExtType::Path(inner),
+ Type::Ptr(inner) => ExtType::Ptr(inner),
+ Type::Reference(inner) => ExtType::Reference(inner),
+ Type::Slice(inner) => ExtType::Slice(inner),
+ Type::Tuple(inner) => ExtType::Tuple(inner),
+ Type::BareFn(inner) => ExtType::BareFn(inner),
+ Type::TraitObject(inner) => ExtType::TraitObject(inner),
+
+ Type::ImplTrait(_) | Type::Infer(_) | Type::Macro(_) | Type::Verbatim(_) | _ => abort!(
+ self_ty.span(),
+ "#[ext] is not supported for this kind of type"
+ ),
+ }
+}
+
+impl<'a> From<&'a Type> for ExtType<'a> {
+ fn from(inner: &'a Type) -> ExtType<'a> {
+ parse_self_ty(inner)
+ }
+}
+
+impl<'a> ToTokens for ExtType<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ match self {
+ ExtType::Array(inner) => inner.to_tokens(tokens),
+ ExtType::Group(inner) => inner.to_tokens(tokens),
+ ExtType::Never(inner) => inner.to_tokens(tokens),
+ ExtType::Paren(inner) => inner.to_tokens(tokens),
+ ExtType::Path(inner) => inner.to_tokens(tokens),
+ ExtType::Ptr(inner) => inner.to_tokens(tokens),
+ ExtType::Reference(inner) => inner.to_tokens(tokens),
+ ExtType::Slice(inner) => inner.to_tokens(tokens),
+ ExtType::Tuple(inner) => inner.to_tokens(tokens),
+ ExtType::BareFn(inner) => inner.to_tokens(tokens),
+ ExtType::TraitObject(inner) => inner.to_tokens(tokens),
+ }
+ }
+}
+
+fn ext_trait_name(self_ty: &ExtType) -> Ident {
+ fn inner_self_ty(self_ty: &ExtType) -> Ident {
+ match self_ty {
+ ExtType::Path(inner) => find_and_combine_idents(inner),
+ ExtType::Reference(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).into());
+ if inner.mutability.is_some() {
+ format_ident!("RefMut{}", name)
+ } else {
+ format_ident!("Ref{}", name)
+ }
+ }
+ ExtType::Array(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).into());
+ format_ident!("ListOf{}", name)
+ }
+ ExtType::Group(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).into());
+ format_ident!("Group{}", name)
+ }
+ ExtType::Paren(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).into());
+ format_ident!("Paren{}", name)
+ }
+ ExtType::Ptr(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).into());
+ format_ident!("PointerTo{}", name)
+ }
+ ExtType::Slice(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).into());
+ format_ident!("SliceOf{}", name)
+ }
+ ExtType::Tuple(inner) => {
+ let mut name = format_ident!("TupleOf");
+ for elem in &inner.elems {
+ name = format_ident!("{}{}", name, inner_self_ty(&elem.into()));
+ }
+ name
+ }
+ ExtType::Never(_) => format_ident!("Never"),
+ ExtType::BareFn(inner) => {
+ let mut name = format_ident!("BareFn");
+ for input in inner.inputs.iter() {
+ name = format_ident!("{}{}", name, inner_self_ty(&(&input.ty).into()));
+ }
+ match &inner.output {
+ syn::ReturnType::Default => {
+ name = format_ident!("{}Unit", name);
+ }
+ syn::ReturnType::Type(_, ty) => {
+ name = format_ident!("{}{}", name, inner_self_ty(&(&**ty).into()));
+ }
+ }
+ name
+ }
+ ExtType::TraitObject(inner) => {
+ let mut name = format_ident!("TraitObject");
+ for bound in inner.bounds.iter() {
+ match bound {
+ TypeParamBound::Trait(bound) => {
+ for segment in bound.path.segments.iter() {
+ name = format_ident!("{}{}", name, segment.ident);
+ }
+ }
+ TypeParamBound::Lifetime(lifetime) => {
+ name = format_ident!("{}{}", name, lifetime.ident);
+ }
+ }
+ }
+ name
+ }
+ }
+ }
+
+ format_ident!("{}Ext", inner_self_ty(self_ty))
+}
+
+fn find_and_combine_idents(type_path: &TypePath) -> Ident {
+ use syn::visit::{self, Visit};
+
+ struct IdentVisitor<'a>(Vec<&'a Ident>);
+
+ impl<'a> Visit<'a> for IdentVisitor<'a> {
+ fn visit_ident(&mut self, i: &'a Ident) {
+ self.0.push(i);
+ }
+ }
+
+ let mut visitor = IdentVisitor(Vec::new());
+ visit::visit_type_path(&mut visitor, type_path);
+ let idents = visitor.0;
+
+ if idents.is_empty() {
+ abort!(type_path.span(), "Empty type path")
+ } else {
+ let start = &idents[0].span();
+ let combined_span = idents
+ .iter()
+ .map(|i| i.span())
+ .fold(*start, |a, b| a.join(b).unwrap_or(a));
+
+ let combined_name = idents.iter().map(|i| i.to_string()).collect::<String>();
+
+ Ident::new(&combined_name, combined_span)
+ }
+}
+
+#[derive(Debug, Default)]
+struct MethodsAndConsts {
+ trait_methods: Vec<TraitItemMethod>,
+ trait_consts: Vec<TraitItemConst>,
+}
+
+#[allow(clippy::wildcard_in_or_patterns)]
+fn extract_allowed_items(items: &[ImplItem]) -> MethodsAndConsts {
+ let mut acc = MethodsAndConsts::default();
+ for item in items {
+ match item {
+ ImplItem::Method(method) => acc.trait_methods.push(TraitItemMethod {
+ attrs: method.attrs.clone(),
+ sig: method.sig.clone(),
+ default: None,
+ semi_token: Some(Semi::default()),
+ }),
+ ImplItem::Const(const_) => acc.trait_consts.push(TraitItemConst {
+ attrs: const_.attrs.clone(),
+ const_token: Default::default(),
+ ident: const_.ident.clone(),
+ colon_token: Default::default(),
+ ty: const_.ty.clone(),
+ default: None,
+ semi_token: Default::default(),
+ }),
+ ImplItem::Type(_) => abort!(
+ item.span(),
+ "Associated types are not allowed in #[ext] impls"
+ ),
+ ImplItem::Macro(_) => abort!(item.span(), "Macros are not allowed in #[ext] impls"),
+ ImplItem::Verbatim(_) | _ => abort!(item.span(), "Not allowed in #[ext] impls"),
+ }
+ }
+ acc
+}
+
+#[derive(Debug)]
+struct Config {
+ ext_trait_name: Option<Ident>,
+ visibility: Visibility,
+ supertraits: Option<Punctuated<TypeParamBound, Add>>,
+}
+
+impl Parse for Config {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ let mut config = Config::default();
+
+ if let Ok(visibility) = input.parse::<Visibility>() {
+ config.visibility = visibility;
+ }
+
+ input.parse::<Token![,]>().ok();
+
+ while !input.is_empty() {
+ let ident = input.parse::<Ident>()?;
+ input.parse::<Token![=]>()?;
+
+ match &*ident.to_string() {
+ "name" => {
+ config.ext_trait_name = Some(input.parse()?);
+ }
+ "supertraits" => {
+ config.supertraits =
+ Some(Punctuated::<TypeParamBound, Add>::parse_terminated(input)?);
+ }
+ _ => abort!(ident.span(), "Unknown configuration name"),
+ }
+
+ input.parse::<Token![,]>().ok();
+ }
+
+ Ok(config)
+ }
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Self {
+ ext_trait_name: None,
+ visibility: Visibility::Inherited,
+ supertraits: None,
+ }
+ }
+}
+
+fn punctuated_from_iter<I, T, P>(i: I) -> Punctuated<T, P>
+where
+ P: Default,
+ I: IntoIterator<Item = T>,
+{
+ let mut iter = i.into_iter().peekable();
+ let mut acc = Punctuated::default();
+
+ while let Some(item) = iter.next() {
+ acc.push_value(item);
+
+ if iter.peek().is_some() {
+ acc.push_punct(P::default());
+ }
+ }
+
+ acc
+}
+
+#[cfg(test)]
+mod test {
+ #[allow(unused_imports)]
+ use super::*;
+
+ #[test]
+ fn test_ui() {
+ let t = trybuild::TestCases::new();
+ t.pass("tests/compile_pass/*.rs");
+ t.compile_fail("tests/compile_fail/*.rs");
+ }
+}
diff --git a/third_party/rust/extend/tests/compile_fail/double_vis.rs b/third_party/rust/extend/tests/compile_fail/double_vis.rs
new file mode 100644
index 0000000000..4e0e135ad3
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_fail/double_vis.rs
@@ -0,0 +1,14 @@
+mod a {
+ use extend::ext;
+
+ #[ext(pub(super))]
+ pub impl i32 {
+ fn foo() -> Foo {
+ Foo
+ }
+ }
+
+ pub struct Foo;
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_fail/double_vis.stderr b/third_party/rust/extend/tests/compile_fail/double_vis.stderr
new file mode 100644
index 0000000000..c98b614935
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_fail/double_vis.stderr
@@ -0,0 +1,5 @@
+error: Cannot set visibility on `#[ext]` and `impl` block
+ --> $DIR/double_vis.rs:4:11
+ |
+4 | #[ext(pub(super))]
+ | ^^^
diff --git a/third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.rs b/third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.rs
new file mode 100644
index 0000000000..5fba303138
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.rs
@@ -0,0 +1,14 @@
+use extend::ext;
+
+trait MyTrait {}
+
+#[ext(supertraits = MyTrait)]
+impl String {
+ fn my_len(&self) -> usize {
+ self.len()
+ }
+}
+
+fn main() {
+ assert_eq!(String::new().my_len(), 0);
+}
diff --git a/third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.stderr b/third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.stderr
new file mode 100644
index 0000000000..60059f73fb
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.stderr
@@ -0,0 +1,10 @@
+error[E0277]: the trait bound `String: MyTrait` is not satisfied
+ --> $DIR/supertraits_are_actually_included.rs:6:6
+ |
+5 | #[ext(supertraits = MyTrait)]
+ | ------- required by this bound in `StringExt`
+6 | impl String {
+ | ^^^^^^
+ | |
+ | the trait `MyTrait` is not implemented for `String`
+ | required by a bound in this
diff --git a/third_party/rust/extend/tests/compile_pass/associated_constants.rs b/third_party/rust/extend/tests/compile_pass/associated_constants.rs
new file mode 100644
index 0000000000..5cb982c6a4
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/associated_constants.rs
@@ -0,0 +1,10 @@
+use extend::ext;
+
+#[ext]
+impl Option<String> {
+ const FOO: usize = 1;
+}
+
+fn main() {
+ assert_eq!(Option::<String>::FOO, 1);
+}
diff --git a/third_party/rust/extend/tests/compile_pass/async_trait.rs b/third_party/rust/extend/tests/compile_pass/async_trait.rs
new file mode 100644
index 0000000000..067bdc9bb7
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/async_trait.rs
@@ -0,0 +1,25 @@
+use extend::ext;
+use async_trait::async_trait;
+
+#[ext]
+#[async_trait]
+impl String {
+ async fn foo() -> usize {
+ 1
+ }
+}
+
+#[ext]
+#[async_trait]
+pub impl i32 {
+ async fn bar() -> usize {
+ 1
+ }
+}
+
+async fn foo() {
+ let _: usize = String::foo().await;
+ let _: usize = i32::bar().await;
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_pass/changing_extension_trait_name.rs b/third_party/rust/extend/tests/compile_pass/changing_extension_trait_name.rs
new file mode 100644
index 0000000000..fc83b19339
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/changing_extension_trait_name.rs
@@ -0,0 +1,10 @@
+use extend::ext;
+
+#[ext(name = Foo)]
+impl i32 {
+ fn foo() {}
+}
+
+fn main() {
+ <i32 as Foo>::foo();
+}
diff --git a/third_party/rust/extend/tests/compile_pass/complex_trait_name.rs b/third_party/rust/extend/tests/compile_pass/complex_trait_name.rs
new file mode 100644
index 0000000000..3457495132
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/complex_trait_name.rs
@@ -0,0 +1,16 @@
+mod foo {
+ use extend::ext;
+
+ #[ext(pub)]
+ impl<T1, T2, T3> (T1, T2, T3) {
+ fn size(&self) -> usize {
+ 3
+ }
+ }
+}
+
+fn main() {
+ use foo::TupleOfT1T2T3Ext;
+
+ assert_eq!(3, (0, 0, 0).size());
+}
diff --git a/third_party/rust/extend/tests/compile_pass/double_ext_on_same_type.rs b/third_party/rust/extend/tests/compile_pass/double_ext_on_same_type.rs
new file mode 100644
index 0000000000..972d530fa8
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/double_ext_on_same_type.rs
@@ -0,0 +1,17 @@
+use extend::ext;
+
+#[ext]
+impl Option<usize> {
+ fn foo() -> usize {
+ 1
+ }
+}
+
+#[ext]
+impl Option<i32> {
+ fn bar() -> i32 {
+ 1
+ }
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_pass/extension_on_complex_types.rs b/third_party/rust/extend/tests/compile_pass/extension_on_complex_types.rs
new file mode 100644
index 0000000000..20dfb7ca90
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/extension_on_complex_types.rs
@@ -0,0 +1,58 @@
+use extend::ext;
+
+#[ext]
+impl<'a> &'a str {
+ fn foo(self) {}
+}
+
+#[ext]
+impl<T> [T; 3] {
+ fn foo(self) {}
+}
+
+#[ext]
+impl *const i32 {
+ fn foo(self) {}
+}
+
+#[ext]
+impl<T> [T] {
+ fn foo(&self) {}
+}
+
+#[ext]
+impl<'a, T> &'a [T] {
+ fn foo(self) {}
+}
+
+#[ext]
+impl (i32, i64) {
+ fn foo(self) {}
+}
+
+#[ext]
+impl fn(i32) -> bool {
+ fn foo(self) {}
+}
+
+fn bare_fn(_: i32) -> bool {
+ false
+}
+
+#[ext]
+impl dyn Send + Sync + 'static {}
+
+fn main() {
+ "".foo();
+
+ [1, 2, 3].foo();
+
+ let ptr: *const i32 = &123;
+ ptr.foo();
+
+ &[1, 2, 3].foo();
+
+ (1i32, 1i64).foo();
+
+ (bare_fn as fn(i32) -> bool).foo();
+}
diff --git a/third_party/rust/extend/tests/compile_pass/generics.rs b/third_party/rust/extend/tests/compile_pass/generics.rs
new file mode 100644
index 0000000000..4c56b14677
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/generics.rs
@@ -0,0 +1,15 @@
+use extend::ext;
+
+#[ext]
+impl<'a, T: Clone> Vec<&'a T>
+where
+ T: 'a + Copy,
+{
+ fn size(&self) -> usize {
+ self.len()
+ }
+}
+
+fn main() {
+ assert_eq!(3, vec![&1, &2, &3].size());
+}
diff --git a/third_party/rust/extend/tests/compile_pass/hello_world.rs b/third_party/rust/extend/tests/compile_pass/hello_world.rs
new file mode 100644
index 0000000000..75c61183b4
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/hello_world.rs
@@ -0,0 +1,20 @@
+use extend::ext;
+
+#[ext]
+impl i32 {
+ fn add_one(&self) -> Self {
+ self + 1
+ }
+
+ fn foo() -> MyType {
+ MyType
+ }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+struct MyType;
+
+fn main() {
+ assert_eq!(i32::foo(), MyType);
+ assert_eq!(1.add_one(), 2);
+}
diff --git a/third_party/rust/extend/tests/compile_pass/issue_2.rs b/third_party/rust/extend/tests/compile_pass/issue_2.rs
new file mode 100644
index 0000000000..76516486ab
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/issue_2.rs
@@ -0,0 +1,33 @@
+#![allow(unused_variables)]
+
+use extend::ext;
+use std::iter::FromIterator;
+
+#[ext]
+impl<T, K, F, C> C
+where
+ C: IntoIterator<Item = T>,
+ K: Eq,
+ F: Fn(&T) -> K,
+{
+ fn group_by<Out>(self, f: F) -> Out
+ where
+ Out: FromIterator<(K, Vec<T>)>,
+ {
+ todo!()
+ }
+
+ fn group_by_and_map_values<Out, G, T2>(self, f: F, g: G) -> Out
+ where
+ G: Fn(T) -> T2 + Copy,
+ Out: FromIterator<(K, Vec<T2>)>,
+ {
+ todo!()
+ }
+
+ fn group_by_and_return_groups(self, f: F) -> Vec<Vec<T>> {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_pass/more_than_one_extension.rs b/third_party/rust/extend/tests/compile_pass/more_than_one_extension.rs
new file mode 100644
index 0000000000..e5ce539f15
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/more_than_one_extension.rs
@@ -0,0 +1,13 @@
+use extend::ext;
+
+#[ext]
+impl i32 {
+ fn foo() {}
+}
+
+#[ext]
+impl i64 {
+ fn bar() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_pass/multiple_config.rs b/third_party/rust/extend/tests/compile_pass/multiple_config.rs
new file mode 100644
index 0000000000..9ffdad1316
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/multiple_config.rs
@@ -0,0 +1,13 @@
+use extend::ext;
+
+#[ext(pub(crate), name = Foo)]
+impl i32 {
+ fn foo() {}
+}
+
+#[ext(pub, name = Bar)]
+impl i64 {
+ fn foo() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_pass/multiple_generic_params.rs b/third_party/rust/extend/tests/compile_pass/multiple_generic_params.rs
new file mode 100644
index 0000000000..29e4b9ca21
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/multiple_generic_params.rs
@@ -0,0 +1,11 @@
+use extend::ext;
+use std::marker::PhantomData;
+
+struct Foo<T>(PhantomData<T>);
+
+#[ext]
+impl<T, K> T {
+ fn some_method(&self, _: Foo<K>) {}
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_pass/pub_impl.rs b/third_party/rust/extend/tests/compile_pass/pub_impl.rs
new file mode 100644
index 0000000000..f7bff519c0
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/pub_impl.rs
@@ -0,0 +1,15 @@
+mod a {
+ use extend::ext;
+
+ #[ext]
+ pub impl i32 {
+ fn foo() -> Foo { Foo }
+ }
+
+ pub struct Foo;
+}
+
+fn main() {
+ use a::i32Ext;
+ i32::foo();
+}
diff --git a/third_party/rust/extend/tests/compile_pass/ref_and_ref_mut.rs b/third_party/rust/extend/tests/compile_pass/ref_and_ref_mut.rs
new file mode 100644
index 0000000000..156f62027d
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/ref_and_ref_mut.rs
@@ -0,0 +1,13 @@
+use extend::ext;
+
+#[ext]
+impl &i32 {
+ fn foo() {}
+}
+
+#[ext]
+impl &mut i32 {
+ fn bar() {}
+}
+
+fn main() {}
diff --git a/third_party/rust/extend/tests/compile_pass/sized.rs b/third_party/rust/extend/tests/compile_pass/sized.rs
new file mode 100644
index 0000000000..dae05f3f52
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/sized.rs
@@ -0,0 +1,36 @@
+use extend::ext_sized;
+
+#[ext_sized(name = One)]
+impl i32 {
+ fn requires_sized(self) -> Option<Self> {
+ Some(self)
+ }
+}
+
+#[ext_sized(name = Two, supertraits = Default)]
+impl i32 {
+ fn with_another_supertrait(self) -> Option<Self> {
+ Some(self)
+ }
+}
+
+#[ext_sized(name = Three, supertraits = Default + Clone + Copy)]
+impl i32 {
+ fn multiple_supertraits(self) -> Option<Self> {
+ Some(self)
+ }
+}
+
+#[ext_sized(name = Four, supertraits = Sized)]
+impl i32 {
+ fn already_sized(self) -> Option<Self> {
+ Some(self)
+ }
+}
+
+fn main() {
+ 1.requires_sized();
+ 1.with_another_supertrait();
+ 1.multiple_supertraits();
+ 1.already_sized();
+}
diff --git a/third_party/rust/extend/tests/compile_pass/super_trait.rs b/third_party/rust/extend/tests/compile_pass/super_trait.rs
new file mode 100644
index 0000000000..30b147d24a
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/super_trait.rs
@@ -0,0 +1,16 @@
+use extend::ext;
+
+trait MyTrait {}
+
+impl MyTrait for String {}
+
+#[ext(supertraits = Default + Clone + MyTrait)]
+impl String {
+ fn my_len(&self) -> usize {
+ self.len()
+ }
+}
+
+fn main() {
+ assert_eq!(String::new().my_len(), 0);
+}
diff --git a/third_party/rust/extend/tests/compile_pass/visibility_config.rs b/third_party/rust/extend/tests/compile_pass/visibility_config.rs
new file mode 100644
index 0000000000..bcb8b19576
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/visibility_config.rs
@@ -0,0 +1,15 @@
+mod a {
+ use extend::ext;
+
+ #[ext(pub)]
+ impl i32 {
+ fn foo() -> Foo { Foo }
+ }
+
+ pub struct Foo;
+}
+
+fn main() {
+ use a::i32Ext;
+ i32::foo();
+}