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

Issue:
Modules inserted via `include` can't resolve later files
**rust-analyzer version**: 0.4.2042-standalone (b333f85a9 2024-07-19)

**rustc version**: rustc 1.78.0 (9b00956e5 2024-04-29)

**editor or extension**: VS Code

A test case to reproduce based on `nested_include`
```rust
#[test]
fn include_with_mod_path() {
    check(
        r#"
//- minicore: include
//- /lib.rs
include!("out_dir/includes.rs");

//- /out_dir/includes.rs
#[path = "."]
pub mod company_name {
    pub mod network {
        pub mod v1;
    }
}
//- /out_dir/network/v1.rs
pub struct IpAddress {
    pub ip_type: &'static str,
}
/// Nested message and enum types in `IpAddress`.
pub mod ip_address {
    pub enum IpType {
        IpV4(u32),
    }
}

"#,
        expect![[r#"
            crate
            company_name: t

            crate::company_name
            network: t

            crate::company_name::network
            v1: t

            crate::company_name::network::v1
            IpAddress: t
            ip_address: t

            crate::company_name::network::v1::ip_address
            IpType: t
        "#]],
    );
}
```
The types in the `v1` module don't resolve, failing the test. Rust has no problem handling modules with `#[path = "."]`. RA also can handle modules with `#[path = "."]` if they aren't `include!`d, so this seems to be a problem exclusive to `include!`.

Pull request:
Fix path resolution for child mods of those expanded by `include!`
Child modules wouldn't use the correct candidate paths due to a branch that doesn't seem to be doing what it's intended to do. Removing the branch fixes the problem and all existing test cases pass.

Having no knowledge of how any of this works, I believe this fixes #17645. Using another test that writes the included mod directly into `lib.rs` instead, I found the difference can be traced to the candidate files we use to look up mods. A separate branch for if the file comes from an `include!` macro doesn't take into account the original mod we're contained within:

```rust
None if file_id.macro_file().map_or(false, |it| it.is_include_macro(db.upcast())) => {
    candidate_files.push(format!("{}.rs", name.display(db.upcast())));
    candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
}
```

I'm not sure why this branch exists. Tracing the branch back takes us to 3bb9efb but it doesn't say *why* the branch was added. The test case that was added in this commit passes with the branch removed, so I think it's just superfluous at this point.

Patch:
diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs
index 19fdd8e33d..ab4ffbb2c1 100644
--- a/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/crates/hir-def/src/nameres/mod_resolution.rs
@@ -1,7 +1,7 @@
 //! This module resolves `mod foo;` declaration to file.
 use arrayvec::ArrayVec;
 use base_db::AnchoredPath;
-use hir_expand::{name::Name, HirFileIdExt, MacroFileIdExt};
+use hir_expand::{name::Name, HirFileIdExt};
 use limit::Limit;
 use span::EditionedFileId;
 use syntax::ToSmolStr as _;
