Repository: rust-analyzer/rust-analyzer
Pull request #1786 (2019-09-07 14:54:35)

Various minor trait improvements
 - lower bounds on trait definition, i.e. super traits
 - use super traits for associated types
 - use traits from where clauses and their super traits for method resolution
 - lower fn-like paths (i.e. `Fn(X, Y) -> Z`)
 - pass the environment to Chalk in the correct way to make elaboration work, i.e. inferring things like `T: Clone` from `T: Copy`. The clauses need to be wrapped in `FromEnv` clauses for that to work, which I didn't do before.
 - add some tests for closure inference already


Patch:
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index f7efc1b665..1bb2f9f284 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -820,7 +820,52 @@ impl Trait {
         self.trait_data(db).items().to_vec()
     }
 
-    pub fn associated_type_by_name(self, db: &impl DefDatabase, name: Name) -> Option<TypeAlias> {
+    fn direct_super_traits(self, db: &impl HirDatabase) -> Vec<Trait> {
+        let resolver = self.resolver(db);
+        // returning the iterator directly doesn't easily work because of
+        // lifetime problems, but since there usually shouldn't be more than a
+        // few direct traits this should be fine (we could even use some kind of
+        // SmallVec if performance is a concern)
+        self.generic_params(db)
+            .where_predicates
+            .iter()
+            .filter_map(|pred| match &pred.type_ref {
+                TypeRef::Path(p) if p.as_ident() == Some(&crate::name::SELF_TYPE) => {
+                    pred.bound.as_path()
+                }
+                _ => None,
+            })
+            .filter_map(|path| {
+                match resolver.resolve_path_without_assoc_items(db, path).take_types() {
+                    Some(crate::Resolution::Def(ModuleDef::Trait(t))) => Some(t),
+                    _ => None,
+                }
+            })
+            .collect()
+    }
+
+    /// Returns an iterator over the whole super trait hierarchy (including the
+    /// trait itself).
+    pub fn all_super_traits(self, db: &impl HirDatabase) -> Vec<Trait> {
+        // we need to take care a bit here to avoid infinite loops in case of cycles
+        // (i.e. if we have `trait A: B; trait B: A;`)
+        let mut result = vec![self];
+        let mut i = 0;
+        while i < result.len() {
+            let t = result[i];
+            // yeah this is quadratic, but trait hierarchies should be flat
+            // enough that this doesn't matter
+            for tt in t.direct_super_traits(db) {
+                if !result.contains(&tt) {
+                    result.push(tt);
+                }
+            }
+            i += 1;
+        }
+        result
+    }
+
+    pub fn associated_type_by_name(self, db: &impl DefDatabase, name: &Name) -> Option<TypeAlias> {
         let trait_data = self.trait_data(db);
         trait_data
             .items()
@@ -829,7 +874,15 @@ impl Trait {
                 TraitItem::TypeAlias(t) => Some(*t),
                 _ => None,
             })
-            .find(|t| t.name(db) == name)
+            .find(|t| &t.name(db) == name)
+    }
+
+    pub fn associated_type_by_name_including_super_traits(
+        self,
+        db: &impl HirDatabase,
+        name: &Name,
+    ) -> Option<TypeAlias> {
+        self.all_super_traits(db).into_iter().find_map(|t| t.associated_type_by_name(db, name))
     }
 
     pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> {
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs
index d6728cc9f4..c76df0698c 100644
--- a/crates/ra_hir/src/generics.rs
+++ b/crates/ra_hir/src/generics.rs
@@ -87,11 +87,15 @@ impl GenericParams {
                 // traits get the Self type as an implicit first type parameter
                 generics.params.push(GenericParam { idx: start, name: SELF_TYPE, default: None });
                 generics.fill(&it.source(db).ast, start + 1);
+                // add super traits as bounds on Self
+                // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
+                let self_param = TypeRef::Path(SELF_TYPE.into());
+                generics.fill_bounds(&it.source(db).ast, self_param);
             }
             GenericDef::TypeAlias(it) => generics.fill(&it.source(db).ast, start),
             // Note that we don't add `Self` here: in `impl`s, `Self` is not a
             // type-parameter, but rather is a type-alias for impl's target
-            // type, so this is handled by the resovler.
+            // type, so this is handled by the resolver.
             GenericDef::ImplBlock(it) => generics.fill(&it.source(db).ast, start),
             GenericDef::EnumVariant(_) => {}
         }
