Skip to content

Commit 8494aa0

Browse files
committed
Merge branch 'main' into dcreager/iterate-tuple
* main: [ty] Use `ThinVec` for sub segments in `PlaceExpr` (#19470) [ty] Splat variadic arguments into parameter list (#18996) [`flake8-pyi`] Skip fix if all `Union` members are `None` (`PYI016`) (#19416) Skip notebook with errors in ecosystem check (#19491) [ty] Consistent use of American english (in rules) (#19488) [ty] Support iterating over enums (#19486) Fix panic for illegal `Literal[…]` annotations with inner subscript expressions (#19489) Move fix suggestion to subdiagnostic (#19464) [ty] Implement non-stdlib stub mapping for classes and functions (#19471) [ty] Disallow illegal uses of `ClassVar` (#19483) [ty] Disallow `Final` in function parameter/return-type annotations (#19480) [ty] Extend `Final` test suite (#19476) [ty] Minor change to diagnostic message for invalid Literal uses (#19482) [ty] Detect illegal non-enum attribute accesses in Literal annotation (#19477) [ty] Reduce size of `TypeInference` (#19435) Run MD tests for Markdown-only changes (#19479) Revert "[ty] Detect illegal non-enum attribute accesses in Literal annotation" [ty] Detect illegal non-enum attribute accesses in Literal annotation [ty] Added semantic token support for more identifiers (#19473) [ty] Make tuple subclass constructors sound (#19469)
2 parents cf8ea56 + dc10ab8 commit 8494aa0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3404
-598
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,12 @@ jobs:
143143
env:
144144
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
145145
run: |
146-
if git diff --quiet "${MERGE_BASE}...HEAD" -- ':**' \
147-
':!**/*.md' \
148-
':crates/ty_python_semantic/resources/mdtest/**/*.md' \
146+
# NOTE: Do not exclude all Markdown files here, but rather use
147+
# specific exclude patterns like 'docs/**'), because tests for
148+
# 'ty' are written in Markdown.
149+
if git diff --quiet "${MERGE_BASE}...HEAD" -- \
149150
':!docs/**' \
150151
':!assets/**' \
151-
':.github/workflows/ci.yaml' \
152152
; then
153153
echo "changed=false" >> "$GITHUB_OUTPUT"
154154
else

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ strum_macros = { version = "0.27.0" }
166166
syn = { version = "2.0.55" }
167167
tempfile = { version = "3.9.0" }
168168
test-case = { version = "3.3.1" }
169+
thin-vec = { version = "0.2.14" }
169170
thiserror = { version = "2.0.0" }
170171
tikv-jemallocator = { version = "0.6.0" }
171172
toml = { version = "0.9.0" }

crates/ruff/src/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ impl LintCacheData {
454454
CacheMessage {
455455
rule,
456456
body: msg.body().to_string(),
457-
suggestion: msg.suggestion().map(ToString::to_string),
457+
suggestion: msg.first_help_text().map(ToString::to_string),
458458
range: msg.expect_range(),
459459
parent: msg.parent(),
460460
fix: msg.fix().cloned(),

crates/ruff_db/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ ty_static = { workspace = true }
2525
anstyle = { workspace = true }
2626
arc-swap = { workspace = true }
2727
camino = { workspace = true }
28-
countme = { workspace = true }
2928
dashmap = { workspace = true }
3029
dunce = { workspace = true }
3130
filetime = { workspace = true }
@@ -59,6 +58,11 @@ tempfile = { workspace = true }
5958
cache = ["ruff_cache"]
6059
junit = ["dep:quick-junit"]
6160
os = ["ignore", "dep:etcetera"]
62-
serde = ["camino/serde1", "dep:serde", "dep:serde_json", "ruff_diagnostics/serde"]
61+
serde = [
62+
"camino/serde1",
63+
"dep:serde",
64+
"dep:serde_json",
65+
"ruff_diagnostics/serde",
66+
]
6367
# Exposes testing utilities.
6468
testing = ["tracing-subscriber"]

crates/ruff_db/src/diagnostic/mod.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,14 @@ impl Diagnostic {
122122
/// directly. If callers want or need to avoid cloning the diagnostic
123123
/// message, then they can also pass a `DiagnosticMessage` directly.
124124
pub fn info<'a>(&mut self, message: impl IntoDiagnosticMessage + 'a) {
125-
self.sub(SubDiagnostic::new(Severity::Info, message));
125+
self.sub(SubDiagnostic::new(SubDiagnosticSeverity::Info, message));
126+
}
127+
128+
/// Adds a "help" sub-diagnostic with the given message.
129+
///
130+
/// See the closely related [`Diagnostic::info`] method for more details.
131+
pub fn help<'a>(&mut self, message: impl IntoDiagnosticMessage + 'a) {
132+
self.sub(SubDiagnostic::new(SubDiagnosticSeverity::Help, message));
126133
}
127134

128135
/// Adds a "sub" diagnostic to this diagnostic.
@@ -377,9 +384,15 @@ impl Diagnostic {
377384
self.primary_message()
378385
}
379386

380-
/// Returns the fix suggestion for the violation.
381-
pub fn suggestion(&self) -> Option<&str> {
382-
self.primary_annotation()?.get_message()
387+
/// Returns the message of the first sub-diagnostic with a `Help` severity.
388+
///
389+
/// Note that this is used as the fix title/suggestion for some of Ruff's output formats, but in
390+
/// general this is not the guaranteed meaning of such a message.
391+
pub fn first_help_text(&self) -> Option<&str> {
392+
self.sub_diagnostics()
393+
.iter()
394+
.find(|sub| matches!(sub.inner.severity, SubDiagnosticSeverity::Help))
395+
.map(|sub| sub.inner.message.as_str())
383396
}
384397

385398
/// Returns the URL for the rule documentation, if it exists.
@@ -565,7 +578,10 @@ impl SubDiagnostic {
565578
/// Callers can pass anything that implements `std::fmt::Display`
566579
/// directly. If callers want or need to avoid cloning the diagnostic
567580
/// message, then they can also pass a `DiagnosticMessage` directly.
568-
pub fn new<'a>(severity: Severity, message: impl IntoDiagnosticMessage + 'a) -> SubDiagnostic {
581+
pub fn new<'a>(
582+
severity: SubDiagnosticSeverity,
583+
message: impl IntoDiagnosticMessage + 'a,
584+
) -> SubDiagnostic {
569585
let inner = Box::new(SubDiagnosticInner {
570586
severity,
571587
message: message.into_diagnostic_message(),
@@ -643,7 +659,7 @@ impl SubDiagnostic {
643659

644660
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
645661
struct SubDiagnosticInner {
646-
severity: Severity,
662+
severity: SubDiagnosticSeverity,
647663
message: DiagnosticMessage,
648664
annotations: Vec<Annotation>,
649665
}
@@ -1170,6 +1186,30 @@ impl Severity {
11701186
}
11711187
}
11721188

1189+
/// Like [`Severity`] but exclusively for sub-diagnostics.
1190+
///
1191+
/// This supports an additional `Help` severity that may not be needed in main diagnostics.
1192+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, get_size2::GetSize)]
1193+
pub enum SubDiagnosticSeverity {
1194+
Help,
1195+
Info,
1196+
Warning,
1197+
Error,
1198+
Fatal,
1199+
}
1200+
1201+
impl SubDiagnosticSeverity {
1202+
fn to_annotate(self) -> AnnotateLevel {
1203+
match self {
1204+
SubDiagnosticSeverity::Help => AnnotateLevel::Help,
1205+
SubDiagnosticSeverity::Info => AnnotateLevel::Info,
1206+
SubDiagnosticSeverity::Warning => AnnotateLevel::Warning,
1207+
SubDiagnosticSeverity::Error => AnnotateLevel::Error,
1208+
SubDiagnosticSeverity::Fatal => AnnotateLevel::Error,
1209+
}
1210+
}
1211+
}
1212+
11731213
/// Configuration for rendering diagnostics.
11741214
#[derive(Clone, Debug)]
11751215
pub struct DisplayDiagnosticConfig {

crates/ruff_db/src/diagnostic/render.rs

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use azure::AzureRenderer;
2626
use pylint::PylintRenderer;
2727

2828
mod azure;
29+
mod full;
2930
#[cfg(feature = "serde")]
3031
mod json;
3132
#[cfg(feature = "serde")]
@@ -256,7 +257,7 @@ impl<'a> Resolved<'a> {
256257
/// both.)
257258
#[derive(Debug)]
258259
struct ResolvedDiagnostic<'a> {
259-
severity: Severity,
260+
level: AnnotateLevel,
260261
id: Option<String>,
261262
message: String,
262263
annotations: Vec<ResolvedAnnotation<'a>>,
@@ -281,7 +282,7 @@ impl<'a> ResolvedDiagnostic<'a> {
281282
let id = Some(diag.inner.id.to_string());
282283
let message = diag.inner.message.as_str().to_string();
283284
ResolvedDiagnostic {
284-
severity: diag.inner.severity,
285+
level: diag.inner.severity.to_annotate(),
285286
id,
286287
message,
287288
annotations,
@@ -304,7 +305,7 @@ impl<'a> ResolvedDiagnostic<'a> {
304305
})
305306
.collect();
306307
ResolvedDiagnostic {
307-
severity: diag.inner.severity,
308+
level: diag.inner.severity.to_annotate(),
308309
id: None,
309310
message: diag.inner.message.as_str().to_string(),
310311
annotations,
@@ -371,7 +372,7 @@ impl<'a> ResolvedDiagnostic<'a> {
371372
snippets_by_input
372373
.sort_by(|snips1, snips2| snips1.has_primary.cmp(&snips2.has_primary).reverse());
373374
RenderableDiagnostic {
374-
severity: self.severity,
375+
level: self.level,
375376
id: self.id.as_deref(),
376377
message: &self.message,
377378
snippets_by_input,
@@ -459,7 +460,7 @@ struct Renderable<'r> {
459460
#[derive(Debug)]
460461
struct RenderableDiagnostic<'r> {
461462
/// The severity of the diagnostic.
462-
severity: Severity,
463+
level: AnnotateLevel,
463464
/// The ID of the diagnostic. The ID can usually be used on the CLI or in a
464465
/// config file to change the severity of a lint.
465466
///
@@ -478,15 +479,14 @@ struct RenderableDiagnostic<'r> {
478479
impl RenderableDiagnostic<'_> {
479480
/// Convert this to an "annotate" snippet.
480481
fn to_annotate(&self) -> AnnotateMessage<'_> {
481-
let level = self.severity.to_annotate();
482482
let snippets = self.snippets_by_input.iter().flat_map(|snippets| {
483483
let path = snippets.path;
484484
snippets
485485
.snippets
486486
.iter()
487487
.map(|snippet| snippet.to_annotate(path))
488488
});
489-
let mut message = level.title(self.message);
489+
let mut message = self.level.title(self.message);
490490
if let Some(id) = self.id {
491491
message = message.id(id);
492492
}
@@ -864,7 +864,10 @@ mod tests {
864864

865865
use ruff_diagnostics::{Edit, Fix};
866866

867-
use crate::diagnostic::{Annotation, DiagnosticId, SecondaryCode, Severity, Span};
867+
use crate::diagnostic::{
868+
Annotation, DiagnosticId, IntoDiagnosticMessage, SecondaryCode, Severity, Span,
869+
SubDiagnosticSeverity,
870+
};
868871
use crate::files::system_path_to_file;
869872
use crate::system::{DbWithWritableSystem, SystemPath};
870873
use crate::tests::TestDb;
@@ -1548,7 +1551,7 @@ watermelon
15481551

15491552
let mut diag = env.err().primary("animals", "3", "3", "").build();
15501553
diag.sub(
1551-
env.sub_builder(Severity::Info, "this is a helpful note")
1554+
env.sub_builder(SubDiagnosticSeverity::Info, "this is a helpful note")
15521555
.build(),
15531556
);
15541557
insta::assert_snapshot!(
@@ -1577,15 +1580,15 @@ watermelon
15771580

15781581
let mut diag = env.err().primary("animals", "3", "3", "").build();
15791582
diag.sub(
1580-
env.sub_builder(Severity::Info, "this is a helpful note")
1583+
env.sub_builder(SubDiagnosticSeverity::Info, "this is a helpful note")
15811584
.build(),
15821585
);
15831586
diag.sub(
1584-
env.sub_builder(Severity::Info, "another helpful note")
1587+
env.sub_builder(SubDiagnosticSeverity::Info, "another helpful note")
15851588
.build(),
15861589
);
15871590
diag.sub(
1588-
env.sub_builder(Severity::Info, "and another helpful note")
1591+
env.sub_builder(SubDiagnosticSeverity::Info, "and another helpful note")
15891592
.build(),
15901593
);
15911594
insta::assert_snapshot!(
@@ -2370,7 +2373,7 @@ watermelon
23702373
/// sub-diagnostic with "error" severity and canned values for
23712374
/// its identifier and message.
23722375
fn sub_warn(&mut self) -> SubDiagnosticBuilder<'_> {
2373-
self.sub_builder(Severity::Warning, "sub-diagnostic message")
2376+
self.sub_builder(SubDiagnosticSeverity::Warning, "sub-diagnostic message")
23742377
}
23752378

23762379
/// Returns a builder for tersely constructing diagnostics.
@@ -2391,7 +2394,11 @@ watermelon
23912394
}
23922395

23932396
/// Returns a builder for tersely constructing sub-diagnostics.
2394-
fn sub_builder(&mut self, severity: Severity, message: &str) -> SubDiagnosticBuilder<'_> {
2397+
fn sub_builder(
2398+
&mut self,
2399+
severity: SubDiagnosticSeverity,
2400+
message: &str,
2401+
) -> SubDiagnosticBuilder<'_> {
23952402
let subdiag = SubDiagnostic::new(severity, message);
23962403
SubDiagnosticBuilder { env: self, subdiag }
23972404
}
@@ -2494,6 +2501,12 @@ watermelon
24942501
self.diag.set_noqa_offset(noqa_offset);
24952502
self
24962503
}
2504+
2505+
/// Adds a "help" sub-diagnostic with the given message.
2506+
fn help(mut self, message: impl IntoDiagnosticMessage) -> DiagnosticBuilder<'e> {
2507+
self.diag.help(message);
2508+
self
2509+
}
24972510
}
24982511

24992512
/// A helper builder for tersely populating a `SubDiagnostic`.
@@ -2600,7 +2613,8 @@ def fibonacci(n):
26002613

26012614
let diagnostics = vec![
26022615
env.builder("unused-import", Severity::Error, "`os` imported but unused")
2603-
.primary("fib.py", "1:7", "1:9", "Remove unused import: `os`")
2616+
.primary("fib.py", "1:7", "1:9", "")
2617+
.help("Remove unused import: `os`")
26042618
.secondary_code("F401")
26052619
.fix(Fix::unsafe_edit(Edit::range_deletion(TextRange::new(
26062620
TextSize::from(0),
@@ -2613,12 +2627,8 @@ def fibonacci(n):
26132627
Severity::Error,
26142628
"Local variable `x` is assigned to but never used",
26152629
)
2616-
.primary(
2617-
"fib.py",
2618-
"6:4",
2619-
"6:5",
2620-
"Remove assignment to unused variable `x`",
2621-
)
2630+
.primary("fib.py", "6:4", "6:5", "")
2631+
.help("Remove assignment to unused variable `x`")
26222632
.secondary_code("F841")
26232633
.fix(Fix::unsafe_edit(Edit::deletion(
26242634
TextSize::from(94),
@@ -2720,7 +2730,8 @@ if call(foo
27202730

27212731
let diagnostics = vec![
27222732
env.builder("unused-import", Severity::Error, "`os` imported but unused")
2723-
.primary("notebook.ipynb", "2:7", "2:9", "Remove unused import: `os`")
2733+
.primary("notebook.ipynb", "2:7", "2:9", "")
2734+
.help("Remove unused import: `os`")
27242735
.secondary_code("F401")
27252736
.fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
27262737
TextSize::from(9),
@@ -2733,12 +2744,8 @@ if call(foo
27332744
Severity::Error,
27342745
"`math` imported but unused",
27352746
)
2736-
.primary(
2737-
"notebook.ipynb",
2738-
"4:7",
2739-
"4:11",
2740-
"Remove unused import: `math`",
2741-
)
2747+
.primary("notebook.ipynb", "4:7", "4:11", "")
2748+
.help("Remove unused import: `math`")
27422749
.secondary_code("F401")
27432750
.fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
27442751
TextSize::from(28),
@@ -2751,12 +2758,8 @@ if call(foo
27512758
Severity::Error,
27522759
"Local variable `x` is assigned to but never used",
27532760
)
2754-
.primary(
2755-
"notebook.ipynb",
2756-
"10:4",
2757-
"10:5",
2758-
"Remove assignment to unused variable `x`",
2759-
)
2761+
.primary("notebook.ipynb", "10:4", "10:5", "")
2762+
.help("Remove assignment to unused variable `x`")
27602763
.secondary_code("F841")
27612764
.fix(Fix::unsafe_edit(Edit::range_deletion(TextRange::new(
27622765
TextSize::from(94),

0 commit comments

Comments
 (0)