Repository: rust-analyzer/rust-analyzer
Pull request #1562 (2019-07-20 10:36:50)

Continue support for .await
- add await expr to ast and HIR Expr
- infer type for `.await`


Patch:
diff --git a/crates/ra_assists/src/add_derive.rs b/crates/ra_assists/src/add_derive.rs
index 9c88644df4..f19196f53d 100644
--- a/crates/ra_assists/src/add_derive.rs
+++ b/crates/ra_assists/src/add_derive.rs
@@ -22,9 +22,9 @@ pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
                 edit.insert(node_start, "#[derive()]\n");
                 node_start + TextUnit::of_str("#[derive(")
             }
-            Some(tt) => tt.syntax().text_range().end() - TextUnit::of_char(')'),
+            Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
         };
-        edit.target(nominal.syntax().text_range());
+        edit.target(nominal.syntax().range());
         edit.set_cursor(offset)
     });
 
@@ -37,7 +37,7 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
         .syntax()
         .children_with_tokens()
         .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
-    Some(non_ws_child.text_range().start())
+    Some(non_ws_child.range().start())
 }
 
 #[cfg(test)]
diff --git a/crates/ra_assists/src/add_explicit_type.rs b/crates/ra_assists/src/add_explicit_type.rs
index 88970929ff..a69cfc8e3a 100644
--- a/crates/ra_assists/src/add_explicit_type.rs
+++ b/crates/ra_assists/src/add_explicit_type.rs
@@ -16,10 +16,10 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
         PatKind::BindPat(bind_pat) => bind_pat,
         _ => return None,
     };
-    let pat_range = pat.syntax().text_range();
+    let pat_range = pat.syntax().range();
     // The binding must have a name
     let name = pat.name()?;
-    let name_range = name.syntax().text_range();
+    let name_range = name.syntax().range();
     // Assist not applicable if the type has already been specified
     if stmt.syntax().children_with_tokens().any(|child| child.kind() == T![:]) {
         return None;
diff --git a/crates/ra_assists/src/add_impl.rs b/crates/ra_assists/src/add_impl.rs
index 9a0cfb4e7f..cebc195396 100644
--- a/crates/ra_assists/src/add_impl.rs
+++ b/crates/ra_assists/src/add_impl.rs
@@ -11,9 +11,9 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
     let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
     let name = nominal.name()?;
     ctx.add_action(AssistId("add_impl"), "add impl", |edit| {
-        edit.target(nominal.syntax().text_range());
+        edit.target(nominal.syntax().range());
         let type_params = nominal.type_param_list();
-        let start_offset = nominal.syntax().text_range().end();
+        let start_offset = nominal.syntax().range().end();
         let mut buf = String::new();
         buf.push_str("\n\nimpl");
         if let Some(type_params) = &type_params {
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
index 995e44d5ee..b992a4dc80 100644
--- a/crates/ra_assists/src/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -43,7 +43,7 @@ fn add_missing_impl_members_inner(
 
     let trait_def = {
         let file_id = ctx.frange.file_id;
-        let position = FilePosition { file_id, offset: impl_node.syntax().text_range().start() };
+        let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
         let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None);
 
         resolve_target_trait_def(ctx.db, &analyzer, &impl_node)?
@@ -87,7 +87,7 @@ fn add_missing_impl_members_inner(
         ast_editor.append_items(items);
 
         let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap();
-        let cursor_position = first_new_item.syntax().text_range().start();
+        let cursor_position = first_new_item.syntax().range().start();
         ast_editor.into_text_edit(edit.text_edit_builder());
 
         edit.set_cursor(cursor_position);
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 4d5a76de67..e52085f85d 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -141,7 +141,7 @@ impl AssistBuilder {
         if let Some(indent) = leading_indent(node) {
             replace_with = reindent(&replace_with, &indent)
         }
-        self.replace(node.text_range(), replace_with)
+        self.replace(node.range(), replace_with)
     }
 
     pub(crate) fn set_edit_builder(&mut self, edit: TextEditBuilder) {
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs
index 36235eb137..a35334d7e2 100644
--- a/crates/ra_assists/src/ast_editor.rs
+++ b/crates/ra_assists/src/ast_editor.rs
@@ -22,10 +22,7 @@ impl<N: AstNode> AstEditor<N> {
         // FIXME: compute a more fine-grained diff here.
         // If *you* know a nice algorithm to compute diff between two syntax
         // tree, tell me about it!
-        builder.replace(
-            self.original_ast.syntax().text_range(),
-            self.ast().syntax().text().to_string(),
-        );
+        builder.replace(self.original_ast.syntax().range(), self.ast().syntax().text().to_string());
     }
 
     pub fn ast(&self) -> &N {
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index 43e75eee18..0eb4bdb622 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -356,7 +356,7 @@ fn best_action_for_target(
             // todo: we should include even whitespace blocks as anchor candidates
             let anchor = container
                 .children()
-                .find(|n| n.text_range().start() < anchor.text_range().start())
+                .find(|n| n.range().start() < anchor.range().start())
                 .or_else(|| Some(anchor));
 
             ImportAction::add_new_use(anchor, false)
@@ -418,7 +418,7 @@ fn make_assist_add_new_use(
                 buf.push_str(&spaces);
             }
         }
-        let position = if after { anchor.text_range().end() } else { anchor.text_range().start() };
+        let position = if after { anchor.range().end() } else { anchor.range().start() };
         edit.insert(position, buf);
     }
 }
@@ -434,10 +434,10 @@ fn make_assist_add_in_tree_list(
         let mut buf = String::new();
         let comma = last.syntax().siblings(Direction::Next).find(|n| n.kind() == T![,]);
         let offset = if let Some(comma) = comma {
-            comma.text_range().end()
+            comma.range().end()
         } else {
             buf.push_str(",");
-            last.syntax().text_range().end()
+            last.syntax().range().end()
         };
         if add_self {
             buf.push_str(" self")
@@ -462,11 +462,11 @@ fn make_assist_add_nested_import(
     if let Some(use_tree) = use_tree {
         let (start, add_colon_colon) = if let Some(first_segment_to_split) = first_segment_to_split
         {
-            (first_segment_to_split.syntax().text_range().start(), false)
+            (first_segment_to_split.syntax().range().start(), false)
         } else {
-            (use_tree.syntax().text_range().end(), true)
+            (use_tree.syntax().range().end(), true)
         };
-        let end = use_tree.syntax().text_range().end();
+        let end = use_tree.syntax().range().end();
 
         let mut buf = String::new();
         if add_colon_colon {
@@ -497,8 +497,8 @@ fn apply_auto_import(
         // Here we are assuming the assist will provide a  correct use statement
         // so we can delete the path qualifier
         edit.delete(TextRange::from_to(
-            path.syntax().text_range().start(),
-            last.syntax().text_range().start(),
+            path.syntax().range().start(),
+            last.syntax().range().start(),
         ));
     }
 }
diff --git a/crates/ra_assists/src/change_visibility.rs b/crates/ra_assists/src/change_visibility.rs
index d28cdd07bf..ab10d2aa44 100644
--- a/crates/ra_assists/src/change_visibility.rs
+++ b/crates/ra_assists/src/change_visibility.rs
@@ -35,15 +35,14 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
         if parent.children().any(|child| child.kind() == VISIBILITY) {
             return None;
         }
-        (vis_offset(&parent), keyword.text_range())
+        (vis_offset(&parent), keyword.range())
     } else {
         let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?;
         let field = ident.parent().ancestors().find_map(ast::NamedFieldDef::cast)?;
-        if field.name()?.syntax().text_range() != ident.text_range() && field.visibility().is_some()
-        {
+        if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
             return None;
         }
-        (vis_offset(field.syntax()), ident.text_range())
+        (vis_offset(field.syntax()), ident.range())
     };
 
     ctx.add_action(AssistId("change_visibility"), "make pub(crate)", |edit| {
@@ -62,25 +61,25 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
             _ => false,
         })
         .next()
-        .map(|it| it.text_range().start())
-        .unwrap_or_else(|| node.text_range().start())
+        .map(|it| it.range().start())
+        .unwrap_or_else(|| node.range().start())
 }
 
 fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> {
     if vis.syntax().text() == "pub" {
         ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
-            edit.target(vis.syntax().text_range());
-            edit.replace(vis.syntax().text_range(), "pub(crate)");
-            edit.set_cursor(vis.syntax().text_range().start())
+            edit.target(vis.syntax().range());
+            edit.replace(vis.syntax().range(), "pub(crate)");
+            edit.set_cursor(vis.syntax().range().start())
         });
 
         return ctx.build();
     }
     if vis.syntax().text() == "pub(crate)" {
         ctx.add_action(AssistId("change_visibility"), "change to pub", |edit| {
-            edit.target(vis.syntax().text_range());
-            edit.replace(vis.syntax().text_range(), "pub");
-            edit.set_cursor(vis.syntax().text_range().start());
+            edit.target(vis.syntax().range());
+            edit.replace(vis.syntax().range(), "pub");
+            edit.set_cursor(vis.syntax().range().start());
         });
 
         return ctx.build();
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index 9394298926..b96806ac66 100644
--- a/crates/ra_assists/src/fill_match_arms.rs
+++ b/crates/ra_assists/src/fill_match_arms.rs
@@ -84,8 +84,8 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
             buf.push_str(" => (),\n");
         }
         buf.push_str("}");
-        edit.target(match_expr.syntax().text_range());
-        edit.set_cursor(expr.syntax().text_range().start());
+        edit.target(match_expr.syntax().range());
+        edit.set_cursor(expr.syntax().range().start());
         edit.replace_node_and_indent(match_expr.syntax(), buf);
     });
 
diff --git a/crates/ra_assists/src/flip_binexpr.rs b/crates/ra_assists/src/flip_binexpr.rs
index b55b36a8ed..2e591ad3bd 100644
--- a/crates/ra_assists/src/flip_binexpr.rs
+++ b/crates/ra_assists/src/flip_binexpr.rs
@@ -8,7 +8,7 @@ pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
     let expr = ctx.node_at_offset::<BinExpr>()?;
     let lhs = expr.lhs()?.syntax().clone();
     let rhs = expr.rhs()?.syntax().clone();
-    let op_range = expr.op_token()?.text_range();
+    let op_range = expr.op_token()?.range();
     // The assist should be applied only if the cursor is on the operator
     let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
     if !cursor_in_range {
@@ -25,8 +25,8 @@ pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
         if let FlipAction::FlipAndReplaceOp(new_op) = action {
             edit.replace(op_range, new_op);
         }
-        edit.replace(lhs.text_range(), rhs.text());
-        edit.replace(rhs.text_range(), lhs.text());
+        edit.replace(lhs.range(), rhs.text());
+        edit.replace(rhs.range(), lhs.text());
     });
 
     ctx.build()
diff --git a/crates/ra_assists/src/flip_comma.rs b/crates/ra_assists/src/flip_comma.rs
index 34489329cb..13016ae06d 100644
--- a/crates/ra_assists/src/flip_comma.rs
+++ b/crates/ra_assists/src/flip_comma.rs
@@ -8,9 +8,9 @@ pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
     let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
     let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
     ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
-        edit.target(comma.text_range());
-        edit.replace(prev.text_range(), next.to_string());
-        edit.replace(next.text_range(), prev.to_string());
+        edit.target(comma.range());
+        edit.replace(prev.range(), next.to_string());
+        edit.replace(next.range(), prev.to_string());
     });
 
     ctx.build()
diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs
index 82c4d54b0c..3c17089deb 100644
--- a/crates/ra_assists/src/inline_local_variable.rs
+++ b/crates/ra_assists/src/inline_local_variable.rs
@@ -22,12 +22,9 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
         .next_sibling_or_token()
         .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone()))
     {
-        TextRange::from_to(
-            let_stmt.syntax().text_range().start(),
-            whitespace.syntax().text_range().end(),
-        )
+        TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end())
     } else {
-        let_stmt.syntax().text_range()
+        let_stmt.syntax().range()
     };
     let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None);
     let refs = analyzer.find_all_refs(&bind_pat);
