Repository: racer-rust/racer (language: rust)

Issue:
Type inference for binary operator
e.g.
```rust
let a = Point(5, 6) - Point(6, 7);
a.~ 
```

Though it doesn't look easy :cold_sweat: 


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f563889..6e45ed92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ project adheres to [Semantic Versioning](http://semver.org/).
 
 # HEAD
 - Completion based on impl<T: Bound> #948
-- Fix for arugument completion #943
+- Fix for argument completion #943
 - Trait bound in where clause #937
 
 # 2.1.5
diff --git a/Cargo.lock b/Cargo.lock
index 7c178e8d..892bfe55 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -102,19 +102,15 @@ dependencies = [
 
 [[package]]
 name = "derive_more"
-version = "0.11.0"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "dtoa"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "either"
 version = "1.5.0"
@@ -164,7 +160,7 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -267,15 +263,7 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "0.4.6"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -286,29 +274,21 @@ name = "quick-error"
 version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "quote"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "quote"
 version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "racer"
-version = "2.1.9"
+version = "2.1.13"
 dependencies = [
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "derive_more 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -317,7 +297,7 @@ dependencies = [
  "racer-cargo-metadata 0.1.1",
  "racer-testutils 0.1.0",
  "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-syntax 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-syntax 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -325,22 +305,22 @@ name = "racer-cargo-metadata"
 version = "0.1.1"
 dependencies = [
  "racer-interner 0.1.0",
- "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "racer-interner"
 version = "0.1.0"
 dependencies = [
- "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "racer-testutils"
 version = "0.1.0"
 dependencies = [
- "racer 2.1.9",
+ "racer 2.1.13",
  "tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -419,15 +399,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "rustc-ap-arena"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "rustc-ap-rustc_data_structures 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_data_structures 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "rustc-ap-graphviz"
+version = "297.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "rustc-ap-rustc_cratesio_shim"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -437,7 +422,7 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_data_structures"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -445,8 +430,9 @@ dependencies = [
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_cratesio_shim 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-serialize 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-graphviz 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_cratesio_shim 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-serialize 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -456,33 +442,34 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_errors"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_cratesio_shim 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_data_structures 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-serialize 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-syntax_pos 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_cratesio_shim 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_data_structures 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-serialize 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-syntax_pos 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rustc-ap-rustc_target"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_cratesio_shim 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-serialize 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_cratesio_shim 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_data_structures 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-serialize 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rustc-ap-serialize"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -490,29 +477,29 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-syntax"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_data_structures 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_errors 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_target 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-serialize 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-syntax_pos 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_data_structures 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_errors 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_target 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-serialize 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-syntax_pos 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rustc-ap-syntax_pos"
-version = "274.0.0"
+version = "297.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-arena 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-rustc_data_structures 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-ap-serialize 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-arena 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-rustc_data_structures 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-ap-serialize 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -554,6 +541,11 @@ dependencies = [
  "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "ryu"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "scoped-tls"
 version = "0.1.2"
@@ -579,30 +571,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "serde"
-version = "1.0.70"
+version = "1.0.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.70"
+version = "1.0.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.22"
+version = "1.0.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -625,20 +617,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "syn"
-version = "0.13.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "syn"
-version = "0.14.4"
+version = "0.15.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -793,15 +775,14 @@ dependencies = [
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
-"checksum derive_more 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46c7f14685a20f5dd08e7f754f2ea8cc064d8f4214ae21116c106a2768ba7b9b"
-"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd"
+"checksum derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f57d78cf3bd45270dad4e70c21ec77a960b36c7a841ff9db76aaa775a8fb871"
 "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
 "checksum ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dc8393b3c7352f94092497f6b52019643e493b6b890eb417cdb7c46117e621"
 "checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
-"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606"
+"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
 "checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739"
 "checksum lazycell 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d33a48d0365c96081958cc663eef834975cb1e8d8bea3378513fc72bdbf11e50"
 "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
@@ -815,10 +796,8 @@ dependencies = [
 "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5"
 "checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
 "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c"
-"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7"
-"checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6"
+"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
 "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
-"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8"
 "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
 "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
 "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
@@ -829,30 +808,31 @@ dependencies = [
 "checksum regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05b06a75f5217880fc5e905952a42750bf44787e56a6c6d6852ed0992f5e1d54"
 "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
 "checksum rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d7c7046dc6a92f2ae02ed302746db4382e75131b9ce20ce967259f6b5867a6a"
-"checksum rustc-ap-arena 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "866fda692855b38f9d6562f0e952c75c6ebe4032d7dd63c608a88e7c4d3f8087"
-"checksum rustc-ap-rustc_cratesio_shim 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6c2343e11a97b4eb3bee29cd5f9314ea409a87baee5d3fec8d1516d1633412e"
-"checksum rustc-ap-rustc_data_structures 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b88f905f7ab99bf14220a3a87eff60ec143af8136fd0ca8573641c78be532ec8"
-"checksum rustc-ap-rustc_errors 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c86fda6cf42e0355b7ecf40f14888340c20b7b864c9d37f6ffca85fe87200652"
-"checksum rustc-ap-rustc_target 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8fa8622299beac8c40270e8536a7b0509ca80f227a2b56550019a325fa5a60c0"
-"checksum rustc-ap-serialize 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d16cc3d014af9a524c0bed6ca806c3170e39d5987180f0f8ce8fb7df5113576c"
-"checksum rustc-ap-syntax 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a29f280f04f4f45e1bdd773ab5e667b36e757bfbbd01193c330815b9e76d05a"
-"checksum rustc-ap-syntax_pos 274.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2ea27b65311571c7614deb393691eb18c5e41fd4fd9d8490304e128e1358646"
+"checksum rustc-ap-arena 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b69fd4a0e8a3ecd99b497965d05f6f04dd2e4601a6146a841dbe4c8e77c2b30c"
+"checksum rustc-ap-graphviz 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8136418dbc491bab74aa0565eaa2086754a7a81a5e74a1d84d6168d18e889e7"
+"checksum rustc-ap-rustc_cratesio_shim 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a972feda82332d1d05b1ba5a097e915cd9c9c8f1af2bd7b08af09fb88c753d5f"
+"checksum rustc-ap-rustc_data_structures 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "582584c6c48b0ece4b8aef3f9bb59d94d17c5665612bc87a71f509e45a3113b5"
+"checksum rustc-ap-rustc_errors 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cd852096944d0ac6af1aefa9639a2ae6dede217606ce97f88ff0dcc8c86d6ff6"
+"checksum rustc-ap-rustc_target 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98301a272ecfeec29d2d4e97b07238707c2b89d86fc3a4a5f31a00728f14e288"
+"checksum rustc-ap-serialize 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f13510e617e2e322e3297038fd6a7346f2297124af9e10e33a627c5d544e9d"
+"checksum rustc-ap-syntax 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0792f5a9ccfc5ec13bb5b0472fa49e145481029c39f6bf5b1a36decc99c3328f"
+"checksum rustc-ap-syntax_pos 297.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0df9f97f41650d23b14f92f7267f8c61089655efb4533d82bf8991f99245198d"
 "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8"
 "checksum rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c6d5a683c6ba4ed37959097e88d71c9e8e26659a3cb5be8b389078e7ad45306"
 "checksum rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40f06724db71e18d68b3b946fdf890ca8c921d9edccc1404fdfdb537b0d12649"
 "checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a"
+"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7"
 "checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
 "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
 "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
 "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-"checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920"
-"checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124"
-"checksum serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "84b8035cabe9b35878adec8ac5fe03d5f6bc97ff6edd7ccb96b44c1276ba390e"
+"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef"
+"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c"
+"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce"
 "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d"
 "checksum stable_deref_trait 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbc596e092fe5f598b12ef46cc03754085ac2f4d8c739ad61c4ae266cc3b3fa"
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
-"checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b"
-"checksum syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2beff8ebc3658f07512a413866875adddd20f4fd47b2a4e6c9da65cd281baaea"
+"checksum syn 0.15.17 (registry+https://github.com/rust-lang/crates.io-index)" = "3391038ebc3e4ab24eb028cb0ef2f2dc4ba0cbf72ee895ed6a6fad730640b5bc"
 "checksum tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47776f63b85777d984a50ce49d6b9e58826b6a3766a449fc95bc66cd5663c15b"
 "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
 "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
diff --git a/Cargo.toml b/Cargo.toml
index c6e3aafa..0d259acf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
 
 [package]
 name = "racer"
-version = "2.1.9"
+version = "2.1.13"
 license = "MIT"
 description = "Code completion for Rust"
 authors = ["Phil Dawes <phil@phildawes.net>", "The Racer developers"]
@@ -24,12 +24,12 @@ debug = true
 [dependencies]
 bitflags = "1.0"
 log = "0.4"
-rustc-ap-syntax = "274.0.0"
+rustc-ap-syntax = "297.0.0"
 env_logger = "0.5"
 clap = "2.32"
 lazy_static = "1.0"
 humantime = "1.1"
-derive_more = "0.11.0"
+derive_more = "0.13.0"
 rls-span = "0.4.0"
 lazycell = {version = "1.0", optional = true}
 
diff --git a/interner/src/lib.rs b/interner/src/lib.rs
index f7f23e0c..6eff3a11 100644
--- a/interner/src/lib.rs
+++ b/interner/src/lib.rs
@@ -1,6 +1,5 @@
 //! string interner
 //! same as cargo::core::interning.rs, but thread local and Deserializable
-
 extern crate serde;
 use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
 
@@ -55,7 +54,6 @@ impl InternedString {
 
 impl Deref for InternedString {
     type Target = str;
-
     fn deref(&self) -> &'static str {
         self.as_str()
     }
diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs
index a280fb27..8ef9b8fb 100644
--- a/metadata/src/lib.rs
+++ b/metadata/src/lib.rs
@@ -72,7 +72,7 @@ pub fn find_manifest(mut current: &Path) -> Option<PathBuf> {
         }
     }
     while let Some(parent) = current.parent() {
-        let manifest = current.join(file);
+        let manifest = parent.join(file);
         if manifest.exists() {
             return Some(manifest);
         }
diff --git a/src/racer/ast.rs b/src/racer/ast.rs
index 900edd3b..74321b67 100644
--- a/src/racer/ast.rs
+++ b/src/racer/ast.rs
@@ -348,61 +348,22 @@ fn destructure_pattern_to_ty(
 struct LetTypeVisitor<'c: 's, 's> {
     scope: Scope,
     session: &'s Session<'c>,
-    srctxt: String,
     pos: BytePos, // pos is relative to the srctxt, scope is global
     result: Option<Ty>,
 }
 
 impl<'c, 's, 'ast> visit::Visitor<'ast> for LetTypeVisitor<'c, 's> {
-    fn visit_expr(&mut self, ex: &ast::Expr) {
-        match ex.node {
-            ExprKind::IfLet(ref pattern, ref expr, _, _)
-            | ExprKind::WhileLet(ref pattern, ref expr, _, _) => {
-                let mut v = ExprTypeVisitor::new(self.scope.clone(), self.session);
-                v.visit_expr(expr);
-                // TODO: too ugly
-                self.result = v
-                    .result
-                    .and_then(|ty| {
-                        pattern
-                            .iter()
-                            .filter_map(|pat| {
-                                destructure_pattern_to_ty(
-                                    pat,
-                                    self.pos,
-                                    &ty,
-                                    &self.scope,
-                                    self.session,
-                                )
-                            })
-                            .nth(0)
-                    })
-                    .and_then(|ty| path_to_match(ty, self.session));
-            }
-            _ => visit::walk_expr(self, ex),
-        }
-    }
-
     fn visit_local(&mut self, local: &ast::Local) {
-        let mut ty = None;
-        if let Some(ref local_ty) = local.ty {
-            ty = Ty::from_ast(local_ty, &self.scope);
-        }
-
-        if ty.is_none() {
-            // oh, no type in the let expr. Try evalling the RHS
-            ty = local.init.as_ref().and_then(|initexpr| {
-                debug!("init node is {:?}", initexpr.node);
+        let ty = match &local.ty {
+            Some(annon) => Ty::from_ast(&*annon, &self.scope),
+            None => local.init.as_ref().and_then(|initexpr| {
+                debug!("[LetTypeVisitor] initexpr is {:?}", initexpr.node);
                 let mut v = ExprTypeVisitor::new(self.scope.clone(), self.session);
                 v.visit_expr(initexpr);
                 v.result
-            });
-        }
-
-        debug!(
-            "LetTypeVisitor: ty is {:?}. pos is {:?}, src is |{}|",
-            ty, self.pos, self.srctxt
-        );
+            }),
+        };
+        debug!("[LetTypeVisitor] ty is {:?}. pos is {:?}", ty, self.pos);
         self.result = ty
             .and_then(|ty| {
                 destructure_pattern_to_ty(&local.pat, self.pos, &ty, &self.scope, self.session)
@@ -809,6 +770,34 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for ExprTypeVisitor<'c, 's> {
                     }
                 }
             }
+            ExprKind::Binary(bin, ref left, ref right) => {
+                self.visit_expr(left);
+                let type_match = match self.result.take() {
+                    Some(Ty::Match(m)) => m,
+                    Some(Ty::PathSearch(ps)) => match ps.resolve_as_match(self.session) {
+                        Some(m) => m,
+                        _ => {
+                            return;
+                        }
+                    },
+                    _ => {
+                        return;
+                    }
+                };
+
+                self.visit_expr(right);
+                let right_expr_type = match self.result.take() {
+                    Some(Ty::Match(m)) => Some(m.matchstr),
+                    Some(Ty::PathSearch(ps)) => ps.resolve_as_match(self.session).map(|m| m.matchstr),
+                    _ => None,
+                };
+                self.result = nameres::resolve_binary_expr_type(
+                    &type_match,
+                    bin.node,
+                    right_expr_type.as_ref().map(|s| s.as_str()),
+                    self.session,
+                );
+            }
             _ => {
                 debug!("- Could not match expr node type: {:?}", expr.node);
             }
@@ -881,7 +870,7 @@ fn find_type_match_including_generics(
 
 struct StructVisitor {
     pub scope: Scope,
-    pub fields: Vec<(String, BytePos, Option<Ty>)>,
+    pub fields: Vec<(String, ByteRange, Option<Ty>)>,
 }
 
 impl<'ast> visit::Visitor<'ast> for StructVisitor {
@@ -894,16 +883,13 @@ impl<'ast> visit::Visitor<'ast> for StructVisitor {
         _: Span,
     ) {
         for field in struct_definition.fields() {
-            let source_map::BytePos(point) = field.span.lo();
-
             let ty = Ty::from_ast(&field.ty, &self.scope);
             let name = match field.ident {
                 Some(ref ident) => ident.to_string(),
                 // name unnamed field by its ordinal, since self.0 works
                 None => format!("{}", self.fields.len()),
             };
-
-            self.fields.push((name, point.into(), ty));
+            self.fields.push((name, field.span.into(), ty));
         }
     }
 }
@@ -917,7 +903,7 @@ pub struct TypeVisitor<'s> {
 
 impl<'ast, 's> visit::Visitor<'ast> for TypeVisitor<'s> {
     fn visit_item(&mut self, item: &ast::Item) {
-        if let ItemKind::Ty(ref ty, ref gen) = item.node {
+        if let ItemKind::Ty(ref ty, _) = item.node {
             self.name = Some(item.ident.name.to_string());
             self.type_ = Ty::from_ast(&ty, self.scope);
             debug!("typevisitor type is {:?}", self.type_);
@@ -976,18 +962,6 @@ impl<'ast, 'p> visit::Visitor<'ast> for ImplVisitor<'p> {
     }
 }
 
-pub struct ModVisitor {
-    pub name: Option<String>,
-}
-
-impl<'ast> visit::Visitor<'ast> for ModVisitor {
-    fn visit_item(&mut self, item: &ast::Item) {
-        if let ItemKind::Mod(_) = item.node {
-            self.name = Some(item.ident.name.to_string());
-        }
-    }
-}
-
 pub struct ExternCrateVisitor {
     pub name: Option<String>,
     pub realname: Option<String>,
@@ -1063,7 +1037,7 @@ pub fn parse_pat_bind_stmt(s: String) -> Vec<ByteRange> {
     v.ident_points
 }
 
-pub fn parse_struct_fields(s: String, scope: Scope) -> Vec<(String, BytePos, Option<Ty>)> {
+pub fn parse_struct_fields(s: String, scope: Scope) -> Vec<(String, ByteRange, Option<Ty>)> {
     let mut v = StructVisitor {
         scope,
         fields: Vec::new(),
@@ -1168,12 +1142,6 @@ pub fn parse_fn_output(s: String, scope: Scope) -> Option<Ty> {
     v.result
 }
 
-pub fn parse_mod(s: String) -> ModVisitor {
-    let mut v = ModVisitor { name: None };
-    with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
-    v
-}
-
 pub fn parse_extern_crate(s: String) -> ExternCrateVisitor {
     let mut v = ExternCrateVisitor {
         name: None,
@@ -1209,7 +1177,6 @@ pub fn get_let_type(s: String, pos: BytePos, scope: Scope, session: &Session) ->
     let mut v = LetTypeVisitor {
         scope,
         session,
-        srctxt: s.clone(),
         pos,
         result: None,
     };
diff --git a/src/racer/ast_types.rs b/src/racer/ast_types.rs
index ee616ef6..8a8bfc21 100644
--- a/src/racer/ast_types.rs
+++ b/src/racer/ast_types.rs
@@ -130,11 +130,11 @@ impl Ty {
                 Ty::from_ast(&*ty.ty, scope).map(|rty| Ty::Ptr(Box::new(rty), ty.mutbl))
             }
             TyKind::Never => None,
-            TyKind::TraitObject(ref traits, _) => {
+            TyKind::TraitObject(ref traits, _) | TyKind::ImplTrait(_, ref traits) => {
                 Some(Ty::TraitObject(TraitBounds::from_generic_bounds(
                     &traits,
                     scope.filepath.clone(),
-                    ty.span.lo().0 as i32,
+                    scope.point.0 as i32,
                 )))
             }
             TyKind::ImplicitSelf => Some(Ty::Self_(scope.clone())),
@@ -529,7 +529,7 @@ impl fmt::Debug for Path {
             }
 
             if !seg.output.is_none() {
-                write!(f, "(");
+                write!(f, "(")?;
             }
 
             if !seg.generics.is_empty() {
diff --git a/src/racer/codeiter.rs b/src/racer/codeiter.rs
index a215df33..16b019ce 100644
--- a/src/racer/codeiter.rs
+++ b/src/racer/codeiter.rs
@@ -58,8 +58,10 @@ impl<'a> Iterator for StmtIndicesIter<'a> {
                 b'{' => {
                     // if we are top level and stmt is not a 'use' or 'let' then
                     // closebrace finishes the stmt
-                    if bracelevel == 0 && parenlevel == 0 && !(is_a_use_stmt(src_bytes, start, pos)
-                        || is_a_let_stmt(src_bytes, start, pos))
+                    if bracelevel == 0
+                        && parenlevel == 0
+                        && !(is_a_use_stmt(src_bytes, start, pos)
+                            || is_a_let_stmt(src_bytes, start, pos))
                     {
                         enddelim = b'}';
                     }
diff --git a/src/racer/core.rs b/src/racer/core.rs
index ec5d6450..e09266f1 100644
--- a/src/racer/core.rs
+++ b/src/racer/core.rs
@@ -1,4 +1,4 @@
-use ast_types::{GenericsArgs, ImplHeader, Pat, Path, TraitBounds, Ty, TypeParameter};
+use ast_types::{GenericsArgs, ImplHeader, Pat, TraitBounds, Ty, TypeParameter};
 use codecleaner;
 use codeiter::StmtIndicesIter;
 use matchers::ImportInfo;
@@ -18,6 +18,7 @@ use std::{path, str};
 use syntax::source_map;
 
 use ast;
+use fileres;
 use nameres;
 use primitive::PrimKind;
 use scopes;
@@ -34,7 +35,7 @@ pub enum MatchType {
     Function,
     Method(Option<Box<GenericsArgs>>),
     Crate,
-    Let,
+    Let(BytePos),
     IfLet(BytePos),
     WhileLet(BytePos),
     For(BytePos),
@@ -43,6 +44,7 @@ pub enum MatchType {
     /// EnumVariant needs to have Enum type to complete methods
     EnumVariant(Option<Box<Match>>),
     UseAlias(Box<Match>),
+    AssocType,
     Type,
     FnArg(Box<(Pat, Option<Ty>)>),
     Trait,
@@ -81,6 +83,7 @@ impl fmt::Display for MatchType {
             MatchType::Struct(_) => write!(f, "Struct"),
             MatchType::Method(_) => write!(f, "Method"),
             MatchType::IfLet(_) => write!(f, "IfLet"),
+            MatchType::Let(_) => write!(f, "Let"),
             MatchType::WhileLet(_) => write!(f, "WhileLet"),
             MatchType::For(_) => write!(f, "For"),
             MatchType::Enum(_) => write!(f, "Enum"),
@@ -115,6 +118,7 @@ mod declare_namespace {
             const Union     = 0b0000000010000;
             const Trait     = 0b0000000100000;
             const TypeDef   = 0b0000001000000;
+            const HasField  = 0b0000001011100;
             const Type      = 0b0000001111100;
             const PathParen = 0b0000001111111;
             const Const     = 0b0000010000000;
@@ -122,7 +126,7 @@ mod declare_namespace {
             const Func      = 0b0001000000000;
             // for use_extern_macros
             const Macro     = 0b0010000000000;
-            const Impl       = 0b001110000000;
+            const Impl      = 0b0001110000000;
             const PathChild = 0b0011110000000;
             const Path      = 0b0011111111111;
             const Primitive = 0b0100000000000;
@@ -171,6 +175,9 @@ impl BytePos {
     pub fn decrement(&self) -> Self {
         BytePos(self.0 - 1)
     }
+    pub fn checked_sub(&self, sub: impl Into<Self>) -> Option<Self> {
+        self.0.checked_sub(sub.into().0).map(BytePos)
+    }
     pub fn try_decrement(&self) -> Option<Self> {
         self.0.checked_sub(1).map(BytePos)
     }
@@ -1060,7 +1067,6 @@ fn complete_from_file_(filepath: &path::Path, cursor: Location, session: &Sessio
             return Vec::new();
         }
     };
-
     let start = scopes::get_start_of_search_expr(src_text, pos);
     let expr = &src_text[start.0..pos.0];
     let (contextstr, searchstr, completetype) = scopes::split_into_context_and_completion(expr);
@@ -1074,13 +1080,12 @@ fn complete_from_file_(filepath: &path::Path, cursor: Location, session: &Sessio
 
     match completetype {
         CompletionType::Path => {
-            let line = &scopes::get_current_line(src.as_src(), pos);
-            debug!("Complete path with line: {:?}", line);
+            let (stmtstart, stmt) = &scopes::get_current_stmt(src.as_src(), pos);
+            debug!("Complete path with stmt: {:?}", stmt);
             // when in the function ident position, only look for methods
             // from a trait to complete.
-            if util::in_fn_name(line) {
+            if util::in_fn_name(stmt) {
                 trace!("Path is in fn declaration: `{}`", expr);
-
                 return nameres::resolve_method(
                     pos,
                     src.as_src(),
@@ -1091,21 +1096,30 @@ fn complete_from_file_(filepath: &path::Path, cursor: Location, session: &Sessio
                     &ImportInfo::default(),
                 );
             }
-            let (path, namespace) = if let Some(use_start) = scopes::use_stmt_start(line) {
-                let path = scopes::construct_path_from_use_tree(&line[use_start.0..]);
+            let (path, namespace) = if let Some(use_start) = scopes::use_stmt_start(stmt) {
+                let path = scopes::construct_path_from_use_tree(&stmt[use_start.0..]);
                 (path, Namespace::Path)
+            } else if scopes::is_extern_crate(stmt) {
+                return fileres::search_crate_names(
+                    searchstr,
+                    SearchType::StartsWith,
+                    filepath,
+                    false,
+                    session,
+                );
+            } else if let Some(str_path) = scopes::is_in_struct_ctor(src.as_src(), *stmtstart, pos)
+            {
+                let path = scopes::expr_to_path(&src[str_path.to_range()]).0;
+                return nameres::get_struct_fields(
+                    &path,
+                    searchstr,
+                    filepath,
+                    pos,
+                    SearchType::StartsWith,
+                    session,
+                );
             } else {
-                let is_global = expr.starts_with("::");
-                let v: Vec<_> = (if is_global { &expr[2..] } else { expr })
-                    .split("::")
-                    .collect();
-                let path = Path::from_vec(is_global, v);
-                let namespace = if path.len() == 1 {
-                    Namespace::Global | Namespace::Path
-                } else {
-                    Namespace::Path
-                };
-                (path, namespace)
+                scopes::expr_to_path(expr)
             };
             debug!("path: {:?}, prefix: {:?}", path, path.prefix);
             out.extend(nameres::resolve_path(
@@ -1183,7 +1197,7 @@ where
         return false;
     }
 
-    let line = &scopes::get_current_line(src.as_src(), pos);
+    let line = &scopes::get_current_stmt(src.as_src(), pos).1;
     scopes::use_stmt_start(line).is_some()
 }
 
@@ -1276,22 +1290,25 @@ pub fn find_definition_(
 
     match completetype {
         CompletionType::Path => {
-            let line = &scopes::get_current_line(src.as_src(), range.end);
-            let (path, namespace) = if let Some(use_start) = scopes::use_stmt_start(line) {
-                let path = scopes::construct_path_from_use_tree(&line[use_start.0..]);
+            let (stmtstart, stmt) = &scopes::get_current_stmt(src.as_src(), range.end);
+            let (path, namespace) = if let Some(use_start) = scopes::use_stmt_start(stmt) {
+                let path = scopes::construct_path_from_use_tree(&stmt[use_start.0..]);
                 (path, Namespace::Path)
+            } else if let Some(str_path) = scopes::is_in_struct_ctor(src.as_src(), *stmtstart, pos)
+            {
+                let path = scopes::expr_to_path(&src[str_path.to_range()]).0;
+                return nameres::get_struct_fields(
+                    &path,
+                    searchstr,
+                    filepath,
+                    pos,
+                    SearchType::StartsWith,
+                    session,
+                )
+                .into_iter()
+                .next();
             } else {
-                let is_global = expr.starts_with("::");
-                let v: Vec<_> = (if is_global { &expr[2..] } else { expr })
-                    .split("::")
-                    .collect();
-                let path = Path::from_vec(is_global, v);
-                let namespace = if path.len() == 1 {
-                    Namespace::Global | Namespace::Path
-                } else {
-                    Namespace::Path
-                };
-                (path, namespace)
+                scopes::expr_to_path(expr)
             };
             debug!("[find_definition_] Path: {:?}", path);
             nameres::resolve_path(
diff --git a/src/racer/fileres.rs b/src/racer/fileres.rs
index 551cd497..360aab94 100644
--- a/src/racer/fileres.rs
+++ b/src/racer/fileres.rs
@@ -1,5 +1,7 @@
-use core::Session;
+use core::{BytePos, Coordinate, Match, MatchType, SearchType, Session, SessionExt};
+use matchers;
 use nameres::RUST_SRC_PATH;
+use project_model::Edition;
 use std::path::{Path, PathBuf};
 
 /// get crate file from current path & crate name
@@ -20,6 +22,55 @@ pub fn get_std_file(name: &str, session: &Session) -> Option<PathBuf> {
     return None;
 }
 
+/// 2018 style crate name resolution
+pub fn search_crate_names(
+    searchstr: &str,
+    search_type: SearchType,
+    file_path: &Path,
+    only_2018: bool,
+    session: &Session,
+) -> Vec<Match> {
+    let manifest_path = try_vec!(session.project_model.discover_project_manifest(file_path));
+    if only_2018 {
+        let edition = session
+            .project_model
+            .edition(&manifest_path)
+            .unwrap_or(Edition::Ed2015);
+        if edition == Edition::Ed2015 {
+            return Vec::new();
+        }
+    }
+    let hyphenated = searchstr.replace('_', "-");
+    let searchstr = searchstr.to_owned();
+    session
+        .project_model
+        .search_dependencies(
+            &manifest_path,
+            Box::new(move |libname| match search_type {
+                SearchType::ExactMatch => libname == hyphenated || libname == searchstr,
+                SearchType::StartsWith => {
+                    libname.starts_with(&hyphenated) || libname.starts_with(&searchstr)
+                }
+            }),
+        )
+        .into_iter()
+        .map(|(name, path)| {
+            let name = name.replace('-', "_");
+            let raw_src = session.load_raw_file(&path);
+            Match {
+                matchstr: name,
+                filepath: path,
+                point: BytePos::ZERO,
+                coords: Some(Coordinate::start()),
+                local: false,
+                mtype: MatchType::Crate,
+                contextstr: String::new(),
+                docs: matchers::find_mod_doc(&raw_src, BytePos::ZERO),
+            }
+        })
+        .collect()
+}
+
 /// get module file from current path & crate name
 pub fn get_module_file(name: &str, parentdir: &Path, session: &Session) -> Option<PathBuf> {
     // try just <name>.rs
diff --git a/src/racer/lib.rs b/src/racer/lib.rs
index 8eadee3e..2f5e7bcc 100644
--- a/src/racer/lib.rs
+++ b/src/racer/lib.rs
@@ -42,7 +42,7 @@ pub use core::{
     BytePos, ByteRange, Coordinate, FileCache, FileLoader, Location, Match, MatchType, Session,
 };
 pub use primitive::PrimKind;
-pub use project_model::ProjectModelProvider;
+pub use project_model::{Edition, ProjectModelProvider};
 pub use snippets::snippet_for_match;
 pub use util::expand_ident;
 
diff --git a/src/racer/matchers.rs b/src/racer/matchers.rs
index 348c4e86..d8376afe 100644
--- a/src/racer/matchers.rs
+++ b/src/racer/matchers.rs
@@ -9,7 +9,6 @@ use core::{BytePos, ByteRange, Coordinate, Match, Session, SessionExt, Src};
 use fileres::{get_crate_file, get_module_file};
 use nameres::resolve_path;
 use std::path::Path;
-use std::rc::Rc;
 use std::{str, vec};
 use util::*;
 use {ast, scopes, typeinf};
@@ -72,7 +71,12 @@ impl<'s, 'p> MatchCxt<'s, 'p> {
     }
 }
 
-fn find_keyword(src: &str, pattern: &str, ignore: &[&str], context: &MatchCxt) -> Option<BytePos> {
+pub(crate) fn find_keyword(
+    src: &str,
+    pattern: &str,
+    ignore: &[&str],
+    context: &MatchCxt,
+) -> Option<BytePos> {
     find_keyword_impl(
         src,
         pattern,
@@ -234,8 +238,8 @@ pub fn match_while_let(msrc: &str, start: BytePos, context: &MatchCxt) -> Vec<Ma
     match_pattern_let(msrc, context, "while let ", WhileLet(start))
 }
 
-pub fn match_let(msrc: &str, context: &MatchCxt) -> Vec<Match> {
-    match_pattern_let(msrc, context, "let ", Let)
+pub fn match_let(msrc: &str, start: BytePos, context: &MatchCxt) -> Vec<Match> {
+    match_pattern_let(msrc, context, "let ", Let(start))
 }
 
 pub fn match_for(msrc: &str, for_start: BytePos, context: &MatchCxt) -> Vec<Match> {
@@ -296,11 +300,12 @@ pub fn match_extern_crate(msrc: Src, context: &MatchCxt, session: &Session) -> O
         context.search_type,
         &format!("extern crate {} as", context.search_str),
         blob,
-    )) || (blob.starts_with("extern crate") && txt_matches(
-        context.search_type,
-        &format!("as {}", context.search_str),
-        blob,
-    )) {
+    )) || (blob.starts_with("extern crate")
+        && txt_matches(
+            context.search_type,
+            &format!("as {}", context.search_str),
+            blob,
+        )) {
         debug!("found an extern crate: |{}|", blob);
 
         let extern_crate = ast::parse_extern_crate(blob.to_owned());
@@ -451,7 +456,7 @@ pub fn match_type(msrc: Src, context: &MatchCxt, session: &Session) -> Option<Ma
 
 pub fn match_trait(msrc: Src, context: &MatchCxt, session: &Session) -> Option<Match> {
     let blob = &msrc[context.range.to_range()];
-    let (start, s) = context.get_key_ident(blob, "trait", &[])?;
+    let (start, s) = context.get_key_ident(blob, "trait", &["unsafe"])?;
     debug!("found!! a trait {}", s);
     let start = context.range.start + start;
     let doc_src = session.load_raw_src_ranged(&msrc, context.filepath);
@@ -681,6 +686,23 @@ pub fn match_fn(msrc: Src, context: &MatchCxt, session: &Session) -> Option<Matc
     if typeinf::first_param_is_self(blob) {
         return None;
     }
+    match_fn_common(blob, msrc, context, session)
+}
+
+pub fn match_method(
+    msrc: Src,
+    context: &MatchCxt,
+    include_assoc_fn: bool,
+    session: &Session,
+) -> Option<Match> {
+    let blob = &msrc[context.range.to_range()];
+    if !include_assoc_fn && !typeinf::first_param_is_self(blob) {
+        return None;
+    }
+    match_fn_common(blob, msrc, context, session)
+}
+
+fn match_fn_common(blob: &str, msrc: Src, context: &MatchCxt, session: &Session) -> Option<Match> {
     let (start, s) = context.get_key_ident(blob, "fn", &["const", "unsafe"])?;
     let start = context.range.start + start;
     let doc_src = session.load_raw_src_ranged(&msrc, context.filepath);
@@ -733,7 +755,7 @@ pub fn find_doc(msrc: &str, match_point: BytePos) -> String {
         .join("\n")
 }
 
-fn find_mod_doc(msrc: &str, blobstart: BytePos) -> String {
+pub(crate) fn find_mod_doc(msrc: &str, blobstart: BytePos) -> String {
     let blob = &msrc[blobstart.0..];
     let mut doc = String::new();
 
diff --git a/src/racer/metadata.rs b/src/racer/metadata.rs
index c3d3956b..63a595c8 100644
--- a/src/racer/metadata.rs
+++ b/src/racer/metadata.rs
@@ -1,8 +1,8 @@
 extern crate lazycell;
 extern crate racer_cargo_metadata as metadata;
 use self::lazycell::LazyCell;
-use self::metadata::mapping::PackageMap;
-use project_model::ProjectModelProvider;
+use self::metadata::mapping::{Edition as Ed, PackageIdx, PackageMap};
+use project_model::{Edition, ProjectModelProvider};
 use std::path::{Path, PathBuf};
 
 struct MetadataCache {
@@ -20,7 +20,8 @@ impl MetadataCache {
             .or_else(|e| {
                 if let metadata::ErrorKind::Subprocess(ref s) = e {
                     // HACK: if --frozen failed, try again without --frozen
-                    if s.contains("error: unable to get packages from source") {
+                    // see https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/registry.rs#L344
+                    if s.contains("--frozen") {
                         info!("MetadataCache: try again without --frozen");
                         return metadata::run(manifest, false);
                     }
@@ -35,22 +36,55 @@ impl MetadataCache {
             warn!("Error in initialize lazy cell");
         })
     }
+    fn setup(&self, manifest: &Path) -> Option<(&PackageMap, PackageIdx)> {
+        if !self.pkg_map.filled() {
+            self.fill(manifest).ok()?;
+        }
+        let pkg_map: &PackageMap = self.pkg_map.borrow().unwrap();
+        let idx = if manifest.is_relative() {
+            let path = manifest.canonicalize().ok()?;
+            pkg_map.get_idx(&path)?
+        } else {
+            pkg_map.get_idx(manifest)?
+        };
+        Some((pkg_map, idx))
+    }
 }
 
 impl ProjectModelProvider for MetadataCache {
+    fn edition(&self, manifest: &Path) -> Option<Edition> {
+        let (pkg_map, idx) = self.setup(manifest)?;
+        let edition = pkg_map.get_edition(idx);
+        Some(match edition {
+            Ed::Ed2015 => Edition::Ed2015,
+            Ed::Ed2018 => Edition::Ed2018,
+        })
+    }
     fn discover_project_manifest(&self, path: &Path) -> Option<PathBuf> {
         metadata::find_manifest(path)
     }
+    fn search_dependencies(
+        &self,
+        manifest: &Path,
+        search_fn: Box<Fn(&str) -> bool>,
+    ) -> Vec<(String, PathBuf)> {
+        let (pkg_map, idx) = match self.setup(manifest) {
+            Some(x) => x,
+            None => return vec![],
+        };
+        pkg_map
+            .get_dependencies(idx)
+            .iter()
+            .filter(|(s, _)| search_fn(s))
+            .map(|(s, p)| (s.to_string(), p.to_path_buf()))
+            .collect()
+    }
     fn resolve_dependency(&self, manifest: &Path, libname: &str) -> Option<PathBuf> {
         debug!(
             "MetadataCache::resolve_dependency manifest: {:?} libname: {}",
             manifest, libname
         );
-        if !self.pkg_map.filled() {
-            self.fill(manifest).ok()?;
-        }
-        let pkg_map: &PackageMap = self.pkg_map.borrow().unwrap();
-        let idx = pkg_map.get_idx(manifest)?;
+        let (pkg_map, idx) = self.setup(manifest)?;
         pkg_map
             .get_src_path_from_libname(idx, libname)
             .or_else(|| {
diff --git a/src/racer/nameres.rs b/src/racer/nameres.rs
index 8fd77934..2a675c95 100644
--- a/src/racer/nameres.rs
+++ b/src/racer/nameres.rs
@@ -1,20 +1,22 @@
 //! Name resolving
-
 use std::collections::HashSet;
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
 use std::{self, vec};
 
+use primitive::PrimKind;
+use syntax::ast::BinOpKind;
+
 use ast_types::{ImplHeader, Path as RacerPath, PathPrefix, PathSegment, Ty};
 use core::Namespace;
 use core::SearchType::{self, ExactMatch, StartsWith};
 use core::{BytePos, ByteRange, Coordinate, Match, MatchType, Scope, Session, SessionExt, Src};
-use fileres::{get_crate_file, get_module_file, get_std_file};
+use fileres::{get_crate_file, get_module_file, get_std_file, search_crate_names};
 use matchers::{find_doc, ImportInfo, MatchCxt};
 use primitive;
 use util::{
-    self, calculate_str_hash, find_ident_end, get_rust_src_path,
-    strip_words, symbol_matches, trim_visibility, txt_matches,
+    self, calculate_str_hash, find_ident_end, get_rust_src_path, strip_words, symbol_matches,
+    trim_visibility, txt_matches,
 };
 use {ast, core, matchers, scopes, typeinf};
 
@@ -22,12 +24,16 @@ lazy_static! {
     pub static ref RUST_SRC_PATH: Option<PathBuf> = get_rust_src_path().ok();
 }
 
-fn search_struct_fields(
+pub(crate) fn search_struct_fields(
     searchstr: &str,
     structmatch: &Match,
     search_type: SearchType,
     session: &Session,
 ) -> Vec<Match> {
+    match structmatch.mtype {
+        MatchType::Struct(_) | MatchType::EnumVariant(_) => {}
+        _ => return Vec::new(),
+    }
     let src = session.load_source_file(&structmatch.filepath);
     let mut out = Vec::new();
 
@@ -37,58 +43,30 @@ fn search_struct_fields(
     } else {
         return out;
     };
-    let structsrc = &src[struct_range.clone()];
-    let fields =
-        ast::parse_struct_fields(structsrc.to_owned(), core::Scope::from_match(structmatch));
-
-    for (field, field_point, ty) in fields {
+    let structsrc = match structmatch.mtype {
+        MatchType::EnumVariant(_) => "struct ".to_string() + &src[struct_range.clone()],
+        _ => src[struct_range.clone()].to_string(),
+    };
+    let fields = ast::parse_struct_fields(structsrc, core::Scope::from_match(structmatch));
+    for (field, field_range, _) in fields {
         if symbol_matches(search_type, searchstr, &field) {
-            let contextstr = if let Some(t) = ty {
-                t.to_string()
-            } else {
-                field.clone()
-            };
             let raw_src = session.load_raw_file(&structmatch.filepath);
+            let contextstr = src[field_range.shift(struct_start).to_range()].to_owned();
             out.push(Match {
                 matchstr: field,
                 filepath: structmatch.filepath.clone(),
-                point: field_point + struct_start,
+                point: field_range.start + struct_start,
                 coords: None,
                 local: structmatch.local,
                 mtype: MatchType::StructField,
-                contextstr: contextstr,
-                docs: find_doc(&raw_src[struct_range.clone()], field_point),
+                contextstr,
+                docs: find_doc(&raw_src[struct_range.clone()], field_range.start),
             });
         }
     }
     out
 }
 
-fn collect_trait_methods(
-    trait_match: &Match,
-    search_type: SearchType,
-    query: &str,
-    includes_assoc_fn: bool,
-    session: &Session,
-) -> impl Iterator<Item = Match> {
-    let src = session.load_source_file(&trait_match.filepath);
-    src[trait_match.point.0..]
-        .find('{')
-        .map(|n| {
-            let point = trait_match.point.increment() + n.into();
-            search_scope_for_methods(
-                point,
-                src.as_src(),
-                query,
-                &trait_match.filepath,
-                includes_assoc_fn,
-                search_type,
-            )
-        })
-        .into_iter()
-        .flatten()
-}
-
 pub fn search_for_impl_methods(
     match_request: &Match,
     fieldsearchstr: &str,
@@ -119,7 +97,9 @@ pub fn search_for_impl_methods(
             fieldsearchstr,
             header.file_path(),
             false,
+            false,
             search_type,
+            session,
         ) {
             found_methods.insert(calculate_str_hash(&m.matchstr));
             out.push(m);
@@ -145,7 +125,8 @@ pub fn search_for_impl_methods(
             continue;
         }
         let trait_match = try_continue!(header.resolve_trait(session, &ImportInfo::default()));
-        for m in collect_trait_methods(&trait_match, search_type, fieldsearchstr, false, session) {
+        for m in search_for_trait_methods(trait_match.clone(), fieldsearchstr, search_type, session)
+        {
             if !found_methods.contains(&calculate_str_hash(&m.matchstr)) {
                 out.push(m);
             }
@@ -178,7 +159,9 @@ fn search_scope_for_methods(
     searchstr: &str,
     filepath: &Path,
     includes_assoc_fn: bool,
+    includes_assoc_ty_and_const: bool,
     search_type: SearchType,
+    session: &Session,
 ) -> Vec<Match> {
     debug!(
         "searching scope for methods {:?} |{}| {:?}",
@@ -186,35 +169,47 @@ fn search_scope_for_methods(
         searchstr,
         filepath.display()
     );
-
     let scopesrc = src.shift_start(point);
     let mut out = Vec::new();
+    macro_rules! ret_or_continue {
+        () => {
+            if search_type == ExactMatch {
+                return out;
+            } else {
+                continue;
+            }
+        };
+    }
     for blob_range in scopesrc.iter_stmts() {
-        let blob = &scopesrc[blob_range.to_range()];
-        if let Some(n) = blob.find(|c| c == '{' || c == ';') {
-            let signature = blob[..n].trim_right();
-
-            if txt_matches(search_type, &format!("fn {}", searchstr), signature)
-                && (includes_assoc_fn || typeinf::first_param_is_self(blob))
-            {
-                debug!("found a method starting |{}| |{}|", searchstr, blob);
-                // TODO: parse this properly, or, txt_matches should return match pos?
-                let start = BytePos::from(blob.find(&format!("fn {}", searchstr)).unwrap() + 3);
-                let end = find_ident_end(blob, start);
-                let l = &blob[start.0..end.0];
-                // TODO: make a better context string for functions
-                let m = Match {
-                    matchstr: l.to_owned(),
-                    filepath: filepath.to_path_buf(),
-                    point: point + blob_range.start + start,
-                    coords: None,
-                    local: true,
-                    mtype: MatchType::Function,
-                    contextstr: signature.to_owned(),
-                    docs: find_doc(&scopesrc, blob_range.start + start),
-                };
-                out.push(m);
+        let matchcxt = MatchCxt {
+            filepath,
+            range: blob_range.shift(point),
+            search_str: searchstr,
+            search_type,
+            is_local: true,
+        };
+        let method = matchers::match_method(src, &matchcxt, includes_assoc_fn, session);
+        if let Some(mut m) = method {
+            // for backward compatibility
+            if m.contextstr.ends_with(";") {
+                m.contextstr.pop();
             }
+            out.push(m);
+            ret_or_continue!();
+        }
+        if !includes_assoc_ty_and_const {
+            continue;
+        }
+        let type_ = matchers::match_type(src, &matchcxt, session);
+        if let Some(mut m) = type_ {
+            m.mtype = MatchType::AssocType;
+            out.push(m);
+            ret_or_continue!();
+        }
+        let const_ = matchers::match_const(&src, &matchcxt);
+        if let Some(m) = const_ {
+            out.push(m);
+            ret_or_continue!();
         }
     }
     out
@@ -359,7 +354,7 @@ fn search_for_impls(
 
 // trait_only version of search_for_impls
 // needs both `Self` type name and trait name
-fn search_trait_impls(
+pub(crate) fn search_trait_impls(
     pos: BytePos,
     self_search: &str,
     trait_search: &[&str],
@@ -646,9 +641,14 @@ fn search_scope_headers(
             }
         }
         return out;
-    } else if let Some(vec) =
-        search_closure_args(search_str, preblock, stmtstart, point - stmtstart, filepath, search_type)
-    {
+    } else if let Some(vec) = search_closure_args(
+        search_str,
+        preblock,
+        stmtstart,
+        point - stmtstart,
+        filepath,
+        search_type,
+    ) {
         return vec;
     }
     Vec::new()
@@ -737,6 +737,17 @@ fn search_fn_args_and_generics(
                     docs: String::new(),
                 };
                 out.push(m);
+                if search_type == SearchType::ExactMatch {
+                    break;
+                }
+            }
+        }
+    }
+    for type_param in generics.0 {
+        if symbol_matches(search_type, searchstr, type_param.name()) {
+            out.push(type_param.into_match());
+            if search_type == SearchType::ExactMatch {
+                break;
             }
         }
     }
@@ -748,23 +759,19 @@ fn test_do_file_search_std() {
     let cache = core::FileCache::default();
     let session = Session::new(&cache);
     let matches = do_file_search("std", &Path::new("."), &session);
-    assert!(
-        matches
-            .into_iter()
-            .any(|m| m.filepath.ends_with("src/libstd/lib.rs"))
-    );
+    assert!(matches
+        .into_iter()
+        .any(|m| m.filepath.ends_with("src/libstd/lib.rs")));
 }
 
 #[test]
 fn test_do_file_search_local() {
     let cache = core::FileCache::default();
     let session = Session::new(&cache);
-    let mut matches = do_file_search("submodule", &Path::new("fixtures/arst/src"), &session);
-    assert!(
-        matches
-            .into_iter()
-            .any(|m| m.filepath.ends_with("fixtures/arst/src/submodule/mod.rs"))
-    );
+    let matches = do_file_search("submodule", &Path::new("fixtures/arst/src"), &session);
+    assert!(matches
+        .into_iter()
+        .any(|m| m.filepath.ends_with("fixtures/arst/src/submodule/mod.rs")));
 }
 
 pub fn do_file_search(searchstr: &str, currentdir: &Path, session: &Session) -> Vec<Match> {
@@ -929,7 +936,6 @@ pub fn search_next_scope(
         // is a scope inside the file. Point should point to the definition
         // (e.g. mod blah {...}), so the actual scope is past the first open brace.
         let src = &filesrc[startpoint.0..];
-        //debug!("search_next_scope src1 |{}|",src);
         // find the opening brace and skip to it.
         if let Some(n) = src.find('{') {
             startpoint += BytePos(n + 1);
@@ -937,7 +943,7 @@ pub fn search_next_scope(
     }
     search_scope(
         startpoint,
-        startpoint,
+        None,
         filesrc.as_src(),
         pathseg,
         filepath,
@@ -951,7 +957,7 @@ pub fn search_next_scope(
 
 pub fn search_scope(
     start: BytePos,
-    point: BytePos,
+    complete_point: Option<BytePos>,
     src: Src,
     pathseg: &PathSegment,
     filepath: &Path,
@@ -968,7 +974,7 @@ pub fn search_scope(
         "searching scope {:?} start: {:?} point: {:?} '{}' {:?} {:?} local: {}, session: {:?}",
         namespace,
         start,
-        point,
+        complete_point,
         search_str,
         filepath.display(),
         search_type,
@@ -982,14 +988,6 @@ pub fn search_scope(
     let mut codeit = scopesrc.iter_stmts();
     let mut v = Vec::new();
 
-    // collect up to point so we can search backwards for let bindings
-    //  (these take precidence over local fn declarations etc..
-    for blob_range in &mut codeit {
-        v.push(blob_range);
-        if blob_range.start > point {
-            break;
-        }
-    }
     let get_match_cxt = |range| MatchCxt {
         filepath,
         search_str,
@@ -997,28 +995,35 @@ pub fn search_scope(
         is_local,
         range,
     };
-    // search backwards from point for let bindings
-    for &blob_range in v.iter().rev() {
-        if (start + blob_range.end) >= point {
-            continue;
+    if let Some(point) = complete_point {
+        // collect up to point so we can search backwards for let bindings
+        //  (these take precidence over local fn declarations etc..
+        for blob_range in &mut codeit {
+            v.push(blob_range);
+            if blob_range.start > point {
+                break;
+            }
         }
-        let match_cxt = get_match_cxt(blob_range.shift(start));
-        for m in matchers::match_let(&src, &match_cxt) {
-            out.push(m);
-            if let ExactMatch = search_type {
-                return out;
+        // search backwards from point for let bindings
+        for &blob_range in v.iter().rev() {
+            if (start + blob_range.end) >= point {
+                continue;
+            }
+            let range = blob_range.shift(start);
+            let match_cxt = get_match_cxt(range);
+            for m in matchers::match_let(&src, range.start, &match_cxt) {
+                out.push(m);
+                if let ExactMatch = search_type {
+                    return out;
+                }
             }
         }
     }
-
     // since we didn't find a `let` binding, now search from top of scope for items etc..
     let mut codeit = v.into_iter().chain(codeit);
     for blob_range in &mut codeit {
         let blob = &scopesrc[blob_range.to_range()];
-
-        let is_an_import = blob.starts_with("use") || blob.starts_with("pub use");
-
-        if is_an_import {
+        if util::trim_visibility(blob).starts_with("use") {
             // A `use` item can import a value
             // with the same name as a "type" (type/module/etc.) in the same scope.
             // However, that type might appear after the `use`,
@@ -1038,7 +1043,6 @@ pub fn search_scope(
             } else {
                 delayed_single_imports.push(blob_range);
             }
-
             continue;
         }
 
@@ -1073,7 +1077,7 @@ pub fn search_scope(
                 let start = blob_range.start + BytePos(block_start + 8);
                 out.extend(search_scope(
                     start,
-                    start,
+                    None,
                     src,
                     pathseg,
                     filepath,
@@ -1128,17 +1132,23 @@ pub fn search_scope(
         }
     }
 
-    if let Some(vec) = search_closure_args(search_str, &scopesrc[0..], start, point - start, filepath, search_type)
-    {
-        for mat in vec {
-            out.push(mat)
-        }
-
-        if let ExactMatch = search_type {
-            return out;
+    if let Some(point) = complete_point {
+        if let Some(vec) = search_closure_args(
+            search_str,
+            &scopesrc[0..],
+            start,
+            point - start,
+            filepath,
+            search_type,
+        ) {
+            for mat in vec {
+                out.push(mat);
+                if let ExactMatch = search_type {
+                    return out;
+                }
+            }
         }
     }
-
     debug!("search_scope found matches {:?} {:?}", search_type, out);
     out
 }
@@ -1162,7 +1172,7 @@ fn search_closure_args(
     );
 
     if let Some((pipe_range, body_range)) = util::find_closure(scope_src) {
-        let pipe_str =  &scope_src[pipe_range.to_range()];
+        let pipe_str = &scope_src[pipe_range.to_range()];
         if point < pipe_range.start || point > body_range.end {
             return None;
         }
@@ -1212,34 +1222,26 @@ fn run_matchers_on_blob(
     import_info: &ImportInfo,
 ) -> Vec<Match> {
     debug!(
-        "[run_matchers_on_blob] src: {}, cxt: {:?}, namespace: {:?}",
-        &src[context.range.to_range()],
-        context,
-        namespace
+        "[run_matchers_on_blob] cxt: {:?}, namespace: {:?}",
+        context, namespace
     );
-    let mut out = Vec::new();
-    macro_rules! run_matcher {
-        ($ns: expr, $matcher: path) => {
+    macro_rules! run_matcher_common {
+        ($ns: expr, $matcher: expr) => {
             if namespace.contains($ns) {
-                for m in $matcher(src, context, session).into_iter() {
-                    out.push(m);
-                    if context.search_type == ExactMatch {
-                        return out;
-                    }
+                if let Some(m) = $matcher {
+                    return vec![m];
                 }
             }
         };
     }
+    macro_rules! run_matcher {
+        ($ns: expr, $matcher: path) => {
+            run_matcher_common!($ns, $matcher(src, context, session))
+        };
+    }
     macro_rules! run_const_matcher {
         ($ns: expr, $matcher: path) => {
-            if namespace.contains($ns) {
-                for m in $matcher(&src, context).into_iter() {
-                    out.push(m);
-                    if context.search_type == ExactMatch {
-                        return out;
-                    }
-                }
-            }
+            run_matcher_common!($ns, $matcher(&src, context))
         };
     }
     run_matcher!(Namespace::Crate, matchers::match_extern_crate);
@@ -1253,6 +1255,7 @@ fn run_matchers_on_blob(
     run_const_matcher!(Namespace::Static, matchers::match_static);
     // TODO(kngwyu): support use_extern_macros
     run_matcher!(Namespace::Global, matchers::match_macro);
+    let mut out = Vec::new();
     if namespace.intersects(Namespace::PathParen) {
         for m in matchers::match_use(src, context, session, import_info) {
             out.push(m);
@@ -1287,7 +1290,7 @@ fn search_local_scopes(
         // search the whole file
         search_scope(
             BytePos::ZERO,
-            BytePos::ZERO,
+            None,
             msrc,
             pathseg,
             filepath,
@@ -1305,7 +1308,7 @@ fn search_local_scopes(
             start = scopes::scope_start(msrc, start);
             for m in search_scope(
                 start,
-                point,
+                Some(point),
                 msrc,
                 pathseg,
                 filepath,
@@ -1359,7 +1362,7 @@ pub fn search_prelude_file(
             let is_local = true;
             for m in search_scope(
                 BytePos::ZERO,
-                BytePos::ZERO,
+                None,
                 msrc.as_src(),
                 pathseg,
                 &filepath,
@@ -1464,10 +1467,8 @@ pub fn resolve_name(
             });
         }
 
-        if let ExactMatch = search_type {
-            if !out.is_empty() {
-                return out;
-            }
+        if is_exact_match && !out.is_empty() {
+            return out;
         }
     }
 
@@ -1482,7 +1483,7 @@ pub fn resolve_name(
         import_info,
     ) {
         out.push(m);
-        if let ExactMatch = search_type {
+        if is_exact_match {
             return out;
         }
     }
@@ -1496,7 +1497,20 @@ pub fn resolve_name(
         import_info,
     ) {
         out.push(m);
-        if let ExactMatch = search_type {
+        if is_exact_match {
+            return out;
+        }
+    }
+
+    if namespace.contains(Namespace::Crate) {
+        out.extend(search_crate_names(
+            searchstr,
+            search_type,
+            filepath,
+            true,
+            session,
+        ));
+        if is_exact_match && !out.is_empty() {
             return out;
         }
     }
@@ -1516,11 +1530,11 @@ pub fn resolve_name(
 
     for m in search_prelude_file(pathseg, search_type, namespace, session, import_info) {
         out.push(m);
-        if let ExactMatch = search_type {
+        if is_exact_match {
             return out;
         }
     }
-    // filesearch. Used to complete e.g. extern crate blah or mod foo
+    // filesearch. Used to complete e.g. mod foo
     if let StartsWith = search_type {
         for m in do_file_search(searchstr, filepath.parent().unwrap(), session) {
             out.push(m);
@@ -1702,7 +1716,7 @@ fn get_impled_items(
     }
     // for return type inference
     if let Some(gen) = context.to_generics() {
-        for mut m in &mut out {
+        for m in &mut out {
             if m.mtype == MatchType::Function {
                 m.mtype = MatchType::Method(Some(Box::new(gen.to_owned())));
             }
@@ -1767,7 +1781,7 @@ pub fn resolve_path(
         )
     } else if len != 0 {
         let mut parent_path = path.clone();
-        parent_path.segments.pop();
+        let last_seg = parent_path.segments.pop().unwrap();
         let context = resolve_path(
             &parent_path,
             filepath,
@@ -1779,10 +1793,14 @@ pub fn resolve_path(
         )
         .into_iter()
         .nth(0);
+        debug!(
+            "[resolve_path] context: {:?}, last_seg: {:?}",
+            context, last_seg
+        );
         if let Some(followed_match) = context {
             resolve_following_path(
                 followed_match,
-                &path.segments[len - 1],
+                &last_seg,
                 namespace,
                 search_type,
                 import_info,
@@ -1807,7 +1825,7 @@ fn resolve_following_path(
     session: &Session,
 ) -> Vec<Match> {
     match followed_match.mtype {
-        MatchType::Module => {
+        MatchType::Module | MatchType::Crate => {
             let mut searchstr: &str = &following_seg.name;
             if let Some(i) = searchstr.rfind(',') {
                 searchstr = searchstr[i + 1..].trim();
@@ -1838,14 +1856,23 @@ fn resolve_following_path(
             session,
             import_info,
         ),
-        MatchType::Trait => collect_trait_methods(
-            &followed_match,
-            search_type,
+        MatchType::Trait => search_for_trait_items(
+            followed_match,
             &following_seg.name,
+            search_type,
+            true,
             true,
             session,
         )
         .collect(),
+        MatchType::TypeParameter(bounds) => bounds
+            .get_traits(session)
+            .into_iter()
+            .map(|m| {
+                search_for_trait_items(m, &following_seg.name, search_type, true, true, session)
+            })
+            .flatten()
+            .collect(),
         MatchType::Type => {
             if let Some(match_) = typeinf::get_type_of_typedef(&followed_match, session) {
                 get_impled_items(following_seg, search_type, &match_, session, import_info)
@@ -1921,7 +1948,9 @@ pub fn resolve_method(
                             searchstr,
                             &m.filepath,
                             true,
+                            false,
                             search_type,
+                            session,
                         ) {
                             out.push(m);
                         }
@@ -2108,26 +2137,6 @@ pub fn search_for_field_or_method(
 ) -> Vec<Match> {
     let m = context;
     let mut out = Vec::new();
-    // for Trait and TypeParameter
-    let find_inherited_traits = |m: Match| {
-        let traits = collect_inherited_traits(m, session);
-        traits
-            .into_iter()
-            .filter_map(|tr| {
-                let src = session.load_source_file(&tr.filepath);
-                src[tr.point.0..].find('{').map(|start| {
-                    search_scope_for_methods(
-                        tr.point + BytePos(start + 1),
-                        src.as_src(),
-                        searchstr,
-                        &tr.filepath,
-                        false,
-                        search_type,
-                    )
-                })
-            })
-            .flatten()
-    };
     match m.mtype {
         MatchType::Struct(_) => {
             debug!(
@@ -2182,13 +2191,13 @@ pub fn search_for_field_or_method(
         }
         MatchType::Trait => {
             debug!("got a trait, looking for methods {}", m.matchstr);
-            out.extend(find_inherited_traits(m));
+            out.extend(search_for_trait_methods(m, searchstr, search_type, session))
         }
         MatchType::TypeParameter(bounds) => {
             debug!("got a trait bound, looking for methods {}", m.matchstr);
             let traits = bounds.get_traits(session);
             traits.into_iter().for_each(|m| {
-                out.extend(find_inherited_traits(m));
+                out.extend(search_for_trait_methods(m, searchstr, search_type, session))
             });
         }
         _ => {
@@ -2201,6 +2210,46 @@ pub fn search_for_field_or_method(
     out
 }
 
+#[inline(always)]
+fn search_for_trait_methods<'s, 'sess: 's>(
+    traitm: Match,
+    search_str: &'s str,
+    search_type: SearchType,
+    session: &'sess Session<'sess>,
+) -> impl 's + Iterator<Item = Match> {
+    search_for_trait_items(traitm, search_str, search_type, false, false, session)
+}
+
+// search trait items by search_str
+fn search_for_trait_items<'s, 'sess: 's>(
+    traitm: Match,
+    search_str: &'s str,
+    search_type: SearchType,
+    includes_assoc_fn: bool,
+    includes_assoc_ty_and_const: bool,
+    session: &'sess Session<'sess>,
+) -> impl 's + Iterator<Item = Match> {
+    let traits = collect_inherited_traits(traitm, session);
+    traits
+        .into_iter()
+        .filter_map(move |tr| {
+            let src = session.load_source_file(&tr.filepath);
+            src[tr.point.0..].find('{').map(|start| {
+                search_scope_for_methods(
+                    tr.point + BytePos(start + 1),
+                    src.as_src(),
+                    search_str,
+                    &tr.filepath,
+                    includes_assoc_fn,
+                    includes_assoc_ty_and_const,
+                    search_type,
+                    session,
+                )
+            })
+        })
+        .flatten()
+}
+
 fn search_for_deref_matches(
     target_ty: Ty,      // target = ~
     type_match: &Match, // the type which implements Deref
@@ -2477,9 +2526,18 @@ pub(crate) fn get_index_output(selfm: &Match, session: &Session) -> Option<Ty> {
     )
     .into_iter()
     .next()?;
+    get_associated_type_match(&index_header, "Output", selfm, session)
+}
+
+pub(crate) fn get_associated_type_match(
+    impl_header: &ImplHeader,
+    type_name: &str,
+    context: &Match,
+    session: &Session,
+) -> Option<Ty> {
     let output = search_scope_for_impled_assoc_types(
-        &index_header,
-        "Output",
+        impl_header,
+        type_name,
         core::SearchType::ExactMatch,
         session,
     );
@@ -2488,8 +2546,105 @@ pub(crate) fn get_index_output(selfm: &Match, session: &Session) -> Option<Ty> {
         .next()
         .and_then(|(_, item_ty)| match item_ty {
             Ty::PathSearch(paths) => {
-                get_assoc_type_from_header(&paths.path, selfm, &index_header, session)
+                get_assoc_type_from_header(&paths.path, context, impl_header, session)
             }
             _ => Some(item_ty),
         })
 }
+
+pub(crate) fn get_struct_fields(
+    path: &RacerPath,
+    search_str: &str,
+    filepath: &Path,
+    complete_pos: BytePos,
+    stype: SearchType,
+    session: &Session,
+) -> Vec<Match> {
+    resolve_path(
+        &path,
+        filepath,
+        complete_pos,
+        SearchType::ExactMatch,
+        Namespace::HasField,
+        session,
+        &ImportInfo::default(),
+    )
+    .into_iter()
+    .next()
+    .map_or_else(
+        || Vec::new(),
+        |m| match m.mtype {
+            MatchType::Struct(_) | MatchType::EnumVariant(_) => {
+                search_struct_fields(search_str, &m, stype, session)
+            }
+            MatchType::Type => {
+                let m = try_vec!(typeinf::get_type_of_typedef(&m, session));
+                search_struct_fields(search_str, &m, stype, session)
+            }
+            MatchType::UseAlias(m) => search_struct_fields(search_str, &*m, stype, session),
+            _ => Vec::new(),
+        },
+    )
+}
+
+/// Checks if trait_impl is the impl TraitName<OtherType>
+fn has_impl_for_other_type(
+    trait_impl: &ImplHeader,
+    trait_name: &str,
+    other_type: Option<&str>,
+) -> bool {
+    if let Some(ref path) = trait_impl.trait_path() {
+        if path.name() == Some(trait_name) {
+            if other_type.is_none() && path.segments[0].generics.len() == 0 {
+                return true;
+            }
+            if let Some(ty) = path.segments[0].generics.get(0) {
+                return match ty.to_owned().dereference() {
+                    // TODO: Handle generics arguments
+                    Ty::PathSearch(ref g) => other_type == g.path.name(),
+                    _ => false,
+                };
+            }
+            // default is self
+            return trait_impl.self_path().name() == other_type;
+        }
+    }
+    false
+}
+
+/// Resolves the type of a binary expression
+/// # Arguments
+/// * base_type: the type on the left hand side
+/// * node: the operator
+/// * other_type: the type on the right hand side
+pub(crate) fn resolve_binary_expr_type(
+    base_type: &Match,
+    node: BinOpKind,
+    other_type: Option<&str>,
+    session: &Session,
+) -> Option<Ty> {
+    let trait_name = typeinf::get_operator_trait(node);
+    if trait_name == "bool" {
+        return PrimKind::Bool.to_module_match().map(Ty::Match);
+    }
+
+    let matching_impl = search_trait_impls(
+        base_type.point,
+        &base_type.matchstr,
+        &[trait_name],
+        false,
+        &base_type.filepath,
+        base_type.local,
+        session,
+    )
+    .into_iter()
+    .filter(|trait_impl| has_impl_for_other_type(trait_impl, trait_name, other_type))
+    .next();
+    if let Some(matching_impl) = matching_impl {
+        get_associated_type_match(&matching_impl, "Output", &base_type, session)
+            .or_else(|| Some(Ty::Match(base_type.clone())))
+    } else {
+        // default to base type if an impl can't be found
+        Some(Ty::Match(base_type.clone()))
+    }
+}
diff --git a/src/racer/primitive.rs b/src/racer/primitive.rs
index 0aa4c46e..38645627 100644
--- a/src/racer/primitive.rs
+++ b/src/racer/primitive.rs
@@ -78,32 +78,6 @@ impl PrimKind {
             LitIntType::Unsuffixed => PrimKind::U32,
         }
     }
-    // Do not remove this method.
-    // See #963
-    fn is_int(self) -> bool {
-        match self {
-            PrimKind::I8
-            | PrimKind::I16
-            | PrimKind::I32
-            | PrimKind::I64
-            | PrimKind::I128
-            | PrimKind::Isize => true,
-            _ => false,
-        }
-    }
-    // Do not remove this method.
-    // See #963
-    fn is_uint(self) -> bool {
-        match self {
-            PrimKind::U8
-            | PrimKind::U16
-            | PrimKind::U32
-            | PrimKind::U64
-            | PrimKind::U128
-            | PrimKind::Usize => true,
-            _ => false,
-        }
-    }
     fn impl_files(self) -> Option<&'static [&'static str]> {
         match self {
             PrimKind::Bool => None,
@@ -165,12 +139,7 @@ impl PrimKind {
     pub(crate) fn get_impl_files(&self) -> Option<Vec<PathBuf>> {
         let src_path = RUST_SRC_PATH.as_ref()?;
         let impls = self.impl_files()?;
-        Some(
-            impls
-                .iter()
-                .map(|file| src_path.join(file))
-                .collect(),
-        )
+        Some(impls.iter().map(|file| src_path.join(file)).collect())
     }
     pub fn to_module_match(self) -> Option<Match> {
         let _impl_files = self.impl_files()?;
diff --git a/src/racer/project_model.rs b/src/racer/project_model.rs
index 9376c267..2e1b5472 100644
--- a/src/racer/project_model.rs
+++ b/src/racer/project_model.rs
@@ -1,6 +1,18 @@
 use std::path::{Path, PathBuf};
 
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Edition {
+    Ed2015,
+    Ed2018,
+}
+
 pub trait ProjectModelProvider {
+    fn edition(&self, manifest: &Path) -> Option<Edition>;
     fn discover_project_manifest(&self, path: &Path) -> Option<PathBuf>;
+    fn search_dependencies(
+        &self,
+        manifest: &Path,
+        search_fn: Box<Fn(&str) -> bool>,
+    ) -> Vec<(String, PathBuf)>;
     fn resolve_dependency(&self, manifest: &Path, dep_name: &str) -> Option<PathBuf>;
 }
diff --git a/src/racer/scopes.rs b/src/racer/scopes.rs
index 2e69737b..f71b48b5 100644
--- a/src/racer/scopes.rs
+++ b/src/racer/scopes.rs
@@ -1,13 +1,12 @@
 use ast_types::Path as RacerPath;
 #[cfg(test)]
 use core::{self, Coordinate};
-use core::{BytePos, ByteRange, CompletionType, RangedRawSrc, Src};
-use {ast, typeinf, util};
+use core::{BytePos, ByteRange, CompletionType, Namespace, RangedRawSrc, Src};
 
 use std::iter::Iterator;
 use std::path::{Path, PathBuf};
 use std::str::from_utf8;
-use util::*;
+use util::{self, char_at};
 
 fn find_close<'a, A>(iter: A, open: u8, close: u8, level_end: u32) -> Option<BytePos>
 where
@@ -67,19 +66,11 @@ pub fn find_closure_scope_start(
 ) -> Option<BytePos> {
     let closing_paren_pos = find_closing_paren(&src[..], point - parentheses_open_pos);
     let src_between_parent = &src[..closing_paren_pos.0];
-    closure_valid_arg_scope(src_between_parent).map(|_| parentheses_open_pos)
+    util::closure_valid_arg_scope(src_between_parent).map(|_| parentheses_open_pos)
 }
 
 pub fn scope_start(src: Src, point: BytePos) -> BytePos {
     let src = src.change_length(point);
-    let ret_curly = |pos: BytePos| -> BytePos {
-        if pos > BytePos::ZERO && src[..pos.0].ends_with("::{") {
-            if let Some(pos) = src[..pos.0].rfind("use") {
-                return scope_start(src, pos.into());
-            }
-        }
-        pos
-    };
     let (mut clev, mut plev) = (0u32, 0u32);
     let mut iter = src[..].as_bytes().into_iter().enumerate().rev();
     for (pos, b) in &mut iter {
@@ -87,7 +78,7 @@ pub fn scope_start(src: Src, point: BytePos) -> BytePos {
             b'{' => {
                 // !!! found { earlier than (
                 if clev == 0 {
-                    return ret_curly(BytePos(pos).increment());
+                    return BytePos(pos).increment();
                 }
                 clev -= 1;
             }
@@ -110,12 +101,15 @@ pub fn scope_start(src: Src, point: BytePos) -> BytePos {
         }
     }
     // fallback: return curly_parent_open_pos
-    let pos = find_close_with_pos(iter, b'}', b'{', 0).unwrap_or(BytePos::ZERO);
-    ret_curly(pos)
+    find_close_with_pos(iter, b'}', b'{', 0).unwrap_or(BytePos::ZERO)
 }
 
 pub fn find_stmt_start(msrc: Src, point: BytePos) -> Option<BytePos> {
     let scope_start = scope_start(msrc, point);
+    find_stmt_start_given_scope(msrc, point, scope_start)
+}
+
+fn find_stmt_start_given_scope(msrc: Src, point: BytePos, scope_start: BytePos) -> Option<BytePos> {
     // Iterate the scope to find the start of the statement that surrounds the point.
     debug!(
         "[find_stmt_start] now we are in scope {:?} ~ {:?}",
@@ -133,40 +127,6 @@ pub fn expect_stmt_start(msrc: Src, point: BytePos) -> BytePos {
     find_stmt_start(msrc, point).expect("Statement does not have a beginning")
 }
 
-/// Finds the start of a `let` statement; includes handling of struct pattern matches in the
-/// statement.
-pub fn find_let_start(msrc: Src, point: BytePos) -> Option<BytePos> {
-    let mut scopestart = scope_start(msrc, point);
-    let mut let_start = None;
-
-    // To avoid infinite loops, we cap the number of times we'll
-    // expand the search in an attempt to find statements.
-    // TODO: it's too hacky, and, 1..6 is appropriate?
-    for step in 1..6 {
-        let_start = msrc
-            .shift_start(scopestart)
-            .iter_stmts()
-            .find(|&range| scopestart + range.end > point);
-
-        if let Some(ref range) = let_start {
-            // Check if we've actually reached the start of the "let" stmt.
-            let stmt = &msrc.src.code[range.shift(scopestart).to_range()];
-            if stmt.starts_with("let") {
-                break;
-            }
-        }
-
-        debug!(
-            "find_let_start failed to find start on attempt {}: Restarting search from {:?}",
-            step,
-            scopestart.decrement(),
-        );
-        scopestart = scope_start(msrc, scopestart.decrement());
-    }
-
-    let_start.map(|range| scopestart + range.start)
-}
-
 pub fn get_local_module_path(msrc: Src, point: BytePos) -> Vec<String> {
     let mut v = Vec::new();
     get_local_module_path_(msrc, point, &mut v);
@@ -177,17 +137,18 @@ fn get_local_module_path_(msrc: Src, point: BytePos, out: &mut Vec<String>) {
     for range in msrc.iter_stmts() {
         if range.contains_exclusive(point) {
             let blob = msrc.shift_range(range);
-            if blob.starts_with("pub mod ") || blob.starts_with("mod ") {
-                let p = typeinf::generate_skeleton_for_parsing(&blob);
-                if let Some(name) = ast::parse_mod(p).name {
-                    out.push(name);
-                    let newstart = blob.find('{').unwrap() + 1;
-                    get_local_module_path_(
-                        blob.shift_start(newstart.into()),
-                        point - range.start - newstart.into(),
-                        out,
-                    );
-                }
+            let start = util::strip_visibility(&blob).unwrap_or(BytePos::ZERO);
+            if !blob[start.0..].starts_with("mod") {
+                continue;
+            }
+            if let Some(newstart) = blob[start.0 + 3..].find('{') {
+                let newstart = newstart + start.0 + 4;
+                out.push(blob[start.0 + 3..newstart - 1].trim().to_owned());
+                get_local_module_path_(
+                    blob.shift_start(newstart.into()),
+                    point - range.start - newstart.into(),
+                    out,
+                );
             }
         }
     }
@@ -233,14 +194,10 @@ pub fn find_impl_start(msrc: Src, point: BytePos, scopestart: BytePos) -> Option
         .find(|range| range.end > len)
         .and_then(|range| {
             let blob = msrc.shift_start(scopestart + range.start);
-            // TODO:: the following is a bit weak at matching traits. make this better
-            if blob.starts_with("impl")
-                || blob.starts_with("trait")
-                || blob.starts_with("pub trait")
-            {
+            if blob.starts_with("impl") || util::trim_visibility(&blob[..]).starts_with("trait") {
                 Some(scopestart + range.start)
             } else {
-                let newstart = blob.find('{').expect("[find_impl_start] { was not found.") + 1;
+                let newstart = blob.find('{')? + 1;
                 find_impl_start(msrc, point, scopestart + range.start + newstart.into())
             }
         })
@@ -283,16 +240,6 @@ pub fn split_into_context_and_completion(s: &str) -> (&str, &str, CompletionType
     }
 }
 
-pub fn get_line(src: &str, point: BytePos) -> BytePos {
-    src.as_bytes()[..point.0]
-        .iter()
-        .enumerate()
-        .rev()
-        .find(|(_, byte)| **byte == b'\n')
-        .map(|t| t.0.into())
-        .unwrap_or(BytePos::ZERO)
-}
-
 /// search in reverse for the start of the current expression
 /// allow . and :: to be surrounded by white chars to enable multi line call chains
 pub fn get_start_of_search_expr(src: &str, point: BytePos) -> BytePos {
@@ -641,8 +588,18 @@ struct foo {
 /// get start of path from use statements
 /// e.g. get Some(16) from "pub(crate)  use a"
 pub(crate) fn use_stmt_start(line_str: &str) -> Option<BytePos> {
-    let use_start = strip_visibility(line_str).unwrap_or(BytePos::ZERO);
-    strip_word(&line_str[use_start.0..], "use").map(|b| b + use_start)
+    let use_start = util::strip_visibility(line_str).unwrap_or(BytePos::ZERO);
+    util::strip_word(&line_str[use_start.0..], "use").map(|b| b + use_start)
+}
+
+pub(crate) fn is_extern_crate(line_str: &str) -> bool {
+    let extern_start = util::strip_visibility(line_str).unwrap_or(BytePos::ZERO);
+    if let Some(crate_start) = util::strip_word(&line_str[extern_start.0..], "extern") {
+        let crate_str = &line_str[(extern_start + crate_start).0..];
+        crate_str.starts_with("crate ")
+    } else {
+        false
+    }
 }
 
 #[test]
@@ -650,6 +607,13 @@ fn test_use_stmt_start() {
     assert_eq!(use_stmt_start("pub(crate)   use   some::").unwrap().0, 19);
 }
 
+#[test]
+fn test_is_extern_crate() {
+    assert!(is_extern_crate("extern crate "));
+    assert!(is_extern_crate("pub extern crate abc"));
+    assert!(!is_extern_crate("pub extern crat"));
+}
+
 #[inline(always)]
 fn next_use_item(expr: &str) -> Option<usize> {
     let bytes = expr.as_bytes();
@@ -680,7 +644,7 @@ pub(crate) fn construct_path_from_use_tree(expr: &str) -> RacerPath {
     let mut ident_end = Some(i - 1);
     while i > 0 {
         i -= 1;
-        if is_ident_char(bytes[i] as char) {
+        if util::is_ident_char(bytes[i] as char) {
             if ident_end.is_none() {
                 ident_end = Some(i)
             }
@@ -736,21 +700,174 @@ fn test_construct_path_from_use_tree() {
     assert_eq!(get_path_idents("{Str1, module::Str2, Str3"), vec!["Str3"],);
 }
 
-/// get current line string parsed by ;
-pub(crate) fn get_current_line<'c>(src: Src<'c>, pos: BytePos) -> String {
-    // reparse the string, searchstr is not corrected parsed with split_into_context_and_completion
-    // it will stop by character like '{', and ' ', which occurs in the following case
-    // 1. The line is use contextstr::{A, B, C, searchstr
-    // 2. The line started with contextstr or ::
-    // 3. FIXME(may not correct): Neither above case, then expr parsed above is corrected
-    let linestart = find_stmt_start(src, pos).unwrap_or_else(|| get_line(&src[..], pos));
-
-    // step 1, get full line, take the rightmost part split by semicolon
-    //   prevent the case that someone write multiple line in one line
-    (&src[linestart.0..pos.0])
-        .trim()
-        .rsplit(';')
-        .nth(0)
-        .unwrap()
-        .to_owned()
+/// get current statement for completion context
+pub(crate) fn get_current_stmt<'c>(src: Src<'c>, pos: BytePos) -> (BytePos, String) {
+    let mut scopestart = scope_start(src, pos);
+    // for use statement
+    while scopestart > BytePos::ZERO && src[..scopestart.0].ends_with("::{") {
+        if let Some(pos) = src[..pos.0].rfind("use") {
+            scopestart = scope_start(src, pos.into());
+        }
+    }
+    let linestart = find_stmt_start_given_scope(src, pos, scopestart).unwrap_or(scopestart);
+    (
+        linestart,
+        (&src[linestart.0..pos.0])
+            .trim()
+            .rsplit(';')
+            .next()
+            .unwrap()
+            .to_owned(),
+    )
+}
+
+pub(crate) fn expr_to_path(expr: &str) -> (RacerPath, Namespace) {
+    let is_global = expr.starts_with("::");
+    let v: Vec<_> = (if is_global { &expr[2..] } else { expr })
+        .split("::")
+        .collect();
+    let path = RacerPath::from_vec(is_global, v);
+    let namespace = if path.len() == 1 {
+        Namespace::Global | Namespace::Path
+    } else {
+        Namespace::Path
+    };
+    (path, namespace)
+}
+
+pub(crate) fn is_in_struct_ctor(src: Src, stmt_start: BytePos, pos: BytePos) -> Option<ByteRange> {
+    const ALLOW_SYMBOL: [u8; 5] = [b'{', b'(', b'|', b';', b','];
+    const ALLOW_KEYWORDS: [&'static str; 3] = ["let", "mut", "ref"];
+    if stmt_start.0 <= 3 || src.as_bytes()[stmt_start.0 - 1] != b'{' || pos <= stmt_start {
+        return None;
+    }
+    {
+        for &b in src[stmt_start.0..pos.0].as_bytes().iter().rev() {
+            match b {
+                b',' => break,
+                b':' => return None,
+                _ => continue,
+            }
+        }
+    }
+    let src = &src[..stmt_start.0 - 1];
+    #[derive(Clone, Copy, Debug)]
+    enum State {
+        Initial,
+        Name(usize),
+        End,
+    }
+    let mut state = State::Initial;
+    let mut result = None;
+    let bytes = src.as_bytes();
+    for (i, b) in bytes.iter().enumerate().rev() {
+        match (state, *b) {
+            (State::Initial, b) if util::is_whitespace_byte(b) => continue,
+            (State::Initial, b) if util::is_ident_char(b.into()) => state = State::Name(i),
+            (State::Initial, _) => return None,
+            (State::Name(_), b) if b == b':' || util::is_ident_char(b.into()) => continue,
+            (State::Name(end), b) if util::is_whitespace_byte(b) => {
+                result = Some(ByteRange::new(i + 1, end + 1));
+                state = State::End;
+            }
+            (State::Name(end), b) if ALLOW_SYMBOL.contains(&b) => {
+                result = Some(ByteRange::new(i + 1, end + 1));
+                break;
+            }
+            (State::End, b) if util::is_ident_char(b.into()) => {
+                let bytes = &bytes[..=i];
+                if !ALLOW_KEYWORDS.iter().any(|s| bytes.ends_with(s.as_bytes())) {
+                    return None;
+                } else {
+                    break;
+                }
+            }
+            (State::End, b) if util::is_whitespace_byte(b) => continue,
+            (State::End, b) if ALLOW_SYMBOL.contains(&b) => break,
+            (_, _) => return None,
+        }
+    }
+    match state {
+        State::Initial => None,
+        State::Name(end) => Some(ByteRange::new(0, end + 1)),
+        State::End => result,
+    }
+}
+
+#[cfg(test)]
+mod ctor_test {
+    use super::{is_in_struct_ctor, scope_start};
+    use crate::core::{ByteRange, MaskedSource};
+    fn check(src: &str) -> Option<ByteRange> {
+        let source = MaskedSource::new(src);
+        let point = src.find("~").unwrap();
+        let scope_start = scope_start(source.as_src(), point.into());
+        is_in_struct_ctor(source.as_src(), scope_start, point.into())
+    }
+    #[test]
+    fn first_line() {
+        let src = "
+    struct UserData {
+        name: String,
+        id: usize,
+    }
+    fn main() {
+        UserData {
+            na~
+        }
+    }";
+        assert!(check(src).is_some())
+    }
+    #[test]
+    fn second_line() {
+        let src = r#"
+    fn main() {
+        UserData {
+            name: "ahkj".to_owned(), 
+            i~d:
+        }
+    }"#;
+        assert!(check(src).is_some())
+    }
+    #[test]
+    fn tuple() {
+        let src = r#"
+    fn main() {
+        let (a,
+            UserData {
+                name: "ahkj".to_owned(),
+                i~d:
+            }
+        ) = f();
+    }"#;
+        assert!(check(src).is_some())
+    }
+    #[test]
+    fn expr_pos() {
+        let src = r#"
+    fn main() {
+        UserData {
+            name: ~
+        }
+    }"#;
+        assert!(check(src).is_none())
+    }
+    #[test]
+    fn fnarg() {
+        let src = r#"
+        func(UserData {
+            name~
+        })
+    "#;
+        assert!(check(src).is_some())
+    }
+    #[test]
+    fn closure() {
+        let src = r#"
+        let f = || UserData {
+            name~
+        };
+    "#;
+        assert!(check(src).is_some())
+    }
 }
diff --git a/src/racer/typeinf.rs b/src/racer/typeinf.rs
index 731bccad..b88a49be 100644
--- a/src/racer/typeinf.rs
+++ b/src/racer/typeinf.rs
@@ -1,9 +1,12 @@
 //! Type inference
 //! THIS MODULE IS ENTIRELY TOO UGLY SO REALLY NEADS REFACTORING(kngwyu)
 use ast;
+use syntax::ast::BinOpKind;
 use ast_types::{Pat, Ty};
 use core;
-use core::{BytePos, Match, MatchType, Namespace, Scope, SearchType, Session, SessionExt, Src};
+use core::{
+    BytePos, ByteRange, Match, MatchType, Namespace, Scope, SearchType, Session, SessionExt, Src,
+};
 use matchers;
 use nameres;
 use primitive::PrimKind;
@@ -11,20 +14,30 @@ use scopes;
 use std::path::Path;
 use util::{self, txt_matches};
 
-// TODO: Option
-fn find_start_of_function_body(src: &str) -> BytePos {
-    // TODO: this should ignore anything inside parens so as to skip the arg list
-    src.find('{')
-        .map(|u| BytePos::from(u))
-        .expect("Function body should have a beginning")
-}
-
 // Removes the body of the statement (anything in the braces {...}), leaving just
 // the header
-// TODO: this should skip parens (e.g. function arguments)
-pub fn generate_skeleton_for_parsing(src: &str) -> String {
-    let n = find_start_of_function_body(src);
-    src[..n.0 + 1].to_owned() + "}"
+pub fn generate_skeleton_for_parsing(src: &str) -> Option<String> {
+    src.find('{').map(|n| src[..=n].to_owned() + "}")
+}
+
+/// Get the trait name implementing which overrides the operator `op`
+/// For comparison operators, it is `bool`
+pub(crate) fn get_operator_trait(op: BinOpKind) -> &'static str {
+    match op {
+        BinOpKind::Add => "Add",
+        BinOpKind::Sub => "Sub",
+        BinOpKind::Mul => "Mul",
+        BinOpKind::Div => "Div",
+        BinOpKind::Rem => "Rem",
+        BinOpKind::And => "And",
+        BinOpKind::Or => "Or",
+        BinOpKind::BitXor => "BitXor",
+        BinOpKind::BitAnd => "BitAnd",
+        BinOpKind::BitOr => "BitOr",
+        BinOpKind::Shl => "Shl",
+        BinOpKind::Shr => "Shr",
+        _ => "bool",
+    }
 }
 
 // TODO(kngwyu): use libsyntax parser
@@ -85,7 +98,7 @@ pub fn first_param_is_self(blob: &str) -> bool {
 #[test]
 fn generates_skeleton_for_mod() {
     let src = "mod foo { blah }";
-    let out = generate_skeleton_for_parsing(src);
+    let out = generate_skeleton_for_parsing(src).unwrap();
     assert_eq!("mod foo {}", out);
 }
 
@@ -103,9 +116,8 @@ pub fn get_type_of_self(
     session: &Session,
 ) -> Option<Ty> {
     let start = scopes::find_impl_start(msrc, point, BytePos::ZERO)?;
-    let decl = generate_skeleton_for_parsing(&msrc.shift_start(start));
+    let decl = generate_skeleton_for_parsing(&msrc.shift_start(start))?;
     debug!("get_type_of_self_arg impl skeleton |{}|", decl);
-
     if decl.starts_with("impl") {
         // we have to do 2 operations around generics here
         // 1. Checks if self's type is T
@@ -175,24 +187,25 @@ fn get_type_of_fnarg(m: Match, session: &Session) -> Option<Ty> {
     resolve_lvalue_ty(pat, ty, &matchstr, &filepath, point, session)
 }
 
-fn get_type_of_let_expr(m: &Match, msrc: Src, session: &Session) -> Option<Ty> {
-    // ASSUMPTION: this is being called on a let decl
-    let point = scopes::find_let_start(msrc, m.point).expect("`let` should have a beginning");
-    let src = msrc.shift_start(point);
-
-    if let Some(range) = src.iter_stmts().next() {
-        let blob = &src[range.to_range()];
-        debug!("get_type_of_let_expr calling parse_let |{}|", blob);
-
-        let pos = m.point - point - range.start;
-        let scope = Scope {
-            filepath: m.filepath.clone(),
-            point,
-        };
-        ast::get_let_type(blob.to_owned(), pos, scope, session)
-    } else {
-        None
-    }
+fn get_type_of_let_expr(m: Match, session: &Session) -> Option<Ty> {
+    let Match {
+        mtype,
+        contextstr,
+        filepath,
+        point,
+        ..
+    } = m;
+    let let_start = match mtype {
+        MatchType::Let(s) => s,
+        _ => return None,
+    };
+    debug!("get_type_of_let_expr calling parse_let |{}|", contextstr);
+    let pos = point - let_start;
+    let scope = Scope {
+        filepath,
+        point: let_start,
+    };
+    ast::get_let_type(contextstr, pos, scope, session)
 }
 
 /// Decide l_value's type given r_value and ident query
@@ -375,7 +388,7 @@ pub fn get_struct_field_type(
 pub(crate) fn get_tuplestruct_fields(
     structmatch: &Match,
     session: &Session,
-) -> Vec<(String, BytePos, Option<Ty>)> {
+) -> Vec<(String, ByteRange, Option<Ty>)> {
     let src = session.load_source_file(&structmatch.filepath);
     let structsrc = if let core::MatchType::EnumVariant(_) = structmatch.mtype {
         // decorate the enum variant src to make it look like a tuple struct
@@ -423,7 +436,7 @@ pub fn get_type_of_match(m: Match, msrc: Src, session: &Session) -> Option<Ty> {
     debug!("get_type_of match {:?} ", m);
 
     match m.mtype {
-        core::MatchType::Let => get_type_of_let_expr(&m, msrc, session),
+        core::MatchType::Let(_) => get_type_of_let_expr(m, session),
         core::MatchType::IfLet(start) | core::MatchType::WhileLet(start) => {
             get_type_of_if_let(&m, session, start)
         }
@@ -507,10 +520,13 @@ pub fn get_return_type_of_function(
     let src = session.load_source_file(&fnmatch.filepath);
     let point = scopes::expect_stmt_start(src.as_src(), fnmatch.point);
     let out = src[point.0..].find('{').and_then(|n| {
-        // wrap in "impl blah { }" so that methods get parsed correctly too
-        let decl = "impl blah {".to_string() + &src[point.0..point.0 + n + 1] + "}}";
+        // wrap in "impl b { }" so that methods get parsed correctly too
+        let decl = "impl b{".to_string() + &src[point.0..point.0 + n + 1] + "}}";
         debug!("get_return_type_of_function: passing in |{}|", decl);
-        ast::parse_fn_output(decl, Scope::from_match(fnmatch))
+        let mut scope = Scope::from_match(fnmatch);
+        // TODO(kngwyu): if point <= 5 scope is incorrect
+        scope.point = point.checked_sub("impl b{".len()).unwrap_or(BytePos::ZERO);
+        ast::parse_fn_output(decl, scope)
     });
     // Convert output arg of type Self to the correct type
     if let Some(Ty::PathSearch(ref paths)) = out {
diff --git a/src/racer/util.rs b/src/racer/util.rs
index f61d83e4..48ae4103 100644
--- a/src/racer/util.rs
+++ b/src/racer/util.rs
@@ -81,13 +81,14 @@ pub fn symbol_matches(stype: SearchType, searchstr: &str, candidate: &str) -> bo
 
 pub fn find_closure(src: &str) -> Option<(ByteRange, ByteRange)> {
     let (pipe_range, _) = closure_valid_arg_scope(src)?;
-    let mut chars = src.chars()
-                       .enumerate()
-                       .skip(pipe_range.end.0)
-                       .skip_while(|(_, c)| {
-                           c.is_whitespace()
-                       });
-    let (start, start_char) = chars.next().map(|(i, c)| (if c == '{' { i + 1 } else { i }, c))?;
+    let mut chars = src
+        .chars()
+        .enumerate()
+        .skip(pipe_range.end.0)
+        .skip_while(|(_, c)| c.is_whitespace());
+    let (start, start_char) = chars
+        .next()
+        .map(|(i, c)| (if c == '{' { i + 1 } else { i }, c))?;
 
     let mut clevel = if start_char == '{' { 1 } else { 0 };
     let mut plevel = 0;
@@ -103,13 +104,13 @@ pub fn find_closure(src: &str) -> Option<(ByteRange, ByteRange)> {
                     last = Some(i);
                     break;
                 }
-            },
+            }
             ';' => {
                 if start_char != '{' {
                     last = Some(i);
                     break;
                 }
-            },
+            }
             ')' => {
                 plevel -= 1;
                 if plevel == 0 {
@@ -119,7 +120,7 @@ pub fn find_closure(src: &str) -> Option<(ByteRange, ByteRange)> {
                     last = Some(i + 1);
                     break;
                 }
-            },
+            }
             _ => {}
         }
     }
@@ -143,19 +144,49 @@ fn test_find_closure() {
     let src9 = "let p = |z| something() + 5;";
     let get_range = |a, b| ByteRange::new(BytePos(a as usize), BytePos(b as usize));
     let find = |src: &str, a, off1: i32, b, off2: i32| {
-        get_range(src.find(a).unwrap() as i32 + off1, src.rfind(b).unwrap() as i32 + 1 + off2)
+        get_range(
+            src.find(a).unwrap() as i32 + off1,
+            src.rfind(b).unwrap() as i32 + 1 + off2,
+        )
     };
     let get_pipe = |src| find(src, '|', 0, '|', 0);
 
-    assert_eq!(Some((get_pipe(src), find(src, 's', 0, ')', 0))), find_closure(src));
-    assert_eq!(Some((get_pipe(src2), find(src2, '{', 1, '}', -1))), find_closure(src2));
-    assert_eq!(Some((get_pipe(src3), find(src3, 's', 0, ')', 0))), find_closure(src3));
-    assert_eq!(Some((get_pipe(src4), find(src4, 's', 0, ')', 0))), find_closure(src4));
-    assert_eq!(Some((find(src5, '|', 0, 'y', -2), find(src5, 'y', 0, ')', 0))), find_closure(src5));
-    assert_eq!(Some((get_pipe(src6), find(src6, 'S', 0, ';', -1))), find_closure(src6));
-    assert_eq!(Some((find(src7, '|', 0, 'y', -2), find(src7, '2', 4, ')', 0))), find_closure(src7));
-    assert_eq!(Some((get_pipe(src8), find(src8, ' ', 1, ')', 0))), find_closure(src8));
-    assert_eq!(Some((get_pipe(src9), find(src9, 's', 0, '5', 0))), find_closure(src9));
+    assert_eq!(
+        Some((get_pipe(src), find(src, 's', 0, ')', 0))),
+        find_closure(src)
+    );
+    assert_eq!(
+        Some((get_pipe(src2), find(src2, '{', 1, '}', -1))),
+        find_closure(src2)
+    );
+    assert_eq!(
+        Some((get_pipe(src3), find(src3, 's', 0, ')', 0))),
+        find_closure(src3)
+    );
+    assert_eq!(
+        Some((get_pipe(src4), find(src4, 's', 0, ')', 0))),
+        find_closure(src4)
+    );
+    assert_eq!(
+        Some((find(src5, '|', 0, 'y', -2), find(src5, 'y', 0, ')', 0))),
+        find_closure(src5)
+    );
+    assert_eq!(
+        Some((get_pipe(src6), find(src6, 'S', 0, ';', -1))),
+        find_closure(src6)
+    );
+    assert_eq!(
+        Some((find(src7, '|', 0, 'y', -2), find(src7, '2', 4, ')', 0))),
+        find_closure(src7)
+    );
+    assert_eq!(
+        Some((get_pipe(src8), find(src8, ' ', 1, ')', 0))),
+        find_closure(src8)
+    );
+    assert_eq!(
+        Some((get_pipe(src9), find(src9, 's', 0, '5', 0))),
+        find_closure(src9)
+    );
 }
 
 /// Try to valid if the given scope contains a valid closure arg scope.
@@ -813,6 +844,16 @@ macro_rules! try_continue {
     };
 }
 
+#[macro_export]
+macro_rules! try_vec {
+    ($res: expr) => {
+        match ::std::ops::Try::into_result($res) {
+            Ok(o) => o,
+            Err(_) => return Vec::new(),
+        }
+    };
+}
+
 pub(crate) fn gen_tuple_fields(u: usize) -> impl Iterator<Item = &'static str> {
     const NUM: [&'static str; 16] = [
         "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15",


Test patch:
diff --git a/metadata/tests/from_json.rs b/metadata/tests/from_json.rs
index 340f4446..ae3c197d 100644
--- a/metadata/tests/from_json.rs
+++ b/metadata/tests/from_json.rs
@@ -14,11 +14,9 @@ fn full() {
     assert!(meta.resolve.is_some());
     let pkg_map = PackageMap::from_metadata(meta);
     let regex = pkg_map.ids().find(|id| id.name() == "regex").unwrap();
-    assert!(
-        pkg_map
-            .get_src_path_from_libname(pkg_map.id_to_idx(regex).unwrap(), "memchr")
-            .is_some()
-    );
+    assert!(pkg_map
+        .get_src_path_from_libname(pkg_map.id_to_idx(regex).unwrap(), "memchr")
+        .is_some());
 }
 
 #[test]
diff --git a/metadata/tests/self_test.rs b/metadata/tests/self_test.rs
index 2f4c5522..58d250e3 100644
--- a/metadata/tests/self_test.rs
+++ b/metadata/tests/self_test.rs
@@ -17,14 +17,10 @@ fn get_self_metadata() {
         .join("Cargo.toml");
     let racer = pkg_map.get_idx(&racer_manifest).unwrap();
     assert!(pkg_map.get_id(racer).name() == "racer");
-    assert!(
-        pkg_map
-            .get_src_path_from_libname(racer, "lazy_static")
-            .is_some()
-    );
-    assert!(
-        pkg_map
-            .get_src_path_from_libname(racer, "im-not-a-crate")
-            .is_none()
-    );
+    assert!(pkg_map
+        .get_src_path_from_libname(racer, "lazy_static")
+        .is_some());
+    assert!(pkg_map
+        .get_src_path_from_libname(racer, "im-not-a-crate")
+        .is_none());
 }
diff --git a/test_project/Cargo.lock b/test_project/Cargo.lock
index 62e6b5f0..dbf3e45d 100644
--- a/test_project/Cargo.lock
+++ b/test_project/Cargo.lock
@@ -129,6 +129,14 @@ dependencies = [
  "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "test-crate4"
+version = "0.1.0"
+dependencies = [
+ "rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "test-crate2 0.1.0",
+]
+
 [[package]]
 name = "test_fixtures"
 version = "0.1.0"
diff --git a/test_project/Cargo.toml b/test_project/Cargo.toml
index 15cef8e6..6f842e46 100644
--- a/test_project/Cargo.toml
+++ b/test_project/Cargo.toml
@@ -10,4 +10,4 @@ test_fixtures = { path = "./test_fixtures" }
 test-crate2 = { path = "./test-crate2"}
 
 [workspace]
-members = ["test-crate2", "test-crate3", "test_fixtures"]
\ No newline at end of file
+members = ["test-crate2", "test-crate3", "test_fixtures", "test-crate4"]
\ No newline at end of file
diff --git a/test_project/test-crate3/src/main.rs b/test_project/test-crate3/src/main.rs
index dbe62c73..e7a11a96 100644
--- a/test_project/test-crate3/src/main.rs
+++ b/test_project/test-crate3/src/main.rs
@@ -1,5 +1,3 @@
-extern crate rand;
-extern crate rayon;
 fn main() {
     println!("Hello, world!");
 }
diff --git a/test_project/test-crate4/Cargo.toml b/test_project/test-crate4/Cargo.toml
new file mode 100644
index 00000000..93b8faac
--- /dev/null
+++ b/test_project/test-crate4/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "test-crate4"
+version = "0.1.0"
+authors = ["kngwyu <yuji.kngw.80s.revive@gmail.com>"]
+edition = "2018"
+description = "Test crate for 2018 edition path"
+workspace = ".."
+
+[dependencies]
+rayon = "1.0"
+test-crate2 = { path = "../test-crate2" }
diff --git a/test_project/test-crate4/src/main.rs b/test_project/test-crate4/src/main.rs
new file mode 100644
index 00000000..e7a11a96
--- /dev/null
+++ b/test_project/test-crate4/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+    println!("Hello, world!");
+}
diff --git a/tests/binary.rs b/tests/binary.rs
new file mode 100644
index 00000000..b7170a38
--- /dev/null
+++ b/tests/binary.rs
@@ -0,0 +1,136 @@
+extern crate racer_testutils;
+use racer_testutils::*;
+
+#[test]
+fn completes_fields_for_binary_operators() {
+    let src = r"
+    use std::ops::Add;
+    struct Left {
+        x: i32,
+        y: i32,
+    }
+    impl Add for Left {
+        type Output = Left;
+        fn add(self, other: Left) -> Self::Output {
+            Left {
+                x: self.x + other.x,
+                y: self.y + other.y,
+            }
+        }
+    }
+    fn main() {
+        let a = Left { x: 1, y: 0 } + Left { x: 2, y: 3 };
+        a.~
+    }
+";
+    let got = get_all_completions(src, None);
+    assert!(got.iter().any(|ma| ma.matchstr == "x"));
+    assert!(got.iter().any(|ma| ma.matchstr == "y"));
+    assert!(got.iter().any(|ma| ma.matchstr == "add"));
+}
+
+#[test]
+fn completes_binary_operators_for_different_types() {
+    let src = r"
+    use std::ops::Sub;
+    struct Left {
+        x: i32,
+        y: i32,
+    }
+    struct Right {
+        x: i32,
+        y: i32
+    }
+    impl Sub<Right> for Left {
+        type Output = Left;
+        fn sub(self, other: Right) -> Self::Output {
+            Left {
+                x: self.x - other.x,
+                y: self.y - other.y,
+            }
+        }
+    }
+    fn main() {
+        let a = Left { x: 1, y: 0 } - Right { x: 2, y: 3 };
+        a.~
+    }
+";
+    let got = get_all_completions(src, None);
+    assert!(got.iter().any(|ma| ma.matchstr == "x"));
+    assert!(got.iter().any(|ma| ma.matchstr == "y"));
+    assert!(got.iter().any(|ma| ma.matchstr == "sub"));
+}
+
+#[test]
+fn test_operator_precedence_given_to_type_on_left() {
+    let src = r"
+    use std::ops::Sub;
+    struct Left {
+        x: i32,
+        y: i32,
+    }
+    struct Right {
+        x: i32,
+        y: i32
+    }
+    struct Foo;
+    impl Foo {
+        fn foo(&self) -> Option<i32>  {
+            Some(33)
+        }
+    }
+    struct Bar;
+    impl Bar {
+        fn bar(&self) -> Option<i32>  {
+            Some(33)
+        }
+    }
+    impl Sub<Right> for Left {
+        type Output = Foo;
+        fn sub(self, other: Right) -> Self::Output {
+             Foo
+        }
+    }
+    impl Sub<Left> for Right {
+        type Output = Bar;
+        fn sub(self, other: Right) -> Self::Output {
+             Bar
+        }
+    }
+    fn main() {
+        let a = Left { x: 1, y: 0 } - Right { x: 2, y: 3 };
+        a.~
+    }
+";
+    let got = get_one_completion(src, None);
+    assert_eq!(got.matchstr, "foo");
+}
+
+#[test]
+fn completes_if_operator_trait_is_not_explicit() {
+    let src = r"
+    use std::ops::Sub;
+    struct Left {
+        x: i32,
+        y: i32,
+    }
+    struct Bar;
+    impl Bar {
+        fn bar(&self) -> Option<i32>  {
+            Some(33)
+        }
+    }
+    impl Sub for Left {
+        type Output = Bar;
+        fn sub(self, other: Left) -> Self::Output {
+             Bar
+        }
+    }
+    fn main() {
+        let a = Left { x: 1, y: 0 } - Left { x: 2, y: 3 };
+        a.~
+    }
+";
+    let got = get_one_completion(src, None);
+    assert_eq!(got.matchstr, "bar");
+}
diff --git a/tests/external.rs b/tests/external.rs
new file mode 100644
index 00000000..ee87cf8c
--- /dev/null
+++ b/tests/external.rs
@@ -0,0 +1,205 @@
+extern crate racer;
+extern crate racer_testutils;
+
+use racer_testutils::*;
+
+#[test]
+fn completes_pub_fn_from_local_package() {
+    let src = "
+    extern crate fixtures;
+
+    use fixtures::foo;
+
+    fn main() {
+        let x = foo::~
+    }
+    ";
+
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_one_completion(src, Some(srcdir));
+        assert_eq!("test", got.matchstr);
+    })
+}
+
+#[test]
+fn completes_pub_fn_from_local_submodule_package() {
+    let src = "
+    extern crate fixtures;
+
+    use fixtures::bar;
+
+    fn main() {
+        let x = bar::~
+    }
+    ";
+
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_one_completion(src, Some(srcdir));
+        assert_eq!("bartest", got.matchstr);
+    })
+}
+
+#[test]
+fn follows_use_local_package() {
+    let src = "
+    extern crate fixtures;
+
+    use fixtures::~
+    ";
+
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_all_completions(src, Some(srcdir));
+        assert!(got.into_iter().any(|ma| ma.matchstr == "foo"));
+    })
+}
+
+#[test]
+fn finds_extern_crate() {
+    let src = "
+    extern crate fixtures;
+    f~ixtures
+    ";
+
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_definition(src, Some(srcdir));
+        assert_eq!(got.matchstr, "fixtures");
+    })
+}
+
+// regression test for #800
+#[test]
+fn completes_typedef_in_external_crate() {
+    let src = "
+    extern crate fixtures;
+
+    use fixtures::Usize~
+    ";
+
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_only_completion(src, Some(srcdir));
+        assert_eq!(got.matchstr, "UsizeVec");
+        assert_eq!(got.contextstr, "pub type UsizeVec = Vec<usize>;");
+    })
+}
+
+#[test]
+fn follows_external_re_export() {
+    let src = "
+    extern crate rayon;
+    fn main() {
+        rayon::sco~
+    }
+    ";
+    with_test_project(|dir| {
+        let src_dir = dir.nested_dir("test-crate3").nested_dir("src");
+        let got = get_only_completion(src, Some(src_dir));
+        assert_eq!(got.matchstr, "scope");
+    });
+}
+
+#[test]
+fn follows_rand_crate() {
+    let src = "
+    extern crate rand;
+    use rand::{Rng, thread_rng};
+    fn main() {
+        let mut rng: Box<Rng> = Box::new(thread_rng());
+        rng.gen_rang~
+    }
+    ";
+    with_test_project(|dir| {
+        let src_dir = dir.nested_dir("test-crate3").nested_dir("src");
+        let got = get_only_completion(src, Some(src_dir));
+        assert_eq!(got.matchstr, "gen_range");
+    });
+}
+
+// For issue 826
+#[test]
+fn find_crate_doc() {
+    let src = "
+    extern crate fixtures;
+    use fixtur~
+    ";
+    let doc_str = r#"This is a test project for racer.
+
+# Example:
+Basic Usage.
+
+```
+extern crate fixtures;
+use fixtures::foo;
+fn main {
+    println!("Racer")
+}
+```
+
+## Notes:
+- We should check racer can parse rust doc style comments
+- and some comments..."#;
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_one_completion(src, Some(srcdir));
+        assert_eq!(doc_str, got.docs);
+    })
+}
+
+// test for re-export
+#[test]
+fn follows_use_for_reexport() {
+    let src = "
+    extern crate fixtures;
+
+    use fixtures::use~;
+    ";
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_only_completion(src, Some(srcdir));
+        assert_eq!(got.matchstr, "useless_func");
+    })
+}
+
+#[test]
+fn completes_cratename_without_extern() {
+    let src = "
+    fn main() {
+        rayon::sco~
+    }
+    ";
+    with_test_project(|dir| {
+        let src_dir = dir.nested_dir("test-crate4").nested_dir("src");
+        let got = get_only_completion(src, Some(src_dir));
+        assert_eq!(got.matchstr, "scope");
+    });
+}
+
+#[test]
+fn doesnt_complete_cratename_without_extern_in_2015() {
+    let src = "
+    fn main() {
+        rayon::sco~
+    }
+    ";
+    with_test_project(|dir| {
+        let src_dir = dir.nested_dir("test-crate3").nested_dir("src");
+        let got = get_all_completions(src, Some(src_dir));
+        assert!(got.is_empty());
+    });
+}
+
+#[test]
+fn complete_extern_crate() {
+    let src = "
+    extern crate ray~
+    ";
+    with_test_project(|dir| {
+        let src_dir = dir.nested_dir("test-crate3").nested_dir("src");
+        let got = get_only_completion(src, Some(src_dir));
+        assert_eq!(got.matchstr, "rayon");
+    });
+}
diff --git a/tests/primitive.rs b/tests/primitive.rs
index 38451aab..367bc329 100644
--- a/tests/primitive.rs
+++ b/tests/primitive.rs
@@ -60,10 +60,9 @@ fn completes_tuple_field() {
     }
     ";
     let got = get_all_completions(src, None);
-    assert!(
-        got.into_iter()
-            .all(|ma| ma.matchstr == "0" || ma.matchstr == "1")
-    )
+    assert!(got
+        .into_iter()
+        .all(|ma| ma.matchstr == "0" || ma.matchstr == "1"))
 }
 
 #[test]
diff --git a/tests/struct_field.rs b/tests/struct_field.rs
new file mode 100644
index 00000000..4a47a57a
--- /dev/null
+++ b/tests/struct_field.rs
@@ -0,0 +1,138 @@
+extern crate racer;
+extern crate racer_testutils;
+use racer_testutils::*;
+
+#[test]
+fn follows_struct_field_in_constructor() {
+    let src = "
+    pub struct UserData {
+        name: String,
+        id: usize,
+    }
+    fn main() {
+        UserData {
+            na~  
+        }
+    }";
+    assert_eq!(get_only_completion(src, None).matchstr, "name");
+}
+
+#[test]
+fn follows_struct_field_for_typedef() {
+    let src = "
+    pub struct UserData {
+        name: String,
+        id: usize,
+    }
+    type U = UserData;
+    fn main() {
+        U {
+            na~
+        }
+    }";
+    assert_eq!(get_only_completion(src, None).matchstr, "name");
+}
+
+#[test]
+fn follows_struct_field_for_use_as() {
+    let src = "
+    mod m {
+        pub struct UserData {
+            name: String,
+            id: usize,
+        }
+    }
+    fn main() {
+        use m::UserData as U;
+        U {
+            na~
+        }
+    }";
+    assert_eq!(get_only_completion(src, None).matchstr, "name");
+}
+
+#[test]
+fn follows_enum_variant_field_in_constructor() {
+    let src = "
+    enum UserData {
+        Type1 { name: String, id: usize },
+        Type2,
+    }
+    fn main() {
+        UserData::Type1 { nam~ }
+    }";
+    assert_eq!(get_only_completion(src, None).matchstr, "name");
+}
+
+#[test]
+fn follows_struct_field_in_let() {
+    let src = "
+    struct UserData {
+        name: String,
+        id: usize,
+    }
+    fn main() {
+        let UserData { id, nam~  } = f();
+    }";
+    assert_eq!(get_only_completion(src, None).matchstr, "name");
+}
+
+#[test]
+fn follows_struct_field_in_if_let() {
+    let src = "
+    struct UserData {
+        name: String,
+        id: usize,
+    }
+    fn main() {
+        if let UserData { id, nam~  } = f() {
+
+        }
+    }";
+    assert_eq!(get_only_completion(src, None).matchstr, "name");
+}
+
+#[test]
+fn finds_struct_field_in_constructor() {
+    let src = r#"
+    struct UserData {
+        name: String,
+        id: usize,
+    }
+    fn main() {
+        UserData {
+            name: "abc".to_owned(),
+            i~d: 
+        }
+    }"#;
+    assert_eq!(get_definition(src, None).matchstr, "id");
+}
+
+#[test]
+fn struct_field_scalar_primitive_types() {
+    let src = "
+    struct Foo<'a> {
+        reference: &'a u8,
+        array: [u8; 5],
+        slice: &'a [u8],
+    }
+
+    fn foo(x: Foo) {
+        x.~
+    }
+    ";
+
+    let completions = get_all_completions(src, None);
+    assert_eq!(completions.len(), 3);
+
+    for completion in completions {
+        let expected = match completion.matchstr.as_ref() {
+            "reference" => "reference: &'a u8",
+            "array" => "array: [u8; 5]",
+            "slice" => "slice: &'a [u8]",
+            _ => panic!("unexpected match from Foo struct ({})", completion.matchstr),
+        };
+
+        assert_eq!(completion.contextstr, expected);
+    }
+}
diff --git a/tests/system.rs b/tests/system.rs
index bfa378c9..4ed0e377 100644
--- a/tests/system.rs
+++ b/tests/system.rs
@@ -151,44 +151,6 @@ fn completes_pub_fn_locally_precached() {
     assert_eq!("apple", got.matchstr);
 }
 
-#[test]
-fn completes_pub_fn_from_local_package() {
-    let src = "
-    extern crate fixtures;
-
-    use fixtures::foo;
-
-    fn main() {
-        let x = foo::~
-    }
-    ";
-
-    with_test_project(|dir| {
-        let srcdir = dir.nested_dir("src");
-        let got = get_one_completion(src, Some(srcdir));
-        assert_eq!("test", got.matchstr);
-    })
-}
-
-#[test]
-fn completes_pub_fn_from_local_submodule_package() {
-    let src = "
-    extern crate fixtures;
-
-    use fixtures::bar;
-
-    fn main() {
-        let x = bar::~
-    }
-    ";
-
-    with_test_project(|dir| {
-        let srcdir = dir.nested_dir("src");
-        let got = get_one_completion(src, Some(srcdir));
-        assert_eq!("bartest", got.matchstr);
-    })
-}
-
 #[test]
 fn completes_pub_const_fn_locally() {
     let src = "
@@ -660,11 +622,9 @@ fn follows_use_self() {
     ";
 
     let completions = get_all_completions(src, None);
-    assert!(
-        completions
-            .into_iter()
-            .any(|m| m.matchstr == "use_self_test")
-    );
+    assert!(completions
+        .into_iter()
+        .any(|m| m.matchstr == "use_self_test"));
 
     let src = "
     use use_self_test::self;
@@ -676,11 +636,9 @@ fn follows_use_self() {
     ";
 
     let completions = get_all_completions(src, None);
-    assert!(
-        completions
-            .into_iter()
-            .any(|m| m.matchstr == "use_self_test")
-    );
+    assert!(completions
+        .into_iter()
+        .any(|m| m.matchstr == "use_self_test"));
 }
 
 /// This test addresses https://github.com/racer-rust/racer/issues/645 by
@@ -930,21 +888,6 @@ pub fn bar() {
     assert_eq!("Hello world\n(quux)", got.docs);
 }
 
-#[test]
-fn follows_use_local_package() {
-    let src = "
-    extern crate fixtures;
-
-    use fixtures::~
-    ";
-
-    with_test_project(|dir| {
-        let srcdir = dir.nested_dir("src");
-        let got = get_all_completions(src, Some(srcdir));
-        assert!(got.into_iter().any(|ma| ma.matchstr == "foo"));
-    })
-}
-
 #[test]
 fn completes_struct_field_via_assignment() {
     let src = "
@@ -1013,36 +956,6 @@ fn follows_use_to_inline_mod() {
     assert_eq!(got.matchstr, "myfn");
 }
 
-#[test]
-fn struct_field_scalar_primitive_types() {
-    let src = "
-    struct Foo<'a> {
-        reference: &'a u8,
-        array: [u8; 5],
-        slice: &'a [u8],
-    }
-
-    fn foo(x: Foo) {
-        x.~
-    }
-    ";
-
-    let completions = get_all_completions(src, None);
-    assert_eq!(completions.len(), 3);
-
-    for completion in completions {
-        println!("match: {:?}", completion);
-        let expected = match completion.matchstr.as_ref() {
-            "reference" => "&u8",
-            "array" => "[u8; 5]",
-            "slice" => "&[u8]",
-            _ => panic!("unexpected match from Foo struct ({})", completion.matchstr),
-        };
-
-        assert_eq!(completion.contextstr, expected);
-    }
-}
-
 #[test]
 fn finds_enum() {
     let src = "
@@ -1093,20 +1006,6 @@ fn finds_macro() {
     assert_eq!(got.matchstr, "my_macro!");
 }
 
-#[test]
-fn finds_extern_crate() {
-    let src = "
-    extern crate fixtures;
-    f~ixtures
-    ";
-
-    with_test_project(|dir| {
-        let srcdir = dir.nested_dir("src");
-        let got = get_definition(src, Some(srcdir));
-        assert_eq!(got.matchstr, "fixtures");
-    })
-}
-
 #[test]
 fn finds_fn_arg() {
     let src = "
@@ -3319,41 +3218,9 @@ fn completes_methods_after_raw_string() {
         v.l~
     }
     "##;
-    assert!(
-        get_all_completions(src, None)
-            .iter()
-            .any(|ma| ma.matchstr == "len")
-    );
-}
-
-// For issue 826
-#[test]
-fn find_crate_doc() {
-    let src = "
-    extern crate fixtures;
-    use fixtur~
-    ";
-    let doc_str = r#"This is a test project for racer.
-
-# Example:
-Basic Usage.
-
-```
-extern crate fixtures;
-use fixtures::foo;
-fn main {
-    println!("Racer")
-}
-```
-
-## Notes:
-- We should check racer can parse rust doc style comments
-- and some comments..."#;
-    with_test_project(|dir| {
-        let srcdir = dir.nested_dir("src");
-        let got = get_one_completion(src, Some(srcdir));
-        assert_eq!(doc_str, got.docs);
-    })
+    assert!(get_all_completions(src, None)
+        .iter()
+        .any(|ma| ma.matchstr == "len"));
 }
 
 #[test]
@@ -3365,11 +3232,9 @@ fn completes_methods_for_tuple_struct() {
             a.1.appen~
         }
     ";
-    assert!(
-        get_all_completions(src, None)
-            .into_iter()
-            .any(|ma| ma.matchstr == "append")
-    );
+    assert!(get_all_completions(src, None)
+        .into_iter()
+        .any(|ma| ma.matchstr == "append"));
 }
 
 // for use_nested_groups
@@ -3416,21 +3281,6 @@ fn use_tree_complete_all() {
     assert!(got.iter().any(|ma| ma.matchstr == "ErrorInfo"));
 }
 
-// test for re-export
-#[test]
-fn follows_use_for_reexport() {
-    let src = "
-    extern crate fixtures;
-
-    use fixtures::use~;
-    ";
-    with_test_project(|dir| {
-        let srcdir = dir.nested_dir("src");
-        let got = get_only_completion(src, Some(srcdir));
-        assert_eq!(got.matchstr, "useless_func");
-    })
-}
-
 // for issue 847
 #[test]
 fn match_statements_confusing_closure_args() {
@@ -3470,11 +3320,9 @@ fn completes_methods_for_closure_arg() {
             let x = s.map(|v: Vec<i32>| v.appen~);
         }
     ";
-    assert!(
-        get_all_completions(src, None)
-            .into_iter()
-            .any(|ma| ma.matchstr == "append")
-    );
+    assert!(get_all_completions(src, None)
+        .into_iter()
+        .any(|ma| ma.matchstr == "append"));
     let src = r"
         fn main() {
             let mut v = Vec::new();
@@ -3485,11 +3333,9 @@ fn completes_methods_for_closure_arg() {
             });
         }
     ";
-    assert!(
-        get_all_completions(src, None)
-            .into_iter()
-            .any(|ma| ma.matchstr == "append")
-    );
+    assert!(get_all_completions(src, None)
+        .into_iter()
+        .any(|ma| ma.matchstr == "append"));
 }
 
 // for #856
@@ -3623,23 +3469,6 @@ fn follows_use_crate() {
     assert_eq!(got.contextstr, "pub fn myfn()");
 }
 
-// regression test for #800
-#[test]
-fn completes_typedef_in_external_crate() {
-    let src = "
-    extern crate fixtures;
-
-    use fixtures::Usize~
-    ";
-
-    with_test_project(|dir| {
-        let srcdir = dir.nested_dir("src");
-        let got = get_only_completion(src, Some(srcdir));
-        assert_eq!(got.matchstr, "UsizeVec");
-        assert_eq!(got.contextstr, "pub type UsizeVec = Vec<usize>;");
-    })
-}
-
 #[test]
 fn completes_static_method_for_typedef() {
     let src = "
@@ -3822,38 +3651,6 @@ fn follows_multiline_use() {
     assert_eq!(got.matchstr, "HashMap");
 }
 
-#[test]
-fn follows_external_re_export() {
-    let src = "
-    extern crate rayon;
-    fn main() {
-        rayon::sco~
-    }
-    ";
-    with_test_project(|dir| {
-        let src_dir = dir.nested_dir("test-crate3").nested_dir("src");
-        let got = get_only_completion(src, Some(src_dir));
-        assert_eq!(got.matchstr, "scope");
-    });
-}
-
-#[test]
-fn follows_rand_crate() {
-    let src = "
-    extern crate rand;
-    use rand::{Rng, thread_rng};
-    fn main() {
-        let mut rng: Box<Rng> = Box::new(thread_rng());
-        rng.gen_rang~
-    }
-    ";
-    with_test_project(|dir| {
-        let src_dir = dir.nested_dir("test-crate3").nested_dir("src");
-        let got = get_only_completion(src, Some(src_dir));
-        assert_eq!(got.matchstr, "gen_range");
-    });
-}
-
 #[test]
 fn completes_trait_method_only_once() {
     let src = "
diff --git a/tests/trait_bounds.rs b/tests/trait_bounds.rs
index b60791b7..587b3bcf 100644
--- a/tests/trait_bounds.rs
+++ b/tests/trait_bounds.rs
@@ -58,11 +58,9 @@ fn completes_external_methods_for_fnarg_by_trait_bounds() {
             }
         }
         ";
-    assert!(
-        get_all_completions(src, None)
-            .into_iter()
-            .any(|ma| ma.matchstr == "clone")
-    );
+    assert!(get_all_completions(src, None)
+        .into_iter()
+        .any(|ma| ma.matchstr == "clone"));
 }
 
 #[test]
@@ -195,3 +193,188 @@ fn completes_fn_bounds_for_struct_member() {
         ";
     assert_eq!(get_only_completion(src, None).matchstr, "clone_from");
 }
+
+#[test]
+fn completes_impl_trait_in_arg_position() {
+    let src = "
+    struct MyType;
+    trait Foo {
+        fn foo(&self);
+    }
+    impl Foo for MyType {
+        fn foo(&self) {
+            unimplemented!();
+        }
+    }
+
+    fn bar(b: impl Foo) {
+        b.~
+    }
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "foo");
+}
+
+#[test]
+fn completes_impl_trait_in_fn_return_position() {
+    let src = "
+    struct MyType;
+    trait Foo {
+        fn foo(&self);
+    }
+    impl Foo for MyType {
+        fn foo(&self) {
+            unimplemented!();
+        }
+    }
+    fn bar() -> impl Foo {
+        MyType
+    }
+    fn main() {
+        let mytype = bar();
+        mytype.~
+    }
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "foo");
+}
+
+#[test]
+fn completes_returned_impl_trait_in_othermod() {
+    let src = "
+     fn main() {
+         m::it().meth~
+     }
+mod empty {                                     }
+mod m {
+     pub trait Trait {
+         fn method(&self) {}
+     }
+     pub fn it() -> impl Trait {
+     }
+}
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "method");
+}
+
+#[test]
+fn completes_arg_impl_trait_in_othermod() {
+    let src = "
+     fn f(t: impl m::Trait) {
+          t.me~
+     }
+mod empty {                                     }
+mod m {
+     pub trait Trait {
+         fn method(&self) {}
+     }
+}
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "method");
+}
+
+#[test]
+fn completes_unsafe_trait_methods_for_fnarg() {
+    let src = "
+        fn main() {
+            unsafe trait Trait {
+                fn method(&self);
+            }
+            fn func<T: Trait>(arg: &T) {
+                unsafe { arg.meth~ }
+            }
+        }
+        ";
+    assert_eq!(get_only_completion(src, None).matchstr, "method");
+}
+
+#[test]
+fn completes_assoc_type_for_type_param_in_fn() {
+    let src = "
+    trait Object {
+        type BaseObj: Object;
+    }
+    fn f<O: Object>(o: O) {
+        O::Base~
+    }
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "BaseObj");
+}
+
+#[test]
+fn completes_assoc_fn_for_type_param_in_fn() {
+    let src = "
+    trait Object {
+        fn init_typeobj() {}
+    }
+    fn f<O: Object>(o: O) {
+        O::init_~
+    }
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "init_typeobj");
+}
+
+#[test]
+fn completes_assoc_type_for_type_param_in_impl() {
+    let src = "
+    trait Object {
+        type BaseObj: Object;
+    }
+    struct ObjectWrapper<O> {
+        inner: O,
+    }
+    impl<O: Object> ObjectWrapper<O> {
+        const A: O::BaseO~
+    }
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "BaseObj");
+}
+
+#[test]
+fn completes_assoc_fn_for_type_param_in_impl() {
+    let src = "
+    trait Object {
+        fn init_typeobj() {}
+    }
+    struct ObjectWrapper<O> {
+        inner: O,
+    }
+    impl<O: Object> ObjectWrapper<O> {
+        fn method(&self) {
+            O::init~
+        }
+    }
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "init_typeobj");
+}
+
+#[test]
+fn completes_assoc_constant_for_type_param_impl_bound() {
+    let src = "
+    trait Object {
+        const OFFSET: usize;
+    }
+    struct ObjectWrapper<O> {
+        inner: O,
+    }
+    impl<O: Object> ObjectWrapper<O> {
+        fn method(&self) {
+            O::OFF~
+        }
+    }
+    ";
+    assert_eq!(get_only_completion(src, None).matchstr, "OFFSET");
+}
+
+#[test]
+fn print_trait_object() {
+    let src = "
+    trait Object {}
+    struct Obj(Box<dyn Object>);
+    fn main() {
+        let obj: Obj = function();
+        obj.~
+    }
+    ";
+    let got = get_only_completion(src, None);
+    assert_eq!(got.matchstr, "0");
+    assert_eq!(got.contextstr, "Box<dyn Object>");
+}