Repository: rust-analyzer/rust-analyzer
Pull request #520 (2019-01-13 12:03:28)

Imprement tuple inference
related #394

I'm sorry I'm late.

I try implementing array inference next.


Patch:
diff --git a/README.md b/README.md
index cbb72c8cfe..0b7203d2c5 100644
--- a/README.md
+++ b/README.md
@@ -2,13 +2,15 @@
 
 [![Build Status](https://travis-ci.org/rust-analyzer/rust-analyzer.svg?branch=master)](https://travis-ci.org/rust-analyzer/rust-analyzer)
 
-Rust Analyzer is an **experimental** modular compiler frontend for the Rust
-language, which aims to lay a foundation for excellent IDE support.
+Rust Analyzer is an **experimental** modular compiler frontend for the
+Rust language, which aims to lay a foundation for excellent IDE
+support.
 
-It doesn't implement much of compiler functionality yet, but the white-space
-preserving Rust parser works, and there are significant chunks of overall
-architecture (indexing, on-demand & lazy computation, snapshotable world view)
-in place. Some basic IDE functionality is provided via a language server.
+It doesn't implement much of compiler functionality yet, but the
+white-space preserving Rust parser works, and there are significant
+chunks of overall architecture (indexing, on-demand & lazy
+computation, snapshotable world view) in place. Some basic IDE
+functionality is provided via a language server.
 
 Work on the Rust Analyzer is sponsored by
 
@@ -28,45 +30,53 @@ $ cargo run --package ra_cli parse < crates/ra_syntax/src/lib.rs
 
 # show symbols of a Rust file
 $ cargo run --package ra_cli symbols < crates/ra_syntax/src/lib.rs
-
-# install the language server
-$ cargo install --path crates/ra_lsp_server
 ```
 
-See [these instructions](./editors/README.md) for VS Code setup and the list of
-features (some of which are VS Code specific).
+To try out the language server, see [these
+instructions](./editors/README.md). Please note that the server is not
+ready for general use yet. If you are looking for a Rust IDE that
+works, use [IntelliJ
+Rust](https://github.com/intellij-rust/intellij-rust) or
+[RLS](https://github.com/rust-lang-nursery/rls). That being said, the
+basic stuff works, and rust analyzer is developed in the rust analyzer
+powered editor.
+
 
 ## Current Status and Plans
 
-Rust analyzer aims to fill the same niche as the official [Rust Language
-Server](https://github.com/rust-lang-nursery/rls), but uses a significantly
-different architecture. More details can be found [in this
+Rust analyzer aims to fill the same niche as the official [Rust
+Language Server](https://github.com/rust-lang-nursery/rls), but uses a
+significantly different architecture. More details can be found [in
+this
 thread](https://internals.rust-lang.org/t/2019-strategy-for-rustc-and-the-rls/8361),
-but the core issue is that RLS works in the "wait until user stops typing, run
-the build process, save the results of the analysis" mode, which arguably is the
-wrong foundation for IDE.
+but the core issue is that RLS works in the "wait until user stops
+typing, run the build process, save the results of the analysis" mode,
+which arguably is the wrong foundation for IDE.
 
-Rust Analyzer is an experimental project at the moment, there's exactly zero
+Rust Analyzer is a hobby project at the moment, there's exactly zero
 guarantees that it becomes production-ready one day.
 
-The near/mid term plan is to work independently of the main rustc compiler and
-implement at least simplistic versions of name resolution, macro expansion and
-type inference. The purpose is two fold:
+The near/mid term plan is to work independently of the main rustc
+compiler and implement at least simplistic versions of name
+resolution, macro expansion and type inference. The purpose is two
+fold:
 
-* to quickly bootstrap usable and useful language server: solution that covers
-  80% of Rust code will be useful for IDEs, and will be vastly simpler than 100%
-  solution.
+* to quickly bootstrap usable and useful language server: solution
+  that covers 80% of Rust code will be useful for IDEs, and will be
+  vastly simpler than 100% solution.
 
 * to understand how the consumer-side of compiler API should look like
-  (especially it's on-demand aspects). If you have `get_expression_type`
-  function, you can write a ton of purely-IDE features on top of it, even if the
-  function is only partially correct. Pluging in the precise function afterwards
-  should just make IDE features more reliable.
-
-The long term plan is to merge with the mainline rustc compiler, probably around
-the HIR boundary? That is, use rust analyzer for parsing, macro expansion and
-related bits of name resolution, but leave the rest (including type inference
-and trait selection) to the existing rustc.
+  (especially it's on-demand aspects). If you have
+  `get_expression_type` function, you can write a ton of purely-IDE
+  features on top of it, even if the function is only partially
+  correct. Plugin in the precise function afterwards should just make
+  IDE features more reliable.
+
+The long term plan is to merge with the mainline rustc compiler,
+probably around the HIR boundary? That is, use rust analyzer for
+parsing, macro expansion and related bits of name resolution, but
+leave the rest (including type inference and trait selection) to the
+existing rustc.
 
 ## Getting in touch
 
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 593fe15981..f0936e9f33 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -183,6 +183,9 @@ pub enum Expr {
         arg_types: Vec<Option<TypeRef>>,
         body: ExprId,
     },
+    Tuple {
+        exprs: Vec<ExprId>,
+    },
 }
 
 pub use ra_syntax::ast::PrefixOp as UnaryOp;
@@ -297,6 +300,11 @@ impl Expr {
             | Expr::UnaryOp { expr, .. } => {
                 f(*expr);
             }
+            Expr::Tuple { exprs } => {
+                for expr in exprs {
+                    f(*expr);
+                }
+            }
         }
     }
 }
@@ -621,11 +629,14 @@ impl ExprCollector {
                 let op = e.op();
                 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
             }
+            ast::ExprKind::TupleExpr(e) => {
+                let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect();
+                self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr)
+            }
 
             // TODO implement HIR for these:
             ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
             ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
-            ast::ExprKind::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
             ast::ExprKind::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
             ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
             ast::ExprKind::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 5d5568d69a..0692d3b2a8 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -1040,6 +1040,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 }
                 _ => Ty::Unknown,
             },
+            Expr::Tuple { exprs } => {
+                let mut ty_vec = Vec::with_capacity(exprs.len());
+                for arg in exprs.iter() {
+                    ty_vec.push(self.infer_expr(*arg, &Expectation::none())?);
+                }
+
+                Ty::Tuple(Arc::from(ty_vec))
+            }
         };
         // use a new type variable if we got Ty::Unknown here
         let ty = self.insert_type_vars_shallow(ty);
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index c445d4fa1d..2968b807ca 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -21,11 +21,7 @@ pub(crate) fn hover(
         match ref_result {
             Exact(nav) => res.extend(doc_text_for(db, nav)?),
             Approximate(navs) => {
-                let mut msg = String::from("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support glob imports or traits.");
-                if !navs.is_empty() {
-                    msg.push_str("  \nThese items were found instead:");
-                }
-                res.push(msg);
+                res.push("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support glob imports or traits.  \nThese methods were found instead:".to_string());
                 for nav in navs {
                     res.extend(doc_text_for(db, nav)?)
                 }
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index f745cb1cbf..94842a5143 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -2969,7 +2969,11 @@ impl AstNode for TupleExpr {
 }
 
 
-impl TupleExpr {}
+impl TupleExpr {
+    pub fn exprs(&self) -> impl Iterator<Item = &Expr> {
+        super::children(self)
+    }
+}
 
 // TuplePat
 #[derive(Debug, PartialEq, Eq, Hash)]
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index bac62fa047..dfd88bd109 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -357,7 +357,9 @@ Grammar(
             enum: ["FnDef", "TypeDef", "ConstDef"]
         ),
 
-        "TupleExpr": (),
+        "TupleExpr": (
+            collections: [["exprs", "Expr"]]
+        ),
         "ArrayExpr": (),
         "ParenExpr": (options: ["Expr"]),
         "PathExpr": (options: ["Path"]),
diff --git a/editors/README.md b/editors/README.md
index 9ed65e631c..5b09750e6d 100644
--- a/editors/README.md
+++ b/editors/README.md
@@ -4,96 +4,72 @@ To install experimental VS Code plugin:
 $ git clone https://github.com/rust-analyzer/rust-analyzer.git --depth 1
 $ cd rust-analyzer
 $ cargo install-code
-
-# for stdlib support
-$ rustup component add rust-src
 ```
 
-This will run `cargo install --packge ra_lsp_server` to install the server
-binary into `~/.cargo/bin`, and then will build and install plugin from
-`editors/code`. See
-[this](https://github.com/rust-analyzer/rust-analyzer/blob/0199572a3d06ff66eeae85a2d2c9762996f0d2d8/crates/tools/src/main.rs#L150)
-for details. The installation is expected to *just work*, if it doesn't, report
-bugs!
+This will run `cargo install --packge ra_lsp_server` to install the
+server binary into `~/.cargo/bin`, and then will build and install
+plugin from `editors/code`. See
+[this](https://github.com/matklad/rust-analyzer/blob/cc76b0d31d8ba013c499dd3a4ca69b37004795e6/crates/tools/src/main.rs#L192)
+for details
 
 It's better to remove existing Rust plugins to avoid interference.
 
-## Rust Analyzer Specifc Features
-
-These features are implemented as extensions to the langauge server protocol.
-They are more experimental in nature and work only with VS Code.
-
-### Syntax highlighting
-
-It overrides built-in highlighting, and works only with a specific theme
-(zenburn). `ra-lsp.highlightingOn` setting can be used to disable it.
-
-### Go to symbol in workspace <kbd>ctrl+t</kbd>
-
-It mostly works on top of the built-in LSP functionality, however `#` and `*`
-symbols can be used to narrow down the search. Specifically,
-
-- `#Foo` searches for `Foo` type in the current workspace
-- `#foo#` searches for `foo` function in the current workspace
-- `#Foo*` searches for `Foo` type among dependencies, excluding `stdlib`
-- `#foo#*` seaches for `foo` function among dependencies.
-
-That is, `#` switches from "types" to all symbols, `*` switches from the current
-workspace to dependencies.
-
-### Commands <kbd>ctrl+shift+p</kbd>
-
-#### Show Rust Syntax Tree
-
-Shows the parse tree of the current file. It exists mostly for debugging
-rust-analyzer itself.
-
-#### Extend Selection
-
-Extends the current selection to the encompassing syntactic construct
-(expression, statement, item, module, etc). It works with multiple cursors. Do
-bind this command to a key, its super-useful! Expected to be upstreamed to LSP soonish:
-https://github.com/Microsoft/language-server-protocol/issues/613
-
-#### Matching Brace
-
-If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
-moves cursor to the matching brace. It uses the actual parser to determine
-braces, so it won't confuse generics with comparisons.
-
-#### Parent Module
-
-Navigates to the parent module of the current module.
+### Features:
+
+* syntax highlighting (LSP does not have API for it, so impl is hacky
+  and sometimes fall-backs to the horrible built-in highlighting)
+
+* **Go to symbol in workspace** (`ctrl+t`)
+  - `#Foo` searches for `Foo` type in the current workspace
+  - `#foo#` searches for `foo` function in the current workspace
+  - `#Foo*` searches for `Foo` type among dependencies, excluding `stdlib`
+  - Sorry for a weired UI, neither LSP, not VSCode have any sane API for filtering! :)
+
+* **Go to symbol in file** (`alt+shift+o`)
+
+* **Go to definition** ("correct" for `mod foo;` decls, approximate for other things).
+
+* commands (`ctrl+shift+p` or keybindings)
+  - **Show Rust Syntax Tree** (use it to verify that plugin works)
+  - **Rust Extend Selection**. Extends the current selection to the
+    encompassing syntactic construct (expression, statement, item,
+    module, etc). It works with multiple cursors. Do bind this command
+    to a key, its super-useful!
+  - **Rust Matching Brace**. If the cursor is on any brace
+    (`<>(){}[]`) which is a part of a brace-pair, moves cursor to the
+    matching brace.
+  - **Rust Parent Module**. Navigate to the parent module of the current module
+  - **Rust Join Lines**. Join selected lines into one, smartly fixing
+    up whitespace and trailing commas.
+  - **Run test at caret**. When cursor is inside a function marked
+    `#[test]`, this action runs this specific test. If the cursor is
+    outside of the test function, this re-runs the last test. Do bind
+    this to a shortcut!
+  - **Format document**. Formats the current file with rustfmt.
+    Rustfmt must be installed separately with `rustup component add rustfmt`.
+
+* Typing assists
+  - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression.
+  - Enter inside comments continues comment (`<|>` signifies cursor position):
 
-#### Join Lines
-
-Join selected lines into one, smartly fixing up whitespace and trailing commas.
-
-#### Run
-
-Shows popup suggesting to run a test/benchmark/binary **at the current cursor
-location**. Super useful for repeatedly running just a single test. Do bind this
-to a shortcut!
-
-
-### On Typing Assists
-
-Some features trigger on typing certain characters:
-
-- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression.
-- Enter inside comments automatically inserts `///`
-- typing `.` in a chain method call auto-indents
+```
+/// Docs<|>
+fn foo() {}
+```
 
+```
+/// Docs
+/// <|>
+fn foo() {}
+```
 
-### Code Actions (Assists)
+* code actions (use `ctrl+.` to activate).
 
-These are triggered in a particular context via lightbulb. We use custom code on
-the VS Code side to be able to position cursor.
 
 
 - Flip `,`
 
-```rust
+```
 // before:
 fn foo(x: usize,<|> dim: (usize, usize))
 // after:
@@ -102,7 +78,7 @@ fn foo(dim: (usize, usize), x: usize)
 
 - Add `#[derive]`
 
-```rust
+```
 // before:
 struct Foo {
     <|>x: i32
@@ -116,7 +92,7 @@ struct Foo {
 
 - Add `impl`
 
-```rust
+```
 // before:
 struct Foo<'a, T: Debug> {
     <|>t: T
@@ -130,90 +106,3 @@ impl<'a, T: Debug> Foo<'a, T> {
     <|>
 }
 ```
-
-- Change visibility
-
-```rust
-// before:
-fn<|> foo() {}
-
-// after
-pub(crate) fn foo() {}
-```
-
-- Introduce variable:
-
-```rust
-// before:
-fn foo() {
-    foo(<|>1 + 1<|>);
-}
-
-// after:
-fn foo() {
-    let var_name = 1 + 1;
-    foo(var_name);
-}
-```
-
-- Replace if-let with match:
-
-```rust
-// before:
-impl VariantData {
-    pub fn is_struct(&self) -> bool {
-        if <|>let VariantData::Struct(..) = *self {
-            true
-        } else {
-            false
-        }
-    }
-}
-
-// after:
-impl VariantData {
-    pub fn is_struct(&self) -> bool {
-        <|>match *self {
-            VariantData::Struct(..) => true,
-            _ => false,
-        }
-    }
-}
-```
-
-- Split import
-
-```rust
-// before:
-use algo:<|>:visitor::{Visitor, visit};
-//after:
-use algo::{<|>visitor::{Visitor, visit}};
-```
-
-## LSP features
-
-* **Go to definition**: works correctly for local variables and some paths,
-  falls back to heuristic name matching for other things for the time being.
-
-* **Completion**: completes paths, including dependencies and standard library.
-  Does not handle glob imports and macros. Completes fields and inherent methods
-
-* **Outline** <kbd>alt+shift+o</kbd>
-
-* **Signature Info**
-
-* **Format document**. Formats the current file with rustfmt. Rustfmt must be
-  installed separately with `rustup component add rustfmt`.
-
-* **Hover** shows types of expressions and docstings
-
-* **Rename** works for local variables
-
-* **Code Lens** for running tests
-
-* **Folding**
-
-* **Diagnostics**
-  - missing module for `mod foo;` with a fix to create `foo.rs`.
-  - struct field shorthand
-  - unnessary braces in use item


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 1c31294410..920188fc91 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -268,6 +268,25 @@ fn test(a: A) {
     );
 }
 
+#[test]
+fn infer_tuple() {
+    check_inference(
+        r#"
+fn test(x: &str, y: isize) {
+    let a: (u32, &str) = (1, "a");
+    let b = (a, x);
+    let c = (y, x);
+    let d = (c, x);
+
+    // we have not infered these case yet.
+    let e = (1, "e");
+    let f = (e, "d");
+}
+"#,
+        "tuple.txt",
+    );
+}
+
 fn infer(content: &str) -> String {
     let (db, _, file_id) = MockDatabase::with_single_file(content);
     let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty/tests/data/tuple.txt b/crates/ra_hir/src/ty/tests/data/tuple.txt
new file mode 100644
index 0000000000..96169180d4
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/tuple.txt
@@ -0,0 +1,27 @@
+[9; 10) 'x': &str
+[18; 19) 'y': isize
+[28; 214) '{     ...d"); }': ()
+[38; 39) 'a': (u32, &str)
+[55; 63) '(1, "a")': (u32, &str)
+[56; 57) '1': u32
+[59; 62) '"a"': &str
+[73; 74) 'b': ((u32, &str), &str)
+[77; 83) '(a, x)': ((u32, &str), &str)
+[78; 79) 'a': (u32, &str)
+[81; 82) 'x': &str
+[93; 94) 'c': (isize, &str)
+[97; 103) '(y, x)': (isize, &str)
+[98; 99) 'y': isize
+[101; 102) 'x': &str
+[113; 114) 'd': ((isize, &str), &str)
+[117; 123) '(c, x)': ((isize, &str), &str)
+[118; 119) 'c': (isize, &str)
+[121; 122) 'x': &str
+[177; 178) 'e': ([unknown], [unknown])
+[181; 189) '(1, "e")': ([unknown], [unknown])
+[182; 183) '1': [unknown]
+[185; 188) '"e"': [unknown]
+[199; 200) 'f': (([unknown], [unknown]), [unknown])
+[203; 211) '(e, "d")': (([unknown], [unknown]), [unknown])
+[204; 205) 'e': ([unknown], [unknown])
+[207; 210) '"d"': [unknown]