2020-05-14 18:51:48 -05:00
//! This module provides the functionality needed to convert diagnostics from
//! `cargo check` json format to the LSP diagnostic format.
2022-04-25 08:21:30 -05:00
use flycheck ::{ Applicability , DiagnosticLevel , DiagnosticSpan } ;
2021-08-06 08:52:47 -05:00
use itertools ::Itertools ;
2024-02-01 09:16:38 -06:00
use rustc_hash ::FxHashMap ;
2020-05-14 19:08:50 -05:00
use stdx ::format_to ;
2021-07-17 09:40:13 -05:00
use vfs ::{ AbsPath , AbsPathBuf } ;
2020-05-14 19:08:50 -05:00
2022-05-07 22:06:52 -05:00
use crate ::{
2023-09-02 07:01:58 -05:00
global_state ::GlobalStateSnapshot , line_index ::PositionEncoding ,
lsp ::to_proto ::url_from_abs_path , lsp_ext ,
2022-05-07 22:06:52 -05:00
} ;
2020-05-14 18:58:39 -05:00
2021-12-04 22:59:26 -06:00
use super ::{ DiagnosticsMapConfig , Fix } ;
2020-06-25 02:13:46 -05:00
2020-06-16 15:26:33 -05:00
/// Determines the LSP severity from a diagnostic
2020-07-09 08:34:37 -05:00
fn diagnostic_severity (
2020-08-18 09:03:15 -05:00
config : & DiagnosticsMapConfig ,
2020-07-09 08:34:37 -05:00
level : flycheck ::DiagnosticLevel ,
code : Option < flycheck ::DiagnosticCode > ,
) -> Option < lsp_types ::DiagnosticSeverity > {
let res = match level {
2021-10-14 01:37:57 -05:00
DiagnosticLevel ::Ice = > lsp_types ::DiagnosticSeverity ::ERROR ,
DiagnosticLevel ::Error = > lsp_types ::DiagnosticSeverity ::ERROR ,
2020-07-09 08:34:37 -05:00
DiagnosticLevel ::Warning = > match & code {
2021-11-22 07:14:21 -06:00
// HACK: special case for `warnings` rustc lint.
Some ( code )
if config . warnings_as_hint . iter ( ) . any ( | lint | {
2022-03-12 06:04:13 -06:00
lint = = " warnings " | | ide_db ::helpers ::lint_eq_or_in_group ( & code . code , lint )
2021-11-22 07:14:21 -06:00
} ) = >
{
2021-10-14 01:37:57 -05:00
lsp_types ::DiagnosticSeverity ::HINT
2020-07-09 08:34:37 -05:00
}
2021-11-22 07:14:21 -06:00
// HACK: special case for `warnings` rustc lint.
Some ( code )
if config . warnings_as_info . iter ( ) . any ( | lint | {
2022-03-12 06:04:13 -06:00
lint = = " warnings " | | ide_db ::helpers ::lint_eq_or_in_group ( & code . code , lint )
2021-11-22 07:14:21 -06:00
} ) = >
{
2021-10-14 01:37:57 -05:00
lsp_types ::DiagnosticSeverity ::INFORMATION
2020-06-16 15:26:33 -05:00
}
2021-10-14 01:37:57 -05:00
_ = > lsp_types ::DiagnosticSeverity ::WARNING ,
2020-06-16 15:26:33 -05:00
} ,
2021-10-14 01:37:57 -05:00
DiagnosticLevel ::Note = > lsp_types ::DiagnosticSeverity ::INFORMATION ,
DiagnosticLevel ::Help = > lsp_types ::DiagnosticSeverity ::HINT ,
2021-03-02 06:27:29 -06:00
_ = > return None ,
2020-05-14 19:08:50 -05:00
} ;
Some ( res )
2020-05-14 18:51:48 -05:00
}
2021-04-01 08:51:02 -05:00
/// Checks whether a file name is from macro invocation and does not refer to an actual file.
fn is_dummy_macro_file ( file_name : & str ) -> bool {
// FIXME: current rustc does not seem to emit `<macro file>` files anymore?
file_name . starts_with ( '<' ) & & file_name . ends_with ( '>' )
}
2020-05-14 18:51:48 -05:00
/// Converts a Rust span to a LSP location
2021-04-20 22:03:35 -05:00
fn location (
config : & DiagnosticsMapConfig ,
2021-07-17 09:40:13 -05:00
workspace_root : & AbsPath ,
2021-04-20 22:03:35 -05:00
span : & DiagnosticSpan ,
2022-05-07 22:06:52 -05:00
snap : & GlobalStateSnapshot ,
2021-04-20 22:03:35 -05:00
) -> lsp_types ::Location {
let file_name = resolve_path ( config , workspace_root , & span . file_name ) ;
2020-06-13 04:00:06 -05:00
let uri = url_from_abs_path ( & file_name ) ;
2020-05-14 18:51:48 -05:00
2022-05-18 05:35:51 -05:00
let range = {
2022-10-25 06:43:26 -05:00
let position_encoding = snap . config . position_encoding ( ) ;
2022-05-07 22:06:52 -05:00
lsp_types ::Range ::new (
2022-10-25 06:43:26 -05:00
position ( & position_encoding , span , span . line_start , span . column_start ) ,
position ( & position_encoding , span , span . line_end , span . column_end ) ,
2022-05-07 22:06:52 -05:00
)
2022-05-18 05:35:51 -05:00
} ;
2022-05-07 22:06:52 -05:00
lsp_types ::Location ::new ( uri , range )
}
fn position (
2022-10-25 06:43:26 -05:00
position_encoding : & PositionEncoding ,
2022-05-07 22:06:52 -05:00
span : & DiagnosticSpan ,
line_offset : usize ,
2023-05-06 02:52:32 -05:00
column_offset_utf32 : usize ,
2022-05-07 22:06:52 -05:00
) -> lsp_types ::Position {
let line_index = line_offset - span . line_start ;
2023-05-06 02:52:32 -05:00
let column_offset_encoded = match span . text . get ( line_index ) {
// Fast path.
Some ( line ) if line . text . is_ascii ( ) = > column_offset_utf32 ,
Some ( line ) = > {
let line_prefix_len = line
. text
. char_indices ( )
. take ( column_offset_utf32 )
. last ( )
. map ( | ( pos , c ) | pos + c . len_utf8 ( ) )
. unwrap_or ( 0 ) ;
let line_prefix = & line . text [ .. line_prefix_len ] ;
match position_encoding {
PositionEncoding ::Utf8 = > line_prefix . len ( ) ,
PositionEncoding ::Wide ( enc ) = > enc . measure ( line_prefix ) ,
2022-05-07 22:06:52 -05:00
}
}
2023-05-06 02:52:32 -05:00
None = > column_offset_utf32 ,
} ;
2020-05-14 18:51:48 -05:00
2022-05-07 22:06:52 -05:00
lsp_types ::Position {
line : ( line_offset as u32 ) . saturating_sub ( 1 ) ,
2023-05-06 02:52:32 -05:00
character : ( column_offset_encoded as u32 ) . saturating_sub ( 1 ) ,
2022-05-07 22:06:52 -05:00
}
2020-05-14 18:51:48 -05:00
}
2021-03-31 10:50:19 -05:00
/// Extracts a suitable "primary" location from a rustc diagnostic.
///
/// This takes locations pointing into the standard library, or generally outside the current
/// workspace into account and tries to avoid those, in case macros are involved.
2021-04-20 22:03:35 -05:00
fn primary_location (
config : & DiagnosticsMapConfig ,
2021-07-17 09:40:13 -05:00
workspace_root : & AbsPath ,
2021-04-20 22:03:35 -05:00
span : & DiagnosticSpan ,
2022-05-07 22:06:52 -05:00
snap : & GlobalStateSnapshot ,
2021-04-20 22:03:35 -05:00
) -> lsp_types ::Location {
2021-03-31 10:50:19 -05:00
let span_stack = std ::iter ::successors ( Some ( span ) , | span | Some ( & span . expansion . as_ref ( ) ? . span ) ) ;
2021-04-01 08:51:02 -05:00
for span in span_stack . clone ( ) {
2021-04-20 22:03:35 -05:00
let abs_path = resolve_path ( config , workspace_root , & span . file_name ) ;
2021-04-01 08:51:02 -05:00
if ! is_dummy_macro_file ( & span . file_name ) & & abs_path . starts_with ( workspace_root ) {
2022-05-07 22:06:52 -05:00
return location ( config , workspace_root , span , snap ) ;
2021-03-31 10:50:19 -05:00
}
}
2021-04-01 08:51:02 -05:00
// Fall back to the outermost macro invocation if no suitable span comes up.
let last_span = span_stack . last ( ) . unwrap ( ) ;
2022-05-07 22:06:52 -05:00
location ( config , workspace_root , last_span , snap )
2021-03-31 10:50:19 -05:00
}
2021-03-30 12:29:26 -05:00
/// Converts a secondary Rust span to a LSP related information
2020-05-14 18:51:48 -05:00
///
/// If the span is unlabelled this will return `None`.
2020-07-09 08:34:37 -05:00
fn diagnostic_related_information (
2021-04-20 22:03:35 -05:00
config : & DiagnosticsMapConfig ,
2021-07-17 09:40:13 -05:00
workspace_root : & AbsPath ,
2020-07-09 08:34:37 -05:00
span : & DiagnosticSpan ,
2022-05-07 22:06:52 -05:00
snap : & GlobalStateSnapshot ,
2020-07-09 08:34:37 -05:00
) -> Option < lsp_types ::DiagnosticRelatedInformation > {
2020-05-14 19:08:50 -05:00
let message = span . label . clone ( ) ? ;
2022-05-07 22:06:52 -05:00
let location = location ( config , workspace_root , span , snap ) ;
2020-07-09 08:34:37 -05:00
Some ( lsp_types ::DiagnosticRelatedInformation { location , message } )
2020-05-14 18:51:48 -05:00
}
2021-04-20 22:03:35 -05:00
/// Resolves paths applying any matching path prefix remappings, and then
/// joining the path to the workspace root.
2021-07-17 09:40:13 -05:00
fn resolve_path (
config : & DiagnosticsMapConfig ,
workspace_root : & AbsPath ,
file_name : & str ,
) -> AbsPathBuf {
2021-04-21 17:09:37 -05:00
match config
. remap_prefix
. iter ( )
. find_map ( | ( from , to ) | file_name . strip_prefix ( from ) . map ( | file_name | ( to , file_name ) ) )
{
2022-12-23 12:42:58 -06:00
Some ( ( to , file_name ) ) = > workspace_root . join ( format! ( " {to} {file_name} " ) ) ,
2021-04-20 22:03:35 -05:00
None = > workspace_root . join ( file_name ) ,
2021-04-19 22:18:54 -05:00
}
}
2021-01-15 11:11:54 -06:00
struct SubDiagnostic {
related : lsp_types ::DiagnosticRelatedInformation ,
2021-12-04 22:59:26 -06:00
suggested_fix : Option < Fix > ,
2021-01-15 11:11:54 -06:00
}
2020-05-14 18:51:48 -05:00
enum MappedRustChildDiagnostic {
2021-01-15 11:11:54 -06:00
SubDiagnostic ( SubDiagnostic ) ,
2020-05-14 18:51:48 -05:00
MessageLine ( String ) ,
}
fn map_rust_child_diagnostic (
2021-04-20 22:03:35 -05:00
config : & DiagnosticsMapConfig ,
2021-07-17 09:40:13 -05:00
workspace_root : & AbsPath ,
2020-07-09 08:34:37 -05:00
rd : & flycheck ::Diagnostic ,
2022-05-07 22:06:52 -05:00
snap : & GlobalStateSnapshot ,
2020-05-14 18:51:48 -05:00
) -> MappedRustChildDiagnostic {
let spans : Vec < & DiagnosticSpan > = rd . spans . iter ( ) . filter ( | s | s . is_primary ) . collect ( ) ;
if spans . is_empty ( ) {
// `rustc` uses these spanless children as a way to print multi-line
// messages
return MappedRustChildDiagnostic ::MessageLine ( rd . message . clone ( ) ) ;
}
2024-02-01 09:16:38 -06:00
let mut edit_map : FxHashMap < lsp_types ::Url , Vec < lsp_types ::TextEdit > > = FxHashMap ::default ( ) ;
2021-08-06 08:52:47 -05:00
let mut suggested_replacements = Vec ::new ( ) ;
2023-01-03 08:46:08 -06:00
let mut is_preferred = true ;
2020-05-14 18:51:48 -05:00
for & span in & spans {
Don't filter code suggestions on Applicability
I've noticed that there are various suggestions that rust-analyzer seems
to filter out, even if they make sense.
Here's an example of where it seems like there should be a suggestion,
but there isn't:
![https://i.imgur.com/wsjM6iz.png](https://i.imgur.com/wsjM6iz.png)
It turns out that this specific suggestion is not considered
`MachineApplicable`, which are the only suggestions that rust-analyzer
accepts. However if you read the documentation for `MachineApplicable`,
https://github.com/rust-lang/rust/blob/b3897e3d1302391ed02efbac1dce8073646b8173/compiler/rustc_lint_defs/src/lib.rs#L27-L29
then you realize that these are specifically only those suggestions that
rust-analyzer could even automatically apply (in some distant future,
behind some setting or so). Other suggestions that may have some
semantic impact do not use `MachineApplicable`. So all other suggestions
are still intended to be suggested to the user, just not automatically
applied without the user being consulted.
https://github.com/rust-lang/rust/blob/b3897e3d1302391ed02efbac1dce8073646b8173/compiler/rustc_lint_defs/src/lib.rs#L22-L24
So with that in mind, rust-analyzer should almost definitely not filter
out `MaybeIncorrect` (which honestly is named horribly, it just means
that it's a semantic change, not just a syntactical one).
Then there's `HasPlaceholders` which basically is just another semantic
one, but with placeholders. The user will have to make some adjustments,
but the suggestion still is perfectly valid. rust-analyzer could
probably detect those placeholders and put proper "tab through" markers
there for the IDE, but that's not necessary for now.
Then the last one is `Unspecified` which is so unknown that I don't even
know how to judge it, meaning that the suggestion should probably also
just be suggested to the user and then they can decide.
So with all that in mind, I'm proposing to get rid of the check for
Applicability entirely.
2021-02-01 09:57:04 -06:00
if let Some ( suggested_replacement ) = & span . suggested_replacement {
2021-08-06 08:58:55 -05:00
if ! suggested_replacement . is_empty ( ) {
suggested_replacements . push ( suggested_replacement ) ;
}
2022-05-07 22:06:52 -05:00
let location = location ( config , workspace_root , span , snap ) ;
2020-07-09 08:34:37 -05:00
let edit = lsp_types ::TextEdit ::new ( location . range , suggested_replacement . clone ( ) ) ;
2022-04-25 08:21:30 -05:00
// Only actually emit a quickfix if the suggestion is "valid enough".
// We accept both "MaybeIncorrect" and "MachineApplicable". "MaybeIncorrect" means that
// the suggestion is *complete* (contains no placeholders where code needs to be
// inserted), but might not be what the user wants, or might need minor adjustments.
if matches! (
span . suggestion_applicability ,
None | Some ( Applicability ::MaybeIncorrect | Applicability ::MachineApplicable )
) {
edit_map . entry ( location . uri ) . or_default ( ) . push ( edit ) ;
}
2023-01-03 08:46:08 -06:00
is_preferred & =
matches! ( span . suggestion_applicability , Some ( Applicability ::MachineApplicable ) ) ;
2020-05-14 18:51:48 -05:00
}
}
2021-08-06 08:52:47 -05:00
// rustc renders suggestion diagnostics by appending the suggested replacement, so do the same
// here, otherwise the diagnostic text is missing useful information.
let mut message = rd . message . clone ( ) ;
if ! suggested_replacements . is_empty ( ) {
message . push_str ( " : " ) ;
let suggestions =
2022-12-23 12:42:58 -06:00
suggested_replacements . iter ( ) . map ( | suggestion | format! ( " ` {suggestion} ` " ) ) . join ( " , " ) ;
2021-08-06 08:52:47 -05:00
message . push_str ( & suggestions ) ;
}
2020-05-14 19:08:50 -05:00
if edit_map . is_empty ( ) {
2021-01-15 11:11:54 -06:00
MappedRustChildDiagnostic ::SubDiagnostic ( SubDiagnostic {
2020-12-05 18:24:37 -06:00
related : lsp_types ::DiagnosticRelatedInformation {
2022-05-07 22:06:52 -05:00
location : location ( config , workspace_root , spans [ 0 ] , snap ) ,
2021-08-06 08:52:47 -05:00
message ,
2020-12-05 18:24:37 -06:00
} ,
suggested_fix : None ,
2021-01-15 11:11:54 -06:00
} )
2020-05-14 19:08:50 -05:00
} else {
2021-01-15 11:11:54 -06:00
MappedRustChildDiagnostic ::SubDiagnostic ( SubDiagnostic {
2020-12-05 18:24:37 -06:00
related : lsp_types ::DiagnosticRelatedInformation {
2022-05-07 22:06:52 -05:00
location : location ( config , workspace_root , spans [ 0 ] , snap ) ,
2021-08-06 08:52:47 -05:00
message : message . clone ( ) ,
2020-12-05 18:24:37 -06:00
} ,
2021-12-04 22:59:26 -06:00
suggested_fix : Some ( Fix {
ranges : spans
. iter ( )
2022-05-07 22:06:52 -05:00
. map ( | & span | location ( config , workspace_root , span , snap ) . range )
2021-12-04 22:59:26 -06:00
. collect ( ) ,
action : lsp_ext ::CodeAction {
title : message ,
group : None ,
kind : Some ( lsp_types ::CodeActionKind ::QUICKFIX ) ,
edit : Some ( lsp_ext ::SnippetWorkspaceEdit {
// FIXME: there's no good reason to use edit_map here....
changes : Some ( edit_map ) ,
document_changes : None ,
change_annotations : None ,
} ) ,
2023-01-03 08:46:08 -06:00
is_preferred : Some ( is_preferred ) ,
2021-12-04 22:59:26 -06:00
data : None ,
2022-04-19 11:37:18 -05:00
command : None ,
2021-12-04 22:59:26 -06:00
} ,
2020-05-17 17:11:40 -05:00
} ) ,
2021-01-15 11:11:54 -06:00
} )
2020-05-14 18:51:48 -05:00
}
}
#[ derive(Debug) ]
pub ( crate ) struct MappedRustDiagnostic {
2020-07-09 08:35:52 -05:00
pub ( crate ) url : lsp_types ::Url ,
2020-07-09 08:34:37 -05:00
pub ( crate ) diagnostic : lsp_types ::Diagnostic ,
2021-12-04 22:59:26 -06:00
pub ( crate ) fix : Option < Fix > ,
2020-05-14 18:51:48 -05:00
}
/// Converts a Rust root diagnostic to LSP form
///
/// This flattens the Rust diagnostic by:
///
/// 1. Creating a LSP diagnostic with the root message and primary span.
/// 2. Adding any labelled secondary spans to `relatedInformation`
/// 3. Categorising child diagnostics as either `SuggestedFix`es,
/// `relatedInformation` or additional message lines.
///
/// If the diagnostic has no primary span this will return `None`
pub ( crate ) fn map_rust_diagnostic_to_lsp (
2020-08-18 09:03:15 -05:00
config : & DiagnosticsMapConfig ,
2020-06-25 02:13:46 -05:00
rd : & flycheck ::Diagnostic ,
2021-07-17 09:40:13 -05:00
workspace_root : & AbsPath ,
2022-05-07 22:06:52 -05:00
snap : & GlobalStateSnapshot ,
2020-05-14 18:51:48 -05:00
) -> Vec < MappedRustDiagnostic > {
let primary_spans : Vec < & DiagnosticSpan > = rd . spans . iter ( ) . filter ( | s | s . is_primary ) . collect ( ) ;
if primary_spans . is_empty ( ) {
2020-05-14 19:08:50 -05:00
return Vec ::new ( ) ;
2020-05-14 18:51:48 -05:00
}
2021-03-16 19:56:31 -05:00
let severity = diagnostic_severity ( config , rd . level , rd . code . clone ( ) ) ;
2020-05-14 18:51:48 -05:00
let mut source = String ::from ( " rustc " ) ;
let mut code = rd . code . as_ref ( ) . map ( | c | c . code . clone ( ) ) ;
2023-05-13 09:12:32 -05:00
if let Some ( code_val ) = & code {
if config . check_ignore . contains ( code_val ) {
return Vec ::new ( ) ;
}
}
2020-05-14 18:51:48 -05:00
if let Some ( code_val ) = & code {
// See if this is an RFC #2103 scoped lint (e.g. from Clippy)
let scoped_code : Vec < & str > = code_val . split ( " :: " ) . collect ( ) ;
if scoped_code . len ( ) = = 2 {
source = String ::from ( scoped_code [ 0 ] ) ;
code = Some ( String ::from ( scoped_code [ 1 ] ) ) ;
}
}
let mut needs_primary_span_label = true ;
2021-01-15 11:11:54 -06:00
let mut subdiagnostics = Vec ::new ( ) ;
2020-05-14 19:08:50 -05:00
let mut tags = Vec ::new ( ) ;
2020-05-14 18:51:48 -05:00
for secondary_span in rd . spans . iter ( ) . filter ( | s | ! s . is_primary ) {
2022-05-07 22:06:52 -05:00
let related = diagnostic_related_information ( config , workspace_root , secondary_span , snap ) ;
2020-05-14 18:51:48 -05:00
if let Some ( related ) = related {
2021-01-15 11:11:54 -06:00
subdiagnostics . push ( SubDiagnostic { related , suggested_fix : None } ) ;
2020-05-14 18:51:48 -05:00
}
}
let mut message = rd . message . clone ( ) ;
for child in & rd . children {
2022-05-07 22:06:52 -05:00
let child = map_rust_child_diagnostic ( config , workspace_root , child , snap ) ;
2020-05-14 18:51:48 -05:00
match child {
2021-01-15 11:11:54 -06:00
MappedRustChildDiagnostic ::SubDiagnostic ( sub ) = > {
subdiagnostics . push ( sub ) ;
2020-12-05 18:24:37 -06:00
}
2020-05-14 18:51:48 -05:00
MappedRustChildDiagnostic ::MessageLine ( message_line ) = > {
2020-05-14 19:08:50 -05:00
format_to! ( message , " \n {} " , message_line ) ;
2020-05-14 18:51:48 -05:00
// These secondary messages usually duplicate the content of the
// primary span label.
needs_primary_span_label = false ;
}
}
}
2020-07-09 08:34:37 -05:00
if let Some ( code ) = & rd . code {
let code = code . code . as_str ( ) ;
if matches! (
code ,
" dead_code "
| " unknown_lints "
| " unreachable_code "
| " unused_attributes "
| " unused_imports "
| " unused_macros "
| " unused_variables "
) {
2021-10-14 01:37:57 -05:00
tags . push ( lsp_types ::DiagnosticTag ::UNNECESSARY ) ;
2020-07-09 08:34:37 -05:00
}
2020-05-14 18:51:48 -05:00
2020-07-09 08:34:37 -05:00
if matches! ( code , " deprecated " ) {
2021-10-14 01:37:57 -05:00
tags . push ( lsp_types ::DiagnosticTag ::DEPRECATED ) ;
2020-07-09 08:34:37 -05:00
}
2020-05-14 18:51:48 -05:00
}
2020-11-17 14:56:37 -06:00
let code_description = match source . as_str ( ) {
" rustc " = > rustc_code_description ( code . as_deref ( ) ) ,
" clippy " = > clippy_code_description ( code . as_deref ( ) ) ,
_ = > None ,
} ;
2020-11-17 14:26:37 -06:00
2020-05-14 18:51:48 -05:00
primary_spans
. iter ( )
2020-12-05 18:24:37 -06:00
. flat_map ( | primary_span | {
2022-05-07 22:06:52 -05:00
let primary_location = primary_location ( config , workspace_root , primary_span , snap ) ;
2022-11-18 12:47:45 -06:00
let message = {
let mut message = message . clone ( ) ;
if needs_primary_span_label {
if let Some ( primary_span_label ) = & primary_span . label {
format_to! ( message , " \n {} " , primary_span_label ) ;
}
2020-05-14 18:51:48 -05:00
}
2022-11-18 12:47:45 -06:00
message
} ;
2020-12-05 18:24:37 -06:00
// Each primary diagnostic span may result in multiple LSP diagnostics.
let mut diagnostics = Vec ::new ( ) ;
2021-03-30 12:29:26 -05:00
let mut related_info_macro_calls = vec! [ ] ;
2020-12-05 18:24:37 -06:00
2020-05-14 18:51:48 -05:00
// If error occurs from macro expansion, add related info pointing to
// where the error originated
2020-09-06 01:22:01 -05:00
// Also, we would generate an additional diagnostic, so that exact place of macro
// will be highlighted in the error origin place.
2021-03-31 10:50:19 -05:00
let span_stack = std ::iter ::successors ( Some ( * primary_span ) , | span | {
2021-03-30 12:29:26 -05:00
Some ( & span . expansion . as_ref ( ) ? . span )
2021-03-31 10:50:19 -05:00
} ) ;
2021-04-01 07:44:20 -05:00
for ( i , span ) in span_stack . enumerate ( ) {
2021-04-01 08:51:02 -05:00
if is_dummy_macro_file ( & span . file_name ) {
continue ;
}
2021-04-01 07:44:20 -05:00
// First span is the original diagnostic, others are macro call locations that
// generated that code.
let is_in_macro_call = i ! = 0 ;
2022-05-07 22:06:52 -05:00
let secondary_location = location ( config , workspace_root , span , snap ) ;
2021-03-31 10:50:19 -05:00
if secondary_location = = primary_location {
continue ;
}
2021-03-30 12:29:26 -05:00
related_info_macro_calls . push ( lsp_types ::DiagnosticRelatedInformation {
2021-03-31 10:50:19 -05:00
location : secondary_location . clone ( ) ,
2021-04-01 07:44:20 -05:00
message : if is_in_macro_call {
2024-02-09 09:46:08 -06:00
" Error originated from macro call here " . to_owned ( )
2021-04-01 07:44:20 -05:00
} else {
2024-02-09 09:46:08 -06:00
" Actual error occurred here " . to_owned ( )
2021-04-01 07:44:20 -05:00
} ,
2020-12-05 18:24:37 -06:00
} ) ;
// For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
let information_for_additional_diagnostic =
vec! [ lsp_types ::DiagnosticRelatedInformation {
2021-03-31 10:18:21 -05:00
location : primary_location . clone ( ) ,
2024-02-09 09:46:08 -06:00
message : " Exact error occurred here " . to_owned ( ) ,
2020-12-05 18:24:37 -06:00
} ] ;
let diagnostic = lsp_types ::Diagnostic {
2021-03-31 10:50:19 -05:00
range : secondary_location . range ,
2021-03-30 12:29:26 -05:00
// downgrade to hint if we're pointing at the macro
2021-10-14 01:37:57 -05:00
severity : Some ( lsp_types ::DiagnosticSeverity ::HINT ) ,
2020-12-05 18:24:37 -06:00
code : code . clone ( ) . map ( lsp_types ::NumberOrString ::String ) ,
code_description : code_description . clone ( ) ,
source : Some ( source . clone ( ) ) ,
message : message . clone ( ) ,
related_information : Some ( information_for_additional_diagnostic ) ,
tags : if tags . is_empty ( ) { None } else { Some ( tags . clone ( ) ) } ,
2022-11-18 12:47:45 -06:00
data : Some ( serde_json ::json! ( { " rendered " : rd . rendered } ) ) ,
2020-09-06 01:22:01 -05:00
} ;
2020-12-05 18:24:37 -06:00
diagnostics . push ( MappedRustDiagnostic {
2021-03-31 10:50:19 -05:00
url : secondary_location . uri ,
2020-12-05 18:24:37 -06:00
diagnostic ,
2021-12-04 21:59:05 -06:00
fix : None ,
2020-12-05 18:24:37 -06:00
} ) ;
}
// Emit the primary diagnostic.
diagnostics . push ( MappedRustDiagnostic {
2021-03-31 10:18:21 -05:00
url : primary_location . uri . clone ( ) ,
2020-12-05 18:24:37 -06:00
diagnostic : lsp_types ::Diagnostic {
2021-03-31 10:18:21 -05:00
range : primary_location . range ,
2020-12-05 18:24:37 -06:00
severity ,
code : code . clone ( ) . map ( lsp_types ::NumberOrString ::String ) ,
code_description : code_description . clone ( ) ,
source : Some ( source . clone ( ) ) ,
message ,
2021-03-30 12:29:26 -05:00
related_information : {
let info = related_info_macro_calls
2021-01-15 11:11:54 -06:00
. iter ( )
2021-03-30 12:29:26 -05:00
. cloned ( )
. chain ( subdiagnostics . iter ( ) . map ( | sub | sub . related . clone ( ) ) )
2021-01-15 11:11:54 -06:00
. collect ::< Vec < _ > > ( ) ;
2021-03-30 12:29:26 -05:00
if info . is_empty ( ) {
None
} else {
Some ( info )
}
2020-12-05 18:24:37 -06:00
} ,
tags : if tags . is_empty ( ) { None } else { Some ( tags . clone ( ) ) } ,
2022-11-18 12:47:45 -06:00
data : Some ( serde_json ::json! ( { " rendered " : rd . rendered } ) ) ,
2020-05-14 18:51:48 -05:00
} ,
2021-12-04 21:59:05 -06:00
fix : None ,
2020-12-05 18:24:37 -06:00
} ) ;
// Emit hint-level diagnostics for all `related_information` entries such as "help"s.
// This is useful because they will show up in the user's editor, unlike
// `related_information`, which just produces hard-to-read links, at least in VS Code.
let back_ref = lsp_types ::DiagnosticRelatedInformation {
2021-03-31 10:18:21 -05:00
location : primary_location ,
2024-02-09 09:46:08 -06:00
message : " original diagnostic " . to_owned ( ) ,
2020-05-14 18:51:48 -05:00
} ;
2021-01-15 11:11:54 -06:00
for sub in & subdiagnostics {
2020-12-05 18:24:37 -06:00
diagnostics . push ( MappedRustDiagnostic {
2021-01-15 11:11:54 -06:00
url : sub . related . location . uri . clone ( ) ,
2021-12-04 21:59:05 -06:00
fix : sub . suggested_fix . clone ( ) ,
2020-12-05 18:24:37 -06:00
diagnostic : lsp_types ::Diagnostic {
2021-01-15 11:11:54 -06:00
range : sub . related . location . range ,
2021-10-14 01:37:57 -05:00
severity : Some ( lsp_types ::DiagnosticSeverity ::HINT ) ,
2020-12-05 18:24:37 -06:00
code : code . clone ( ) . map ( lsp_types ::NumberOrString ::String ) ,
code_description : code_description . clone ( ) ,
source : Some ( source . clone ( ) ) ,
2022-07-18 14:21:24 -05:00
message : sub . related . message . clone ( ) ,
2020-12-05 18:24:37 -06:00
related_information : Some ( vec! [ back_ref . clone ( ) ] ) ,
tags : None , // don't apply modifiers again
data : None ,
} ,
} ) ;
2020-09-06 01:22:01 -05:00
}
2020-12-05 18:24:37 -06:00
diagnostics
2020-05-14 18:51:48 -05:00
} )
. collect ( )
}
2020-11-17 14:56:37 -06:00
fn rustc_code_description ( code : Option < & str > ) -> Option < lsp_types ::CodeDescription > {
code . filter ( | code | {
let mut chars = code . chars ( ) ;
2020-11-17 14:26:37 -06:00
chars . next ( ) . map_or ( false , | c | c = = 'E' )
& & chars . by_ref ( ) . take ( 4 ) . all ( | c | c . is_ascii_digit ( ) )
& & chars . next ( ) . is_none ( )
} )
2020-11-17 14:56:37 -06:00
. and_then ( | code | {
2022-12-23 12:42:58 -06:00
lsp_types ::Url ::parse ( & format! ( " https://doc.rust-lang.org/error-index.html# {code} " ) )
2020-11-17 14:26:37 -06:00
. ok ( )
. map ( | href | lsp_types ::CodeDescription { href } )
} )
}
2020-11-17 14:56:37 -06:00
fn clippy_code_description ( code : Option < & str > ) -> Option < lsp_types ::CodeDescription > {
code . and_then ( | code | {
lsp_types ::Url ::parse ( & format! (
2022-12-30 01:59:11 -06:00
" https://rust-lang.github.io/rust-clippy/master/index.html#{code} "
2020-11-17 14:56:37 -06:00
) )
. ok ( )
. map ( | href | lsp_types ::CodeDescription { href } )
} )
}
2020-05-14 18:51:48 -05:00
#[ cfg(test) ]
2020-06-13 04:00:06 -05:00
#[ cfg(not(windows)) ]
2020-05-14 18:51:48 -05:00
mod tests {
2022-08-08 18:16:32 -05:00
use std ::path ::Path ;
2021-07-17 09:40:13 -05:00
2022-05-07 22:06:52 -05:00
use crate ::{ config ::Config , global_state ::GlobalState } ;
2020-05-14 18:51:48 -05:00
use super ::* ;
2020-08-21 06:19:31 -05:00
use expect_test ::{ expect_file , ExpectFile } ;
2022-05-07 22:06:52 -05:00
use lsp_types ::ClientCapabilities ;
2020-07-09 09:04:29 -05:00
fn check ( diagnostics_json : & str , expect : ExpectFile ) {
2020-08-18 09:03:15 -05:00
check_with_config ( DiagnosticsMapConfig ::default ( ) , diagnostics_json , expect )
2020-07-09 09:04:29 -05:00
}
2020-08-18 09:03:15 -05:00
fn check_with_config ( config : DiagnosticsMapConfig , diagnostics_json : & str , expect : ExpectFile ) {
2020-07-09 09:04:29 -05:00
let diagnostic : flycheck ::Diagnostic = serde_json ::from_str ( diagnostics_json ) . unwrap ( ) ;
2021-07-17 09:40:13 -05:00
let workspace_root : & AbsPath = Path ::new ( " /test/ " ) . try_into ( ) . unwrap ( ) ;
2022-05-07 22:06:52 -05:00
let ( sender , _ ) = crossbeam_channel ::unbounded ( ) ;
let state = GlobalState ::new (
sender ,
2023-09-19 13:34:43 -05:00
Config ::new (
workspace_root . to_path_buf ( ) ,
ClientCapabilities ::default ( ) ,
Vec ::new ( ) ,
false ,
) ,
2022-05-07 22:06:52 -05:00
) ;
let snap = state . snapshot ( ) ;
2022-11-18 13:11:48 -06:00
let mut actual = map_rust_diagnostic_to_lsp ( & config , & diagnostic , workspace_root , & snap ) ;
actual . iter_mut ( ) . for_each ( | diag | diag . diagnostic . data = None ) ;
2020-07-09 09:04:29 -05:00
expect . assert_debug_eq ( & actual )
2020-05-14 18:51:48 -05:00
}
#[ test ]
2020-07-09 09:04:29 -05:00
fn rustc_incompatible_type_for_trait ( ) {
check (
2020-05-14 18:51:48 -05:00
r ##" {
" message " : " method `next` has an incompatible type for trait " ,
" code " : {
" code " : " E0053 " ,
" explanation " : " \n The parameters of any trait method must match between a trait implementation \n and the trait definition. \n \n Here are a couple examples of this error: \n \n ```compile_fail,E0053 \n trait Foo { \n fn foo(x: u16); \n fn bar(&self); \n } \n \n struct Bar; \n \n impl Foo for Bar { \n // error, expected u16, found i16 \n fn foo(x: i16) { } \n \n // error, types differ in mutability \n fn bar(&mut self) { } \n } \n ``` \n "
} ,
" level " : " error " ,
" spans " : [
{
" file_name " : " compiler/ty/list_iter.rs " ,
" byte_start " : 1307 ,
" byte_end " : 1350 ,
" line_start " : 52 ,
" line_end " : 52 ,
" column_start " : 5 ,
" column_end " : 48 ,
" is_primary " : true ,
" text " : [
{
" text " : " fn next(&self) -> Option<&'list ty::Ref<M>> { " ,
" highlight_start " : 5 ,
" highlight_end " : 48
}
] ,
" label " : " types differ in mutability " ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [
{
" message " : " expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>` \n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>` " ,
" code " : null ,
" level " : " note " ,
" spans " : [ ] ,
" children " : [ ] ,
" rendered " : null
}
] ,
" rendered " : " error[E0053]: method `next` has an incompatible type for trait \n --> compiler/ty/list_iter.rs:52:5 \n | \n 52 | fn next(&self) -> Option<&'list ty::Ref<M>> { \n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability \n | \n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>` \n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>` \n \n "
}
" ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/rustc_incompatible_type_for_trait.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
}
#[ test ]
2020-07-09 09:04:29 -05:00
fn rustc_unused_variable ( ) {
check (
2020-05-14 18:51:48 -05:00
r ##" {
" message " : " unused variable: `foo` " ,
" code " : {
" code " : " unused_variables " ,
" explanation " : null
} ,
" level " : " warning " ,
" spans " : [
{
" file_name " : " driver/subcommand/repl.rs " ,
" byte_start " : 9228 ,
" byte_end " : 9231 ,
" line_start " : 291 ,
" line_end " : 291 ,
" column_start " : 9 ,
" column_end " : 12 ,
" is_primary " : true ,
" text " : [
{
" text " : " let foo = 42; " ,
" highlight_start " : 9 ,
" highlight_end " : 12
}
] ,
" label " : null ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [
{
" message " : " #[warn(unused_variables)] on by default " ,
" code " : null ,
" level " : " note " ,
" spans " : [ ] ,
" children " : [ ] ,
" rendered " : null
} ,
{
" message " : " consider prefixing with an underscore " ,
" code " : null ,
" level " : " help " ,
" spans " : [
{
" file_name " : " driver/subcommand/repl.rs " ,
" byte_start " : 9228 ,
" byte_end " : 9231 ,
" line_start " : 291 ,
" line_end " : 291 ,
" column_start " : 9 ,
" column_end " : 12 ,
" is_primary " : true ,
" text " : [
{
" text " : " let foo = 42; " ,
" highlight_start " : 9 ,
" highlight_end " : 12
}
] ,
" label " : null ,
" suggested_replacement " : " _foo " ,
" suggestion_applicability " : " MachineApplicable " ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : null
}
] ,
" rendered " : " warning: unused variable: `foo` \n --> driver/subcommand/repl.rs:291:9 \n | \n 291 | let foo = 42; \n | ^^^ help: consider prefixing with an underscore: `_foo` \n | \n = note: #[warn(unused_variables)] on by default \n \n "
} " ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/rustc_unused_variable.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
2020-06-16 15:26:33 -05:00
}
#[ test ]
#[ cfg(not(windows)) ]
2020-07-09 09:04:29 -05:00
fn rustc_unused_variable_as_info ( ) {
check_with_config (
2020-08-18 09:03:15 -05:00
DiagnosticsMapConfig {
2024-02-09 09:46:08 -06:00
warnings_as_info : vec ! [ " unused_variables " . to_owned ( ) ] ,
2020-08-18 09:03:15 -05:00
.. DiagnosticsMapConfig ::default ( )
2020-07-09 09:04:29 -05:00
} ,
2020-06-16 15:26:33 -05:00
r ##" {
" message " : " unused variable: `foo` " ,
" code " : {
" code " : " unused_variables " ,
" explanation " : null
} ,
" level " : " warning " ,
" spans " : [
{
" file_name " : " driver/subcommand/repl.rs " ,
" byte_start " : 9228 ,
" byte_end " : 9231 ,
" line_start " : 291 ,
" line_end " : 291 ,
" column_start " : 9 ,
" column_end " : 12 ,
" is_primary " : true ,
" text " : [
{
" text " : " let foo = 42; " ,
" highlight_start " : 9 ,
" highlight_end " : 12
}
] ,
" label " : null ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [
{
" message " : " #[warn(unused_variables)] on by default " ,
" code " : null ,
" level " : " note " ,
" spans " : [ ] ,
" children " : [ ] ,
" rendered " : null
} ,
{
" message " : " consider prefixing with an underscore " ,
" code " : null ,
" level " : " help " ,
" spans " : [
{
" file_name " : " driver/subcommand/repl.rs " ,
" byte_start " : 9228 ,
" byte_end " : 9231 ,
" line_start " : 291 ,
" line_end " : 291 ,
" column_start " : 9 ,
" column_end " : 12 ,
" is_primary " : true ,
" text " : [
{
" text " : " let foo = 42; " ,
" highlight_start " : 9 ,
" highlight_end " : 12
}
] ,
" label " : null ,
" suggested_replacement " : " _foo " ,
" suggestion_applicability " : " MachineApplicable " ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : null
}
] ,
" rendered " : " warning: unused variable: `foo` \n --> driver/subcommand/repl.rs:291:9 \n | \n 291 | let foo = 42; \n | ^^^ help: consider prefixing with an underscore: `_foo` \n | \n = note: #[warn(unused_variables)] on by default \n \n "
} " ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/rustc_unused_variable_as_info.txt " ] ,
2020-06-16 15:26:33 -05:00
) ;
}
#[ test ]
#[ cfg(not(windows)) ]
2020-07-09 09:04:29 -05:00
fn rustc_unused_variable_as_hint ( ) {
check_with_config (
2020-08-18 09:03:15 -05:00
DiagnosticsMapConfig {
2024-02-09 09:46:08 -06:00
warnings_as_hint : vec ! [ " unused_variables " . to_owned ( ) ] ,
2020-08-18 09:03:15 -05:00
.. DiagnosticsMapConfig ::default ( )
2020-07-09 09:04:29 -05:00
} ,
2020-06-16 15:26:33 -05:00
r ##" {
" message " : " unused variable: `foo` " ,
" code " : {
" code " : " unused_variables " ,
" explanation " : null
} ,
" level " : " warning " ,
" spans " : [
{
" file_name " : " driver/subcommand/repl.rs " ,
" byte_start " : 9228 ,
" byte_end " : 9231 ,
" line_start " : 291 ,
" line_end " : 291 ,
" column_start " : 9 ,
" column_end " : 12 ,
" is_primary " : true ,
" text " : [
{
" text " : " let foo = 42; " ,
" highlight_start " : 9 ,
" highlight_end " : 12
}
] ,
" label " : null ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [
{
" message " : " #[warn(unused_variables)] on by default " ,
" code " : null ,
" level " : " note " ,
" spans " : [ ] ,
" children " : [ ] ,
" rendered " : null
} ,
{
" message " : " consider prefixing with an underscore " ,
" code " : null ,
" level " : " help " ,
" spans " : [
{
" file_name " : " driver/subcommand/repl.rs " ,
" byte_start " : 9228 ,
" byte_end " : 9231 ,
" line_start " : 291 ,
" line_end " : 291 ,
" column_start " : 9 ,
" column_end " : 12 ,
" is_primary " : true ,
" text " : [
{
" text " : " let foo = 42; " ,
" highlight_start " : 9 ,
" highlight_end " : 12
}
] ,
" label " : null ,
" suggested_replacement " : " _foo " ,
" suggestion_applicability " : " MachineApplicable " ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : null
}
] ,
" rendered " : " warning: unused variable: `foo` \n --> driver/subcommand/repl.rs:291:9 \n | \n 291 | let foo = 42; \n | ^^^ help: consider prefixing with an underscore: `_foo` \n | \n = note: #[warn(unused_variables)] on by default \n \n "
} " ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/rustc_unused_variable_as_hint.txt " ] ,
2020-06-16 15:26:33 -05:00
) ;
2020-05-14 18:51:48 -05:00
}
#[ test ]
2020-07-09 09:04:29 -05:00
fn rustc_wrong_number_of_parameters ( ) {
check (
2020-05-14 18:51:48 -05:00
r ##" {
" message " : " this function takes 2 parameters but 3 parameters were supplied " ,
" code " : {
" code " : " E0061 " ,
" explanation " : " \n The number of arguments passed to a function must match the number of arguments \n specified in the function signature. \n \n For example, a function like: \n \n ``` \n fn f(a: u16, b: &str) {} \n ``` \n \n Must always be called with exactly two arguments, e.g., `f(2, \" test \" )`. \n \n Note that Rust does not have a notion of optional function arguments or \n variadic functions (except for its C-FFI). \n "
} ,
" level " : " error " ,
" spans " : [
{
" file_name " : " compiler/ty/select.rs " ,
" byte_start " : 8787 ,
" byte_end " : 9241 ,
" line_start " : 219 ,
" line_end " : 231 ,
" column_start " : 5 ,
" column_end " : 6 ,
" is_primary " : false ,
" text " : [
{
" text " : " pub fn add_evidence( " ,
" highlight_start " : 5 ,
" highlight_end " : 25
} ,
{
" text " : " &mut self, " ,
" highlight_start " : 1 ,
" highlight_end " : 19
} ,
{
" text " : " target_poly: &ty::Ref<ty::Poly>, " ,
" highlight_start " : 1 ,
" highlight_end " : 41
} ,
{
" text " : " evidence_poly: &ty::Ref<ty::Poly>, " ,
" highlight_start " : 1 ,
" highlight_end " : 43
} ,
{
" text " : " ) { " ,
" highlight_start " : 1 ,
" highlight_end " : 8
} ,
{
" text " : " match target_poly { " ,
" highlight_start " : 1 ,
" highlight_end " : 28
} ,
{
" text " : " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly), " ,
" highlight_start " : 1 ,
" highlight_end " : 81
} ,
{
" text " : " ty::Ref::Fixed(target_ty) => { " ,
" highlight_start " : 1 ,
" highlight_end " : 43
} ,
{
" text " : " let evidence_ty = evidence_poly.resolve_to_ty(); " ,
" highlight_start " : 1 ,
" highlight_end " : 65
} ,
{
" text " : " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty) " ,
" highlight_start " : 1 ,
" highlight_end " : 76
} ,
{
" text " : " } " ,
" highlight_start " : 1 ,
" highlight_end " : 14
} ,
{
" text " : " } " ,
" highlight_start " : 1 ,
" highlight_end " : 10
} ,
{
" text " : " } " ,
" highlight_start " : 1 ,
" highlight_end " : 6
}
] ,
" label " : " defined here " ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
} ,
{
" file_name " : " compiler/ty/select.rs " ,
" byte_start " : 4045 ,
" byte_end " : 4057 ,
" line_start " : 104 ,
" line_end " : 104 ,
" column_start " : 18 ,
" column_end " : 30 ,
" is_primary " : true ,
" text " : [
{
" text " : " self.add_evidence(target_fixed, evidence_fixed, false); " ,
" highlight_start " : 18 ,
" highlight_end " : 30
}
] ,
" label " : " expected 2 parameters " ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : " error[E0061]: this function takes 2 parameters but 3 parameters were supplied \n --> compiler/ty/select.rs:104:18 \n | \n 104 | self.add_evidence(target_fixed, evidence_fixed, false); \n | ^^^^^^^^^^^^ expected 2 parameters \n ... \n 219 | / pub fn add_evidence( \n 220 | | &mut self, \n 221 | | target_poly: &ty::Ref<ty::Poly>, \n 222 | | evidence_poly: &ty::Ref<ty::Poly>, \n ... | \n 230 | | } \n 231 | | } \n | |_____- defined here \n \n "
} " ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/rustc_wrong_number_of_parameters.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
}
#[ test ]
2020-07-09 09:04:29 -05:00
fn clippy_pass_by_ref ( ) {
check (
2020-05-14 18:51:48 -05:00
r ##" {
" message " : " this argument is passed by reference, but would be more efficient if passed by value " ,
" code " : {
" code " : " clippy::trivially_copy_pass_by_ref " ,
" explanation " : null
} ,
" level " : " warning " ,
" spans " : [
{
" file_name " : " compiler/mir/tagset.rs " ,
" byte_start " : 941 ,
" byte_end " : 946 ,
" line_start " : 42 ,
" line_end " : 42 ,
" column_start " : 24 ,
" column_end " : 29 ,
" is_primary " : true ,
" text " : [
{
" text " : " pub fn is_disjoint(&self, other: Self) -> bool { " ,
" highlight_start " : 24 ,
" highlight_end " : 29
}
] ,
" label " : null ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [
{
" message " : " lint level defined here " ,
" code " : null ,
" level " : " note " ,
" spans " : [
{
" file_name " : " compiler/lib.rs " ,
" byte_start " : 8 ,
" byte_end " : 19 ,
" line_start " : 1 ,
" line_end " : 1 ,
" column_start " : 9 ,
" column_end " : 20 ,
" is_primary " : true ,
" text " : [
{
" text " : " #![warn(clippy::all)] " ,
" highlight_start " : 9 ,
" highlight_end " : 20
}
] ,
" label " : null ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : null
} ,
{
" message " : " #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)] " ,
" code " : null ,
" level " : " note " ,
" spans " : [ ] ,
" children " : [ ] ,
" rendered " : null
} ,
{
" message " : " for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref " ,
" code " : null ,
" level " : " help " ,
" spans " : [ ] ,
" children " : [ ] ,
" rendered " : null
} ,
{
" message " : " consider passing by value instead " ,
" code " : null ,
" level " : " help " ,
" spans " : [
{
" file_name " : " compiler/mir/tagset.rs " ,
" byte_start " : 941 ,
" byte_end " : 946 ,
" line_start " : 42 ,
" line_end " : 42 ,
" column_start " : 24 ,
" column_end " : 29 ,
" is_primary " : true ,
" text " : [
{
" text " : " pub fn is_disjoint(&self, other: Self) -> bool { " ,
" highlight_start " : 24 ,
" highlight_end " : 29
}
] ,
" label " : null ,
" suggested_replacement " : " self " ,
" suggestion_applicability " : " Unspecified " ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : null
}
] ,
" rendered " : " warning: this argument is passed by reference, but would be more efficient if passed by value \n --> compiler/mir/tagset.rs:42:24 \n | \n 42 | pub fn is_disjoint(&self, other: Self) -> bool { \n | ^^^^^ help: consider passing by value instead: `self` \n | \n note: lint level defined here \n --> compiler/lib.rs:1:9 \n | \n 1 | #![warn(clippy::all)] \n | ^^^^^^^^^^^ \n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)] \n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref \n \n "
} " ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/clippy_pass_by_ref.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
}
2022-05-07 22:06:52 -05:00
#[ test ]
fn rustc_range_map_lsp_position ( ) {
check (
r ##" {
" message " : " mismatched types " ,
" code " : {
" code " : " E0308 " ,
" explanation " : " Expected type did not match the received type. \n \n Erroneous code examples: \n \n ```compile_fail,E0308 \n fn plus_one(x: i32) -> i32 { \n x + 1 \n } \n \n plus_one( \" Not a number \" ); \n // ^^^^^^^^^^^^^^ expected `i32`, found `&str` \n \n if \" Not a bool \" { \n // ^^^^^^^^^^^^ expected `bool`, found `&str` \n } \n \n let x: f32 = \" Not a float \" ; \n // --- ^^^^^^^^^^^^^ expected `f32`, found `&str` \n // | \n // expected due to this \n ``` \n \n This error occurs when an expression was used in a place where the compiler \n expected an expression of a different type. It can occur in several cases, the \n most common being when calling a function and passing an argument which has a \n different type than the matching type in the function declaration. \n "
} ,
" level " : " error " ,
" spans " : [
{
" file_name " : " crates/test_diagnostics/src/main.rs " ,
" byte_start " : 87 ,
" byte_end " : 105 ,
" line_start " : 4 ,
" line_end " : 4 ,
" column_start " : 18 ,
" column_end " : 24 ,
" is_primary " : true ,
" text " : [
{
" text " : " let x: u32 = \" 𐐀𐐀𐐀𐐀 \" ; // 17-23 " ,
" highlight_start " : 18 ,
" highlight_end " : 24
}
] ,
" label " : " expected `u32`, found `&str` " ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
} ,
{
" file_name " : " crates/test_diagnostics/src/main.rs " ,
" byte_start " : 81 ,
" byte_end " : 84 ,
" line_start " : 4 ,
" line_end " : 4 ,
" column_start " : 12 ,
" column_end " : 15 ,
" is_primary " : false ,
" text " : [
{
" text " : " let x: u32 = \" 𐐀𐐀𐐀𐐀 \" ; // 17-23 " ,
" highlight_start " : 12 ,
" highlight_end " : 15
}
] ,
" label " : " expected due to this " ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : " error[E0308]: mismatched types \n --> crates/test_diagnostics/src/main.rs:4:18 \n | \n 4 | let x: u32 = \" 𐐀𐐀𐐀𐐀 \" ; // 17-23 \n | --- ^^^^^^ expected `u32`, found `&str` \n | | \n | expected due to this \n \n "
} " ##,
expect_file! ( " ./test_data/rustc_range_map_lsp_position.txt " ) ,
)
}
2020-05-14 18:51:48 -05:00
#[ test ]
2020-07-09 09:04:29 -05:00
fn rustc_mismatched_type ( ) {
check (
2020-05-14 18:51:48 -05:00
r ##" {
" message " : " mismatched types " ,
" code " : {
" code " : " E0308 " ,
" explanation " : " \n This error occurs when the compiler was unable to infer the concrete type of a \n variable. It can occur for several cases, the most common of which is a \n mismatch in the expected type that the compiler inferred for a variable's \n initializing expression, and the actual type explicitly assigned to the \n variable. \n \n For example: \n \n ```compile_fail,E0308 \n let x: i32 = \" I am not a number! \" ; \n // ~~~ ~~~~~~~~~~~~~~~~~~~~ \n // | | \n // | initializing expression; \n // | compiler infers type `&str` \n // | \n // type `i32` assigned to variable `x` \n ``` \n "
} ,
" level " : " error " ,
" spans " : [
{
" file_name " : " runtime/compiler_support.rs " ,
" byte_start " : 1589 ,
" byte_end " : 1594 ,
" line_start " : 48 ,
" line_end " : 48 ,
" column_start " : 65 ,
" column_end " : 70 ,
" is_primary " : true ,
" text " : [
{
" text " : " let layout = alloc::Layout::from_size_align_unchecked(size, align); " ,
" highlight_start " : 65 ,
" highlight_end " : 70
}
] ,
" label " : " expected usize, found u32 " ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [ ] ,
" rendered " : " error[E0308]: mismatched types \n --> runtime/compiler_support.rs:48:65 \n | \n 48 | let layout = alloc::Layout::from_size_align_unchecked(size, align); \n | ^^^^^ expected usize, found u32 \n \n "
} " ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/rustc_mismatched_type.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
}
#[ test ]
2020-07-09 09:04:29 -05:00
fn handles_macro_location ( ) {
check (
2020-05-14 18:51:48 -05:00
r ##" {
" rendered " : " error[E0277]: can't compare `{integer}` with `&str` \n --> src/main.rs:2:5 \n | \n 2 | assert_eq!(1, \" love \" ); \n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str` \n | \n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}` \n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) \n \n " ,
" children " : [
{
" children " : [ ] ,
" code " : null ,
" level " : " help " ,
" message " : " the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}` " ,
" rendered " : null ,
" spans " : [ ]
}
] ,
" code " : {
" code " : " E0277 " ,
" explanation " : " \n You tried to use a type which doesn't implement some trait in a place which \n expected that trait. Erroneous code example: \n \n ```compile_fail,E0277 \n // here we declare the Foo trait with a bar method \n trait Foo { \n fn bar(&self); \n } \n \n // we now declare a function which takes an object implementing the Foo trait \n fn some_func<T: Foo>(foo: T) { \n foo.bar(); \n } \n \n fn main() { \n // we now call the method with the i32 type, which doesn't implement \n // the Foo trait \n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied \n } \n ``` \n \n In order to fix this error, verify that the type you're using does implement \n the trait. Example: \n \n ``` \n trait Foo { \n fn bar(&self); \n } \n \n fn some_func<T: Foo>(foo: T) { \n foo.bar(); // we can now use this method since i32 implements the \n // Foo trait \n } \n \n // we implement the trait on the i32 type \n impl Foo for i32 { \n fn bar(&self) {} \n } \n \n fn main() { \n some_func(5i32); // ok! \n } \n ``` \n \n Or in a generic context, an erroneous code example would look like: \n \n ```compile_fail,E0277 \n fn some_func<T>(foo: T) { \n println!( \" {:?} \" , foo); // error: the trait `core::fmt::Debug` is not \n // implemented for the type `T` \n } \n \n fn main() { \n // We now call the method with the i32 type, \n // which *does* implement the Debug trait. \n some_func(5i32); \n } \n ``` \n \n Note that the error here is in the definition of the generic function: Although \n we only call it with a parameter that does implement `Debug`, the compiler \n still rejects the function: It must work with all possible input types. In \n order to make this example compile, we need to restrict the generic type we're \n accepting: \n \n ``` \n use std::fmt; \n \n // Restrict the input type to types that implement Debug. \n fn some_func<T: fmt::Debug>(foo: T) { \n println!( \" {:?} \" , foo); \n } \n \n fn main() { \n // Calling the method is still fine, as i32 implements Debug. \n some_func(5i32); \n \n // This would fail to compile now: \n // struct WithoutDebug; \n // some_func(WithoutDebug); \n } \n ``` \n \n Rust only looks at the signature of the called function, as such it must \n already specify all requirements that will be used for every type parameter. \n "
} ,
" level " : " error " ,
" message " : " can't compare `{integer}` with `&str` " ,
" spans " : [
{
" byte_end " : 155 ,
" byte_start " : 153 ,
" column_end " : 33 ,
" column_start " : 31 ,
" expansion " : {
" def_site_span " : {
" byte_end " : 940 ,
" byte_start " : 0 ,
" column_end " : 6 ,
" column_start " : 1 ,
" expansion " : null ,
" file_name " : " <::core::macros::assert_eq macros> " ,
" is_primary " : false ,
" label " : null ,
" line_end " : 36 ,
" line_start " : 1 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 35 ,
" highlight_start " : 1 ,
" text " : " ($ left : expr, $ right : expr) => "
} ,
{
" highlight_end " : 3 ,
" highlight_start " : 1 ,
" text " : " ({ "
} ,
{
" highlight_end " : 33 ,
" highlight_start " : 1 ,
" text " : " match (& $ left, & $ right) "
} ,
{
" highlight_end " : 7 ,
" highlight_start " : 1 ,
" text " : " { "
} ,
{
" highlight_end " : 34 ,
" highlight_start " : 1 ,
" text " : " (left_val, right_val) => "
} ,
{
" highlight_end " : 11 ,
" highlight_start " : 1 ,
" text " : " { "
} ,
{
" highlight_end " : 46 ,
" highlight_start " : 1 ,
" text " : " if ! (* left_val == * right_val) "
} ,
{
" highlight_end " : 15 ,
" highlight_start " : 1 ,
" text " : " { "
} ,
{
" highlight_end " : 25 ,
" highlight_start " : 1 ,
" text " : " panic ! "
} ,
{
" highlight_end " : 57 ,
" highlight_start " : 1 ,
" text " : " (r# \" assertion failed: `(left == right)` "
} ,
{
" highlight_end " : 16 ,
" highlight_start " : 1 ,
" text " : " left: `{:?}`, "
} ,
{
" highlight_end " : 18 ,
" highlight_start " : 1 ,
" text " : " right: `{:?}` \" #, "
} ,
{
" highlight_end " : 47 ,
" highlight_start " : 1 ,
" text " : " & * left_val, & * right_val) "
} ,
{
" highlight_end " : 15 ,
" highlight_start " : 1 ,
" text " : " } "
} ,
{
" highlight_end " : 11 ,
" highlight_start " : 1 ,
" text " : " } "
} ,
{
" highlight_end " : 7 ,
" highlight_start " : 1 ,
" text " : " } "
} ,
{
" highlight_end " : 42 ,
" highlight_start " : 1 ,
" text " : " }) ; ($ left : expr, $ right : expr,) => "
} ,
{
" highlight_end " : 49 ,
" highlight_start " : 1 ,
" text " : " ({ $ crate :: assert_eq ! ($ left, $ right) }) ; "
} ,
{
" highlight_end " : 53 ,
" highlight_start " : 1 ,
" text " : " ($ left : expr, $ right : expr, $ ($ arg : tt) +) => "
} ,
{
" highlight_end " : 3 ,
" highlight_start " : 1 ,
" text " : " ({ "
} ,
{
" highlight_end " : 37 ,
" highlight_start " : 1 ,
" text " : " match (& ($ left), & ($ right)) "
} ,
{
" highlight_end " : 7 ,
" highlight_start " : 1 ,
" text " : " { "
} ,
{
" highlight_end " : 34 ,
" highlight_start " : 1 ,
" text " : " (left_val, right_val) => "
} ,
{
" highlight_end " : 11 ,
" highlight_start " : 1 ,
" text " : " { "
} ,
{
" highlight_end " : 46 ,
" highlight_start " : 1 ,
" text " : " if ! (* left_val == * right_val) "
} ,
{
" highlight_end " : 15 ,
" highlight_start " : 1 ,
" text " : " { "
} ,
{
" highlight_end " : 25 ,
" highlight_start " : 1 ,
" text " : " panic ! "
} ,
{
" highlight_end " : 57 ,
" highlight_start " : 1 ,
" text " : " (r# \" assertion failed: `(left == right)` "
} ,
{
" highlight_end " : 16 ,
" highlight_start " : 1 ,
" text " : " left: `{:?}`, "
} ,
{
" highlight_end " : 22 ,
" highlight_start " : 1 ,
" text " : " right: `{:?}`: {} \" #, "
} ,
{
" highlight_end " : 72 ,
" highlight_start " : 1 ,
" text " : " & * left_val, & * right_val, $ crate :: format_args ! "
} ,
{
" highlight_end " : 33 ,
" highlight_start " : 1 ,
" text " : " ($ ($ arg) +)) "
} ,
{
" highlight_end " : 15 ,
" highlight_start " : 1 ,
" text " : " } "
} ,
{
" highlight_end " : 11 ,
" highlight_start " : 1 ,
" text " : " } "
} ,
{
" highlight_end " : 7 ,
" highlight_start " : 1 ,
" text " : " } "
} ,
{
" highlight_end " : 6 ,
" highlight_start " : 1 ,
" text " : " }) ; "
}
]
} ,
" macro_decl_name " : " assert_eq! " ,
" span " : {
" byte_end " : 38 ,
" byte_start " : 16 ,
" column_end " : 27 ,
" column_start " : 5 ,
" expansion " : null ,
" file_name " : " src/main.rs " ,
" is_primary " : false ,
" label " : null ,
" line_end " : 2 ,
" line_start " : 2 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 27 ,
" highlight_start " : 5 ,
" text " : " assert_eq!(1, \" love \" ); "
}
]
}
} ,
" file_name " : " <::core::macros::assert_eq macros> " ,
" is_primary " : true ,
" label " : " no implementation for `{integer} == &str` " ,
" line_end " : 7 ,
" line_start " : 7 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 33 ,
" highlight_start " : 31 ,
" text " : " if ! (* left_val == * right_val) "
}
]
}
]
} " ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/handles_macro_location.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
}
#[ test ]
2020-07-09 09:04:29 -05:00
fn macro_compiler_error ( ) {
check (
2020-05-14 18:51:48 -05:00
r ##" {
2020-08-13 09:28:27 -05:00
" rendered " : " error: Please register your known path in the path module \n --> crates/hir_def/src/path.rs:265:9 \n | \n 265 | compile_error!( \" Please register your known path in the path module \" ) \n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \n | \n ::: crates/hir_def/src/data.rs:80:16 \n | \n 80 | let path = path![std::future::Future]; \n | -------------------------- in this macro invocation \n \n " ,
2020-05-14 18:51:48 -05:00
" children " : [ ] ,
" code " : null ,
" level " : " error " ,
" message " : " Please register your known path in the path module " ,
" spans " : [
{
" byte_end " : 8285 ,
" byte_start " : 8217 ,
" column_end " : 77 ,
" column_start " : 9 ,
" expansion " : {
" def_site_span " : {
" byte_end " : 8294 ,
" byte_start " : 7858 ,
" column_end " : 2 ,
" column_start " : 1 ,
" expansion " : null ,
2020-08-13 09:28:27 -05:00
" file_name " : " crates/hir_def/src/path.rs " ,
2020-05-14 18:51:48 -05:00
" is_primary " : false ,
" label " : null ,
" line_end " : 267 ,
" line_start " : 254 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 28 ,
" highlight_start " : 1 ,
" text " : " macro_rules! __known_path { "
} ,
{
" highlight_end " : 37 ,
" highlight_start " : 1 ,
" text " : " (std::iter::IntoIterator) => {}; "
} ,
{
" highlight_end " : 33 ,
" highlight_start " : 1 ,
" text " : " (std::result::Result) => {}; "
} ,
{
" highlight_end " : 29 ,
" highlight_start " : 1 ,
" text " : " (std::ops::Range) => {}; "
} ,
{
" highlight_end " : 33 ,
" highlight_start " : 1 ,
" text " : " (std::ops::RangeFrom) => {}; "
} ,
{
" highlight_end " : 33 ,
" highlight_start " : 1 ,
" text " : " (std::ops::RangeFull) => {}; "
} ,
{
" highlight_end " : 31 ,
" highlight_start " : 1 ,
" text " : " (std::ops::RangeTo) => {}; "
} ,
{
" highlight_end " : 40 ,
" highlight_start " : 1 ,
" text " : " (std::ops::RangeToInclusive) => {}; "
} ,
{
" highlight_end " : 38 ,
" highlight_start " : 1 ,
" text " : " (std::ops::RangeInclusive) => {}; "
} ,
{
" highlight_end " : 27 ,
" highlight_start " : 1 ,
" text " : " (std::ops::Try) => {}; "
} ,
{
" highlight_end " : 22 ,
" highlight_start " : 1 ,
" text " : " ($path:path) => { "
} ,
{
" highlight_end " : 77 ,
" highlight_start " : 1 ,
" text " : " compile_error!( \" Please register your known path in the path module \" ) "
} ,
{
" highlight_end " : 7 ,
" highlight_start " : 1 ,
" text " : " }; "
} ,
{
" highlight_end " : 2 ,
" highlight_start " : 1 ,
" text " : " } "
}
]
} ,
" macro_decl_name " : " $crate::__known_path! " ,
" span " : {
" byte_end " : 8427 ,
" byte_start " : 8385 ,
" column_end " : 51 ,
" column_start " : 9 ,
" expansion " : {
" def_site_span " : {
" byte_end " : 8611 ,
" byte_start " : 8312 ,
" column_end " : 2 ,
" column_start " : 1 ,
" expansion " : null ,
2020-08-13 09:28:27 -05:00
" file_name " : " crates/hir_def/src/path.rs " ,
2020-05-14 18:51:48 -05:00
" is_primary " : false ,
" label " : null ,
" line_end " : 277 ,
" line_start " : 270 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 22 ,
" highlight_start " : 1 ,
" text " : " macro_rules! __path { "
} ,
{
" highlight_end " : 43 ,
" highlight_start " : 1 ,
" text " : " ($start:ident $(:: $seg:ident)*) => ({ "
} ,
{
" highlight_end " : 51 ,
" highlight_start " : 1 ,
" text " : " $crate::__known_path!($start $(:: $seg)*); "
} ,
{
" highlight_end " : 87 ,
" highlight_start " : 1 ,
" text " : " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec![ "
} ,
{
" highlight_end " : 76 ,
" highlight_start " : 1 ,
" text " : " $crate::path::__name![$start], $($crate::path::__name![$seg],)* "
} ,
{
" highlight_end " : 11 ,
" highlight_start " : 1 ,
" text " : " ]) "
} ,
{
" highlight_end " : 8 ,
" highlight_start " : 1 ,
" text " : " }); "
} ,
{
" highlight_end " : 2 ,
" highlight_start " : 1 ,
" text " : " } "
}
]
} ,
" macro_decl_name " : " path! " ,
" span " : {
" byte_end " : 2966 ,
" byte_start " : 2940 ,
" column_end " : 42 ,
" column_start " : 16 ,
" expansion " : null ,
2020-08-13 09:28:27 -05:00
" file_name " : " crates/hir_def/src/data.rs " ,
2020-05-14 18:51:48 -05:00
" is_primary " : false ,
" label " : null ,
" line_end " : 80 ,
" line_start " : 80 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 42 ,
" highlight_start " : 16 ,
" text " : " let path = path![std::future::Future]; "
}
]
}
} ,
2020-08-13 09:28:27 -05:00
" file_name " : " crates/hir_def/src/path.rs " ,
2020-05-14 18:51:48 -05:00
" is_primary " : false ,
" label " : null ,
" line_end " : 272 ,
" line_start " : 272 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 51 ,
" highlight_start " : 9 ,
" text " : " $crate::__known_path!($start $(:: $seg)*); "
}
]
}
} ,
2020-08-13 09:28:27 -05:00
" file_name " : " crates/hir_def/src/path.rs " ,
2020-05-14 18:51:48 -05:00
" is_primary " : true ,
" label " : null ,
" line_end " : 265 ,
" line_start " : 265 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 77 ,
" highlight_start " : 9 ,
" text " : " compile_error!( \" Please register your known path in the path module \" ) "
}
]
}
]
}
" ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/macro_compiler_error.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
}
#[ test ]
fn snap_multi_line_fix ( ) {
2020-07-09 09:04:29 -05:00
check (
2020-05-14 18:51:48 -05:00
r ##" {
" rendered " : " warning: returning the result of a let binding from a block \n --> src/main.rs:4:5 \n | \n 3 | let a = (0..10).collect(); \n | -------------------------- unnecessary let binding \n 4 | a \n | ^ \n | \n = note: `#[warn(clippy::let_and_return)]` on by default \n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return \n help: return the expression directly \n | \n 3 | \n 4 | (0..10).collect() \n | \n \n " ,
" children " : [
{
" children " : [ ] ,
" code " : null ,
" level " : " note " ,
" message " : " `#[warn(clippy::let_and_return)]` on by default " ,
" rendered " : null ,
" spans " : [ ]
} ,
{
" children " : [ ] ,
" code " : null ,
" level " : " help " ,
" message " : " for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return " ,
" rendered " : null ,
" spans " : [ ]
} ,
{
" children " : [ ] ,
" code " : null ,
" level " : " help " ,
" message " : " return the expression directly " ,
" rendered " : null ,
" spans " : [
{
" byte_end " : 55 ,
" byte_start " : 29 ,
" column_end " : 31 ,
" column_start " : 5 ,
" expansion " : null ,
" file_name " : " src/main.rs " ,
" is_primary " : true ,
" label " : null ,
" line_end " : 3 ,
" line_start " : 3 ,
" suggested_replacement " : " " ,
" suggestion_applicability " : " MachineApplicable " ,
" text " : [
{
" highlight_end " : 31 ,
" highlight_start " : 5 ,
" text " : " let a = (0..10).collect(); "
}
]
} ,
{
" byte_end " : 61 ,
" byte_start " : 60 ,
" column_end " : 6 ,
" column_start " : 5 ,
" expansion " : null ,
" file_name " : " src/main.rs " ,
" is_primary " : true ,
" label " : null ,
" line_end " : 4 ,
" line_start " : 4 ,
" suggested_replacement " : " (0..10).collect() " ,
" suggestion_applicability " : " MachineApplicable " ,
" text " : [
{
" highlight_end " : 6 ,
" highlight_start " : 5 ,
" text " : " a "
}
]
}
]
}
] ,
" code " : {
" code " : " clippy::let_and_return " ,
" explanation " : null
} ,
" level " : " warning " ,
" message " : " returning the result of a let binding from a block " ,
" spans " : [
{
" byte_end " : 55 ,
" byte_start " : 29 ,
" column_end " : 31 ,
" column_start " : 5 ,
" expansion " : null ,
" file_name " : " src/main.rs " ,
" is_primary " : false ,
" label " : " unnecessary let binding " ,
" line_end " : 3 ,
" line_start " : 3 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 31 ,
" highlight_start " : 5 ,
" text " : " let a = (0..10).collect(); "
}
]
} ,
{
" byte_end " : 61 ,
" byte_start " : 60 ,
" column_end " : 6 ,
" column_start " : 5 ,
" expansion " : null ,
" file_name " : " src/main.rs " ,
" is_primary " : true ,
" label " : null ,
" line_end " : 4 ,
" line_start " : 4 ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" text " : [
{
" highlight_end " : 6 ,
" highlight_start " : 5 ,
" text " : " a "
}
]
}
]
}
" ##,
2020-08-28 07:47:14 -05:00
expect_file! [ " ./test_data/snap_multi_line_fix.txt " ] ,
2020-05-14 18:51:48 -05:00
) ;
}
2022-03-31 16:37:23 -05:00
#[ test ]
fn reasonable_line_numbers_from_empty_file ( ) {
check (
r ##" {
" message " : " `main` function not found in crate `current` " ,
" code " : {
" code " : " E0601 " ,
" explanation " : " No `main` function was found in a binary crate. \n \n To fix this error, add a `main` function: \n \n ``` \n fn main() { \n // Your program will start here. \n println!( \" Hello world! \" ); \n } \n ``` \n \n If you don't know the basics of Rust, you can look at the \n [Rust Book][rust-book] to get started. \n \n [rust-book]: https://doc.rust-lang.org/book/ \n "
} ,
" level " : " error " ,
" spans " : [
{
" file_name " : " src/bin/current.rs " ,
" byte_start " : 0 ,
" byte_end " : 0 ,
" line_start " : 0 ,
" line_end " : 0 ,
" column_start " : 1 ,
" column_end " : 1 ,
" is_primary " : true ,
" text " : [ ] ,
" label " : null ,
" suggested_replacement " : null ,
" suggestion_applicability " : null ,
" expansion " : null
}
] ,
" children " : [
{
" message " : " consider adding a `main` function to `src/bin/current.rs` " ,
" code " : null ,
" level " : " note " ,
" spans " : [ ] ,
" children " : [ ] ,
" rendered " : null
}
] ,
" rendered " : " error[E0601]: `main` function not found in crate `current` \n | \n = note: consider adding a `main` function to `src/bin/current.rs` \n \n "
} " ##,
expect_file! [ " ./test_data/reasonable_line_numbers_from_empty_file.txt " ] ,
) ;
}
2020-05-14 18:51:48 -05:00
}