@@ -73,10 +73,6 @@ impl ModDir {
             Some(attr_path) => {
                 candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
             }
-            None if file_id.macro_file().map_or(false, |it| it.is_include_macro(db.upcast())) => {
-                candidate_files.push(format!("{}.rs", name.display(db.upcast())));
-                candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
-            }
             None => {
                 candidate_files.push(format!(
                     "{}{}.rs",
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index a12e201cf3..7bf8af4cab 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -86,15 +86,6 @@ impl<'db> MatchCheckCtx<'db> {
         arms: &[MatchArm<'db>],
         scrut_ty: Ty,
     ) -> Result<UsefulnessReport<'db, Self>, ()> {
-        if scrut_ty.contains_unknown() {
-            return Err(());
-        }
-        for arm in arms {
-            if arm.pat.ty().contains_unknown() {
-                return Err(());
-            }
-        }
-
         // FIXME: Determine place validity correctly. For now, err on the safe side.
         let place_validity = PlaceValidity::MaybeInvalid;
         // Measured to take ~100ms on modern hardware.
diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs
index 839ffa2614..aba06f38ef 100644
--- a/crates/ide-assists/src/handlers/bind_unused_param.rs
+++ b/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -43,7 +43,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
 
     acc.add(
         AssistId("bind_unused_param", AssistKind::QuickFix),
-        &format!("Bind as `let _ = {ident_pat};`"),
+        &format!("Bind as `let _ = {};`", &ident_pat),
         param.syntax().text_range(),
         |builder| {
             let line_index = ctx.db().line_index(ctx.file_id().into());
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 3657a7d969..debfefc480 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -664,7 +664,7 @@ mod tests {
     /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert:
     ///     a.score < b.score == c.score < d.score
     fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) {
-        let expected = format!("{expected_relevance_order:#?}");
+        let expected = format!("{:#?}", &expected_relevance_order);
 
         let actual_relevance_order = expected_relevance_order
             .into_iter()
@@ -685,7 +685,7 @@ mod tests {
             )
             .1;
 
-        let actual = format!("{actual_relevance_order:#?}");
+        let actual = format!("{:#?}", &actual_relevance_order);
 
         assert_eq_text!(&expected, &actual);
     }
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 4e52d28051..4c25532228 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -745,18 +745,6 @@ fn f() {
         0
     }
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_17585() {
-        check_diagnostics(
-            r#"
-fn f() {
-    let (_, _, _, ..) = (true, 42);
-     // ^^^^^^^^^^^^^ error: expected (bool, i32), found (bool, i32, {unknown})
-}
 "#,
         );
     }
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index b4a566e318..3fa38ed9ee 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -75,7 +75,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
         .unique()
         .map(|code| Assist {
             id: AssistId("typed-hole", AssistKind::QuickFix),
-            label: Label::new(format!("Replace `_` with `{code}`")),
+            label: Label::new(format!("Replace `_` with `{}`", &code)),
             group: Some(GroupLabel("Replace `_` with a term".to_owned())),
             target: original_range.range,
             source_change: Some(SourceChange::from_text_edit(
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index ea96c9504e..d8aa4ba4e1 100644
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -18,7 +18,7 @@ pub(super) fn hints(
     sema: &Semantics<'_, RootDatabase>,
     config: &InlayHintsConfig,
     file_id: EditionedFileId,
-    mut node: SyntaxNode,
+    node: SyntaxNode,
 ) -> Option<()> {
     let min_lines = config.closing_brace_hints_min_lines?;
 
@@ -52,15 +52,6 @@ pub(super) fn hints(
 
         let module = ast::Module::cast(list.syntax().parent()?)?;
         (format!("mod {}", module.name()?), module.name().map(name))
-    } else if let Some(label) = ast::Label::cast(node.clone()) {
-        // in this case, `ast::Label` could be seen as a part of `ast::BlockExpr`
-        // the actual number of lines in this case should be the line count of the parent BlockExpr,
-        // which the `min_lines` config cares about
-        node = node.parent()?;
-        let block = label.syntax().parent().and_then(ast::BlockExpr::cast)?;
-        closing_token = block.stmt_list()?.r_curly_token()?;
-        let lifetime = label.lifetime().map_or_else(String::new, |it| it.to_string());
-        (lifetime, Some(label.syntax().text_range()))
     } else if let Some(block) = ast::BlockExpr::cast(node.clone()) {
         closing_token = block.stmt_list()?.r_curly_token()?;
 
@@ -198,29 +189,6 @@ fn f() {
     ];
   }
 //^ fn f
-"#,
-        );
-    }
-
-    #[test]
-    fn hints_closing_brace_for_block_expr() {
-        check_with_config(
-            InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG },
-            r#"
-fn test() {
-    'end: {
-        'do_a: {
-            'do_b: {
-
-            }
-          //^ 'do_b
-            break 'end;
-        }
-      //^ 'do_a
-    }
-  //^ 'end
-  }
-//^ fn test
 "#,
         );
     }
diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs
index 01b8f9e918..86e19fbe5f 100644
--- a/crates/parser/src/grammar/paths.rs
+++ b/crates/parser/src/grammar/paths.rs
@@ -2,6 +2,8 @@ use super::*;
 
 pub(super) const PATH_FIRST: TokenSet =
     TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self], T![:], T![<]]);
+pub(super) const WEAK_DYN_PATH_FIRST: TokenSet =
+    TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self]]);
 
 pub(super) fn is_path_start(p: &Parser<'_>) -> bool {
     is_use_path_start(p) || p.at(T![<]) || p.at(T![Self])
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index f4e57d3d6f..9a1c6dfdf7 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -1,3 +1,5 @@
+use crate::grammar::paths::WEAK_DYN_PATH_FIRST;
+
 use super::*;
 
 pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
@@ -49,7 +51,13 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
         T![dyn] => dyn_trait_type(p),
         // Some path types are not allowed to have bounds (no plus)
         T![<] => path_type_bounds(p, allow_bounds),
-        T![ident] if !p.edition().at_least_2018() && is_dyn_weak(p) => dyn_trait_type_weak(p),
+        T![ident]
+            if !p.edition().at_least_2018()
+                && p.at_contextual_kw(T![dyn])
+                && WEAK_DYN_PATH_FIRST.contains(p.nth(1)) =>
+        {
+            dyn_trait_type_weak(p)
+        }
         _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds),
         LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
         _ => {
@@ -58,25 +66,6 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
     }
 }
 
