Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature nightly-diagnostics; improve macro error msgs #464

Merged
merged 9 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ jobs:
cargo fmt --all -- --check
- name: Build docs
run: |
cargo doc --all --no-deps
cargo doc --features nightly --all --no-deps
cargo doc --all --no-deps -Zwarnings --config 'build.warnings="deny"'
cargo doc --features nightly --all --no-deps -Zwarnings --config 'build.warnings="deny"'
- name: Test kas-macros
run: |
cargo test --manifest-path crates/kas-macros/Cargo.toml
Expand All @@ -55,11 +55,11 @@ jobs:
cargo test --manifest-path crates/kas-dylib/Cargo.toml --features kas-core/winit,kas-core/wayland
cargo test --manifest-path crates/kas-dylib/Cargo.toml --all-features --features kas-core/winit,kas-core/x11
- name: Test kas
run: cargo test --features nightly
- name: Test kas (experimental)
run: cargo test --features nightly -Zwarnings --config 'build.warnings="deny"'
- name: Test kas (experimental; some warnings may be expected)
run: cargo test --features nightly,experimental
- name: Test examples/mandlebrot
run: cargo test --manifest-path examples/mandlebrot/Cargo.toml
run: cargo test --manifest-path examples/mandlebrot/Cargo.toml -Zwarnings --config 'build.warnings="deny"'

