summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/parser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /src/tools/rust-analyzer/crates/parser
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/parser')
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar.rs136
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs19
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lib.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/shortcuts.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast48
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast44
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast63
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast33
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs1
16 files changed, 199 insertions, 168 deletions
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 1814e0e54..333318f53 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -165,6 +165,40 @@ pub(crate) mod entry {
}
m.complete(p, ERROR);
}
+
+ pub(crate) fn eager_macro_input(p: &mut Parser<'_>) {
+ let m = p.start();
+
+ let closing_paren_kind = match p.current() {
+ T!['{'] => T!['}'],
+ T!['('] => T![')'],
+ T!['['] => T![']'],
+ _ => {
+ p.error("expected `{`, `[`, `(`");
+ while !p.at(EOF) {
+ p.bump_any();
+ }
+ m.complete(p, ERROR);
+ return;
+ }
+ };
+ p.bump_any();
+ while !p.at(EOF) && !p.at(closing_paren_kind) {
+ expressions::expr(p);
+ if !p.at(EOF) && !p.at(closing_paren_kind) {
+ p.expect(T![,]);
+ }
+ }
+ p.expect(closing_paren_kind);
+ if p.at(EOF) {
+ m.complete(p, MACRO_EAGER_INPUT);
+ return;
+ }
+ while !p.at(EOF) {
+ p.bump_any();
+ }
+ m.complete(p, ERROR);
+ }
}
}
@@ -211,70 +245,54 @@ impl BlockLike {
const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]);
fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
- match p.current() {
- T![pub] => {
- let m = p.start();
- p.bump(T![pub]);
- if p.at(T!['(']) {
- match p.nth(1) {
- // test crate_visibility
- // pub(crate) struct S;
- // pub(self) struct S;
- // pub(super) struct S;
-
- // test_err crate_visibility_empty_recover
- // pub() struct S;
-
- // test pub_parens_typepath
- // struct B(pub (super::A));
- // struct B(pub (crate::A,));
- T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
- // If we are in a tuple struct, then the parens following `pub`
- // might be an tuple field, not part of the visibility. So in that
- // case we don't want to consume an identifier.
-
- // test pub_tuple_field
- // struct MyStruct(pub (u32, u32));
- // struct MyStruct(pub (u32));
- // struct MyStruct(pub ());
- if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
- p.bump(T!['(']);
- paths::use_path(p);
- p.expect(T![')']);
- }
- }
- // test crate_visibility_in
- // pub(in super::A) struct S;
- // pub(in crate) struct S;
- T![in] => {
- p.bump(T!['(']);
- p.bump(T![in]);
- paths::use_path(p);
- p.expect(T![')']);
- }
- _ => {}
+ if !p.at(T![pub]) {
+ return false;
+ }
+
+ let m = p.start();
+ p.bump(T![pub]);
+ if p.at(T!['(']) {
+ match p.nth(1) {
+ // test crate_visibility
+ // pub(crate) struct S;
+ // pub(self) struct S;
+ // pub(super) struct S;
+
+ // test_err crate_visibility_empty_recover
+ // pub() struct S;
+
+ // test pub_parens_typepath
+ // struct B(pub (super::A));
+ // struct B(pub (crate::A,));
+ T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
+ // If we are in a tuple struct, then the parens following `pub`
+ // might be an tuple field, not part of the visibility. So in that
+ // case we don't want to consume an identifier.
+
+ // test pub_tuple_field
+ // struct MyStruct(pub (u32, u32));
+ // struct MyStruct(pub (u32));
+ // struct MyStruct(pub ());
+ if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
+ p.bump(T!['(']);
+ paths::use_path(p);
+ p.expect(T![')']);
}
}
- m.complete(p, VISIBILITY);
- true
- }
- // test crate_keyword_vis
- // crate fn main() { }
- // struct S { crate field: u32 }
- // struct T(crate u32);
- T![crate] => {
- if p.nth_at(1, T![::]) {
- // test crate_keyword_path
- // fn foo() { crate::foo(); }
- return false;
+ // test crate_visibility_in
+ // pub(in super::A) struct S;
+ // pub(in crate) struct S;
+ T![in] => {
+ p.bump(T!['(']);
+ p.bump(T![in]);
+ paths::use_path(p);
+ p.expect(T![')']);
}
- let m = p.start();
- p.bump(T![crate]);
- m.complete(p, VISIBILITY);
- true
+ _ => {}
}
- _ => false,
}
+ m.complete(p, VISIBILITY);
+ true
}
fn opt_rename(p: &mut Parser<'_>) {
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
index e589b6993..211af98e6 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
@@ -32,6 +32,9 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
])
.union(types::TYPE_FIRST);
+// Despite its name, it can also be used for generic param list.
+const GENERIC_ARG_RECOVERY_SET: TokenSet = TokenSet::new(&[T![>], T![,]]);
+
// test generic_arg
// type T = S<i32>;
fn generic_arg(p: &mut Parser<'_>) -> bool {
@@ -55,6 +58,15 @@ fn generic_arg(p: &mut Parser<'_>) -> bool {
// test assoc_type_eq
// type T = StreamingIterator<Item<'a> = &'a T>;
types::type_(p);
+ } else if p.at_ts(GENERIC_ARG_RECOVERY_SET) {
+ // Although `const_arg()` recovers as expected, we want to
+ // handle those here to give the following message because
+ // we don't know whether this associated item is a type or
+ // const at this point.
+
+ // test_err recover_from_missing_assoc_item_binding
+ // fn f() -> impl Iterator<Item = , Item = > {}
+ p.error("missing associated item binding");
} else {
// test assoc_const_eq
// fn foo<F: Foo<N=3>>() {}
@@ -141,12 +153,17 @@ pub(super) fn const_arg_expr(p: &mut Parser<'_>) {
expressions::literal(p);
lm.complete(p, PREFIX_EXPR);
}
- _ => {
+ _ if paths::is_use_path_start(p) => {
// This shouldn't be hit by `const_arg`
let lm = p.start();
paths::use_path(p);
lm.complete(p, PATH_EXPR);
}
+ _ => {
+ // test_err recover_from_missing_const_default
+ // struct A<const N: i32 = , const M: i32 =>;
+ p.err_recover("expected a generic const argument", GENERIC_ARG_RECOVERY_SET);
+ }
}
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
index 7fcf938ba..8ed1c84c4 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
@@ -79,10 +79,9 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
p.error("missing type for const parameter");
}
- if p.at(T![=]) {
+ if p.eat(T![=]) {
// test const_param_default_literal
// struct A<const N: i32 = -1>;
- p.bump(T![=]);
// test const_param_default_expression
// struct A<const N: i32 = { 1 }>;
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
index 1c056819f..4e850b1f7 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
@@ -328,9 +328,6 @@ fn macro_rules(p: &mut Parser<'_>, m: Marker) {
p.bump_remap(T![macro_rules]);
p.expect(T![!]);
- if p.at(IDENT) {
- name(p);
- }
// Special-case `macro_rules! try`.
// This is a hack until we do proper edition support
@@ -340,6 +337,8 @@ fn macro_rules(p: &mut Parser<'_>, m: Marker) {
let m = p.start();
p.bump_remap(IDENT);
m.complete(p, NAME);
+ } else {
+ name(p);
}
match p.current() {
diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs
index 1aba1f767..c155e8aaf 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs
@@ -75,6 +75,8 @@ pub enum TopEntryPoint {
/// Edge case -- macros generally don't expand to attributes, with the
/// exception of `cfg_attr` which does!
MetaItem,
+ /// Edge case 2 -- eager macros expand their input to a delimited list of comma separated expressions
+ MacroEagerInput,
}
impl TopEntryPoint {
@@ -87,6 +89,7 @@ impl TopEntryPoint {
TopEntryPoint::Type => grammar::entry::top::type_,
TopEntryPoint::Expr => grammar::entry::top::expr,
TopEntryPoint::MetaItem => grammar::entry::top::meta_item,
+ TopEntryPoint::MacroEagerInput => grammar::entry::top::eager_macro_input,
};
let mut p = parser::Parser::new(input);
entry_point(&mut p);
diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
index 5cdb39700..53cdad649 100644
--- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
@@ -24,7 +24,7 @@ pub enum StrStep<'a> {
Error { msg: &'a str, pos: usize },
}
-impl<'a> LexedStr<'a> {
+impl LexedStr<'_> {
pub fn to_input(&self) -> crate::Input {
let mut res = crate::Input::default();
let mut was_joint = false;
@@ -223,7 +223,8 @@ fn n_attached_trivias<'a>(
) -> usize {
match kind {
CONST | ENUM | FN | IMPL | MACRO_CALL | MACRO_DEF | MACRO_RULES | MODULE | RECORD_FIELD
- | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT => {
+ | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT
+ | EXTERN_CRATE => {
let mut res = 0;
let mut trivias = trivias.enumerate().peekable();
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index a8fbcfacf..48f407623 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -262,6 +262,7 @@ pub enum SyntaxKind {
TYPE_BOUND_LIST,
MACRO_ITEMS,
MACRO_STMTS,
+ MACRO_EAGER_INPUT,
#[doc(hidden)]
__LAST,
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
index 11f9c34ab..2f3c7febc 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
@@ -6,7 +6,6 @@ fn vis() {
check(PrefixEntryPoint::Vis, "fn foo() {}", "");
check(PrefixEntryPoint::Vis, "pub(fn foo() {}", "pub");
check(PrefixEntryPoint::Vis, "pub(crate fn foo() {}", "pub(crate");
- check(PrefixEntryPoint::Vis, "crate fn foo() {}", "crate");
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast
new file mode 100644
index 000000000..fc59db84e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rast
@@ -0,0 +1,48 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "f"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ IMPL_TRAIT_TYPE
+ IMPL_KW "impl"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Iterator"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ R_ANGLE ">"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 30: missing associated item binding
+error 39: missing associated item binding
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs
new file mode 100644
index 000000000..e484e433a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0021_recover_from_missing_assoc_item_binding.rs
@@ -0,0 +1 @@
+fn f() -> impl Iterator<Item = , Item = > {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
new file mode 100644
index 000000000..809ad1b8d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
@@ -0,0 +1,44 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ GENERIC_PARAM_LIST
+ L_ANGLE "<"
+ CONST_PARAM
+ CONST_KW "const"
+ WHITESPACE " "
+ NAME
+ IDENT "N"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ COMMA ","
+ WHITESPACE " "
+ CONST_PARAM
+ CONST_KW "const"
+ WHITESPACE " "
+ NAME
+ IDENT "M"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ EQ "="
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 23: expected a generic const argument
+error 40: expected a generic const argument
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs
new file mode 100644
index 000000000..5bab13da9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rs
@@ -0,0 +1 @@
+struct A<const N: i32 = , const M: i32 =>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast
deleted file mode 100644
index 07b0210e4..000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rast
+++ /dev/null
@@ -1,63 +0,0 @@
-SOURCE_FILE
- FN
- VISIBILITY
- CRATE_KW "crate"
- WHITESPACE " "
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "main"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE " "
- R_CURLY "}"
- WHITESPACE "\n"
- STRUCT
- STRUCT_KW "struct"
- WHITESPACE " "
- NAME
- IDENT "S"
- WHITESPACE " "
- RECORD_FIELD_LIST
- L_CURLY "{"
- WHITESPACE " "
- RECORD_FIELD
- VISIBILITY
- CRATE_KW "crate"
- WHITESPACE " "
- NAME
- IDENT "field"
- COLON ":"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "u32"
- WHITESPACE " "
- R_CURLY "}"
- WHITESPACE "\n"
- STRUCT
- STRUCT_KW "struct"
- WHITESPACE " "
- NAME
- IDENT "T"
- TUPLE_FIELD_LIST
- L_PAREN "("
- TUPLE_FIELD
- VISIBILITY
- CRATE_KW "crate"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "u32"
- R_PAREN ")"
- SEMICOLON ";"
- WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs
deleted file mode 100644
index e2b5f2161..000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0040_crate_keyword_vis.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-crate fn main() { }
-struct S { crate field: u32 }
-struct T(crate u32);
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast
deleted file mode 100644
index 8d9b61630..000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rast
+++ /dev/null
@@ -1,33 +0,0 @@
-SOURCE_FILE
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "foo"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE " "
- EXPR_STMT
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH
- PATH_SEGMENT
- NAME_REF
- CRATE_KW "crate"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "foo"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
- SEMICOLON ";"
- WHITESPACE " "
- R_CURLY "}"
- WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs
deleted file mode 100644
index 0f454d121..000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0125_crate_keyword_path.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn foo() { crate::foo(); }