diff --git a/crates/ra_assists/src/introduce_variable.rs b/crates/ra_assists/src/introduce_variable.rs
index df6c589899..ce28132c9a 100644
--- a/crates/ra_assists/src/introduce_variable.rs
+++ b/crates/ra_assists/src/introduce_variable.rs
@@ -48,7 +48,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
             if !full_stmt.unwrap().has_semi() {
                 buf.push_str(";");
             }
-            edit.replace(expr.syntax().text_range(), buf);
+            edit.replace(expr.syntax().range(), buf);
         } else {
             buf.push_str(";");
 
@@ -66,14 +66,14 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
                 buf.push_str(text);
             }
 
-            edit.target(expr.syntax().text_range());
-            edit.replace(expr.syntax().text_range(), "var_name".to_string());
-            edit.insert(anchor_stmt.text_range().start(), buf);
+            edit.target(expr.syntax().range());
+            edit.replace(expr.syntax().range(), "var_name".to_string());
+            edit.insert(anchor_stmt.range().start(), buf);
             if wrap_in_block {
-                edit.insert(anchor_stmt.text_range().end(), " }");
+                edit.insert(anchor_stmt.range().end(), " }");
             }
         }
-        edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset);
+        edit.set_cursor(anchor_stmt.range().start() + cursor_offset);
     });
 
     ctx.build()
diff --git a/crates/ra_assists/src/move_guard.rs b/crates/ra_assists/src/move_guard.rs
index 0f3cdbe533..313c9ad187 100644
--- a/crates/ra_assists/src/move_guard.rs
+++ b/crates/ra_assists/src/move_guard.rs
@@ -17,11 +17,11 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
     let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
 
     ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
-        edit.target(guard.syntax().text_range());
+        edit.target(guard.syntax().range());
         let offseting_amount = match &space_before_guard {
             Some(SyntaxElement::Token(tok)) => {
                 if let Some(_) = ast::Whitespace::cast(tok.clone()) {
-                    let ele = space_before_guard.unwrap().text_range();
+                    let ele = space_before_guard.unwrap().range();
                     edit.delete(ele);
                     ele.len()
                 } else {
@@ -31,11 +31,9 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
             _ => TextUnit::from(0),
         };
 
-        edit.delete(guard.syntax().text_range());
+        edit.delete(guard.syntax().range());
         edit.replace_node_and_indent(arm_expr.syntax(), buf);
-        edit.set_cursor(
-            arm_expr.syntax().text_range().start() + TextUnit::from(3) - offseting_amount,
-        );
+        edit.set_cursor(arm_expr.syntax().range().start() + TextUnit::from(3) - offseting_amount);
     });
     ctx.build()
 }
@@ -64,18 +62,18 @@ pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>)
         AssistId("move_arm_cond_to_match_guard"),
         "move condition to match guard",
         |edit| {
-            edit.target(if_expr.syntax().text_range());
+            edit.target(if_expr.syntax().range());
             let then_only_expr = then_block.statements().next().is_none();
 
             match &then_block.expr() {
                 Some(then_expr) if then_only_expr => {
-                    edit.replace(if_expr.syntax().text_range(), then_expr.syntax().text())
+                    edit.replace(if_expr.syntax().range(), then_expr.syntax().text())
                 }
-                _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()),
+                _ => edit.replace(if_expr.syntax().range(), then_block.syntax().text()),
             }
 
-            edit.insert(last_match_pat.syntax().text_range().end(), buf);
-            edit.set_cursor(last_match_pat.syntax().text_range().end() + TextUnit::from(1));
+            edit.insert(last_match_pat.syntax().range().end(), buf);
+            edit.set_cursor(last_match_pat.syntax().range().end() + TextUnit::from(1));
         },
     );
     ctx.build()
diff --git a/crates/ra_assists/src/remove_dbg.rs b/crates/ra_assists/src/remove_dbg.rs
index 870133fda0..5657ee4b87 100644
--- a/crates/ra_assists/src/remove_dbg.rs
+++ b/crates/ra_assists/src/remove_dbg.rs
@@ -12,7 +12,7 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
         return None;
     }
 
-    let macro_range = macro_call.syntax().text_range();
+    let macro_range = macro_call.syntax().range();
 
     // If the cursor is inside the macro call, we'll try to maintain the cursor
     // position by subtracting the length of dbg!( from the start of the file
@@ -43,7 +43,7 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
     };
 
     ctx.add_action(AssistId("remove_dbg"), "remove dbg!()", |edit| {
-        edit.target(macro_call.syntax().text_range());
+        edit.target(macro_call.syntax().range());
         edit.replace(macro_range, macro_content);
         edit.set_cursor(cursor_pos);
     });
diff --git a/crates/ra_assists/src/replace_if_let_with_match.rs b/crates/ra_assists/src/replace_if_let_with_match.rs
index c0bf6d2351..5de6aa2664 100644
--- a/crates/ra_assists/src/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/replace_if_let_with_match.rs
@@ -17,9 +17,9 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
 
     ctx.add_action(AssistId("replace_if_let_with_match"), "replace with match", |edit| {
         let match_expr = build_match_expr(expr, pat, then_block, else_block);
-        edit.target(if_expr.syntax().text_range());
+        edit.target(if_expr.syntax().range());
         edit.replace_node_and_indent(if_expr.syntax(), match_expr);
-        edit.set_cursor(if_expr.syntax().text_range().start())
+        edit.set_cursor(if_expr.syntax().range().start())
     });
 
     ctx.build()
diff --git a/crates/ra_assists/src/split_import.rs b/crates/ra_assists/src/split_import.rs
index 2c1edddb90..a8feb67c8c 100644
--- a/crates/ra_assists/src/split_import.rs
+++ b/crates/ra_assists/src/split_import.rs
@@ -15,14 +15,14 @@ pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
         return None;
     }
 
-    let l_curly = colon_colon.text_range().end();
+    let l_curly = colon_colon.range().end();
     let r_curly = match top_path.syntax().parent().and_then(ast::UseTree::cast) {
-        Some(tree) => tree.syntax().text_range().end(),
-        None => top_path.syntax().text_range().end(),
+        Some(tree) => tree.syntax().range().end(),
+        None => top_path.syntax().range().end(),
     };
 
     ctx.add_action(AssistId("split_import"), "split import", |edit| {
-        edit.target(colon_colon.text_range());
+        edit.target(colon_colon.range());
         edit.insert(l_curly, "{");
         edit.insert(r_curly, "}");
         edit.set_cursor(l_curly + TextUnit::of_str("{"));
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs
index 403aab3526..c2a2b66d54 100644
--- a/crates/ra_cli/src/analysis_stats.rs
+++ b/crates/ra_cli/src/analysis_stats.rs
@@ -70,7 +70,7 @@ pub fn run(verbose: bool, memory_usage: bool, path: &Path, only: Option<&str>) -
             let src = f.source(db);
             let original_file = src.file_id.original_file(db);
             let path = db.file_relative_path(original_file);
-            let syntax_range = src.ast.syntax().text_range();
+            let syntax_range = src.ast.syntax().range();
             write!(msg, " ({:?} {})", path, syntax_range).unwrap();
         }
         bar.set_message(&msg);
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index de8191ca31..375e2f508f 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -55,7 +55,7 @@ fn main() -> Result<()> {
             let _p = profile("parsing");
             let file = file()?;
             if !matches.is_present("no-dump") {
-                println!("{:#?}", file.syntax());
+                println!("{}", file.syntax().debug_dump());
             }
             std::mem::forget(file);
         }
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 6eed67f50c..7e3e0a07df 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -220,6 +220,9 @@ pub enum Expr {
         expr: ExprId,
         name: Name,
     },
+    Await {
+        expr: ExprId,
+    },
     Try {
         expr: ExprId,
     },
@@ -359,6 +362,7 @@ impl Expr {
                 f(*rhs);
             }
             Expr::Field { expr, .. }
+            | Expr::Await { expr }
             | Expr::Try { expr }
             | Expr::Cast { expr, .. }
             | Expr::Ref { expr, .. }
@@ -729,6 +733,10 @@ where
                 };
                 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
             }
