Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Inspection: unnessary braces in use statement.
For code like

```
use ra_db::input::{CrateId};
```

we should suggest removing the `{}`. The first step would be merely detecting such cases and producing a warning. The code for this is here:

https://github.com/rust-analyzer/rust-analyzer/blob/d77520fde3c953968beb09a3da80a0e7b17bbc04/crates/ra_editor/src/lib.rs#L92-L107

To check the file, one should taverse it using `.descendats()` method of the root node and check for a use tree with a single child.  A similar logic was recently implented [here](https://github.com/rust-analyzer/rust-analyzer/commit/67ac0a423f767ce8973348d5188e8baa6979d380)

Then one needs to add a `Severety` type & field to `Diagnostic`, so that we can distinguish between errors and warnings. Currently all diagnostis are errors, but this new one should be a warning. Then, fixing all usages of `Diagnostic` should basically lead to this being supported in the LSP. 

Pull request description:
resolved #324: remove unnecessary braces in use statement.
Add inspection for unnecessary braces in use statement

Patch:
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index b01382808e..00cdf7effb 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -3,7 +3,7 @@ use std::{
     sync::Arc,
 };
 
-use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit};
+use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit, Severity};
 use ra_syntax::{
     ast::{self, ArgListOwner, Expr, NameOwner},
     AstNode, SourceFileNode,
@@ -364,6 +364,7 @@ impl AnalysisImpl {
             .map(|d| Diagnostic {
                 range: d.range,
                 message: d.msg,
+                severity: d.severity,
                 fix: None,
             })
             .collect::<Vec<_>>();
@@ -385,6 +386,7 @@ impl AnalysisImpl {
                         Diagnostic {
                             range: name_node.range(),
                             message: "unresolved module".to_string(),
+                            severity: Severity::Error,
                             fix: Some(fix),
                         }
                     }
@@ -407,6 +409,7 @@ impl AnalysisImpl {
                         Diagnostic {
                             range: name_node.range(),
                             message: "can't declare module at this location".to_string(),
+                            severity: Severity::Error,
                             fix: Some(fix),
                         }
                     }
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 85df9c089c..fbb19ed6b5 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -29,11 +29,10 @@ use crate::{
     symbol_index::SymbolIndex,
 };
 
-pub use crate::{
-    completion::{CompletionItem, CompletionItemKind, InsertText},
-};
+pub use crate::completion::{CompletionItem, CompletionItemKind, InsertText};
 pub use ra_editor::{
     FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,
+    Severity
 };
 pub use hir::FnSignatureInfo;
 
@@ -198,6 +197,7 @@ pub struct Diagnostic {
     pub message: String,
     pub range: TextRange,
     pub fix: Option<SourceChange>,
+    pub severity: Severity,
 }
 
 #[derive(Debug)]
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs
index 7cbdfb9530..5ce2aa2b47 100644
--- a/crates/ra_analysis/src/mock_analysis.rs
+++ b/crates/ra_analysis/src/mock_analysis.rs
@@ -1,6 +1,6 @@
 use std::sync::Arc;
 
-use relative_path::{RelativePathBuf};
+use relative_path::RelativePathBuf;
 use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
 use ra_db::mock::FileMap;
 
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index 36cabed259..75e4a5f324 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -31,10 +31,17 @@ pub struct HighlightedRange {
     pub tag: &'static str,
 }
 
+#[derive(Debug, Copy, Clone)]
+pub enum Severity {
+    Error,
+    WeakWarning,
+}
+
 #[derive(Debug)]
 pub struct Diagnostic {
     pub range: TextRange,
     pub msg: String,
+    pub severity: Severity,
 }
 
 #[derive(Debug)]
@@ -97,13 +104,37 @@ pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> {
         }
     }
 
-    file.errors()
+    let mut errors: Vec<Diagnostic> = file
+        .errors()
         .into_iter()
         .map(|err| Diagnostic {
             range: location_to_range(err.location()),
             msg: format!("Syntax Error: {}", err),
+            severity: Severity::Error,
         })
-        .collect()
+        .collect();
+
+    let warnings = check_unnecessary_braces_in_use_statement(file);
+
+    errors.extend(warnings);
+    errors
+}
+
+fn check_unnecessary_braces_in_use_statement(file: &SourceFileNode) -> Vec<Diagnostic> {
+    let mut diagnostics = Vec::new();
+    for node in file.syntax().descendants() {
+        if let Some(use_tree_list) = ast::UseTreeList::cast(node) {
+            if use_tree_list.use_trees().count() <= 1 {
+                diagnostics.push(Diagnostic {
+                    range: use_tree_list.syntax().range(),
+                    msg: format!("Unnecessary braces in use statement"),
+                    severity: Severity::WeakWarning,
+                })
+            }
+        }
+    }
+
+    diagnostics
 }
 
 pub fn syntax_tree(file: &SourceFileNode) -> String {
@@ -204,4 +235,25 @@ fn test_foo() {}
 
         do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }");
     }