-fn is_dyn_weak(p: &Parser<'_>) -> bool {
-    const WEAK_DYN_PATH_FIRST: TokenSet = TokenSet::new(&[
-        IDENT,
-        T![self],
-        T![super],
-        T![crate],
-        T![Self],
-        T![lifetime_ident],
-        T![?],
-        T![for],
-        T!['('],
-    ]);
-
-    p.at_contextual_kw(T![dyn]) && {
-        let la = p.nth(1);
-        WEAK_DYN_PATH_FIRST.contains(la) && (la != T![:] || la != T![<])
-    }
-}
-
 pub(super) fn ascription(p: &mut Parser<'_>) {
     assert!(p.at(T![:]));
     p.bump(T![:]);
@@ -300,14 +289,9 @@ fn dyn_trait_type(p: &mut Parser<'_>) {
 }
 
 // test dyn_trait_type_weak 2015
-// type DynPlain = dyn Path;
-// type DynRef = &dyn Path;
-// type DynLt = dyn 'a + Path;
-// type DynQuestion = dyn ?Path;
-// type DynFor = dyn for<'a> Path;
-// type DynParen = dyn(Path);
-// type Path = dyn::Path;
-// type Generic = dyn<Path>;
+// type A = dyn Iterator<Item=Foo<'a>> + 'a;
+// type A = &dyn Iterator<Item=Foo<'a>> + 'a;
+// type A = dyn::Path;
 fn dyn_trait_type_weak(p: &mut Parser<'_>) {
     assert!(p.at_contextual_kw(T![dyn]));
     let m = p.start();
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 9191ced27a..f26f9abe80 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -422,7 +422,7 @@ impl flags::AnalysisStats {
                 if found_terms.is_empty() {
                     acc.tail_expr_no_term += 1;
                     acc.total_tail_exprs += 1;
-                    // println!("\n{original_text}\n");
+                    // println!("\n{}\n", &original_text);
                     continue;
                 };
 
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index 06e893f0f4..4d9cbae7d1 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -292,6 +292,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                 .into_iter()
                 .flat_map(|id| world.analysis.transitive_rev_deps(id))
                 .flatten()
+                .sorted()
                 .unique()
                 .collect();
 
diff --git a/crates/rust-analyzer/src/lsp/utils.rs b/crates/rust-analyzer/src/lsp/utils.rs
index 9a9e66be51..46797ec658 100644
--- a/crates/rust-analyzer/src/lsp/utils.rs
+++ b/crates/rust-analyzer/src/lsp/utils.rs
@@ -79,7 +79,7 @@ impl GlobalState {
     pub(crate) fn show_and_log_error(&mut self, message: String, additional_info: Option<String>) {
         match additional_info {
             Some(additional_info) => {
-                tracing::error!("{message}:\n{additional_info}");
+                tracing::error!("{}:\n{}", &message, &additional_info);
                 self.show_message(
                     lsp_types::MessageType::ERROR,
                     message,
@@ -87,7 +87,7 @@ impl GlobalState {
                 );
             }
             None => {
-                tracing::error!("{message}");
+                tracing::error!("{}", &message);
                 self.send_notification::<lsp_types::notification::ShowMessage>(
                     lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message },
                 );
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 8dc6d36a7e..9b43da8341 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -643,7 +643,7 @@ fn main() {
         let deletions = diff
             .deletions
             .iter()
-            .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), fmt_syntax(v))));
+            .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), &fmt_syntax(v))));
 
         let actual = format!(
             "insertions:\n\n{insertions}\n\nreplacements:\n\n{replacements}\n\ndeletions:\n\n{deletions}\n"
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 0228d9dd71..35ec9b1013 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -179,18 +179,18 @@ pub fn ty_alias(
     }
 
     if let Some(list) = type_param_bounds {
-        s.push_str(&format!(" : {list}"));
+        s.push_str(&format!(" : {}", &list));
     }
 
     if let Some(cl) = where_clause {
-        s.push_str(&format!(" {cl}"));
+        s.push_str(&format!(" {}", &cl.to_string()));
     }
 
     if let Some(exp) = assignment {
         if let Some(cl) = exp.1 {
-            s.push_str(&format!(" = {} {cl}", exp.0));
+            s.push_str(&format!(" = {} {}", &exp.0.to_string(), &cl.to_string()));
         } else {
-            s.push_str(&format!(" = {}", exp.0));
+            s.push_str(&format!(" = {}", &exp.0.to_string()));
         }
     }
 


Test patch:
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index d278b75e81..b6e1cfb3eb 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -1309,6 +1309,52 @@ pub mod ip_address {
     );
 }
 