@@ -108,6 +112,14 @@ impl GenericParams {
         }
     }
 
+    fn fill_bounds(&mut self, node: &impl ast::TypeBoundsOwner, type_ref: TypeRef) {
+        for bound in
+            node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
+        {
+            self.add_where_predicate_from_bound(bound, type_ref.clone());
+        }
+    }
+
     fn fill_params(&mut self, params: ast::TypeParamList, start: u32) {
         for (idx, type_param) in params.type_params().enumerate() {
             let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
@@ -117,13 +129,7 @@ impl GenericParams {
             self.params.push(param);
 
             let type_ref = TypeRef::Path(name.into());
-            for bound in type_param
-                .type_bound_list()
-                .iter()
-                .flat_map(|type_bound_list| type_bound_list.bounds())
-            {
-                self.add_where_predicate_from_bound(bound, type_ref.clone());
-            }
+            self.fill_bounds(&type_param, type_ref);
         }
     }
 
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index 9c4822d917..13bc901bc9 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -38,11 +38,6 @@ impl Name {
         Name::new(idx.to_string().into())
     }
 
-    // Needed for Deref
-    pub(crate) fn target() -> Name {
-        Name::new("Target".into())
-    }
-
     // There's should be no way to extract a string out of `Name`: `Name` in the
     // future, `Name` will include hygiene information, and you can't encode
     // hygiene into a String.
@@ -123,6 +118,7 @@ pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6,
 pub(crate) const RESULT_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result"));
 pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result"));
 pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
+pub(crate) const TARGET: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Target"));
 
 fn resolve_name(text: &SmolStr) -> SmolStr {
     let raw_start = "r#";
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 24316fc911..d6c78593bc 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -1,11 +1,11 @@
 use std::sync::Arc;
 
 use ra_syntax::{
-    ast::{self, NameOwner},
+    ast::{self, NameOwner, TypeAscriptionOwner},
     AstNode,
 };
 
-use crate::{type_ref::TypeRef, AsName, Name};
+use crate::{name, type_ref::TypeRef, AsName, Name};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Path {
@@ -76,8 +76,16 @@ impl Path {
 
             match segment.kind()? {
                 ast::PathSegmentKind::Name(name) => {
-                    let args =
-                        segment.type_arg_list().and_then(GenericArgs::from_ast).map(Arc::new);
+                    let args = segment
+                        .type_arg_list()
+                        .and_then(GenericArgs::from_ast)
+                        .or_else(|| {
+                            GenericArgs::from_fn_like_path_ast(
+                                segment.param_list(),
+                                segment.ret_type(),
+                            )
+                        })
+                        .map(Arc::new);
                     let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
                     segments.push(segment);
                 }
@@ -187,6 +195,34 @@ impl GenericArgs {
         }
     }
 
+    /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
+    /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
+    pub(crate) fn from_fn_like_path_ast(
+        params: Option<ast::ParamList>,
+        ret_type: Option<ast::RetType>,
+    ) -> Option<GenericArgs> {
+        let mut args = Vec::new();
+        let mut bindings = Vec::new();
+        if let Some(params) = params {
+            let mut param_types = Vec::new();
+            for param in params.params() {
+                let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
+                param_types.push(type_ref);
+            }
+            let arg = GenericArg::Type(TypeRef::Tuple(param_types));
+            args.push(arg);
+        }
+        if let Some(ret_type) = ret_type {
+            let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
+            bindings.push((name::OUTPUT, type_ref))
+        }
+        if args.is_empty() && bindings.is_empty() {
+            None
+        } else {
+            Some(GenericArgs { args, has_self_type: false, bindings })
+        }
+    }
+
     pub(crate) fn empty() -> GenericArgs {
         GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() }
     }
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs
index 08f52a53bc..caa17f64ea 100644
--- a/crates/ra_hir/src/ty/autoderef.rs
+++ b/crates/ra_hir/src/ty/autoderef.rs
@@ -8,7 +8,7 @@ use std::iter::successors;
 use log::{info, warn};
 
 use super::{traits::Solution, Canonical, Ty, TypeWalk};
-use crate::{HasGenericParams, HirDatabase, Name, Resolver};
+use crate::{name, HasGenericParams, HirDatabase, Resolver};
 
 const AUTODEREF_RECURSION_LIMIT: usize = 10;
 
@@ -42,7 +42,7 @@ fn deref_by_trait(
         crate::lang_item::LangItemTarget::Trait(t) => t,
         _ => return None,
     };
-    let target = deref_trait.associated_type_by_name(db, Name::target())?;
+    let target = deref_trait.associated_type_by_name(db, &name::TARGET)?;
 
     if target.generic_params(db).count_params_including_parent() != 1 {
         // the Target type + Deref trait should only have one generic parameter,
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index ec3b7ffeff..0e6ebd3652 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -1453,7 +1453,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
 
         match self.resolver.resolve_path_segments(self.db, &into_iter_path).into_fully_resolved() {
             PerNs { types: Some(Def(Trait(trait_))), .. } => {
-                Some(trait_.associated_type_by_name(self.db, name::ITEM)?)
+                Some(trait_.associated_type_by_name(self.db, &name::ITEM)?)
             }
             _ => None,
         }
@@ -1471,7 +1471,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
 
         match self.resolver.resolve_path_segments(self.db, &ops_try_path).into_fully_resolved() {
             PerNs { types: Some(Def(Trait(trait_))), .. } => {
-                Some(trait_.associated_type_by_name(self.db, name::OK)?)
+                Some(trait_.associated_type_by_name(self.db, &name::OK)?)
             }
             _ => None,
         }
@@ -1493,7 +1493,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             .into_fully_resolved()
         {
             PerNs { types: Some(Def(Trait(trait_))), .. } => {
-                Some(trait_.associated_type_by_name(self.db, name::OUTPUT)?)
+                Some(trait_.associated_type_by_name(self.db, &name::OUTPUT)?)
             }
             _ => None,
         }
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index f6f0137cff..480bae7401 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -132,14 +132,16 @@ impl Ty {
             if let Some(remaining_index) = remaining_index {
                 if remaining_index == path.segments.len() - 1 {
                     let segment = &path.segments[remaining_index];
-                    let associated_ty =
-                        match trait_ref.trait_.associated_type_by_name(db, segment.name.clone()) {
-                            Some(t) => t,
-                            None => {
-                                // associated type not found
-                                return Ty::Unknown;
-                            }
-                        };
+                    let associated_ty = match trait_ref
+                        .trait_
+                        .associated_type_by_name_including_super_traits(db, &segment.name)
+                    {
+                        Some(t) => t,
+                        None => {
+                            // associated type not found
+                            return Ty::Unknown;
+                        }
+                    };
                     // FIXME handle type parameters on the segment
                     Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs })
                 } else {
@@ -387,10 +389,11 @@ fn assoc_type_bindings_from_type_bound<'a>(
         .flat_map(|segment| segment.args_and_bindings.iter())
         .flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
         .map(move |(name, type_ref)| {
-            let associated_ty = match trait_ref.trait_.associated_type_by_name(db, name.clone()) {
-                None => return GenericPredicate::Error,
-                Some(t) => t,
-            };
+            let associated_ty =
+                match trait_ref.trait_.associated_type_by_name_including_super_traits(db, &name) {
+                    None => return GenericPredicate::Error,
+                    Some(t) => t,
+                };
             let projection_ty =
                 ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() };
             let ty = Ty::from_hir(db, resolver, type_ref);
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index 9873a0440b..cf787bdaad 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -212,7 +212,13 @@ fn iterate_trait_method_candidates<T>(
     // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
     let env = lower::trait_env(db, resolver);
     // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
-    let traits = ty.value.inherent_trait().into_iter().chain(resolver.traits_in_scope(db));
+    let inherent_trait = ty.value.inherent_trait().into_iter();
+    // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
+    let traits_from_env = env
+        .trait_predicates_for_self_ty(&ty.value)
+        .map(|tr| tr.trait_)
+        .flat_map(|t| t.all_super_traits(db));
+    let traits = inherent_trait.chain(traits_from_env).chain(resolver.traits_in_scope(db));
     'traits: for t in traits {
         let data = t.trait_data(db);
 
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs
index 6e0271a966..c0c132809b 100644
--- a/crates/ra_hir/src/ty/traits.rs
+++ b/crates/ra_hir/src/ty/traits.rs
@@ -96,6 +96,21 @@ pub struct TraitEnvironment {
     pub predicates: Vec<GenericPredicate>,
 }
 
+impl TraitEnvironment {
+    /// Returns trait refs with the given self type which are supposed to hold
+    /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will
+    /// find that `T: SomeTrait` if we call it for `T`.
+    pub(crate) fn trait_predicates_for_self_ty<'a>(
+        &'a self,
+        ty: &'a Ty,
+    ) -> impl Iterator<Item = &'a TraitRef> + 'a {
+        self.predicates.iter().filter_map(move |pred| match pred {
+            GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr),
+            _ => None,
+        })
+    }
+}
+
 /// Something (usually a goal), along with an environment.
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct InEnvironment<T> {
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs
index c201c5e50e..8a127efa19 100644
--- a/crates/ra_hir/src/ty/traits/chalk.rs
+++ b/crates/ra_hir/src/ty/traits/chalk.rs
@@ -314,7 +314,8 @@ impl ToChalk for Arc<super::TraitEnvironment> {
                 // for env, we just ignore errors
                 continue;
             }
-            clauses.push(pred.clone().to_chalk(db).cast());
+            let program_clause: chalk_ir::ProgramClause = pred.clone().to_chalk(db).cast();
+            clauses.push(program_clause.into_from_env_clause());
         }
         chalk_ir::Environment::new().add_clauses(clauses)
     }
@@ -636,7 +637,7 @@ pub(crate) fn impl_datum_query(
             _ => None,
         })
         .filter_map(|t| {
-            let assoc_ty = trait_.associated_type_by_name(db, t.name(db))?;
+            let assoc_ty = trait_.associated_type_by_name(db, &t.name(db))?;
             let ty = db.type_for_def(t.into(), crate::Namespace::Types).subst(&bound_vars);
             Some(chalk_rust_ir::AssociatedTyValue {
                 impl_id,
diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs
index fa91bfb22f..bc8acc7eed 100644
--- a/crates/ra_hir/src/type_ref.rs
+++ b/crates/ra_hir/src/type_ref.rs
@@ -150,4 +150,11 @@ impl TypeBound {
             ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error,
         }
     }
+
+    pub fn as_path(&self) -> Option<&Path> {
+        match self {
+            TypeBound::Path(p) => Some(p),
+            _ => None,
+        }
+    }
 }
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index bcf753f784..d274b6fbc0 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -2312,6 +2312,12 @@ impl PathSegment {
     pub fn type_arg_list(&self) -> Option<TypeArgList> {
         AstChildren::new(&self.syntax).next()
     }
+    pub fn param_list(&self) -> Option<ParamList> {
+        AstChildren::new(&self.syntax).next()
+    }
+    pub fn ret_type(&self) -> Option<RetType> {
+        AstChildren::new(&self.syntax).next()
+    }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct PathType {
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 3e6c2d3f3c..993e58e645 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -684,7 +684,7 @@ Grammar(
             ]
         ),
         "PathSegment": (
-            options: [ "NameRef", "TypeArgList" ]
+            options: [ "NameRef", "TypeArgList", "ParamList", "RetType" ]
         ),
         "TypeArgList": (collections: [
             ("type_args", "TypeArg"),


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index d92d4659b2..c4bddde85d 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -3643,6 +3643,231 @@ fn test<T: Trait1<Type = u32>>(x: T) {
     "###
     );
 }
+
+#[test]
+fn where_clause_trait_in_scope_for_method_resolution() {
+    let t = type_at(
+        r#"
+//- /main.rs
+mod foo {
+    trait Trait {
+        fn foo(&self) -> u32 {}
+    }
+}
+
+fn test<T: foo::Trait>(x: T) {
+    x.foo()<|>;
+}
+"#,
+    );
+    assert_eq!(t, "u32");
+}
+
+#[test]
+fn super_trait_method_resolution() {
+    assert_snapshot!(
+        infer(r#"
+mod foo {
+    trait SuperTrait {
+        fn foo(&self) -> u32 {}
+    }
+}
+trait Trait1: foo::SuperTrait {}
+trait Trait2 where Self: foo::SuperTrait {}
+
+fn test<T: Trait1, U: Trait2>(x: T, y: U) {
+    x.foo();
+    y.foo();
+}
+"#),
+        @r###"
+    [50; 54) 'self': &Self
+    [63; 65) '{}': ()
+    [182; 183) 'x': T
+    [188; 189) 'y': U
+    [194; 223) '{     ...o(); }': ()
+    [200; 201) 'x': T
+    [200; 207) 'x.foo()': u32
+    [213; 214) 'y': U
+    [213; 220) 'y.foo()': u32
+    "###
+    );
+}
+
+#[test]
+fn super_trait_cycle() {
+    // This just needs to not crash
+    assert_snapshot!(
+        infer(r#"
+trait A: B {}
+trait B: A {}
+
+fn test<T: A>(x: T) {
+    x.foo();
+}
+"#),
+        @r###"
+    [44; 45) 'x': T
+    [50; 66) '{     ...o(); }': ()
+    [56; 57) 'x': T
+    [56; 63) 'x.foo()': {unknown}
+    "###
+    );
+}
+
+#[test]
+fn super_trait_assoc_type_bounds() {
+    assert_snapshot!(
+        infer(r#"
+trait SuperTrait { type Type; }
+trait Trait where Self: SuperTrait {}
+
+fn get2<U, T: Trait<Type = U>>(t: T) -> U {}
+fn set<T: Trait<Type = u64>>(t: T) -> T {t}
+
+struct S<T>;
+impl<T> SuperTrait for S<T> { type Type = T; }
+impl<T> Trait for S<T> {}
+
+fn test() {
+    get2(set(S));
+}
+"#),
+        @r###"
+    [103; 104) 't': T
+    [114; 116) '{}': ()
+    [146; 147) 't': T
+    [157; 160) '{t}': T
+    [158; 159) 't': T
+    [259; 280) '{     ...S)); }': ()
+    [265; 269) 'get2': fn get2<u64, S<u64>>(T) -> U
+    [265; 277) 'get2(set(S))': u64
+    [270; 273) 'set': fn set<S<u64>>(T) -> T
+    [270; 276) 'set(S)': S<u64>
+    [274; 275) 'S': S<u64>
+    "###
+    );
+}
+
+#[test]
+fn fn_trait() {
+    assert_snapshot!(
+        infer(r#"
+trait FnOnce<Args> {
+    type Output;
+
+    fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output;
+}
+
+fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
+    f.call_once((1, 2));
+}
+"#),
+        @r###"
+    [57; 61) 'self': Self
+    [63; 67) 'args': Args
+    [150; 151) 'f': F
+    [156; 184) '{     ...2)); }': ()
+    [162; 163) 'f': F
+    [162; 181) 'f.call...1, 2))': {unknown}
+    [174; 180) '(1, 2)': (u32, u64)
+    [175; 176) '1': u32
+    [178; 179) '2': u64
+    "###
+    );
+}
+
+#[test]
+fn closure_1() {
+    assert_snapshot!(
+        infer(r#"
+trait FnOnce<Args> {
+    type Output;
+}
+
+enum Option<T> { Some(T), None }
+impl<T> Option<T> {
+    fn map<U, F: FnOnce(T) -> U>(self, f: F) -> U {}
+}
+
+fn test() {
+    let x = Option::Some(1i32);
+    x.map(|v| v + 1);
+    x.map(|_v| 1u64);
+    let y: Option<i64> = x.map(|_v| 1);
+}
+"#),
+        @r###"
+    [128; 132) 'self': Option<T>
+    [134; 135) 'f': F
+    [145; 147) '{}': ()
+    [161; 280) '{     ... 1); }': ()
+    [171; 172) 'x': Option<i32>
+    [175; 187) 'Option::Some': Some<i32>(T) -> Option<T>
+    [175; 193) 'Option...(1i32)': Option<i32>
+    [188; 192) '1i32': i32
+    [199; 200) 'x': Option<i32>
+    [199; 215) 'x.map(...v + 1)': {unknown}
+    [205; 214) '|v| v + 1': {unknown}
+    [206; 207) 'v': {unknown}
+    [209; 210) 'v': {unknown}
+    [209; 214) 'v + 1': i32
+    [213; 214) '1': i32
+    [221; 222) 'x': Option<i32>
+    [221; 237) 'x.map(... 1u64)': {unknown}
+    [227; 236) '|_v| 1u64': {unknown}
+    [228; 230) '_v': {unknown}
+    [232; 236) '1u64': u64
+    [247; 248) 'y': Option<i64>
+    [264; 265) 'x': Option<i32>
+    [264; 277) 'x.map(|_v| 1)': Option<i64>
+    [270; 276) '|_v| 1': {unknown}
+    [271; 273) '_v': {unknown}
+    [275; 276) '1': i32
+    "###
+    );
+}
+
+#[test]
+fn closure_2() {
+    assert_snapshot!(
+        infer(r#"
+trait FnOnce<Args> {
+    type Output;
+}
+
+fn test<F: FnOnce(u32) -> u64>(f: F) {
+    f(1);
+    let g = |v| v + 1;
+    g(1u64);
+    let h = |v| 1u128 + v;
+}
+"#),
+        @r###"
+    [73; 74) 'f': F
+    [79; 155) '{     ...+ v; }': ()
+    [85; 86) 'f': F
+    [85; 89) 'f(1)': {unknown}
+    [87; 88) '1': i32
+    [99; 100) 'g': {unknown}
+    [103; 112) '|v| v + 1': {unknown}
+    [104; 105) 'v': {unknown}
+    [107; 108) 'v': {unknown}
+    [107; 112) 'v + 1': i32
+    [111; 112) '1': i32
+    [118; 119) 'g': {unknown}
+    [118; 125) 'g(1u64)': {unknown}
+    [120; 124) '1u64': u64
+    [135; 136) 'h': {unknown}
+    [139; 152) '|v| 1u128 + v': {unknown}
+    [140; 141) 'v': u128
+    [143; 148) '1u128': u128
+    [143; 152) '1u128 + v': u128
+    [151; 152) 'v': u128
+    "###
+    );
+}
+
 fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
     let file = db.parse(pos.file_id).ok().unwrap();
     let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();