+            ast::ExprKind::AwaitExpr(e) => {
+                let expr = self.collect_expr_opt(e.expr());
+                self.alloc_expr(Expr::Await { expr }, syntax_ptr)
+            }
             ast::ExprKind::TryExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
                 self.alloc_expr(Expr::Try { expr }, syntax_ptr)
@@ -843,7 +851,7 @@ where
                         let file_id = call_id.as_file(MacroFileKind::Expr);
                         if let Some(node) = self.db.parse_or_expand(file_id) {
                             if let Some(expr) = ast::Expr::cast(node) {
-                                log::debug!("macro expansion {:#?}", expr.syntax());
+                                log::debug!("macro expansion {}", expr.syntax().debug_dump());
                                 let old_file_id =
                                     std::mem::replace(&mut self.current_file_id, file_id);
                                 let id = self.collect_expr(expr);
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index 79e1857f93..6589b782c4 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -296,7 +296,7 @@ mod tests {
         let local_name_entry = analyzer.resolve_local_name(&name_ref).unwrap();
         let local_name =
             local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
-        assert_eq!(local_name.range(), expected_name.syntax().text_range());
+        assert_eq!(local_name.range(), expected_name.syntax().range());
     }
 
     #[test]
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index c589f8aba6..6d14eea8ec 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -118,6 +118,9 @@ pub(crate) const ITEM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item
 pub(crate) const OPS: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"ops"));
 pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try"));
 pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
+pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future"));
+pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future"));
+pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
 
 fn resolve_name(text: &SmolStr) -> SmolStr {
     let raw_start = "r#";
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 4c173a4f73..df67d2c393 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -361,7 +361,7 @@ impl SourceAnalyzer {
             })
             .map(|name_ref| ReferenceDescriptor {
                 name: name_ref.text().to_string(),
-                range: name_ref.syntax().text_range(),
+                range: name_ref.syntax().range(),
             })
             .collect()
     }
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index 0e030576d8..a82dff711d 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -1114,6 +1114,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                 .unwrap_or(Ty::Unknown);
                 self.insert_type_vars(ty)
             }
+            Expr::Await { expr } => {
+                let inner_ty = self.infer_expr(*expr, &Expectation::none());
+                let ty = match self.resolve_future_future_output() {
+                    Some(future_future_output_alias) => {
+                        let ty = self.new_type_var();
+                        let projection = ProjectionPredicate {
+                            ty: ty.clone(),
+                            projection_ty: ProjectionTy {
+                                associated_ty: future_future_output_alias,
+                                parameters: vec![inner_ty].into(),
+                            },
+                        };
+                        self.obligations.push(Obligation::Projection(projection));
+                        self.resolve_ty_as_possible(&mut vec![], ty)
+                    }
+                    None => Ty::Unknown,
+                };
+                ty
+            }
             Expr::Try { expr } => {
                 let inner_ty = self.infer_expr(*expr, &Expectation::none());
                 let ty = match self.resolve_ops_try_ok() {
@@ -1368,6 +1387,28 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             _ => None,
         }
     }
+
+    fn resolve_future_future_output(&self) -> Option<TypeAlias> {
+        let future_future_path = Path {
+            kind: PathKind::Abs,
+            segments: vec![
+                PathSegment { name: name::STD, args_and_bindings: None },
+                PathSegment { name: name::FUTURE_MOD, args_and_bindings: None },
+                PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None },
+            ],
+        };
+
+        match self
+            .resolver
+            .resolve_path_segments(self.db, &future_future_path)
+            .into_fully_resolved()
+        {
+            PerNs { types: Some(Def(Trait(trait_))), .. } => {
+                Some(trait_.associated_type_by_name(self.db, name::OUTPUT)?)
+            }
+            _ => None,
+        }
+    }
 }
 
 /// The ID of a type variable.
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs
index 212448d413..270499612b 100644
--- a/crates/ra_ide_api/src/call_info.rs
+++ b/crates/ra_ide_api/src/call_info.rs
@@ -47,7 +47,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
             // Number of arguments specified at the call site
             let num_args_at_callsite = arg_list.args().count();
 
-            let arg_list_range = arg_list.syntax().text_range();
+            let arg_list_range = arg_list.syntax().range();
             if !arg_list_range.contains_inclusive(position.offset) {
                 tested_by!(call_info_bad_offset);
                 return None;
@@ -57,7 +57,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
                 num_args_at_callsite,
                 arg_list
                     .args()
-                    .take_while(|arg| arg.syntax().text_range().end() < position.offset)
+                    .take_while(|arg| arg.syntax().range().end() < position.offset)
                     .count(),
             );
 
