mirror of
https://github.com/zed-industries/zed.git
synced 2026-08-04 13:24:41 +00:00
Adds a dylint library under tooling/lints that flags Zed-specific anti-patterns: * shared_string_from_str_literal, * async_block_without_await, * entity_update_in_render, * notify_in_render, * owned_string_into_shared, * len_in_loop_condition, and * blocking_io_on_foreground. Includes UI tests, a single-lint helper, and workspace.metadata.dylint registration so cargo dylint --all discovers it. The library pins its own nightly toolchain (kept out of the main workspace) and tracks dylint 6. Release Notes: - N/A Self-Review Checklist: - [ ] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Closes #ISSUE Release Notes: - N/A or Added/Fixed/Improved ...
58 lines
1.7 KiB
Rust
58 lines
1.7 KiB
Rust
use clippy_utils::diagnostics::span_lint;
|
|
use rustc_hir::{Expr, ExprKind};
|
|
use rustc_lint::{LateContext, LateLintPass};
|
|
|
|
use crate::render_helpers::{is_directly_in_render_method, is_gpui_context};
|
|
|
|
rustc_session::declare_lint! {
|
|
/// ### What it does
|
|
///
|
|
/// Flags calls to `Context::notify()` that execute synchronously inside a
|
|
/// `Render::render` method.
|
|
///
|
|
/// ### Why is this bad?
|
|
///
|
|
/// `notify()` tells the framework that the entity's state has changed and
|
|
/// it should be re-rendered. Calling it during render means every render
|
|
/// pass schedules another render pass — either an infinite loop or wasted
|
|
/// work.
|
|
pub NOTIFY_IN_RENDER,
|
|
Warn,
|
|
"calling `cx.notify()` during render schedules a redundant re-render"
|
|
}
|
|
|
|
pub(crate) struct NotifyInRender;
|
|
|
|
rustc_session::impl_lint_pass!(NotifyInRender => [NOTIFY_IN_RENDER]);
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for NotifyInRender {
|
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|
if expr.span.from_expansion() {
|
|
return;
|
|
}
|
|
|
|
let ExprKind::MethodCall(segment, receiver, _args, _span) = &expr.kind else {
|
|
return;
|
|
};
|
|
|
|
if segment.ident.name.as_str() != "notify" {
|
|
return;
|
|
}
|
|
|
|
let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
|
if !is_gpui_context(cx, receiver_ty) {
|
|
return;
|
|
}
|
|
|
|
if !is_directly_in_render_method(cx, expr.hir_id) {
|
|
return;
|
|
}
|
|
|
|
span_lint(
|
|
cx,
|
|
NOTIFY_IN_RENDER,
|
|
expr.span,
|
|
"`cx.notify()` called during render schedules a re-render every render pass",
|
|
);
|
|
}
|
|
}
|