Tim Kurdov 48cdc3dff4
#[hook]: clippy::multiple_bound_locations lint no longer triggered (#3803)
This is achieved by reworking the logic for rewriting the function signature of the hook.
2025-02-26 22:57:18 +09:00

191 lines
5.3 KiB
Rust

#![cfg_attr(nightly_yew, feature(proc_macro_span))]
//! This crate provides Yew's procedural macro `html!` which allows using JSX-like syntax
//! for generating html and the `Properties` derive macro for deriving the `Properties` trait
//! for components.
//!
//! ```
//! use yew::prelude::*;
//!
//! struct Component;
//!
//! #[derive(Properties, PartialEq)]
//! struct Props {
//! prop: String,
//! }
//!
//! # enum Msg { Submit }
//! #
//! # impl yew::Component for Component {
//! # type Message = Msg;
//! # type Properties = Props;
//! # fn create(_ctx: &Context<Self>) -> Self {
//! # unimplemented!()
//! # }
//! #
//! #
//! # fn view(&self, ctx: &Context<Self>) -> Html {
//! #
//! // ...
//!
//! html! {
//! <div>
//! <button onclick={ctx.link().callback(|_| Msg::Submit)}>
//! { "Submit" }
//! </button>
//! <>
//! <Component prop="first" />
//! <Component prop="second" />
//! </>
//! </div>
//! }
//! #
//! # }
//! # }
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://github.com/yewstack/yew](https://github.com/yewstack/yew) for how to set this up.
mod classes;
mod derive_props;
mod function_component;
mod hook;
mod html_tree;
mod props;
mod stringify;
mod use_prepared_state;
mod use_transitive_state;
use derive_props::DerivePropsInput;
use function_component::{function_component_impl, FunctionComponent, FunctionComponentName};
use hook::{hook_impl, HookFn};
use html_tree::{HtmlRoot, HtmlRootVNode};
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::buffer::Cursor;
use syn::parse_macro_input;
use use_prepared_state::PreparedState;
use use_transitive_state::TransitiveState;
trait Peek<'a, T> {
fn peek(cursor: Cursor<'a>) -> Option<(T, Cursor<'a>)>;
}
trait PeekValue<T> {
fn peek(cursor: Cursor) -> Option<T>;
}
fn non_capitalized_ascii(string: &str) -> bool {
if !string.is_ascii() {
false
} else if let Some(c) = string.bytes().next() {
c.is_ascii_lowercase()
} else {
false
}
}
/// Combine multiple `syn` errors into a single one.
/// Returns `Result::Ok` if the given iterator is empty
fn join_errors(mut it: impl Iterator<Item = syn::Error>) -> syn::Result<()> {
it.next().map_or(Ok(()), |mut err| {
for other in it {
err.combine(other);
}
Err(err)
})
}
fn is_ide_completion() -> bool {
match std::env::var_os("RUST_IDE_PROC_MACRO_COMPLETION_DUMMY_IDENTIFIER") {
None => false,
Some(dummy_identifier) => !dummy_identifier.is_empty(),
}
}
#[proc_macro_derive(Properties, attributes(prop_or, prop_or_else, prop_or_default))]
pub fn derive_props(input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as DerivePropsInput);
input.normalise();
TokenStream::from(input.into_token_stream())
}
#[proc_macro_error::proc_macro_error]
#[proc_macro]
pub fn html_nested(input: TokenStream) -> TokenStream {
let root = parse_macro_input!(input as HtmlRoot);
TokenStream::from(root.into_token_stream())
}
#[proc_macro_error::proc_macro_error]
#[proc_macro]
pub fn html(input: TokenStream) -> TokenStream {
let root = parse_macro_input!(input as HtmlRootVNode);
TokenStream::from(root.into_token_stream())
}
#[proc_macro]
pub fn props(input: TokenStream) -> TokenStream {
let props = parse_macro_input!(input as props::PropsMacroInput);
TokenStream::from(props.into_token_stream())
}
#[proc_macro]
pub fn classes(input: TokenStream) -> TokenStream {
let classes = parse_macro_input!(input as classes::Classes);
TokenStream::from(classes.into_token_stream())
}
#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]
pub fn function_component(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as FunctionComponent);
let attr = parse_macro_input!(attr as FunctionComponentName);
function_component_impl(attr, item)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]
pub fn hook(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as HookFn);
if let Some(m) = proc_macro2::TokenStream::from(attr).into_iter().next() {
return syn::Error::new_spanned(m, "hook attribute does not accept any arguments")
.into_compile_error()
.into();
}
hook_impl(item)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro]
pub fn use_prepared_state_with_closure(input: TokenStream) -> TokenStream {
let prepared_state = parse_macro_input!(input as PreparedState);
prepared_state.to_token_stream_with_closure().into()
}
#[proc_macro]
pub fn use_prepared_state_without_closure(input: TokenStream) -> TokenStream {
let prepared_state = parse_macro_input!(input as PreparedState);
prepared_state.to_token_stream_without_closure().into()
}
#[proc_macro]
pub fn use_transitive_state_with_closure(input: TokenStream) -> TokenStream {
let transitive_state = parse_macro_input!(input as TransitiveState);
transitive_state.to_token_stream_with_closure().into()
}
#[proc_macro]
pub fn use_transitive_state_without_closure(input: TokenStream) -> TokenStream {
let transitive_state = parse_macro_input!(input as TransitiveState);
transitive_state.to_token_stream_without_closure().into()
}