diff --git a/crates/ra_ide_api/src/completion/complete_keyword.rs b/crates/ra_ide_api/src/completion/complete_keyword.rs
index 18c1839dcc..4cf34eff8a 100644
--- a/crates/ra_ide_api/src/completion/complete_keyword.rs
+++ b/crates/ra_ide_api/src/completion/complete_keyword.rs
@@ -88,7 +88,7 @@ fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
             .visit::<ast::LoopExpr, _>(|it| it.loop_body())
             .accept(&node);
         if let Some(Some(body)) = loop_body {
-            if leaf.text_range().is_subrange(&body.syntax().text_range()) {
+            if leaf.range().is_subrange(&body.syntax().range()) {
                 return true;
             }
         }
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs
index 5fd41eeb35..29586cd1f1 100644
--- a/crates/ra_ide_api/src/completion/complete_path.rs
+++ b/crates/ra_ide_api/src/completion/complete_path.rs
@@ -26,7 +26,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
                 if Some(module) == ctx.module {
                     if let Some(import) = res.import {
                         if let Either::A(use_tree) = module.import_source(ctx.db, import) {
-                            if use_tree.syntax().text_range().contains_inclusive(ctx.offset) {
+                            if use_tree.syntax().range().contains_inclusive(ctx.offset) {
                                 // for `use self::foo<|>`, don't suggest `foo` as a completion
                                 tested_by!(dont_complete_current_use);
                                 continue;
diff --git a/crates/ra_ide_api/src/completion/complete_postfix.rs b/crates/ra_ide_api/src/completion/complete_postfix.rs
index 6d834f6cec..c75b1c159c 100644
--- a/crates/ra_ide_api/src/completion/complete_postfix.rs
+++ b/crates/ra_ide_api/src/completion/complete_postfix.rs
@@ -12,7 +12,7 @@ use ra_text_edit::TextEditBuilder;
 fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
     let edit = {
         let receiver_range =
-            ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
+            ctx.dot_receiver.as_ref().expect("no receiver available").syntax().range();
         let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
         let mut builder = TextEditBuilder::default();
         builder.replace(delete_range, snippet.to_string());
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 2f78d5409c..b803271aba 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -78,7 +78,7 @@ impl<'a> CompletionContext<'a> {
     pub(crate) fn source_range(&self) -> TextRange {
         match self.token.kind() {
             // workaroud when completion is triggered by trigger characters.
-            IDENT => self.token.text_range(),
+            IDENT => self.token.range(),
             _ => TextRange::offset_len(self.offset, 0.into()),
         }
     }
@@ -123,17 +123,13 @@ impl<'a> CompletionContext<'a> {
     }
 
     fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) {
-        let name_range = name_ref.syntax().text_range();
+        let name_range = name_ref.syntax().range();
         if name_ref.syntax().parent().and_then(ast::NamedField::cast).is_some() {
             self.struct_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset);
         }
 
-        let top_node = name_ref
-            .syntax()
-            .ancestors()
-            .take_while(|it| it.text_range() == name_range)
-            .last()
-            .unwrap();
+        let top_node =
+            name_ref.syntax().ancestors().take_while(|it| it.range() == name_range).last().unwrap();
 
         match top_node.parent().map(|it| it.kind()) {
             Some(SOURCE_FILE) | Some(ITEM_LIST) => {
@@ -184,27 +180,23 @@ impl<'a> CompletionContext<'a> {
                     .ancestors()
                     .find_map(|node| {
                         if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
-                            return Some(
-                                stmt.syntax().text_range() == name_ref.syntax().text_range(),
-                            );
+                            return Some(stmt.syntax().range() == name_ref.syntax().range());
                         }
                         if let Some(block) = ast::Block::cast(node) {
                             return Some(
-                                block.expr().map(|e| e.syntax().text_range())
-                                    == Some(name_ref.syntax().text_range()),
+                                block.expr().map(|e| e.syntax().range())
+                                    == Some(name_ref.syntax().range()),
                             );
                         }
                         None
                     })
                     .unwrap_or(false);
 
-                if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
+                if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
                     if let Some(if_expr) =
                         find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off)
                     {
-                        if if_expr.syntax().text_range().end()
-                            < name_ref.syntax().text_range().start()
-                        {
+                        if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
                             self.after_if = true;
                         }
                     }
@@ -216,14 +208,14 @@ impl<'a> CompletionContext<'a> {
             // ident, so it should have the same range in the non-modified file
             self.dot_receiver = field_expr
                 .expr()
-                .map(|e| e.syntax().text_range())
+                .map(|e| e.syntax().range())
                 .and_then(|r| find_node_with_range(original_file.syntax(), r));
         }
         if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
             // As above
             self.dot_receiver = method_call_expr
                 .expr()
-                .map(|e| e.syntax().text_range())
+                .map(|e| e.syntax().range())
                 .and_then(|r| find_node_with_range(original_file.syntax(), r));
             self.is_call = true;
         }
@@ -237,6 +229,6 @@ fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Op
 fn is_node<N: AstNode>(node: &SyntaxNode) -> bool {
     match node.ancestors().filter_map(N::cast).next() {
         None => false,
-        Some(n) => n.syntax().text_range() == node.text_range(),
+        Some(n) => n.syntax().range() == node.range(),
     }
 }
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs
index 028dc3d4fa..affbad6cd0 100644
--- a/crates/ra_ide_api/src/diagnostics.rs
+++ b/crates/ra_ide_api/src/diagnostics.rs
@@ -96,7 +96,7 @@ fn check_unnecessary_braces_in_use_statement(
 ) -> Option<()> {
     let use_tree_list = ast::UseTreeList::cast(node.clone())?;
     if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
-        let range = use_tree_list.syntax().text_range();
+        let range = use_tree_list.syntax().range();
         let edit =
             text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
                 .unwrap_or_else(|| {
@@ -126,8 +126,8 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
 ) -> Option<TextEdit> {
     let use_tree_list_node = single_use_tree.syntax().parent()?;
     if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] {
-        let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
-        let end = use_tree_list_node.text_range().end();
+        let start = use_tree_list_node.prev_sibling_or_token()?.range().start();
+        let end = use_tree_list_node.range().end();
         let range = TextRange::from_to(start, end);
         let mut edit_builder = TextEditBuilder::default();
         edit_builder.delete(range);
@@ -149,12 +149,12 @@ fn check_struct_shorthand_initialization(
             let field_expr = expr.syntax().text().to_string();
             if field_name == field_expr {
                 let mut edit_builder = TextEditBuilder::default();
-                edit_builder.delete(named_field.syntax().text_range());
-                edit_builder.insert(named_field.syntax().text_range().start(), field_name);
+                edit_builder.delete(named_field.syntax().range());
+                edit_builder.insert(named_field.syntax().range().start(), field_name);
                 let edit = edit_builder.finish();
 
                 acc.push(Diagnostic {
-                    range: named_field.syntax().text_range(),
+                    range: named_field.syntax().range(),
                     message: "Shorthand struct initialization".to_string(),
                     severity: Severity::WeakWarning,
                     fix: Some(SourceChange::source_file_edit(
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 84fabdb9e1..8aff5f2cdf 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -233,7 +233,7 @@ impl NavigationTarget {
 
     pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget {
         let src = macro_call.source(db);
-        log::debug!("nav target {:#?}", src.ast.syntax());
+        log::debug!("nav target {}", src.ast.syntax().debug_dump());
         NavigationTarget::from_named(
             src.file_id.original_file(db),
             &src.ast,
@@ -275,7 +275,7 @@ impl NavigationTarget {
     ) -> NavigationTarget {
         //FIXME: use `_` instead of empty string
         let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
-        let focus_range = node.name().map(|it| it.syntax().text_range());
+        let focus_range = node.name().map(|it| it.syntax().range());
         NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax(), docs, description)
     }
 
@@ -291,7 +291,7 @@ impl NavigationTarget {
             file_id,
             name,
             kind: node.kind(),
-            full_range: node.text_range(),
+            full_range: node.range(),
             focus_range,
             // ptr: Some(LocalSyntaxPtr::new(node)),
             container_name: None,
diff --git a/crates/ra_ide_api/src/display/structure.rs b/crates/ra_ide_api/src/display/structure.rs
index 2ba10b2ef3..0b1a8b6e6f 100644
--- a/crates/ra_ide_api/src/display/structure.rs
+++ b/crates/ra_ide_api/src/display/structure.rs
@@ -73,8 +73,8 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
         Some(StructureNode {
             parent: None,
             label: name.text().to_string(),
-            navigation_range: name.syntax().text_range(),
-            node_range: node.syntax().text_range(),
+            navigation_range: name.syntax().range(),
+            node_range: node.syntax().range(),
             kind: node.syntax().kind(),
             detail,
             deprecated: node.attrs().filter_map(|x| x.as_named()).any(|x| x == "deprecated"),
@@ -140,8 +140,8 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
             let node = StructureNode {
                 parent: None,
                 label,
-                navigation_range: target_type.syntax().text_range(),
-                node_range: im.syntax().text_range(),
+                navigation_range: target_type.syntax().range(),
+                node_range: im.syntax().range(),
                 kind: im.syntax().kind(),
                 detail: None,
                 deprecated: false,
diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs
index 140820df6c..292f61f4ac 100644
--- a/crates/ra_ide_api/src/extend_selection.rs
+++ b/crates/ra_ide_api/src/extend_selection.rs
@@ -42,20 +42,19 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
             TokenAtOffset::None => return None,
             TokenAtOffset::Single(l) => {
                 if string_kinds.contains(&l.kind()) {
-                    extend_single_word_in_comment_or_string(&l, offset)
-                        .unwrap_or_else(|| l.text_range())
+                    extend_single_word_in_comment_or_string(&l, offset).unwrap_or_else(|| l.range())
                 } else {
-                    l.text_range()
+                    l.range()
                 }
             }
-            TokenAtOffset::Between(l, r) => pick_best(l, r).text_range(),
+            TokenAtOffset::Between(l, r) => pick_best(l, r).range(),
         };
         return Some(leaf_range);
     };
     let node = match find_covering_element(root, range) {
         SyntaxElement::Token(token) => {
-            if token.text_range() != range {
-                return Some(token.text_range());
+            if token.range() != range {
+                return Some(token.range());
             }
             if let Some(comment) = ast::Comment::cast(token.clone()) {
                 if let Some(range) = extend_comments(comment) {
@@ -66,12 +65,12 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
         }
         SyntaxElement::Node(node) => node,
     };
-    if node.text_range() != range {
-        return Some(node.text_range());
+    if node.range() != range {
+        return Some(node.range());
     }
 
     // Using shallowest node with same range allows us to traverse siblings.
-    let node = node.ancestors().take_while(|n| n.text_range() == node.text_range()).last().unwrap();
+    let node = node.ancestors().take_while(|n| n.range() == node.range()).last().unwrap();
 
     if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) {
         if let Some(range) = extend_list_item(&node) {
@@ -79,7 +78,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
         }
     }
 
-    node.parent().map(|it| it.text_range())
+    node.parent().map(|it| it.range())
 }
 
 fn extend_single_word_in_comment_or_string(
@@ -87,7 +86,7 @@ fn extend_single_word_in_comment_or_string(
     offset: TextUnit,
 ) -> Option<TextRange> {
     let text: &str = leaf.text();
-    let cursor_position: u32 = (offset - leaf.text_range().start()).into();
+    let cursor_position: u32 = (offset - leaf.range().start()).into();
 
     let (before, after) = text.split_at(cursor_position as usize);
 
@@ -105,31 +104,31 @@ fn extend_single_word_in_comment_or_string(
     if range.is_empty() {
         None
     } else {
-        Some(range + leaf.text_range().start())
+        Some(range + leaf.range().start())
     }
 }
 
 fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextUnit) -> TextRange {
     let ws_text = ws.text();
-    let suffix = TextRange::from_to(offset, ws.text_range().end()) - ws.text_range().start();
-    let prefix = TextRange::from_to(ws.text_range().start(), offset) - ws.text_range().start();
+    let suffix = TextRange::from_to(offset, ws.range().end()) - ws.range().start();
+    let prefix = TextRange::from_to(ws.range().start(), offset) - ws.range().start();
     let ws_suffix = &ws_text.as_str()[suffix];
     let ws_prefix = &ws_text.as_str()[prefix];
     if ws_text.contains('\n') && !ws_suffix.contains('\n') {
         if let Some(node) = ws.next_sibling_or_token() {
             let start = match ws_prefix.rfind('\n') {
-                Some(idx) => ws.text_range().start() + TextUnit::from((idx + 1) as u32),
-                None => node.text_range().start(),
+                Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32),
+                None => node.range().start(),
             };
-            let end = if root.text().char_at(node.text_range().end()) == Some('\n') {
-                node.text_range().end() + TextUnit::of_char('\n')
+            let end = if root.text().char_at(node.range().end()) == Some('\n') {
+                node.range().end() + TextUnit::of_char('\n')
             } else {
-                node.text_range().end()
+                node.range().end()
             };
             return TextRange::from_to(start, end);
         }
     }
-    ws.text_range()
+    ws.range()
 }
 
 fn pick_best<'a>(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken {
@@ -162,7 +161,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
     }
 
     if let Some(comma_node) = nearby_comma(node, Direction::Prev) {
-        return Some(TextRange::from_to(comma_node.text_range().start(), node.text_range().end()));
+        return Some(TextRange::from_to(comma_node.range().start(), node.range().end()));
     }
     if let Some(comma_node) = nearby_comma(node, Direction::Next) {
         // Include any following whitespace when comma if after list item.
@@ -172,7 +171,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
             .filter(|node| is_single_line_ws(node))
             .unwrap_or(comma_node);
 
-        return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end()));
+        return Some(TextRange::from_to(node.range().start(), final_node.range().end()));
     }
 
     None
@@ -182,10 +181,7 @@ fn extend_comments(comment: ast::Comment) -> Option<TextRange> {
     let prev = adj_comments(&comment, Direction::Prev);
     let next = adj_comments(&comment, Direction::Next);
     if prev != next {
-        Some(TextRange::from_to(
-            prev.syntax().text_range().start(),
-            next.syntax().text_range().end(),
-        ))
+        Some(TextRange::from_to(prev.syntax().range().start(), next.syntax().range().end()))
     } else {
         None
     }
diff --git a/crates/ra_ide_api/src/folding_ranges.rs b/crates/ra_ide_api/src/folding_ranges.rs
index 9699000dbb..c2b981aedc 100644
--- a/crates/ra_ide_api/src/folding_ranges.rs
+++ b/crates/ra_ide_api/src/folding_ranges.rs
@@ -35,7 +35,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
                 SyntaxElement::Token(token) => token.text().contains('\n'),
             };
             if is_multiline {
-                res.push(Fold { range: element.text_range(), kind });
+                res.push(Fold { range: element.range(), kind });
                 continue;
             }
         }
@@ -132,7 +132,7 @@ fn contiguous_range_for_group_unless(
     }
 
     if first != &last {
-        Some(TextRange::from_to(first.text_range().start(), last.text_range().end()))
+        Some(TextRange::from_to(first.range().start(), last.range().end()))
     } else {
         // The group consists of only one element, therefore it cannot be folded
         None
@@ -178,10 +178,7 @@ fn contiguous_range_for_comment(
     }
 
     if first != last {
-        Some(TextRange::from_to(
-            first.syntax().text_range().start(),
-            last.syntax().text_range().end(),
-        ))
+        Some(TextRange::from_to(first.syntax().range().start(), last.syntax().range().end()))
     } else {
         // The group consists of only one element, therefore it cannot be folded
         None
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index ddd55a9c15..82b5e3b5e1 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -23,11 +23,11 @@ pub(crate) fn goto_definition(
     let syntax = parse.tree().syntax().clone();
     if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&syntax, position.offset) {
         let navs = reference_definition(db, position.file_id, &name_ref).to_vec();
-        return Some(RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec()));
+        return Some(RangeInfo::new(name_ref.syntax().range(), navs.to_vec()));
     }
     if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) {
         let navs = name_definition(db, position.file_id, &name)?;
-        return Some(RangeInfo::new(name.syntax().text_range(), navs));
+        return Some(RangeInfo::new(name.syntax().range(), navs));
     }
     None
 }
diff --git a/crates/ra_ide_api/src/goto_type_definition.rs b/crates/ra_ide_api/src/goto_type_definition.rs
index 007259d9ea..fc4b6e1af5 100644
--- a/crates/ra_ide_api/src/goto_type_definition.rs
+++ b/crates/ra_ide_api/src/goto_type_definition.rs
@@ -32,7 +32,7 @@ pub(crate) fn goto_type_definition(
     let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
 
     let nav = NavigationTarget::from_adt_def(db, adt_def);
-    Some(RangeInfo::new(node.text_range(), vec![nav]))
+    Some(RangeInfo::new(node.range(), vec![nav]))
 }
 
 #[cfg(test)]
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index 2a5ac78215..e503bf6a96 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -172,7 +172,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
         }
 
         if !res.is_empty() {
-            range = Some(name_ref.syntax().text_range())
+            range = Some(name_ref.syntax().range())
         }
     } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
         if let Some(parent) = name.syntax().parent() {
@@ -210,7 +210,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
         }
 
         if !res.is_empty() && range.is_none() {
-            range = Some(name.syntax().text_range());
+            range = Some(name.syntax().range());
         }
     }
 
@@ -218,9 +218,9 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
         let node = ancestors_at_offset(file.syntax(), position.offset).find(|n| {
             ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some()
         })?;
-        let frange = FileRange { file_id: position.file_id, range: node.text_range() };
+        let frange = FileRange { file_id: position.file_id, range: node.range() };
         res.extend(type_of(db, frange).map(rust_code_markup));
-        range = Some(node.text_range());
+        range = Some(node.range());
     }
 
     let range = range?;
@@ -246,7 +246,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
     // if we picked identifier, expand to pattern/expression
     let node = leaf_node
         .ancestors()
-        .take_while(|it| it.text_range() == leaf_node.text_range())
+        .take_while(|it| it.range() == leaf_node.range())
         .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?;
     let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, &node, None);
     let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs
index f5b6ffd616..15999d4331 100644
--- a/crates/ra_ide_api/src/impls.rs
+++ b/crates/ra_ide_api/src/impls.rs
@@ -15,12 +15,12 @@ pub(crate) fn goto_implementation(
 
     if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) {
         return Some(RangeInfo::new(
-            nominal_def.syntax().text_range(),
+            nominal_def.syntax().range(),
             impls_for_def(db, &nominal_def, module)?,
         ));
     } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) {
         return Some(RangeInfo::new(
-            trait_def.syntax().text_range(),
+            trait_def.syntax().range(),
             impls_for_trait(db, &trait_def, module)?,
         ));
     }
diff --git a/crates/ra_ide_api/src/join_lines.rs b/crates/ra_ide_api/src/join_lines.rs
index fa998ebe1e..9b81ad9e4b 100644
--- a/crates/ra_ide_api/src/join_lines.rs
+++ b/crates/ra_ide_api/src/join_lines.rs
@@ -28,14 +28,14 @@ pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
     };
     let mut edit = TextEditBuilder::default();
     for token in node.descendants_with_tokens().filter_map(|it| it.into_token()) {
-        let range = match range.intersection(&token.text_range()) {
+        let range = match range.intersection(&token.range()) {
             Some(range) => range,
             None => continue,
-        } - token.text_range().start();
+        } - token.range().start();
         let text = token.text();
         for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
             let pos: TextUnit = (pos as u32).into();
-            let off = token.text_range().start() + range.start() + pos;
+            let off = token.range().start() + range.start() + pos;
             if !edit.invalidates_offset(off) {
                 remove_newline(&mut edit, &token, off);
             }
@@ -49,7 +49,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextU
     if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
         // The node is either the first or the last in the file
         let suff = &token.text()[TextRange::from_to(
-            offset - token.text_range().start() + TextUnit::of_char('\n'),
+            offset - token.range().start() + TextUnit::of_char('\n'),
             TextUnit::of_str(token.text()),
         )];
         let spaces = suff.bytes().take_while(|&b| b == b' ').count();
@@ -86,7 +86,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextU
     let next = token.next_sibling_or_token().unwrap();
     if is_trailing_comma(prev.kind(), next.kind()) {
         // Removes: trailing comma, newline (incl. surrounding whitespace)
-        edit.delete(TextRange::from_to(prev.text_range().start(), token.text_range().end()));
+        edit.delete(TextRange::from_to(prev.range().start(), token.range().end()));
     } else if prev.kind() == T![,] && next.kind() == T!['}'] {
         // Removes: comma, newline (incl. surrounding whitespace)
         let space = if let Some(left) = prev.prev_sibling_or_token() {
@@ -95,7 +95,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextU
             " "
         };
         edit.replace(
-            TextRange::from_to(prev.text_range().start(), token.text_range().end()),
+            TextRange::from_to(prev.range().start(), token.range().end()),
             space.to_string(),
         );
     } else if let (Some(_), Some(next)) = (
@@ -104,12 +104,12 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextU
     ) {
         // Removes: newline (incl. surrounding whitespace), start of the next comment
         edit.delete(TextRange::from_to(
-            token.text_range().start(),
-            next.syntax().text_range().start() + TextUnit::of_str(next.prefix()),
+            token.range().start(),
+            next.syntax().range().start() + TextUnit::of_str(next.prefix()),
         ));
     } else {
         // Remove newline but add a computed amount of whitespace characters
-        edit.replace(token.text_range(), compute_ws(prev.kind(), next.kind()).to_string());
+        edit.replace(token.range(), compute_ws(prev.kind(), next.kind()).to_string());
     }
 }
 
@@ -125,7 +125,7 @@ fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Op
     let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
     let expr = extract_trivial_expression(&block)?;
 
-    let block_range = block_expr.syntax().text_range();
+    let block_range = block_expr.syntax().range();
     let mut buf = expr.syntax().text().to_string();
 
     // Match block needs to have a comma after the block
@@ -143,7 +143,7 @@ fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Op
 fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> {
     let use_tree_list = ast::UseTreeList::cast(token.parent())?;
     let (tree,) = use_tree_list.use_trees().collect_tuple()?;
-    edit.replace(use_tree_list.syntax().text_range(), tree.syntax().text().to_string());
+    edit.replace(use_tree_list.syntax().range(), tree.syntax().text().to_string());
     Some(())
 }
 
diff --git a/crates/ra_ide_api/src/matching_brace.rs b/crates/ra_ide_api/src/matching_brace.rs
index 1e2fac8485..102327fd73 100644
--- a/crates/ra_ide_api/src/matching_brace.rs
+++ b/crates/ra_ide_api/src/matching_brace.rs
@@ -12,7 +12,7 @@ pub fn matching_brace(file: &SourceFile, offset: TextUnit) -> Option<TextUnit> {
     let parent = brace_node.parent();
     let matching_kind = BRACES[brace_idx ^ 1];
     let matching_node = parent.children_with_tokens().find(|node| node.kind() == matching_kind)?;
-    Some(matching_node.text_range().start())
+    Some(matching_node.range().start())
 }
 
 #[cfg(test)]
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index 5c74d3e363..1c4cd49dcc 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -166,7 +166,7 @@ fn rename_mod(
         file_id: position.file_id,
         edit: {
             let mut builder = ra_text_edit::TextEditBuilder::default();
-            builder.replace(ast_name.syntax().text_range(), new_name.into());
+            builder.replace(ast_name.syntax().range(), new_name.into());
             builder.finish()
         },
     };
diff --git a/crates/ra_ide_api/src/runnables.rs b/crates/ra_ide_api/src/runnables.rs
index 07412a9efd..2009584341 100644
--- a/crates/ra_ide_api/src/runnables.rs
+++ b/crates/ra_ide_api/src/runnables.rs
@@ -47,7 +47,7 @@ fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
     } else {
         return None;
     };
-    Some(Runnable { range: fn_def.syntax().text_range(), kind })
+    Some(Runnable { range: fn_def.syntax().range(), kind })
 }
 
 fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> {
@@ -62,7 +62,7 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti
     if !has_test_function {
         return None;
     }
-    let range = module.syntax().text_range();
+    let range = module.syntax().range();
     let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?;
 
     let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs
index d4afddab4a..e784b5f69a 100644
--- a/crates/ra_ide_api/src/symbol_index.rs
+++ b/crates/ra_ide_api/src/symbol_index.rs
@@ -271,7 +271,7 @@ fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec
 fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
     fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
         let name = node.name()?;
-        let name_range = name.syntax().text_range();
+        let name_range = name.syntax().range();
         let name = name.text().clone();
         let ptr = SyntaxNodePtr::new(node.syntax());
 
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 878a94f061..477827fa71 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -189,11 +189,11 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
                         if let Some(segment) = path.segment() {
                             if let Some(name_ref) = segment.name_ref() {
                                 highlighted.insert(name_ref.syntax().clone().into());
-                                let range_start = name_ref.syntax().text_range().start();
-                                let mut range_end = name_ref.syntax().text_range().end();
+                                let range_start = name_ref.syntax().range().start();
+                                let mut range_end = name_ref.syntax().range().end();
                                 for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
                                     match sibling.kind() {
-                                        T![!] | IDENT => range_end = sibling.text_range().end(),
+                                        T![!] | IDENT => range_end = sibling.range().end(),
                                         _ => (),
                                     }
                                 }
@@ -209,7 +209,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
                 continue;
             }
         };
-        res.push(HighlightedRange { range: node.text_range(), tag, binding_hash })
+        res.push(HighlightedRange { range: node.range(), tag, binding_hash })
     }
     res
 }
@@ -239,9 +239,9 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
     buf.push_str("<pre><code>");
     let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
     for token in tokens {
-        could_intersect.retain(|it| token.text_range().start() <= it.range.end());
+        could_intersect.retain(|it| token.range().start() <= it.range.end());
         while let Some(r) = ranges.get(frontier) {
-            if r.range.start() <= token.text_range().end() {
+            if r.range.start() <= token.range().end() {
                 could_intersect.push(r);
                 frontier += 1;
             } else {
@@ -251,7 +251,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
         let text = html_escape(&token.text());
         let ranges = could_intersect
             .iter()
-            .filter(|it| token.text_range().is_subrange(&it.range))
+            .filter(|it| token.range().is_subrange(&it.range))
             .collect::<Vec<_>>();
         if ranges.is_empty() {
             buf.push_str(&text);
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs
index 76c50f6d6c..3d7373d02b 100644
--- a/crates/ra_ide_api/src/syntax_tree.rs
+++ b/crates/ra_ide_api/src/syntax_tree.rs
@@ -25,9 +25,9 @@ pub(crate) fn syntax_tree(
             }
         };
 
-        format!("{:#?}", node)
+        node.debug_dump()
     } else {
-        format!("{:#?}", parse.tree().syntax())
+        parse.tree().syntax().debug_dump()
     }
 }
 
@@ -45,7 +45,7 @@ fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<
 
 fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> {
     // Range of the full node
-    let node_range = node.text_range();
+    let node_range = node.range();
     let text = node.text().to_string();
 
     // We start at some point inside the node
@@ -85,7 +85,7 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<St
     // If the "file" parsed without errors,
     // return its syntax
     if parsed.errors().is_empty() {
-        return Some(format!("{:#?}", parsed.tree().syntax()));
+        return Some(parsed.tree().syntax().debug_dump());
     }
 
     None
diff --git a/crates/ra_ide_api/src/typing.rs b/crates/ra_ide_api/src/typing.rs
index 1747a529ec..ad0ababcc0 100644
--- a/crates/ra_ide_api/src/typing.rs
+++ b/crates/ra_ide_api/src/typing.rs
@@ -24,7 +24,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
 
     let prefix = comment.prefix();
     if position.offset
-        < comment.syntax().text_range().start() + TextUnit::of_str(prefix) + TextUnit::from(1)
+        < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1)
     {
         return None;
     }
@@ -45,7 +45,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
 }
 
 fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
-    let ws = match find_token_at_offset(file.syntax(), token.text_range().start()) {
+    let ws = match find_token_at_offset(file.syntax(), token.range().start()) {
         TokenAtOffset::Between(l, r) => {
             assert!(r == *token);
             l
@@ -71,7 +71,7 @@ pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<TextEdit> {
         return None;
     }
     if let Some(expr) = let_stmt.initializer() {
-        let expr_range = expr.syntax().text_range();
+        let expr_range = expr.syntax().range();
         if expr_range.contains(eq_offset) && eq_offset != expr_range.start() {
             return None;
         }
@@ -81,7 +81,7 @@ pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<TextEdit> {
     } else {
         return None;
     }
-    let offset = let_stmt.syntax().text_range().end();
+    let offset = let_stmt.syntax().range().end();
     let mut edit = TextEditBuilder::default();
     edit.insert(offset, ";".to_string());
     Some(edit.finish())
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 7ff0fc4720..64ed6a517d 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -19,7 +19,7 @@ pub struct TokenMap {
 pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> {
     let mut token_map = TokenMap::default();
     let node = ast.syntax();
-    let tt = convert_tt(&mut token_map, node.text_range().start(), node)?;
+    let tt = convert_tt(&mut token_map, node.range().start(), node)?;
     Some((tt, token_map))
 }
 
@@ -27,7 +27,7 @@ pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)
 /// will consume).
 pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, TokenMap)> {
     let mut token_map = TokenMap::default();
-    let tt = convert_tt(&mut token_map, node.text_range().start(), node)?;
+    let tt = convert_tt(&mut token_map, node.range().start(), node)?;
     Some((tt, token_map))
 }
 
@@ -229,7 +229,7 @@ fn convert_tt(
                             || token.kind() == IDENT
                             || token.kind() == LIFETIME
                         {
-                            let relative_range = token.text_range() - global_offset;
+                            let relative_range = token.range() - global_offset;
                             let id = token_map.alloc(relative_range);
                             let text = token.text().clone();
                             tt::Leaf::from(tt::Ident { text, id }).into()
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index f47e11e66d..e2de5e0e39 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -25,7 +25,7 @@ pub fn ancestors_at_offset(
 ) -> impl Iterator<Item = SyntaxNode> {
     find_token_at_offset(node, offset)
         .map(|token| token.parent().ancestors())
-        .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
+        .kmerge_by(|node1, node2| node1.range().len() < node2.range().len())
 }
 
 /// Finds a node of specific Ast type at offset. Note that this is slightly
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 99fcdbd9ac..da8cf4ae8e 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -175,6 +175,32 @@ impl Attr {
     }
 }
 
+// AwaitExpr
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AwaitExpr {
+    pub(crate) syntax: SyntaxNode,
+}
+
+impl AstNode for AwaitExpr {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        match kind {
+            AWAIT_EXPR => true,
+            _ => false,
+        }
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) { Some(AwaitExpr { syntax }) } else { None }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+
+
+impl AwaitExpr {
+    pub fn expr(&self) -> Option<Expr> {
+        super::child_opt(self)
+    }
+}
+
 // BinExpr
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct BinExpr {
@@ -566,7 +592,7 @@ pub struct Expr {
 impl AstNode for Expr {
     fn can_cast(kind: SyntaxKind) -> bool {
         match kind {
-             | TUPLE_EXPR | ARRAY_EXPR | PAREN_EXPR | PATH_EXPR | LAMBDA_EXPR | IF_EXPR | LOOP_EXPR | FOR_EXPR | WHILE_EXPR | CONTINUE_EXPR | BREAK_EXPR | LABEL | BLOCK_EXPR | RETURN_EXPR | MATCH_EXPR | STRUCT_LIT | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR | TRY_EXPR | TRY_BLOCK_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR | LITERAL | MACRO_CALL => true,
+             | TUPLE_EXPR | ARRAY_EXPR | PAREN_EXPR | PATH_EXPR | LAMBDA_EXPR | IF_EXPR | LOOP_EXPR | FOR_EXPR | WHILE_EXPR | CONTINUE_EXPR | BREAK_EXPR | LABEL | BLOCK_EXPR | RETURN_EXPR | MATCH_EXPR | STRUCT_LIT | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR | AWAIT_EXPR | TRY_EXPR | TRY_BLOCK_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR | LITERAL | MACRO_CALL => true,
             _ => false,
         }
     }
@@ -599,6 +625,7 @@ pub enum ExprKind {
     IndexExpr(IndexExpr),
     MethodCallExpr(MethodCallExpr),
     FieldExpr(FieldExpr),
+    AwaitExpr(AwaitExpr),
     TryExpr(TryExpr),
     TryBlockExpr(TryBlockExpr),
     CastExpr(CastExpr),
@@ -669,6 +696,9 @@ impl From<MethodCallExpr> for Expr {
 impl From<FieldExpr> for Expr {
     fn from(n: FieldExpr) -> Expr { Expr { syntax: n.syntax } }
 }
+impl From<AwaitExpr> for Expr {
+    fn from(n: AwaitExpr) -> Expr { Expr { syntax: n.syntax } }
+}
 impl From<TryExpr> for Expr {
     fn from(n: TryExpr) -> Expr { Expr { syntax: n.syntax } }
 }
@@ -719,6 +749,7 @@ impl Expr {
             INDEX_EXPR => ExprKind::IndexExpr(IndexExpr::cast(self.syntax.clone()).unwrap()),
             METHOD_CALL_EXPR => ExprKind::MethodCallExpr(MethodCallExpr::cast(self.syntax.clone()).unwrap()),
             FIELD_EXPR => ExprKind::FieldExpr(FieldExpr::cast(self.syntax.clone()).unwrap()),
+            AWAIT_EXPR => ExprKind::AwaitExpr(AwaitExpr::cast(self.syntax.clone()).unwrap()),
             TRY_EXPR => ExprKind::TryExpr(TryExpr::cast(self.syntax.clone()).unwrap()),
             TRY_BLOCK_EXPR => ExprKind::TryBlockExpr(TryBlockExpr::cast(self.syntax.clone()).unwrap()),
             CAST_EXPR => ExprKind::CastExpr(CastExpr::cast(self.syntax.clone()).unwrap()),
diff --git a/crates/ra_syntax/src/fuzz.rs b/crates/ra_syntax/src/fuzz.rs
index 698a624ecc..716925b2f0 100644
--- a/crates/ra_syntax/src/fuzz.rs
+++ b/crates/ra_syntax/src/fuzz.rs
@@ -51,10 +51,10 @@ impl CheckReparse {
         for (a, b) in
             new_parse.tree().syntax().descendants().zip(full_reparse.tree().syntax().descendants())
         {
-            if (a.kind(), a.text_range()) != (b.kind(), b.text_range()) {
-                eprint!("original:\n{:#?}", parse.tree().syntax());
-                eprint!("reparsed:\n{:#?}", new_parse.tree().syntax());
-                eprint!("full reparse:\n{:#?}", full_reparse.tree().syntax());
+            if (a.kind(), a.range()) != (b.kind(), b.range()) {
+                eprint!("original:\n{}", parse.tree().syntax().debug_dump());
+                eprint!("reparsed:\n{}", new_parse.tree().syntax().debug_dump());
+                eprint!("full reparse:\n{}", full_reparse.tree().syntax().debug_dump());
                 assert_eq!(
                     format!("{:?}", a),
                     format!("{:?}", b),
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 55494239e4..817dedfbfa 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -477,6 +477,7 @@ Grammar(
         ),
         "IndexExpr": (),
         "FieldExpr": (options: ["Expr", "NameRef"]),
+        "AwaitExpr": (options: ["Expr"]),
         "TryExpr": (options: ["Expr"]),
         "CastExpr": (options: ["Expr", "TypeRef"]),
         "RefExpr": (options: ["Expr"]),
@@ -508,6 +509,7 @@ Grammar(
                 "IndexExpr",
                 "MethodCallExpr",
                 "FieldExpr",
+                "AwaitExpr",
                 "TryExpr",
                 "TryBlockExpr",
                 "CastExpr",
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 8af04c1362..604abe5c6c 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -114,7 +114,7 @@ impl Parse<SyntaxNode> {
 
 impl Parse<SourceFile> {
     pub fn debug_dump(&self) -> String {
-        let mut buf = format!("{:#?}", self.tree().syntax());
+        let mut buf = self.tree().syntax().debug_dump();
         for err in self.errors.iter() {
             writeln!(buf, "error {:?}: {}", err.location(), err.kind()).unwrap();
         }
@@ -234,7 +234,7 @@ fn api_walkthrough() {
     assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);
 
     // And text range:
-    assert_eq!(expr_syntax.text_range(), TextRange::from_to(32.into(), 37.into()));
+    assert_eq!(expr_syntax.range(), TextRange::from_to(32.into(), 37.into()));
 
     // You can get node's text as a `SyntaxText` object, which will traverse the
     // tree collecting token's text:
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index 2f388bdfe6..b4ad9e0199 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -46,8 +46,7 @@ fn reparse_token<'node>(
         WHITESPACE | COMMENT | IDENT | STRING | RAW_STRING => {
             if token.kind() == WHITESPACE || token.kind() == COMMENT {
                 // removing a new line may extends previous token
-                if token.text().to_string()[edit.delete - token.text_range().start()].contains('\n')
-                {
+                if token.text().to_string()[edit.delete - token.range().start()].contains('\n') {
                     return None;
                 }
             }
@@ -63,7 +62,7 @@ fn reparse_token<'node>(
                 return None;
             }
 
-            if let Some(next_char) = root.text().char_at(token.text_range().end()) {
+            if let Some(next_char) = root.text().char_at(token.range().end()) {
                 let tokens_with_next_char = tokenize(&format!("{}{}", text, next_char));
                 if tokens_with_next_char.len() == 1 {
                     return None;
@@ -71,7 +70,7 @@ fn reparse_token<'node>(
             }
 
             let new_token = GreenToken::new(rowan::SyntaxKind(token.kind().into()), text.into());
-            Some((token.replace_with(new_token), token.text_range()))
+            Some((token.replace_with(new_token), token.range()))
         }
         _ => None,
     }
@@ -91,12 +90,11 @@ fn reparse_block<'node>(
     let mut tree_sink = TextTreeSink::new(&text, &tokens);
     reparser.parse(&mut token_source, &mut tree_sink);
     let (green, new_errors) = tree_sink.finish();
-    Some((node.replace_with(green), new_errors, node.text_range()))
+    Some((node.replace_with(green), new_errors, node.range()))
 }
 
 fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String {
-    let edit =
-        AtomTextEdit::replace(edit.delete - element.text_range().start(), edit.insert.clone());
+    let edit = AtomTextEdit::replace(edit.delete - element.range().start(), edit.insert.clone());
     let text = match element {
         SyntaxElement::Token(token) => token.text().to_string(),
         SyntaxElement::Node(node) => node.text().to_string(),
@@ -190,8 +188,8 @@ mod tests {
         };
 
         assert_eq_text!(
-            &format!("{:#?}", fully_reparsed.tree().syntax()),
-            &format!("{:#?}", incrementally_reparsed.tree().syntax()),
+            &fully_reparsed.tree().syntax().debug_dump(),
+            &incrementally_reparsed.tree().syntax().debug_dump(),
         );
     }
 
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs
index 8665c8976a..d1b30a2c92 100644
--- a/crates/ra_syntax/src/ptr.rs
+++ b/crates/ra_syntax/src/ptr.rs
@@ -12,15 +12,15 @@ pub struct SyntaxNodePtr {
 
 impl SyntaxNodePtr {
     pub fn new(node: &SyntaxNode) -> SyntaxNodePtr {
-        SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
+        SyntaxNodePtr { range: node.range(), kind: node.kind() }
     }
 
     pub fn to_node(self, root: &SyntaxNode) -> SyntaxNode {
         assert!(root.parent().is_none());
         successors(Some(root.clone()), |node| {
-            node.children().find(|it| self.range.is_subrange(&it.text_range()))
+            node.children().find(|it| self.range.is_subrange(&it.range()))
         })
-        .find(|it| it.text_range() == self.range && it.kind() == self.kind)
+        .find(|it| it.range() == self.range && it.kind() == self.kind)
         .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self))
     }
 
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index c42045d77a..51bae04de7 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -6,7 +6,11 @@
 //! The *real* implementation is in the (language-agnostic) `rowan` crate, this
 //! modules just wraps its API.
 
-use std::{fmt, iter::successors, ops::RangeInclusive};
+use std::{
+    fmt::{self, Write},
+    iter::successors,
+    ops::RangeInclusive,
+};
 
 use ra_parser::ParseError;
 use rowan::GreenNodeBuilder;
@@ -32,29 +36,8 @@ pub enum InsertPosition<T> {
 pub struct SyntaxNode(pub(crate) rowan::cursor::SyntaxNode);
 
 impl fmt::Debug for SyntaxNode {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        if f.alternate() {
-            let mut level = 0;
-            for event in self.preorder_with_tokens() {
-                match event {
-                    WalkEvent::Enter(element) => {
-                        for _ in 0..level {
-                            write!(f, "  ")?;
-                        }
-                        match element {
-                            SyntaxElement::Node(node) => writeln!(f, "{:?}", node)?,
-                            SyntaxElement::Token(token) => writeln!(f, "{:?}", token)?,
-                        }
-                        level += 1;
-                    }
-                    WalkEvent::Leave(_) => level -= 1,
-                }
-            }
-            assert_eq!(level, 0);
-            Ok(())
-        } else {
-            write!(f, "{:?}@{:?}", self.kind(), self.text_range())
-        }
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "{:?}@{:?}", self.kind(), self.range())
     }
 }
 
@@ -80,7 +63,7 @@ impl SyntaxNode {
         self.0.kind().0.into()
     }
 
-    pub fn text_range(&self) -> TextRange {
+    pub fn range(&self) -> TextRange {
         self.0.text_range()
     }
 
@@ -190,6 +173,31 @@ impl SyntaxNode {
         })
     }
 
+    pub fn debug_dump(&self) -> String {
+        let mut level = 0;
+        let mut buf = String::new();
+
+        for event in self.preorder_with_tokens() {
+            match event {
+                WalkEvent::Enter(element) => {
+                    for _ in 0..level {
+                        buf.push_str("  ");
+                    }
+                    match element {
+                        SyntaxElement::Node(node) => writeln!(buf, "{:?}", node).unwrap(),
+                        SyntaxElement::Token(token) => writeln!(buf, "{:?}", token).unwrap(),
+                    }
+                    level += 1;
+                }
+                WalkEvent::Leave(_) => level -= 1,
+            }
+        }
+
+        assert_eq!(level, 0);
+
+        buf
+    }
+
     pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
         self.0.replace_with(replacement)
     }
@@ -291,7 +299,7 @@ pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken);
 
 impl fmt::Debug for SyntaxToken {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write!(fmt, "{:?}@{:?}", self.kind(), self.text_range())?;
+        write!(fmt, "{:?}@{:?}", self.kind(), self.range())?;
         if self.text().len() < 25 {
             return write!(fmt, " {:?}", self.text());
         }
@@ -321,7 +329,7 @@ impl SyntaxToken {
         self.0.text()
     }
 
-    pub fn text_range(&self) -> TextRange {
+    pub fn range(&self) -> TextRange {
         self.0.text_range()
     }
 
@@ -453,10 +461,10 @@ impl SyntaxElement {
         .ancestors()
     }
 
-    pub fn text_range(&self) -> TextRange {
+    pub fn range(&self) -> TextRange {
         match self {
-            SyntaxElement::Node(it) => it.text_range(),
-            SyntaxElement::Token(it) => it.text_range(),
+            SyntaxElement::Node(it) => it.range(),
+            SyntaxElement::Token(it) => it.range(),
         }
     }
 
diff --git a/crates/ra_syntax/src/syntax_text.rs b/crates/ra_syntax/src/syntax_text.rs
index f8ddff48e9..2ad98809b4 100644
--- a/crates/ra_syntax/src/syntax_text.rs
+++ b/crates/ra_syntax/src/syntax_text.rs
@@ -13,7 +13,7 @@ pub struct SyntaxText {
 
 impl SyntaxText {
     pub(crate) fn new(node: SyntaxNode) -> SyntaxText {
-        let range = node.text_range();
+        let range = node.range();
         SyntaxText { node, range }
     }
 
@@ -24,14 +24,14 @@ impl SyntaxText {
         self.node.descendants_with_tokens().try_fold(init, move |acc, element| {
             let res = match element {
                 SyntaxElement::Token(token) => {
-                    let range = match self.range.intersection(&token.text_range()) {
+                    let range = match self.range.intersection(&token.range()) {
                         None => return Ok(acc),
                         Some(it) => it,
                     };
-                    let slice = if range == token.text_range() {
+                    let slice = if range == token.range() {
                         token.text()
                     } else {
-                        let range = range - token.text_range().start();
+                        let range = range - token.range().start();
                         &token.text()[range]
                     };
                     f(acc, slice)?
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 19bdafef22..7140d10c33 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -33,7 +33,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
             if let Some(end) = text.rfind('\'') {
                 if let Some(without_quotes) = text.get(2..end) {
                     if let Err((off, err)) = unescape::unescape_byte(without_quotes) {
-                        let off = token.text_range().start() + TextUnit::from_usize(off + 2);
+                        let off = token.range().start() + TextUnit::from_usize(off + 2);
                         acc.push(SyntaxError::new(err.into(), off))
                     }
                 }
@@ -43,7 +43,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
             if let Some(end) = text.rfind('\'') {
                 if let Some(without_quotes) = text.get(1..end) {
                     if let Err((off, err)) = unescape::unescape_char(without_quotes) {
-                        let off = token.text_range().start() + TextUnit::from_usize(off + 1);
+                        let off = token.range().start() + TextUnit::from_usize(off + 1);
                         acc.push(SyntaxError::new(err.into(), off))
                     }
                 }
@@ -55,7 +55,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
                     unescape::unescape_byte_str(without_quotes, &mut |range, char| {
                         if let Err(err) = char {
                             let off = range.start;
-                            let off = token.text_range().start() + TextUnit::from_usize(off + 2);
+                            let off = token.range().start() + TextUnit::from_usize(off + 2);
                             acc.push(SyntaxError::new(err.into(), off))
                         }
                     })
@@ -68,7 +68,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
                     unescape::unescape_str(without_quotes, &mut |range, char| {
                         if let Err(err) = char {
                             let off = range.start;
-                            let off = token.text_range().start() + TextUnit::from_usize(off + 1);
+                            let off = token.range().start() + TextUnit::from_usize(off + 1);
                             acc.push(SyntaxError::new(err.into(), off))
                         }
                     })
@@ -89,9 +89,9 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) {
                     assert_eq!(
                         node.parent(),
                         pair.parent(),
-                        "\nunpaired curleys:\n{}\n{:#?}\n",
+                        "\nunpaired curleys:\n{}\n{}\n",
                         root.text(),
-                        root,
+                        root.debug_dump(),
                     );
                     assert!(
                         node.next_sibling().is_none() && pair.prev_sibling().is_none(),
diff --git a/crates/ra_syntax/src/validation/block.rs b/crates/ra_syntax/src/validation/block.rs
index c5588658d8..f5573bd8f0 100644
--- a/crates/ra_syntax/src/validation/block.rs
+++ b/crates/ra_syntax/src/validation/block.rs
@@ -16,7 +16,6 @@ pub(crate) fn validate_block_node(node: ast::Block, errors: &mut Vec<SyntaxError
             _ => {}
         }
     }
-    errors.extend(
-        node.attrs().map(|attr| SyntaxError::new(InvalidBlockAttr, attr.syntax().text_range())),
-    )
+    errors
+        .extend(node.attrs().map(|attr| SyntaxError::new(InvalidBlockAttr, attr.syntax().range())))
 }
diff --git a/crates/ra_syntax/src/validation/field_expr.rs b/crates/ra_syntax/src/validation/field_expr.rs
index 004f199fd5..0e18bd9ca0 100644
--- a/crates/ra_syntax/src/validation/field_expr.rs
+++ b/crates/ra_syntax/src/validation/field_expr.rs
@@ -7,7 +7,7 @@ use crate::{
 pub(crate) fn validate_field_expr_node(node: ast::FieldExpr, errors: &mut Vec<SyntaxError>) {
     if let Some(FieldKind::Index(idx)) = node.field_access() {
         if idx.text().chars().any(|c| c < '0' || c > '9') {
-            errors.push(SyntaxError::new(InvalidTupleIndexFormat, idx.text_range()));
+            errors.push(SyntaxError::new(InvalidTupleIndexFormat, idx.range()));
         }
     }
 }


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 7b2bdeb3f6..d4bfcb888f 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -20,6 +20,41 @@ use crate::{
 // against snapshots of the expected results using insta. Use cargo-insta to
 // update the snapshots.
 
+#[test]
+fn infer_await() {
+    let (mut db, pos) = MockDatabase::with_position(
+        r#"
+//- /main.rs
+
+struct IntFuture;
+
+impl Future for IntFuture {
+    type Output = u64;
+}
+
+fn test() {
+    let r = IntFuture;
+    let v = r.await;
+    v<|>;
+}
+
+//- /std.rs
+#[prelude_import] use future::*;
+mod future {
+    trait Future {
+        type Output;
+    }
+}
+
+"#,
+    );
+    db.set_crate_graph_from_fixture(crate_graph! {
+        "main": ("/main.rs", ["std"]),
+        "std": ("/std.rs", []),
+    });
+    assert_eq!("u64", type_at_pos(&db, pos));
+}
+
 #[test]
 fn infer_try() {
     let (mut db, pos) = MockDatabase::with_position(
@@ -3127,7 +3162,7 @@ fn infer(content: &str) -> String {
         for (syntax_ptr, ty) in &types {
             let node = syntax_ptr.to_node(source_file.syntax());
             let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) {
-                (self_param.self_kw_token().text_range(), "self".to_string())
+                (self_param.self_kw_token().range(), "self".to_string())
             } else {
                 (syntax_ptr.range(), node.text().to_string().replace("\n", " "))
             };
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index 9151b6ecdf..38a31109d9 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -412,7 +412,7 @@ fn test_expand_to_item_list() {
     let expansion = expand(&rules, "structs!(Foo, Bar);");
     let tree = token_tree_to_macro_items(&expansion).unwrap().tree();
     assert_eq!(
-        format!("{:#?}", tree.syntax()).trim(),
+        tree.syntax().debug_dump().trim(),
         r#"
 MACRO_ITEMS@[0; 40)
   STRUCT_DEF@[0; 20)
@@ -531,7 +531,7 @@ fn test_tt_to_stmts() {
     let stmts = token_tree_to_macro_stmts(&expanded).unwrap().tree();
 
     assert_eq!(
-        format!("{:#?}", stmts.syntax()).trim(),
+        stmts.syntax().debug_dump().trim(),
         r#"MACRO_STMTS@[0; 15)
   LET_STMT@[0; 7)
     LET_KW@[0; 3) "let"
@@ -669,7 +669,7 @@ fn test_expr_order() {
     );
 
     assert_eq!(
-        format!("{:#?}", expand_to_items(&rules, "foo! { 1 + 1  }").syntax()).trim(),
+        expand_to_items(&rules, "foo! { 1 + 1  }").syntax().debug_dump().trim(),
         r#"MACRO_ITEMS@[0; 15)
   FN_DEF@[0; 15)
     FN_KW@[0; 2) "fn"
@@ -1013,7 +1013,7 @@ fn test_vec() {
     );
 
     assert_eq!(
-        format!("{:#?}", expand_to_expr(&rules, r#"vec![1u32,2];"#).syntax()).trim(),
+        expand_to_expr(&rules, r#"vec![1u32,2];"#).syntax().debug_dump().trim(),
         r#"BLOCK_EXPR@[0; 45)
   BLOCK@[0; 45)
     L_CURLY@[0; 1) "{"