summaryrefslogtreecommitdiffstats
path: root/third_party/rust/extend
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/extend
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
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.md87
-rw-r--r--third_party/rust/extend/Cargo.toml49
-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.toml23
-rw-r--r--third_party/rust/extend/src/lib.rs657
-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.stderr13
-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/destructure.rs12
-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.rs60
-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, 1264 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..fc008ab608
--- /dev/null
+++ b/third_party/rust/extend/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"7f91119554e4587936d7e431dae1b7b77c677230809615ad81e931477e7b7013","Cargo.toml":"fa01cfc6e053fa0072da1ad9e413a147a4f2cfee53a529de57a7c44ce858ad13","LICENSE":"db0efd0bb80126f32214f478434536d07d86a30658fb209d8b543b7261492a2b","README.md":"0872b2b15ec22732a09b121a31cdd8f8cfc80566f671566e4a7fb6b52a742a2e","deny.toml":"5896a0134be617fcfbde7680ae65f0d8bbb14e7250588f27d8bff347f7bd59d3","src/lib.rs":"6128d331b2ee8fb6261548387b691c633b515a3d6dec1533cac69035676c11ef","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":"d88d744cd810a58b3f7a4495ecb020604ddc1badfb765a7b13bd38ba1ba0f243","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/destructure.rs":"274b1c97ecce02fd3412a4da3f194e48dc1339a7714ebf3748b0d9ff16ce93be","tests/compile_pass/double_ext_on_same_type.rs":"0e4d16fe9059f0325e4ad1337d53738876633b0533f9a2d4e9714ddada96eb4d","tests/compile_pass/extension_on_complex_types.rs":"8dd484df9e6a4c8a20b9f14b8d33aeecb8fce6a9e1be2e68b8dc7f312e860562","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":"311a6d2f1f9d60bff73d2c78a0af97ed27f79672f15c238192a5bbb64db56d00"} \ 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..f1f23e25c2
--- /dev/null
+++ b/third_party/rust/extend/CHANGELOG.md
@@ -0,0 +1,87 @@
+# 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.2.0 - 2023-03-18
+
+- Support destructuring function arguments
+
+## 1.1.3 - 2023-03-18
+
+- Update to syn 2.0
+
+## 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..8e33ec31a6
--- /dev/null
+++ b/third_party/rust/extend/Cargo.toml
@@ -0,0 +1,49 @@
+# 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 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"
+name = "extend"
+version = "1.2.0"
+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"
+readme = "README.md"
+keywords = [
+ "extension",
+ "trait",
+]
+categories = ["rust-patterns"]
+license = "MIT"
+repository = "https://github.com/davidpdrsn/extend"
+
+[lib]
+proc-macro = true
+
+[dependencies.proc-macro2]
+version = "1"
+
+[dependencies.quote]
+version = "1"
+
+[dependencies.syn]
+version = "2"
+features = [
+ "full",
+ "extra-traits",
+ "visit",
+]
+
+[dev-dependencies.async-trait]
+version = "0.1.40"
+
+[dev-dependencies.trybuild]
+version = "1.0.17"
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..2661f6d553
--- /dev/null
+++ b/third_party/rust/extend/deny.toml
@@ -0,0 +1,23 @@
+[advisories]
+vulnerability = "deny"
+unmaintained = "warn"
+notice = "warn"
+ignore = []
+
+[licenses]
+unlicensed = "warn"
+allow = []
+deny = []
+copyleft = "warn"
+allow-osi-fsf-free = "either"
+confidence-threshold = 0.8
+
+[bans]
+multiple-versions = "deny"
+highlight = "all"
+skip-tree = []
+
+[sources]
+unknown-registry = "warn"
+unknown-git = "warn"
+allow-git = []
diff --git a/third_party/rust/extend/src/lib.rs b/third_party/rust/extend/src/lib.rs
new file mode 100644
index 0000000000..5081824ff8
--- /dev/null
+++ b/third_party/rust/extend/src/lib.rs
@@ -0,0 +1,657 @@
+//! 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
+//! }
+//! }
+//!
+//! 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
+
+#![allow(clippy::let_and_return)]
+#![deny(unused_variables, dead_code, unused_must_use, unused_imports)]
+
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote, ToTokens};
+use std::convert::{TryFrom, TryInto};
+use syn::{
+ parse::{self, Parse, ParseStream},
+ parse_macro_input, parse_quote,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ token::{Plus, Semi},
+ Ident, ImplItem, ItemImpl, Result, Token, TraitItemConst, TraitItemFn, 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]
+#[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);
+ match go(item, config) {
+ Ok(tokens) => tokens,
+ Err(err) => err.into_compile_error().into(),
+ }
+}
+
+/// 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]
+#[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))
+ };
+
+ match go(item, config) {
+ Ok(tokens) => tokens,
+ Err(err) => err.into_compile_error().into(),
+ }
+}
+
+fn go(item: Input, mut config: Config) -> Result<proc_macro::TokenStream> {
+ if let Some(vis) = item.vis {
+ if config.visibility != Visibility::Inherited {
+ return Err(syn::Error::new(
+ 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_ {
+ return Err(syn::Error::new(
+ path.span(),
+ "Trait impls cannot be used for #[ext]",
+ ));
+ }
+
+ let self_ty = parse_self_ty(&self_ty)?;
+
+ let ext_trait_name = if let Some(ext_trait_name) = config.ext_trait_name {
+ ext_trait_name
+ } 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::<_, _, Plus>(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();
+
+ Ok(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) -> Result<ExtType> {
+ let ty = 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(_) | _ => {
+ return Err(syn::Error::new(
+ self_ty.span(),
+ "#[ext] is not supported for this kind of type",
+ ))
+ }
+ };
+ Ok(ty)
+}
+
+impl<'a> TryFrom<&'a Type> for ExtType<'a> {
+ type Error = syn::Error;
+
+ fn try_from(inner: &'a Type) -> Result<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) -> Result<Ident> {
+ fn inner_self_ty(self_ty: &ExtType) -> Result<Ident> {
+ match self_ty {
+ ExtType::Path(inner) => find_and_combine_idents(inner),
+ ExtType::Reference(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).try_into()?)?;
+ if inner.mutability.is_some() {
+ Ok(format_ident!("RefMut{}", name))
+ } else {
+ Ok(format_ident!("Ref{}", name))
+ }
+ }
+ ExtType::Array(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).try_into()?)?;
+ Ok(format_ident!("ListOf{}", name))
+ }
+ ExtType::Group(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).try_into()?)?;
+ Ok(format_ident!("Group{}", name))
+ }
+ ExtType::Paren(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).try_into()?)?;
+ Ok(format_ident!("Paren{}", name))
+ }
+ ExtType::Ptr(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).try_into()?)?;
+ Ok(format_ident!("PointerTo{}", name))
+ }
+ ExtType::Slice(inner) => {
+ let name = inner_self_ty(&(&*inner.elem).try_into()?)?;
+ Ok(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.try_into()?)?);
+ }
+ Ok(name)
+ }
+ ExtType::Never(_) => Ok(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).try_into()?)?);
+ }
+ match &inner.output {
+ syn::ReturnType::Default => {
+ name = format_ident!("{}Unit", name);
+ }
+ syn::ReturnType::Type(_, ty) => {
+ name = format_ident!("{}{}", name, inner_self_ty(&(&**ty).try_into()?)?);
+ }
+ }
+ Ok(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);
+ }
+ other => {
+ return Err(syn::Error::new(other.span(), "unsupported bound"));
+ }
+ }
+ }
+ Ok(name)
+ }
+ }
+ }
+
+ Ok(format_ident!("{}Ext", inner_self_ty(self_ty)?))
+}
+
+fn find_and_combine_idents(type_path: &TypePath) -> Result<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() {
+ Err(syn::Error::new(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>();
+
+ Ok(Ident::new(&combined_name, combined_span))
+ }
+}
+
+#[derive(Debug, Default)]
+struct MethodsAndConsts {
+ trait_methods: Vec<TraitItemFn>,
+ trait_consts: Vec<TraitItemConst>,
+}
+
+#[allow(clippy::wildcard_in_or_patterns)]
+fn extract_allowed_items(items: &[ImplItem]) -> Result<MethodsAndConsts> {
+ let mut acc = MethodsAndConsts::default();
+ for item in items {
+ match item {
+ ImplItem::Fn(method) => acc.trait_methods.push(TraitItemFn {
+ attrs: method.attrs.clone(),
+ sig: {
+ let mut sig = method.sig.clone();
+ sig.inputs = sig
+ .inputs
+ .into_iter()
+ .map(|fn_arg| match fn_arg {
+ syn::FnArg::Receiver(recv) => syn::FnArg::Receiver(recv),
+ syn::FnArg::Typed(mut pat_type) => {
+ pat_type.pat = Box::new(match *pat_type.pat {
+ syn::Pat::Ident(pat_ident) => syn::Pat::Ident(pat_ident),
+ _ => {
+ parse_quote!(_)
+ }
+ });
+ syn::FnArg::Typed(pat_type)
+ }
+ })
+ .collect();
+ sig
+ },
+ default: None,
+ semi_token: Some(Semi::default()),
+ }),
+ ImplItem::Const(const_) => acc.trait_consts.push(TraitItemConst {
+ attrs: const_.attrs.clone(),
+ generics: const_.generics.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(_) => {
+ return Err(syn::Error::new(
+ item.span(),
+ "Associated types are not allowed in #[ext] impls",
+ ))
+ }
+ ImplItem::Macro(_) => {
+ return Err(syn::Error::new(
+ item.span(),
+ "Macros are not allowed in #[ext] impls",
+ ))
+ }
+ ImplItem::Verbatim(_) | _ => {
+ return Err(syn::Error::new(item.span(), "Not allowed in #[ext] impls"))
+ }
+ }
+ }
+ Ok(acc)
+}
+
+#[derive(Debug)]
+struct Config {
+ ext_trait_name: Option<Ident>,
+ visibility: Visibility,
+ supertraits: Option<Punctuated<TypeParamBound, Plus>>,
+}
+
+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, Plus>::parse_terminated(input)?);
+ }
+ _ => return Err(syn::Error::new(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..5dc91118aa
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.stderr
@@ -0,0 +1,13 @@
+error[E0277]: the trait bound `String: MyTrait` is not satisfied
+ --> tests/compile_fail/supertraits_are_actually_included.rs:6:6
+ |
+6 | impl String {
+ | ^^^^^^ the trait `MyTrait` is not implemented for `String`
+ |
+note: required by a bound in `StringExt`
+ --> tests/compile_fail/supertraits_are_actually_included.rs:5:21
+ |
+5 | #[ext(supertraits = MyTrait)]
+ | ^^^^^^^ required by this bound in `StringExt`
+6 | impl 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/destructure.rs b/third_party/rust/extend/tests/compile_pass/destructure.rs
new file mode 100644
index 0000000000..bdc412cb0e
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/destructure.rs
@@ -0,0 +1,12 @@
+#![allow(warnings)]
+
+use extend::ext;
+
+#[ext]
+impl i32 {
+ fn foo(self, (a, b): (i32, i32)) {}
+
+ fn bar(self, [a, b]: [i32; 2]) {}
+}
+
+fn main() {}
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..e8d77b40a9
--- /dev/null
+++ b/third_party/rust/extend/tests/compile_pass/extension_on_complex_types.rs
@@ -0,0 +1,60 @@
+#![allow(warnings)]
+
+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();
+}