aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/Cargo.toml2
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs282
-rw-r--r--crates/ra_ide/src/diagnostics.rs156
-rw-r--r--crates/ra_ide/src/folding_ranges.rs14
-rw-r--r--crates/ra_ide/src/goto_implementation.rs10
-rw-r--r--crates/ra_ide/src/lib.rs12
-rw-r--r--crates/ra_ide/src/runnables.rs164
-rw-r--r--crates/ra_ide/src/ssr.rs39
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs10
-rw-r--r--crates/ra_ide/test_data/rainbow_highlighting.html12
10 files changed, 534 insertions, 167 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 6f8107491..f4181c4eb 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -17,7 +17,7 @@ indexmap = "1.3.2"
17itertools = "0.9.0" 17itertools = "0.9.0"
18log = "0.4.8" 18log = "0.4.8"
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20rand = { version = "0.7.3", features = ["small_rng"] } 20oorandom = "11.1.2"
21 21
22stdx = { path = "../stdx" } 22stdx = { path = "../stdx" }
23 23
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index d268c92be..109c5e9a8 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -13,13 +13,19 @@ use crate::completion::{
13 13
14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
15 let attribute = ctx.attribute_under_caret.as_ref()?; 15 let attribute = ctx.attribute_under_caret.as_ref()?;
16
17 match (attribute.path(), attribute.input()) { 16 match (attribute.path(), attribute.input()) {
18 (Some(path), Some(ast::AttrInput::TokenTree(token_tree))) 17 (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
19 if path.to_string() == "derive" => 18 if path.to_string() == "derive" =>
20 { 19 {
21 complete_derive(acc, ctx, token_tree) 20 complete_derive(acc, ctx, token_tree)
22 } 21 }
22 (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
23 if ["allow", "warn", "deny", "forbid"]
24 .iter()
25 .any(|lint_level| lint_level == &path.to_string()) =>
26 {
27 complete_lint(acc, ctx, token_tree)
28 }
23 (_, Some(ast::AttrInput::TokenTree(_token_tree))) => {} 29 (_, Some(ast::AttrInput::TokenTree(_token_tree))) => {}
24 _ => complete_attribute_start(acc, ctx, attribute), 30 _ => complete_attribute_start(acc, ctx, attribute),
25 } 31 }
@@ -125,7 +131,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
125]; 131];
126 132
127fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 133fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
128 if let Ok(existing_derives) = parse_derive_input(derive_input) { 134 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) {
129 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 135 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
130 .into_iter() 136 .into_iter()
131 .filter(|completion| !existing_derives.contains(completion.label)) 137 .filter(|completion| !existing_derives.contains(completion.label))
@@ -158,7 +164,26 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input:
158 } 164 }
159} 165}
160 166
161fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { 167fn complete_lint(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
168 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
169 for lint_completion in DEFAULT_LINT_COMPLETIONS
170 .into_iter()
171 .filter(|completion| !existing_lints.contains(completion.label))
172 {
173 acc.add(
174 CompletionItem::new(
175 CompletionKind::Attribute,
176 ctx.source_range(),
177 lint_completion.label,
178 )
179 .kind(CompletionItemKind::Attribute)
180 .detail(lint_completion.description),
181 );
182 }
183 }
184}
185
186fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
162 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { 187 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
163 (Some(left_paren), Some(right_paren)) 188 (Some(left_paren), Some(right_paren))
164 if left_paren.kind() == SyntaxKind::L_PAREN 189 if left_paren.kind() == SyntaxKind::L_PAREN
@@ -212,6 +237,7 @@ struct DeriveCompletion {
212 237
213/// Standard Rust derives and the information about their dependencies 238/// Standard Rust derives and the information about their dependencies
214/// (the dependencies are needed so that the main derive don't break the compilation when added) 239/// (the dependencies are needed so that the main derive don't break the compilation when added)
240#[rustfmt::skip]
215const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ 241const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
216 DeriveCompletion { label: "Clone", dependencies: &[] }, 242 DeriveCompletion { label: "Clone", dependencies: &[] },
217 DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, 243 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
@@ -224,6 +250,130 @@ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
224 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, 250 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
225]; 251];
226 252
253struct LintCompletion {
254 label: &'static str,
255 description: &'static str,
256}
257
258#[rustfmt::skip]
259const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
260 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
261 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
262 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
263 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
264 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
265 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
266 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
267 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
268 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
269 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
270 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
271 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
272 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
273 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
274 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
275 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
276 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
277 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
278 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
279 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
280 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
281 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
282 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
283 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
284 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
285 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
286 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
287 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
288 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
289 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
290 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
291 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
292 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
293 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
294 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
295 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
296 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
297 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
298 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
299 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
300 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
301 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
302 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
303 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
304 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
305 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
306 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
307 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
308 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
309 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
310 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
311 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
312 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
313 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
314 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
315 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
316 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
317 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
318 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
319 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
320 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
321 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
322 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
323 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
324 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
325 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
326 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
327 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
328 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
329 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
330 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
331 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
332 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
333 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
334 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
335 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
336 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
337 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
338 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
339 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
340 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
341 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
342 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
343 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
344 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
345 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
346 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
347 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
348 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
349 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
350 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
351 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
352 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
353 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
354 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
355 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
356 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
357 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
358 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
359 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
360 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
361 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
362 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
363 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
364 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
365 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
366 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
367 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
368 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
369 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
370 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
371 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
372 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
373 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
374 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
375];
376
227#[cfg(test)] 377#[cfg(test)]
228mod tests { 378mod tests {
229 use expect::{expect, Expect}; 379 use expect::{expect, Expect};
@@ -257,6 +407,130 @@ struct Test {}
257 } 407 }
258 408
259 #[test] 409 #[test]
410 fn empty_lint_completion() {
411 check(
412 r#"#[allow(<|>)]"#,
413 expect![[r#"
414 at absolute_paths_not_starting_with_crate fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name
415 at ambiguous_associated_items ambiguous associated items
416 at anonymous_parameters detects anonymous parameters
417 at arithmetic_overflow arithmetic operation overflows
418 at array_into_iter detects calling `into_iter` on arrays
419 at asm_sub_register using only a subset of a register for inline asm inputs
420 at bare_trait_objects suggest using `dyn Trait` for trait objects
421 at bindings_with_variant_name detects pattern bindings with the same name as one of the matched variants
422 at box_pointers use of owned (Box type) heap memory
423 at cenum_impl_drop_cast a C-like enum implementing Drop is cast
424 at clashing_extern_declarations detects when an extern fn has been declared with the same name but different types
425 at coherence_leak_check distinct impls distinguished only by the leak-check code
426 at conflicting_repr_hints conflicts between `#[repr(..)]` hints that were previously accepted and used in practice
427 at confusable_idents detects visually confusable pairs between identifiers
428 at const_err constant evaluation detected erroneous expression
429 at dead_code detect unused, unexported items
430 at deprecated detects use of deprecated items
431 at deprecated_in_future detects use of items that will be deprecated in a future version
432 at elided_lifetimes_in_paths hidden lifetime parameters in types are deprecated
433 at ellipsis_inclusive_range_patterns `...` range patterns are deprecated
434 at explicit_outlives_requirements outlives requirements can be inferred
435 at exported_private_dependencies public interface leaks type from a private dependency
436 at ill_formed_attribute_input ill-formed attribute inputs that were previously accepted and used in practice
437 at illegal_floating_point_literal_pattern floating-point literals cannot be used in patterns
438 at improper_ctypes proper use of libc types in foreign modules
439 at improper_ctypes_definitions proper use of libc types in foreign item definitions
440 at incomplete_features incomplete features that may function improperly in some or all cases
441 at incomplete_include trailing content in included file
442 at indirect_structural_match pattern with const indirectly referencing non-structural-match type
443 at inline_no_sanitize detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`
444 at intra_doc_link_resolution_failure failures in resolving intra-doc link targets
445 at invalid_codeblock_attributes codeblock attribute looks a lot like a known one
446 at invalid_type_param_default type parameter default erroneously allowed in invalid location
447 at invalid_value an invalid value is being created (such as a NULL reference)
448 at irrefutable_let_patterns detects irrefutable patterns in if-let and while-let statements
449 at keyword_idents detects edition keywords being used as an identifier
450 at late_bound_lifetime_arguments detects generic lifetime arguments in path segments with late bound lifetime parameters
451 at macro_expanded_macro_exports_accessed_by_absolute_paths macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
452 at macro_use_extern_crate the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system
453 at meta_variable_misuse possible meta-variable misuse at macro definition
454 at missing_copy_implementations detects potentially-forgotten implementations of `Copy`
455 at missing_crate_level_docs detects crates with no crate-level documentation
456 at missing_debug_implementations detects missing implementations of Debug
457 at missing_doc_code_examples detects publicly-exported items without code samples in their documentation
458 at missing_docs detects missing documentation for public members
459 at missing_fragment_specifier detects missing fragment specifiers in unused `macro_rules!` patterns
460 at mixed_script_confusables detects Unicode scripts whose mixed script confusables codepoints are solely used
461 at mutable_borrow_reservation_conflict reservation of a two-phased borrow conflicts with other shared borrows
462 at mutable_transmutes mutating transmuted &mut T from &T may cause undefined behavior
463 at no_mangle_const_items const items will not have their symbols exported
464 at no_mangle_generic_items generic items must be mangled
465 at non_ascii_idents detects non-ASCII identifiers
466 at non_camel_case_types types, variants, traits and type parameters should have camel case names
467 at non_shorthand_field_patterns using `Struct { x: x }` instead of `Struct { x }` in a pattern
468 at non_snake_case variables, methods, functions, lifetime parameters and modules should have snake case names
469 at non_upper_case_globals static constants should have uppercase identifiers
470 at order_dependent_trait_objects trait-object types were treated as different depending on marker-trait order
471 at overflowing_literals literal out of range for its type
472 at overlapping_patterns detects overlapping patterns
473 at path_statements path statements with no effect
474 at patterns_in_fns_without_body patterns in functions without body were erroneously allowed
475 at private_doc_tests detects code samples in docs of private items not documented by rustdoc
476 at private_in_public detect private items in public interfaces not caught by the old implementation
477 at proc_macro_derive_resolution_fallback detects proc macro derives using inaccessible names from parent modules
478 at pub_use_of_private_extern_crate detect public re-exports of private extern crates
479 at redundant_semicolons detects unnecessary trailing semicolons
480 at renamed_and_removed_lints lints that have been renamed or removed
481 at safe_packed_borrows safe borrows of fields of packed structs were erroneously allowed
482 at single_use_lifetimes detects lifetime parameters that are only used once
483 at soft_unstable a feature gate that doesn't break dependent crates
484 at stable_features stable features found in `#[feature]` directive
485 at trivial_bounds these bounds don't depend on an type parameters
486 at trivial_casts detects trivial casts which could be removed
487 at trivial_numeric_casts detects trivial casts of numeric types which could be removed
488 at type_alias_bounds bounds in type aliases are not enforced
489 at tyvar_behind_raw_pointer raw pointer to an inference variable
490 at unaligned_references detects unaligned references to fields of packed structs
491 at uncommon_codepoints detects uncommon Unicode codepoints in identifiers
492 at unconditional_panic operation will cause a panic at runtime
493 at unconditional_recursion functions that cannot return without calling themselves
494 at unknown_crate_types unknown crate type found in `#[crate_type]` directive
495 at unknown_lints unrecognized lint attribute
496 at unnameable_test_items detects an item that cannot be named being marked as `#[test_case]`
497 at unreachable_code detects unreachable code paths
498 at unreachable_patterns detects unreachable patterns
499 at unreachable_pub `pub` items not reachable from crate root
500 at unsafe_code usage of `unsafe` code
501 at unsafe_op_in_unsafe_fn unsafe operations in unsafe functions without an explicit unsafe block are deprecated
502 at unstable_features enabling unstable features (deprecated. do not use)
503 at unstable_name_collisions detects name collision with an existing but unstable method
504 at unused_allocation detects unnecessary allocations that can be eliminated
505 at unused_assignments detect assignments that will never be read
506 at unused_attributes detects attributes that were not used by the compiler
507 at unused_braces unnecessary braces around an expression
508 at unused_comparisons comparisons made useless by limits of the types involved
509 at unused_crate_dependencies crate dependencies that are never used
510 at unused_doc_comments detects doc comments that aren't used by rustdoc
511 at unused_extern_crates extern crates that are never used
512 at unused_features unused features found in crate-level `#[feature]` directives
513 at unused_import_braces unnecessary braces around an imported item
514 at unused_imports imports that are never used
515 at unused_labels detects labels that are never used
516 at unused_lifetimes detects lifetime parameters that are never used
517 at unused_macros detects macros that were not used
518 at unused_must_use unused result of a type flagged as `#[must_use]`
519 at unused_mut detect mut variables which don't need to be mutable
520 at unused_parens `if`, `match`, `while` and `return` do not need parentheses
521 at unused_qualifications detects unnecessarily qualified names
522 at unused_results unused result of an expression in a statement
523 at unused_unsafe unnecessary use of an `unsafe` block
524 at unused_variables detect variables which are not used in any way
525 at variant_size_differences detects enums with widely varying variant sizes
526 at warnings mass-change the level for lints which produce warnings
527 at where_clauses_object_safety checks the object safety of where clauses
528 at while_true suggest using `loop { }` instead of `while true { }`
529 "#]],
530 )
531 }
532
533 #[test]
260 fn no_completion_for_incorrect_derive() { 534 fn no_completion_for_incorrect_derive() {
261 check( 535 check(
262 r#" 536 r#"
@@ -325,7 +599,7 @@ struct Test {}
325 599
326 #[test] 600 #[test]
327 fn test_attribute_completion_inside_nested_attr() { 601 fn test_attribute_completion_inside_nested_attr() {
328 check(r#"#[allow(<|>)]"#, expect![[]]) 602 check(r#"#[cfg(<|>)]"#, expect![[]])
329 } 603 }
330 604
331 #[test] 605 #[test]
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index e029af0dc..897177d05 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -7,7 +7,7 @@
7use std::cell::RefCell; 7use std::cell::RefCell;
8 8
9use hir::{ 9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, 10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder},
11 HasSource, HirDisplay, Semantics, VariantDef, 11 HasSource, HirDisplay, Semantics, VariantDef,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
@@ -29,7 +29,11 @@ pub enum Severity {
29 WeakWarning, 29 WeakWarning,
30} 30}
31 31
32pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { 32pub(crate) fn diagnostics(
33 db: &RootDatabase,
34 file_id: FileId,
35 enable_experimental: bool,
36) -> Vec<Diagnostic> {
33 let _p = profile("diagnostics"); 37 let _p = profile("diagnostics");
34 let sema = Semantics::new(db); 38 let sema = Semantics::new(db);
35 let parse = db.parse(file_id); 39 let parse = db.parse(file_id);
@@ -48,79 +52,85 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
48 check_struct_shorthand_initialization(&mut res, file_id, &node); 52 check_struct_shorthand_initialization(&mut res, file_id, &node);
49 } 53 }
50 let res = RefCell::new(res); 54 let res = RefCell::new(res);
51 let mut sink = DiagnosticSink::new(|d| { 55 let mut sink = DiagnosticSinkBuilder::new()
52 res.borrow_mut().push(Diagnostic { 56 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
53 message: d.message(), 57 let original_file = d.source().file_id.original_file(db);
54 range: sema.diagnostics_range(d).range, 58 let fix = Fix::new(
55 severity: Severity::Error, 59 "Create module",
56 fix: None, 60 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
57 }) 61 .into(),
58 }) 62 );
59 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 63 res.borrow_mut().push(Diagnostic {
60 let original_file = d.source().file_id.original_file(db); 64 range: sema.diagnostics_range(d).range,
61 let fix = Fix::new( 65 message: d.message(),
62 "Create module", 66 severity: Severity::Error,
63 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(), 67 fix: Some(fix),
64 ); 68 })
65 res.borrow_mut().push(Diagnostic {
66 range: sema.diagnostics_range(d).range,
67 message: d.message(),
68 severity: Severity::Error,
69 fix: Some(fix),
70 }) 69 })
71 }) 70 .on::<hir::diagnostics::MissingFields, _>(|d| {
72 .on::<hir::diagnostics::MissingFields, _>(|d| { 71 // Note that although we could add a diagnostics to
73 // Note that although we could add a diagnostics to 72 // fill the missing tuple field, e.g :
74 // fill the missing tuple field, e.g : 73 // `struct A(usize);`
75 // `struct A(usize);` 74 // `let a = A { 0: () }`
76 // `let a = A { 0: () }` 75 // but it is uncommon usage and it should not be encouraged.
77 // but it is uncommon usage and it should not be encouraged. 76 let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
78 let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { 77 None
79 None 78 } else {
80 } else { 79 let mut field_list = d.ast(db);
81 let mut field_list = d.ast(db); 80 for f in d.missed_fields.iter() {
82 for f in d.missed_fields.iter() { 81 let field =
83 let field = 82 make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
84 make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); 83 field_list = field_list.append_field(&field);
85 field_list = field_list.append_field(&field); 84 }
86 } 85
87 86 let edit = {
88 let edit = { 87 let mut builder = TextEditBuilder::default();
89 let mut builder = TextEditBuilder::default(); 88 algo::diff(&d.ast(db).syntax(), &field_list.syntax())
90 algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); 89 .into_text_edit(&mut builder);
91 builder.finish() 90 builder.finish()
91 };
92 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
92 }; 93 };
93 Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
94 };
95 94
96 res.borrow_mut().push(Diagnostic { 95 res.borrow_mut().push(Diagnostic {
97 range: sema.diagnostics_range(d).range, 96 range: sema.diagnostics_range(d).range,
98 message: d.message(), 97 message: d.message(),
99 severity: Severity::Error, 98 severity: Severity::Error,
100 fix, 99 fix,
100 })
101 }) 101 })
102 }) 102 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
103 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 103 let node = d.ast(db);
104 let node = d.ast(db); 104 let replacement = format!("Ok({})", node.syntax());
105 let replacement = format!("Ok({})", node.syntax()); 105 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
106 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 106 let source_change = SourceFileEdit { file_id, edit }.into();
107 let source_change = SourceFileEdit { file_id, edit }.into(); 107 let fix = Fix::new("Wrap with ok", source_change);
108 let fix = Fix::new("Wrap with ok", source_change); 108 res.borrow_mut().push(Diagnostic {
109 res.borrow_mut().push(Diagnostic { 109 range: sema.diagnostics_range(d).range,
110 range: sema.diagnostics_range(d).range, 110 message: d.message(),
111 message: d.message(), 111 severity: Severity::Error,
112 severity: Severity::Error, 112 fix: Some(fix),
113 fix: Some(fix), 113 })
114 }) 114 })
115 }) 115 .on::<hir::diagnostics::NoSuchField, _>(|d| {
116 .on::<hir::diagnostics::NoSuchField, _>(|d| { 116 res.borrow_mut().push(Diagnostic {
117 res.borrow_mut().push(Diagnostic { 117 range: sema.diagnostics_range(d).range,
118 range: sema.diagnostics_range(d).range, 118 message: d.message(),
119 message: d.message(), 119 severity: Severity::Error,
120 severity: Severity::Error, 120 fix: missing_struct_field_fix(&sema, file_id, d),
121 fix: missing_struct_field_fix(&sema, file_id, d), 121 })
122 }) 122 })
123 }); 123 // Only collect experimental diagnostics when they're enabled.
124 .filter(|diag| !diag.is_experimental() || enable_experimental)
125 // Diagnostics not handled above get no fix and default treatment.
126 .build(|d| {
127 res.borrow_mut().push(Diagnostic {
128 message: d.message(),
129 range: sema.diagnostics_range(d).range,
130 severity: Severity::Error,
131 fix: None,
132 })
133 });
124 134
125 if let Some(m) = sema.to_module_def(file_id) { 135 if let Some(m) = sema.to_module_def(file_id) {
126 m.diagnostics(db, &mut sink); 136 m.diagnostics(db, &mut sink);
@@ -298,7 +308,7 @@ mod tests {
298 let after = trim_indent(ra_fixture_after); 308 let after = trim_indent(ra_fixture_after);
299 309
300 let (analysis, file_position) = analysis_and_position(ra_fixture_before); 310 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
301 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 311 let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap();
302 let mut fix = diagnostic.fix.unwrap(); 312 let mut fix = diagnostic.fix.unwrap();
303 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 313 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
304 let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); 314 let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
@@ -324,7 +334,7 @@ mod tests {
324 let ra_fixture_after = &trim_indent(ra_fixture_after); 334 let ra_fixture_after = &trim_indent(ra_fixture_after);
325 let (analysis, file_pos) = analysis_and_position(ra_fixture_before); 335 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
326 let current_file_id = file_pos.file_id; 336 let current_file_id = file_pos.file_id;
327 let diagnostic = analysis.diagnostics(current_file_id).unwrap().pop().unwrap(); 337 let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap();
328 let mut fix = diagnostic.fix.unwrap(); 338 let mut fix = diagnostic.fix.unwrap();
329 let edit = fix.source_change.source_file_edits.pop().unwrap(); 339 let edit = fix.source_change.source_file_edits.pop().unwrap();
330 let changed_file_id = edit.file_id; 340 let changed_file_id = edit.file_id;
@@ -345,14 +355,14 @@ mod tests {
345 let analysis = mock.analysis(); 355 let analysis = mock.analysis();
346 let diagnostics = files 356 let diagnostics = files
347 .into_iter() 357 .into_iter()
348 .flat_map(|file_id| analysis.diagnostics(file_id).unwrap()) 358 .flat_map(|file_id| analysis.diagnostics(file_id, true).unwrap())
349 .collect::<Vec<_>>(); 359 .collect::<Vec<_>>();
350 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); 360 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
351 } 361 }
352 362
353 fn check_expect(ra_fixture: &str, expect: Expect) { 363 fn check_expect(ra_fixture: &str, expect: Expect) {
354 let (analysis, file_id) = single_file(ra_fixture); 364 let (analysis, file_id) = single_file(ra_fixture);
355 let diagnostics = analysis.diagnostics(file_id).unwrap(); 365 let diagnostics = analysis.diagnostics(file_id, true).unwrap();
356 expect.assert_debug_eq(&diagnostics) 366 expect.assert_debug_eq(&diagnostics)
357 } 367 }
358 368
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs
index e7ec9953f..315808890 100644
--- a/crates/ra_ide/src/folding_ranges.rs
+++ b/crates/ra_ide/src/folding_ranges.rs
@@ -84,7 +84,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
84 match kind { 84 match kind {
85 COMMENT => Some(FoldKind::Comment), 85 COMMENT => Some(FoldKind::Comment),
86 USE_ITEM => Some(FoldKind::Imports), 86 USE_ITEM => Some(FoldKind::Imports),
87 ARG_LIST => Some(FoldKind::ArgList), 87 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
88 RECORD_FIELD_DEF_LIST 88 RECORD_FIELD_DEF_LIST
89 | RECORD_FIELD_PAT_LIST 89 | RECORD_FIELD_PAT_LIST
90 | RECORD_FIELD_LIST 90 | RECORD_FIELD_LIST
@@ -386,4 +386,16 @@ const _: S = S <fold block>{
386"#, 386"#,
387 ) 387 )
388 } 388 }
389
390 #[test]
391 fn fold_multiline_params() {
392 check(
393 r#"
394fn foo<fold arglist>(
395 x: i32,
396 y: String,
397)</fold> {}
398"#,
399 )
400 }
389} 401}
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index 3ee048f28..16a61d071 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -23,7 +23,7 @@ pub(crate) fn goto_implementation(
23 23
24 let krate = sema.to_module_def(position.file_id)?.krate(); 24 let krate = sema.to_module_def(position.file_id)?.krate();
25 25
26 if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { 26 if let Some(nominal_def) = find_node_at_offset::<ast::AdtDef>(&syntax, position.offset) {
27 return Some(RangeInfo::new( 27 return Some(RangeInfo::new(
28 nominal_def.syntax().text_range(), 28 nominal_def.syntax().text_range(),
29 impls_for_def(&sema, &nominal_def, krate)?, 29 impls_for_def(&sema, &nominal_def, krate)?,
@@ -40,13 +40,13 @@ pub(crate) fn goto_implementation(
40 40
41fn impls_for_def( 41fn impls_for_def(
42 sema: &Semantics<RootDatabase>, 42 sema: &Semantics<RootDatabase>,
43 node: &ast::NominalDef, 43 node: &ast::AdtDef,
44 krate: Crate, 44 krate: Crate,
45) -> Option<Vec<NavigationTarget>> { 45) -> Option<Vec<NavigationTarget>> {
46 let ty = match node { 46 let ty = match node {
47 ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db), 47 ast::AdtDef::StructDef(def) => sema.to_def(def)?.ty(sema.db),
48 ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db), 48 ast::AdtDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db),
49 ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db), 49 ast::AdtDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db),
50 }; 50 };
51 51
52 let impls = ImplDef::all_in_crate(sema.db, krate); 52 let impls = ImplDef::all_in_crate(sema.db, krate);
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index dc9192d42..0fede0d87 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -487,8 +487,12 @@ impl Analysis {
487 } 487 }
488 488
489 /// Computes the set of diagnostics for the given file. 489 /// Computes the set of diagnostics for the given file.
490 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 490 pub fn diagnostics(
491 self.with_db(|db| diagnostics::diagnostics(db, file_id)) 491 &self,
492 file_id: FileId,
493 enable_experimental: bool,
494 ) -> Cancelable<Vec<Diagnostic>> {
495 self.with_db(|db| diagnostics::diagnostics(db, file_id, enable_experimental))
492 } 496 }
493 497
494 /// Returns the edit required to rename reference at the position to the new 498 /// Returns the edit required to rename reference at the position to the new
@@ -505,9 +509,11 @@ impl Analysis {
505 &self, 509 &self,
506 query: &str, 510 query: &str,
507 parse_only: bool, 511 parse_only: bool,
512 position: FilePosition,
513 selections: Vec<FileRange>,
508 ) -> Cancelable<Result<SourceChange, SsrError>> { 514 ) -> Cancelable<Result<SourceChange, SsrError>> {
509 self.with_db(|db| { 515 self.with_db(|db| {
510 let edits = ssr::parse_search_replace(query, parse_only, db)?; 516 let edits = ssr::parse_search_replace(query, parse_only, db, position, selections)?;
511 Ok(SourceChange::from(edits)) 517 Ok(SourceChange::from(edits))
512 }) 518 })
513 } 519 }
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 95a35a28d..45e0a7d85 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -220,15 +220,7 @@ fn runnable_mod(
220 module: ast::Module, 220 module: ast::Module,
221 file_id: FileId, 221 file_id: FileId,
222) -> Option<Runnable> { 222) -> Option<Runnable> {
223 let has_test_function = module 223 if !has_test_function_or_multiple_test_submodules(&module) {
224 .item_list()?
225 .items()
226 .filter_map(|it| match it {
227 ast::ModuleItem::FnDef(it) => Some(it),
228 _ => None,
229 })
230 .any(|f| has_test_related_attribute(&f));
231 if !has_test_function {
232 return None; 224 return None;
233 } 225 }
234 let module_def = sema.to_def(&module)?; 226 let module_def = sema.to_def(&module)?;
@@ -246,6 +238,34 @@ fn runnable_mod(
246 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs }) 238 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
247} 239}
248 240
241// We could create runnables for modules with number_of_test_submodules > 0,
242// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
243fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
244 if let Some(item_list) = module.item_list() {
245 let mut number_of_test_submodules = 0;
246
247 for item in item_list.items() {
248 match item {
249 ast::ModuleItem::FnDef(f) => {
250 if has_test_related_attribute(&f) {
251 return true;
252 }
253 }
254 ast::ModuleItem::Module(submodule) => {
255 if has_test_function_or_multiple_test_submodules(&submodule) {
256 number_of_test_submodules += 1;
257 }
258 }
259 _ => (),
260 }
261 }
262
263 number_of_test_submodules > 1
264 } else {
265 false
266 }
267}
268
249#[cfg(test)] 269#[cfg(test)]
250mod tests { 270mod tests {
251 use expect::{expect, Expect}; 271 use expect::{expect, Expect};
@@ -571,19 +591,33 @@ mod test_mod {
571 } 591 }
572 592
573 #[test] 593 #[test]
574 fn test_runnables_one_depth_layer_module() { 594 fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() {
575 check( 595 check(
576 r#" 596 r#"
577//- /lib.rs 597//- /lib.rs
578<|> 598<|>
579mod foo { 599mod root_tests {
580 mod test_mod { 600 mod nested_tests_0 {
581 #[test] 601 mod nested_tests_1 {
582 fn test_foo1() {} 602 #[test]
603 fn nested_test_11() {}
604
605 #[test]
606 fn nested_test_12() {}
607 }
608
609 mod nested_tests_2 {
610 #[test]
611 fn nested_test_2() {}
612 }
613
614 mod nested_tests_3 {}
583 } 615 }
616
617 mod nested_tests_4 {}
584} 618}
585"#, 619"#,
586 &[&TEST, &TEST], 620 &[&TEST, &TEST, &TEST, &TEST, &TEST, &TEST],
587 expect![[r#" 621 expect![[r#"
588 [ 622 [
589 Runnable { 623 Runnable {
@@ -591,18 +625,18 @@ mod foo {
591 file_id: FileId( 625 file_id: FileId(
592 1, 626 1,
593 ), 627 ),
594 full_range: 15..77, 628 full_range: 22..323,
595 focus_range: Some( 629 focus_range: Some(
596 19..27, 630 26..40,
597 ), 631 ),
598 name: "test_mod", 632 name: "nested_tests_0",
599 kind: MODULE, 633 kind: MODULE,
600 container_name: None, 634 container_name: None,
601 description: None, 635 description: None,
602 docs: None, 636 docs: None,
603 }, 637 },
604 kind: TestMod { 638 kind: TestMod {
605 path: "foo::test_mod", 639 path: "root_tests::nested_tests_0",
606 }, 640 },
607 cfg_exprs: [], 641 cfg_exprs: [],
608 }, 642 },
@@ -611,11 +645,31 @@ mod foo {
611 file_id: FileId( 645 file_id: FileId(
612 1, 646 1,
613 ), 647 ),
614 full_range: 38..71, 648 full_range: 51..192,
615 focus_range: Some( 649 focus_range: Some(
616 57..66, 650 55..69,
617 ), 651 ),
618 name: "test_foo1", 652 name: "nested_tests_1",
653 kind: MODULE,
654 container_name: None,
655 description: None,
656 docs: None,
657 },
658 kind: TestMod {
659 path: "root_tests::nested_tests_0::nested_tests_1",
660 },
661 cfg_exprs: [],
662 },
663 Runnable {
664 nav: NavigationTarget {
665 file_id: FileId(
666 1,
667 ),
668 full_range: 84..126,
669 focus_range: Some(
670 107..121,
671 ),
672 name: "nested_test_11",
619 kind: FN_DEF, 673 kind: FN_DEF,
620 container_name: None, 674 container_name: None,
621 description: None, 675 description: None,
@@ -623,7 +677,7 @@ mod foo {
623 }, 677 },
624 kind: Test { 678 kind: Test {
625 test_id: Path( 679 test_id: Path(
626 "foo::test_mod::test_foo1", 680 "root_tests::nested_tests_0::nested_tests_1::nested_test_11",
627 ), 681 ),
628 attr: TestAttr { 682 attr: TestAttr {
629 ignore: false, 683 ignore: false,
@@ -631,46 +685,48 @@ mod foo {
631 }, 685 },
632 cfg_exprs: [], 686 cfg_exprs: [],
633 }, 687 },
634 ]
635 "#]],
636 );
637 }
638
639 #[test]
640 fn test_runnables_multiple_depth_module() {
641 check(
642 r#"
643//- /lib.rs
644<|>
645mod foo {
646 mod bar {
647 mod test_mod {
648 #[test]
649 fn test_foo1() {}
650 }
651 }
652}
653"#,
654 &[&TEST, &TEST],
655 expect![[r#"
656 [
657 Runnable { 688 Runnable {
658 nav: NavigationTarget { 689 nav: NavigationTarget {
659 file_id: FileId( 690 file_id: FileId(
660 1, 691 1,
661 ), 692 ),
662 full_range: 33..107, 693 full_range: 140..182,
663 focus_range: Some( 694 focus_range: Some(
664 37..45, 695 163..177,
665 ), 696 ),
666 name: "test_mod", 697 name: "nested_test_12",
698 kind: FN_DEF,
699 container_name: None,
700 description: None,
701 docs: None,
702 },
703 kind: Test {
704 test_id: Path(
705 "root_tests::nested_tests_0::nested_tests_1::nested_test_12",
706 ),
707 attr: TestAttr {
708 ignore: false,
709 },
710 },
711 cfg_exprs: [],
712 },
713 Runnable {
714 nav: NavigationTarget {
715 file_id: FileId(
716 1,
717 ),
718 full_range: 202..286,
719 focus_range: Some(
720 206..220,
721 ),
722 name: "nested_tests_2",
667 kind: MODULE, 723 kind: MODULE,
668 container_name: None, 724 container_name: None,
669 description: None, 725 description: None,
670 docs: None, 726 docs: None,
671 }, 727 },
672 kind: TestMod { 728 kind: TestMod {
673 path: "foo::bar::test_mod", 729 path: "root_tests::nested_tests_0::nested_tests_2",
674 }, 730 },
675 cfg_exprs: [], 731 cfg_exprs: [],
676 }, 732 },
@@ -679,11 +735,11 @@ mod foo {
679 file_id: FileId( 735 file_id: FileId(
680 1, 736 1,
681 ), 737 ),
682 full_range: 60..97, 738 full_range: 235..276,
683 focus_range: Some( 739 focus_range: Some(
684 83..92, 740 258..271,
685 ), 741 ),
686 name: "test_foo1", 742 name: "nested_test_2",
687 kind: FN_DEF, 743 kind: FN_DEF,
688 container_name: None, 744 container_name: None,
689 description: None, 745 description: None,
@@ -691,7 +747,7 @@ mod foo {
691 }, 747 },
692 kind: Test { 748 kind: Test {
693 test_id: Path( 749 test_id: Path(
694 "foo::bar::test_mod::test_foo1", 750 "root_tests::nested_tests_0::nested_tests_2::nested_test_2",
695 ), 751 ),
696 attr: TestAttr { 752 attr: TestAttr {
697 ignore: false, 753 ignore: false,
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index b3e9e5dfe..4348b43be 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,5 +1,5 @@
1use ra_db::SourceDatabaseExt; 1use ra_db::{FilePosition, FileRange};
2use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 2use ra_ide_db::RootDatabase;
3 3
4use crate::SourceFileEdit; 4use crate::SourceFileEdit;
5use ra_ssr::{MatchFinder, SsrError, SsrRule}; 5use ra_ssr::{MatchFinder, SsrError, SsrRule};
@@ -11,6 +11,22 @@ use ra_ssr::{MatchFinder, SsrError, SsrRule};
11// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement. 11// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
12// Within a macro call, a placeholder will match up until whatever token follows the placeholder. 12// Within a macro call, a placeholder will match up until whatever token follows the placeholder.
13// 13//
14// All paths in both the search pattern and the replacement template must resolve in the context
15// in which this command is invoked. Paths in the search pattern will then match the code if they
16// resolve to the same item, even if they're written differently. For example if we invoke the
17// command in the module `foo` with a pattern of `Bar`, then code in the parent module that refers
18// to `foo::Bar` will match.
19//
20// Paths in the replacement template will be rendered appropriately for the context in which the
21// replacement occurs. For example if our replacement template is `foo::Bar` and we match some
22// code in the `foo` module, we'll insert just `Bar`.
23//
24// Method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will match
25// `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`.
26//
27// The scope of the search / replace will be restricted to the current selection if any, otherwise
28// it will apply to the whole workspace.
29//
14// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`. 30// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
15// 31//
16// Supported constraints: 32// Supported constraints:
@@ -43,21 +59,14 @@ pub fn parse_search_replace(
43 rule: &str, 59 rule: &str,
44 parse_only: bool, 60 parse_only: bool,
45 db: &RootDatabase, 61 db: &RootDatabase,
62 resolve_context: FilePosition,
63 selections: Vec<FileRange>,
46) -> Result<Vec<SourceFileEdit>, SsrError> { 64) -> Result<Vec<SourceFileEdit>, SsrError> {
47 let mut edits = vec![];
48 let rule: SsrRule = rule.parse()?; 65 let rule: SsrRule = rule.parse()?;
66 let mut match_finder = MatchFinder::in_context(db, resolve_context, selections);
67 match_finder.add_rule(rule)?;
49 if parse_only { 68 if parse_only {
50 return Ok(edits); 69 return Ok(Vec::new());
51 }
52 let mut match_finder = MatchFinder::new(db);
53 match_finder.add_rule(rule);
54 for &root in db.local_roots().iter() {
55 let sr = db.source_root(root);
56 for file_id in sr.iter() {
57 if let Some(edit) = match_finder.edits_for_file(file_id) {
58 edits.push(SourceFileEdit { file_id, edit });
59 }
60 }
61 } 70 }
62 Ok(edits) 71 Ok(match_finder.edits())
63} 72}
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index 0be55bca9..a5e7d2867 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -1,5 +1,6 @@
1//! Renders a bit of code as HTML. 1//! Renders a bit of code as HTML.
2 2
3use oorandom::Rand32;
3use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
4use ra_syntax::{AstNode, TextRange, TextSize}; 5use ra_syntax::{AstNode, TextRange, TextSize};
5 6
@@ -9,13 +10,12 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
9 let parse = db.parse(file_id); 10 let parse = db.parse(file_id);
10 11
11 fn rainbowify(seed: u64) -> String { 12 fn rainbowify(seed: u64) -> String {
12 use rand::prelude::*; 13 let mut rng = Rand32::new(seed);
13 let mut rng = SmallRng::seed_from_u64(seed);
14 format!( 14 format!(
15 "hsl({h},{s}%,{l}%)", 15 "hsl({h},{s}%,{l}%)",
16 h = rng.gen_range::<u16, _, _>(0, 361), 16 h = rng.rand_range(0..361),
17 s = rng.gen_range::<u16, _, _>(42, 99), 17 s = rng.rand_range(42..99),
18 l = rng.gen_range::<u16, _, _>(40, 91), 18 l = rng.rand_range(40..91),
19 ) 19 )
20 } 20 }
21 21
diff --git a/crates/ra_ide/test_data/rainbow_highlighting.html b/crates/ra_ide/test_data/rainbow_highlighting.html
index 08d83302c..401e87a73 100644
--- a/crates/ra_ide/test_data/rainbow_highlighting.html
+++ b/crates/ra_ide/test_data/rainbow_highlighting.html
@@ -36,14 +36,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style> 37</style>
38<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 38<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
39 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> 39 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span>
40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
42 42
43 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span> 43 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span>
44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
45<span class="punctuation">}</span> 45<span class="punctuation">}</span>
46 46
47<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 47<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
48 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> 48 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span>
49<span class="punctuation">}</span></code></pre> \ No newline at end of file 49<span class="punctuation">}</span></code></pre> \ No newline at end of file