test:
name: Test
Expand Down
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,21 @@ default = ["minimal", "view", "image", "resvg", "clipboard", "markdown", "shapin
# All standard test target features
stable = ["default", "x11", "serde", "toml", "yaml", "json", "ron", "macros_log"]
# Enables all "recommended" features for nightly rustc
nightly = ["stable"]
nightly = ["stable", "nightly-diagnostics", "kas-core/nightly"]
# Additional, less recommendation-worthy features
experimental = ["dark-light", "recursive-layout-widgets", "unsafe_node"]

# Enable dynamic linking (faster linking via an extra run-time dependency):
dynamic = ["dep:kas-dylib"]

######### optional dependencies / features #########

# Enables better proc-macro diagnostics (including warnings); nightly only.
nightly-diagnostics = ["kas-core/nightly-diagnostics"]

# Use full specialization
spec = ["kas-core/spec"]

######### optional dependencies / features #########

# Enable view widgets
view = ["dep:kas-view"]

Expand Down
5 changes: 4 additions & 1 deletion crates/kas-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ minimal = ["winit", "wayland"]
# All standard test target features
stable = ["minimal", "clipboard", "markdown", "shaping", "spawn", "x11", "serde", "toml", "yaml", "json", "ron", "macros_log", "image"]
# Enables all "recommended" features for nightly rustc
nightly = ["stable", "kas-macros/nightly"]
nightly = ["stable", "nightly-diagnostics"]
# Additional, less recommendation-worthy features
experimental = ["dark-light", "recursive-layout-widgets", "unsafe_node"]

# Enables better proc-macro diagnostics (including warnings); nightly only.
nightly-diagnostics = ["kas-macros/nightly"]

# Use full specialization
spec = []

Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/core/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ pub trait Layout {
/// widget tree) occupying `coord` (exceptions possible; see below).
///
/// The callee may assume that it occupies `coord`.
/// Callers should prefer to call [`Tile::try_probe`] instead.
/// Callers should prefer to call [`Layout::try_probe`] instead.
///
/// This method is used to determine which widget reacts to the mouse and
/// touch events at the given coordinates. The widget identified by this
Expand Down Expand Up @@ -279,7 +279,7 @@ pub trait Layout {
///
/// ### Call order
///
/// It is expected that [`Tile::set_rect`] is called before this method,
/// It is expected that [`Layout::set_rect`] is called before this method,
/// but failure to do so should not cause a fatal error.
fn try_probe(&mut self, coord: Coord) -> Option<Id> {
self.rect().contains(coord).then(|| self.probe(coord))
Expand Down
66 changes: 65 additions & 1 deletion crates/kas-core/src/hidden.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ impl_scope! {
#[autoimpl(Deref, DerefMut using self.inner)]
#[autoimpl(class_traits using self.inner where W: trait)]
#[derive(Clone, Default)]
#[widget{ derive = self.inner; }]
pub struct MapAny<A, W: Widget<Data = ()>> {
_a: std::marker::PhantomData<A>,
pub inner: W,
Expand All @@ -94,6 +93,71 @@ impl_scope! {
}
}

impl Layout for Self {
#[inline]
fn as_layout(&self) -> &dyn ::kas::Layout {
self
}
#[inline]
fn id_ref(&self) -> &::kas::Id {
self.inner.id_ref()
}
#[inline]
fn rect(&self) -> ::kas::geom::Rect {
self.inner.rect()
}

#[inline]
fn widget_name(&self) -> &'static str {
"MapAny"
}

#[inline]
fn num_children(&self) -> usize {
self.inner.num_children()
}
#[inline]
fn get_child(&self, index: usize) -> Option<&dyn ::kas::Layout> {
self.inner.get_child(index)
}
#[inline]
fn find_child_index(&self, id: &::kas::Id) -> Option<usize> {
self.inner.find_child_index(id)
}

#[inline]
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.inner.size_rules(sizer, axis)
}

#[inline]
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.inner.set_rect(cx, rect, hints);
}

#[inline]
fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
self.inner.nav_next(reverse, from)
}

#[inline]
fn translation(&self) -> ::kas::geom::Offset {
self.inner.translation()
}

// NOTE: fn probe is left unimplemented since it should not be called directly

#[inline]
fn try_probe(&mut self, coord: ::kas::geom::Coord) -> Option<::kas::Id> {
self.inner.try_probe(coord)
}

#[inline]
fn draw(&mut self, draw: DrawCx) {
self.inner.draw(draw);
}
}

impl Widget for Self {
type Data = A;

Expand Down
6 changes: 3 additions & 3 deletions crates/kas-core/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ impl From<AxisInfo> for Directions {
/// }
/// impl Layout for Self {
/// fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
/// let mut rules = self.layout_visitor().size_rules(sizer, axis);
/// rules.set_stretch(Stretch::High);
/// rules
/// self.layout_visitor()
/// .size_rules(sizer, axis)
/// .with_stretch(Stretch::High)
/// }
/// }
/// }
Expand Down
20 changes: 4 additions & 16 deletions crates/kas-core/src/layout/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,6 @@ impl<V: Visitable> Visitor<V> {
/// This method is identical to [`Layout::size_rules`].
#[inline]
pub fn size_rules(mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.size_rules_(sizer, axis)
}
fn size_rules_(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.0.size_rules(sizer, axis)
}

Expand All @@ -213,9 +210,6 @@ impl<V: Visitable> Visitor<V> {
/// In other respects, this functions identically to [`Layout::set_rect`].
#[inline]
pub fn set_rect(mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.set_rect_(cx, rect, hints);
}
fn set_rect_(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.0.set_rect(cx, rect, hints);
}

Expand All @@ -229,9 +223,6 @@ impl<V: Visitable> Visitor<V> {
/// 4. Otherwise return `Some(self.id())`
#[inline]
pub fn try_probe(mut self, coord: Coord) -> Option<Id> {
self.try_probe_(coord)
}
fn try_probe_(&mut self, coord: Coord) -> Option<Id> {
self.0.try_probe(coord)
}

Expand All @@ -240,28 +231,25 @@ impl<V: Visitable> Visitor<V> {
/// This method is identical to [`Layout::draw`].
#[inline]
pub fn draw(mut self, draw: DrawCx) {
self.draw_(draw);
}
fn draw_(&mut self, draw: DrawCx) {
self.0.draw(draw);
}
}

impl<V: Visitable> Visitable for Visitor<V> {
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.size_rules_(sizer, axis)
self.0.size_rules(sizer, axis)
}

fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.set_rect_(cx, rect, hints);
self.0.set_rect(cx, rect, hints);
}

fn try_probe(&mut self, coord: Coord) -> Option<Id> {
self.try_probe_(coord)
self.0.try_probe(coord)
}

fn draw(&mut self, draw: DrawCx) {
self.draw_(draw);
self.0.draw(draw);
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/kas-macros/src/make_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::collection::{CellInfo, GridDimensions, NameGenerator, StorIdent};
use crate::widget;
use crate::widget_args::{Child, ChildIdent};
use proc_macro2::{Span, TokenStream as Toks};
use proc_macro_error2::{emit_error, emit_warning};
use proc_macro_error2::emit_error;
use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
use syn::parse::{Parse, ParseStream, Result};
use syn::spanned::Spanned;
Expand Down Expand Up @@ -416,7 +416,7 @@ impl Layout {
let _ = Pack::parse(dot_token, &input2, &mut temp_gen)?;
continue;
} else if let Ok(ident) = input2.parse::<Ident>() {
emit_warning!(
proc_macro_error2::emit_warning!(
ident, "this method call is incompatible with feature `recursive-layout-widgets`";
note = "extract operand from layout expression or wrap with braces",
);
Expand Down
13 changes: 10 additions & 3 deletions crates/kas-macros/src/widget_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ impl ToTokens for CursorIcon {
}
}

#[derive(Debug)]
pub struct Derive {
pub kw: kw::derive,
pub field: syn::Member,
}

#[derive(Debug)]
pub struct Layout {
pub kw: kw::layout,
Expand All @@ -81,7 +87,7 @@ pub struct WidgetArgs {
pub navigable: Option<Toks>,
pub hover_highlight: Option<HoverHighlight>,
pub cursor_icon: Option<CursorIcon>,
pub derive: Option<Member>,
pub derive: Option<Derive>,
pub layout: Option<Layout>,
}

Expand Down Expand Up @@ -122,11 +128,12 @@ impl Parse for WidgetArgs {
expr: content.parse()?,
});
} else if lookahead.peek(kw::derive) && derive.is_none() {
let _ = Some(content.parse::<kw::derive>()?);
let kw = content.parse::<kw::derive>()?;
let _: Eq = content.parse()?;
let _: Token![self] = content.parse()?;
let _: Token![.] = content.parse()?;
derive = Some(content.parse()?);
let field = content.parse()?;
derive = Some(Derive { kw, field });
} else if lookahead.peek(kw::layout) && layout.is_none() {
layout = Some(Layout {
kw: content.parse()?,
Expand Down
42 changes: 34 additions & 8 deletions crates/kas-macros/src/widget_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,43 @@ use syn::Type;
/// It may also inject code into existing methods such that the only observable
/// behaviour is a panic.
pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<()> {
let inner = args.derive.as_ref().unwrap();
let derive = args.derive.unwrap();
let derive_span = proc_macro_error2::SpanRange {
first: derive.kw.span(),
last: derive.field.span(),
}
.collapse();
let inner = &derive.field;

if let Some(ref toks) = args.data_ty {
emit_error!(toks, "not supported by #[widget(derive=FIELD)]")
emit_error!(
toks, "not supported by #[widget(derive=FIELD)]";
note = derive_span => "usage of derive mode";
)
}
if let Some(ref toks) = args.navigable {
emit_error!(toks, "not supported by #[widget(derive=FIELD)]")
emit_error!(
toks, "not supported by #[widget(derive=FIELD)]";
note = derive_span => "usage of derive mode";
)
}
if let Some(ref toks) = args.hover_highlight {
emit_error!(toks.span(), "not supported by #[widget(derive=FIELD)]")
emit_error!(
toks.span(), "not supported by #[widget(derive=FIELD)]";
note = derive_span => "usage of derive mode";
)
}
if let Some(ref toks) = args.cursor_icon {
emit_error!(toks, "not supported by #[widget(derive=FIELD)]")
emit_error!(
toks, "not supported by #[widget(derive=FIELD)]";
note = derive_span => "usage of derive mode";
)
}
if let Some(Layout { ref kw, .. }) = args.layout {
emit_error!(kw, "not supported by #[widget(derive=FIELD)]")
emit_error!(
kw, "not supported by #[widget(derive=FIELD)]";
note = derive_span => "usage of derive mode";
)
}

scope.expand_impl_self();
Expand All @@ -59,13 +81,17 @@ pub fn widget(_attr_span: Span, args: WidgetArgs, scope: &mut Scope) -> Result<(
|| *path == parse_quote! { Events }
{
emit_warning!(
inner, "Events impl is not used by #[widget(derive=FIELD)]";
note = path.span() => "this Events impl";
path, "Events impl is not used by #[widget(derive=FIELD)]";
note = derive_span => "usage of derive mode";
);
} else if *path == parse_quote! { ::kas::Widget }
|| *path == parse_quote! { kas::Widget }
|| *path == parse_quote! { Widget }
{
emit_error!(
path, "Widget impl is supported by #[widget(derive=FIELD)]";
note = derive_span => "usage of derive mode";
);
if widget_impl.is_none() {
widget_impl = Some(index);
}
Expand Down
Loading
Loading