+
+    #[test]
+    fn test_check_unnecessary_braces_in_use_statement() {
+        let file = SourceFileNode::parse(
+            r#"
+use a;
+use {b};
+use a::{c};
+use a::{c, d::e};
+use a::{c, d::{e}};
+fn main() {}
+"#,
+        );
+        let diagnostics = check_unnecessary_braces_in_use_statement(&file);
+        assert_eq_dbg(
+            r#"[Diagnostic { range: [12; 15), msg: "Unnecessary braces in use statement", severity: WeakWarning },
+                Diagnostic { range: [24; 27), msg: "Unnecessary braces in use statement", severity: WeakWarning },
+                Diagnostic { range: [61; 64), msg: "Unnecessary braces in use statement", severity: WeakWarning }]"#,
+            &diagnostics,
+        )
+    }
 }
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 565ec92af5..c0c414db07 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -13,7 +13,7 @@ use gen_lsp_server::{
 };
 use languageserver_types::NumberOrString;
 use ra_analysis::{Canceled, FileId, LibraryData};
-use ra_vfs::{VfsTask};
+use ra_vfs::VfsTask;
 use rayon;
 use threadpool::ThreadPool;
 use rustc_hash::FxHashSet;
@@ -23,7 +23,7 @@ use failure_derive::Fail;
 
 use crate::{
     main_loop::subscriptions::Subscriptions,
-    project_model::{workspace_loader},
+    project_model::workspace_loader,
     req,
     server_world::{ServerWorld, ServerWorldState},
     Result,
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 252d1ba3ea..658d169cda 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -8,7 +8,7 @@ use languageserver_types::{
     PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
     WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents,
 };
-use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition};
+use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition, Severity};
 use ra_syntax::{TextUnit, text_utils::intersect};
 use ra_text_edit::text_utils::contains_offset_nonstrict;
 use rustc_hash::FxHashMap;
@@ -650,7 +650,7 @@ pub fn publish_diagnostics(
         .into_iter()
         .map(|d| Diagnostic {
             range: d.range.conv_with(&line_index),
-            severity: Some(DiagnosticSeverity::Error),
+            severity: Some(to_diagnostic_severity(d.severity)),
             code: None,
             source: Some("rust-analyzer".to_string()),
             message: d.message,
@@ -684,3 +684,14 @@ fn highlight(world: &ServerWorld, file_id: FileId) -> Result<Vec<Decoration>> {
         .collect();
     Ok(res)
 }
+
+fn to_diagnostic_severity(severity: Severity) -> DiagnosticSeverity {
+    use ra_analysis::Severity::*;
+
+    match severity {
+        Error => DiagnosticSeverity::Error,
+        Warning => DiagnosticSeverity::Warning,
+        Information => DiagnosticSeverity::Information,
+        Hint => DiagnosticSeverity::Hint,
+    }
+}


Test patch:
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index 938ca797a2..210fa2a135 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -82,7 +82,8 @@ fn test_unresolved_module_diagnostic() {
                 label: "create module",
                 source_file_edits: [],
                 file_system_edits: [CreateFile { source_root: SourceRootId(0), path: "foo.rs" }],
-                cursor_position: None }) }]"#,
+                cursor_position: None }),
+                severity: Error }]"#,
         &diagnostics,
     );
 }