+#[test]
+fn include_with_submod_file() {
+    check(
+        r#"
+//- minicore: include
+//- /lib.rs
+include!("out_dir/includes.rs");
+
+//- /out_dir/includes.rs
+pub mod company_name {
+    pub mod network {
+        pub mod v1;
+    }
+}
+//- /out_dir/company_name/network/v1.rs
+pub struct IpAddress {
+    pub ip_type: &'static str,
+}
+/// Nested message and enum types in `IpAddress`.
+pub mod ip_address {
+    pub enum IpType {
+        IpV4(u32),
+    }
+}
+
+"#,
+        expect![[r#"
+            crate
+            company_name: t
+
+            crate::company_name
+            network: t
+
+            crate::company_name::network
+            v1: t
+
+            crate::company_name::network::v1
+            IpAddress: t
+            ip_address: t
+
+            crate::company_name::network::v1::ip_address
+            IpType: t
+        "#]],
+    );
+}
+
 #[test]
 fn macro_use_imports_all_macro_types() {
     let db = TestDB::with_files(
@@ -1348,6 +1394,7 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
         .keys()
         .map(|name| name.display(&db).to_string())
         .sorted()
+        .sorted()
         .join("\n");
 
     expect![[r#"
diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
index dcc66dc1e2..00ca92402c 100644
--- a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
+++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
@@ -5,7 +5,7 @@ SOURCE_FILE
     TYPE_KW "type"
     WHITESPACE " "
     NAME
-      IDENT "DynPlain"
+      IDENT "A"
     WHITESPACE " "
     EQ "="
     WHITESPACE " "
@@ -18,14 +18,38 @@ SOURCE_FILE
             PATH
               PATH_SEGMENT
                 NAME_REF
-                  IDENT "Path"
+                  IDENT "Iterator"
+                GENERIC_ARG_LIST
+                  L_ANGLE "<"
+                  ASSOC_TYPE_ARG
+                    NAME_REF
+                      IDENT "Item"
+                    EQ "="
+                    PATH_TYPE
+                      PATH
+                        PATH_SEGMENT
+                          NAME_REF
+                            IDENT "Foo"
+                          GENERIC_ARG_LIST
+                            L_ANGLE "<"
+                            LIFETIME_ARG
+                              LIFETIME
+                                LIFETIME_IDENT "'a"
+                            R_ANGLE ">"
+                  R_ANGLE ">"
+        WHITESPACE " "
+        PLUS "+"
+        WHITESPACE " "
+        TYPE_BOUND
+          LIFETIME
+            LIFETIME_IDENT "'a"
     SEMICOLON ";"
   WHITESPACE "\n"
   TYPE_ALIAS
     TYPE_KW "type"
     WHITESPACE " "
     NAME
-      IDENT "DynRef"
+      IDENT "A"
     WHITESPACE " "
     EQ "="
     WHITESPACE " "
@@ -40,111 +64,38 @@ SOURCE_FILE
               PATH
                 PATH_SEGMENT
                   NAME_REF
-                    IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynLt"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      WHITESPACE " "
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          LIFETIME
-            LIFETIME_IDENT "'a"
-        WHITESPACE " "
-        PLUS "+"
-        WHITESPACE " "
-        TYPE_BOUND
-          PATH_TYPE
-            PATH
-              PATH_SEGMENT
-                NAME_REF
-                  IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynQuestion"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      WHITESPACE " "
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          QUESTION "?"
-          PATH_TYPE
-            PATH
-              PATH_SEGMENT
-                NAME_REF
-                  IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynFor"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      WHITESPACE " "
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          FOR_TYPE
-            FOR_KW "for"
-            GENERIC_PARAM_LIST
-              L_ANGLE "<"
-              LIFETIME_PARAM
-                LIFETIME
-                  LIFETIME_IDENT "'a"
-              R_ANGLE ">"
-            WHITESPACE " "
-            PATH_TYPE
-              PATH
-                PATH_SEGMENT
-                  NAME_REF
-                    IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynParen"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          L_PAREN "("
-          PATH_TYPE
-            PATH
-              PATH_SEGMENT
-                NAME_REF
-                  IDENT "Path"
-          R_PAREN ")"
+                    IDENT "Iterator"
+                  GENERIC_ARG_LIST
+                    L_ANGLE "<"
+                    ASSOC_TYPE_ARG
+                      NAME_REF
+                        IDENT "Item"
+                      EQ "="
+                      PATH_TYPE
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "Foo"
+                            GENERIC_ARG_LIST
+                              L_ANGLE "<"
+                              LIFETIME_ARG
+                                LIFETIME
+                                  LIFETIME_IDENT "'a"
+                              R_ANGLE ">"
+                    R_ANGLE ">"
+          WHITESPACE " "
+          PLUS "+"
+          WHITESPACE " "
+          TYPE_BOUND
+            LIFETIME
+              LIFETIME_IDENT "'a"
     SEMICOLON ";"
   WHITESPACE "\n"
   TYPE_ALIAS
     TYPE_KW "type"
     WHITESPACE " "
     NAME
-      IDENT "Path"
+      IDENT "A"
     WHITESPACE " "
     EQ "="
     WHITESPACE " "
@@ -160,27 +111,3 @@ SOURCE_FILE
             IDENT "Path"
     SEMICOLON ";"
   WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "Generic"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    PATH_TYPE
-      PATH
-        PATH_SEGMENT
-          NAME_REF
-            IDENT "dyn"
-          GENERIC_ARG_LIST
-            L_ANGLE "<"
-            TYPE_ARG
-              PATH_TYPE
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "Path"
-            R_ANGLE ">"
-    SEMICOLON ";"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
index c4ef1f2b7a..c4941e65bf 100644
--- a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
+++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
@@ -1,9 +1,4 @@
 // 2015
-type DynPlain = dyn Path;
-type DynRef = &dyn Path;
-type DynLt = dyn 'a + Path;
-type DynQuestion = dyn ?Path;
-type DynFor = dyn for<'a> Path;
-type DynParen = dyn(Path);
-type Path = dyn::Path;
-type Generic = dyn<Path>;
+type A = dyn Iterator<Item=Foo<'a>> + 'a;
+type A = &dyn Iterator<Item=Foo<'a>> + 'a;
+type A = dyn::Path;