Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attach outer attributes to the elements they annotate #245

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

wetneb
Copy link

@wetneb wetneb commented Nov 14, 2024

Fixes #244.

I reviewed which syntax elements outer attributes can be applied to: https://doc.rust-lang.org/reference/attributes.html.
I am not sure about let_item: I couldn't find the corresponding item declaration in this document. Perhaps it's something that has been added recently and this document hasn't been updated yet (as the preamble states). It would be good if someone with a better knowledge of the language could confirm that. In doubt, I think the safe way is to allow them.

@wetneb
Copy link
Author

wetneb commented Dec 1, 2024

I have improved the description of the issue in #244 to make the problem clearer. Let me know if the issue or this PR needs any further improvement.

@wetneb
Copy link
Author

wetneb commented Jan 6, 2025

Fishing for reviews for this one… perhaps @amaanq has thoughts on this PR?

@wetneb wetneb force-pushed the 244-attribute-parsing branch from 5e6b8aa to 9a51d0a Compare March 19, 2025 09:58
@wetneb
Copy link
Author

wetneb commented Mar 19, 2025

Hello @maxbrunsfeld, I am reaching out to you as you seem to be the only person who merged PRs in this repository over the past months.

I wonder if I can hope to get this PR reviewed at some point, or if there is anything I could do to help with the PR backlog on this repository. Intuitively, I would suggest empowering more contributors to review PRs. For instance, @m4rch3n1ng has been making a lot of (in my opinion) very valuable contributions. Perhaps it would be worth reaching out to them to see if they would be interested in helping out with PR reviews?

Thanks for your amazing work!

Copy link
Contributor

@m4rch3n1ng m4rch3n1ng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i know that i don't have any rights for this, but since i have been asked i figured i'd just give my, well, solicited review here.

this pr introduces a pretty major regression and there is now a lot of valid code that rustc accepts, tree-sitter-rust on the master branch accepts, but this pr doesn't. i have compiled a few examples here:

fn main() {
    #[cfg(unix)]
    {
        println!("unix");
    }
}
#[allow(unused_macros)]
macro_rules! cfg {
    () => {};
}

fn main() {}
fn main() {
    #[allow(unused_parens)]
    match 0 {
        (pat) => assert_eq!(pat, 0),
    }
}

additionally, while rustc doesn't want to compile the following code, it is happy to give me an ast-tree and will compile on nightly with a feature flag:

fn test(_: (u8, u8, u8, u8, u8)) {}

fn main() {
    #[rustfmt::skip]
	test(
		(0, 1, 2, 3,
			4, )
	);
}

additionally here is a diff of all rustc tests, that are valid and compile with rustc, but have regressed with this pr to no longer work with tree-sitter. this, in total, comes out to 87 tests:

diff
--- .tmp/list.txt.now	2025-03-19 15:13:08.220936488 +0100
+++ .tmp/list.txt.244-attribute-parsing	2025-03-19 15:11:21.650635556 +0100
@@ -1,4 +1,7 @@
+tests/assembly/closure-inherit-target-feature.rs
 tests/codegen/async-closure-debug.rs
+tests/codegen/coroutine-debug-msvc.rs
+tests/codegen/coroutine-debug.rs
 tests/codegen/dst-offset.rs
 tests/codegen/function-arguments.rs
 tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs
@@ -12,25 +15,39 @@
 tests/crashes/104685.rs
 tests/crashes/116979.rs
 tests/crashes/123887.rs
+tests/debuginfo/auxiliary/macro-stepping.rs
+tests/debuginfo/collapse-debuginfo-external-attr.rs
+tests/debuginfo/collapse-debuginfo-external-flag-overriden-by-attr.rs
+tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs
+tests/debuginfo/collapse-debuginfo-static-external.rs
+tests/debuginfo/collapse-debuginfo-static.rs
+tests/debuginfo/collapse-debuginfo-with-attr-flag.rs
+tests/debuginfo/collapse-debuginfo-with-attr.rs
 tests/debuginfo/coroutine-closure.rs
 tests/debuginfo/coroutine-locals.rs
 tests/debuginfo/coroutine-objects.rs
 tests/debuginfo/function-names.rs
 tests/debuginfo/issue-57822.rs
+tests/debuginfo/skip_second_statement_collapse.rs
 tests/debuginfo/type-names.rs
+tests/incremental/macro_export.rs
 tests/mir-opt/async_closure_shims.rs
 tests/mir-opt/building/match/never_patterns.rs
 tests/mir-opt/coroutine_drop_cleanup.rs
 tests/mir-opt/coroutine_storage_dead_unwind.rs
 tests/mir-opt/coroutine_tiny.rs
+tests/mir-opt/inline/inline_coroutine.rs
 tests/mir-opt/tail_call_drops.rs
 tests/pretty/delegation.rs
 tests/pretty/macro.rs
 tests/pretty/stmt_expr_attributes.rs
 tests/pretty/yeet-expr.rs
+tests/run-make/lto-linkage-used-attr/lib.rs
 tests/rustdoc-json/traits/trait_alias.rs
 tests/rustdoc/macro-in-async-block.rs
 tests/rustdoc/macro-in-closure.rs
+tests/ui/abi/simd-abi-checks-avx.rs
+tests/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs
 tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty.rs
 tests/ui/associated-consts/assoc-const-eq-supertraits.rs
 tests/ui/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs
@@ -67,15 +84,20 @@
 tests/ui/async-await/for-await.rs
 tests/ui/async-await/issues/issue-65419/issue-65419-coroutine-resume-after-completion.rs
 tests/ui/async-await/no-params-non-move-async-closure.rs
+tests/ui/async-await/non-trivial-drop.rs
 tests/ui/async-await/pin-ergonomics/sugar.rs
 tests/ui/async-await/return-type-notation/issue-110963-late.rs
 tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.rs
 tests/ui/async-await/return-type-notation/rtn-implied-in-supertrait.rs
 tests/ui/async-await/return-type-notation/super-method-bound.rs
 tests/ui/async-await/return-type-notation/supertrait-bound.rs
+tests/ui/attributes/invalid_macro_export_argument.rs
 tests/ui/attributes/issue-115264-pat-field.rs
+tests/ui/attributes/tool_attributes.rs
+tests/ui/binding/pat-tuple-7.rs
 tests/ui/borrowck/alias-liveness/higher-ranked.rs
 tests/ui/borrowck/alias-liveness/rtn-static.rs
+tests/ui/check-cfg/allow-macro-cfg.rs
 tests/ui/closures/binder/async-closure-with-binder.rs
 tests/ui/closures/binder/late-bound-in-body.rs
 tests/ui/closures/binder/nested-closures-regions.rs
@@ -89,40 +111,66 @@
 tests/ui/const-generics/generic_const_exprs/drop_impl.rs
 tests/ui/const-generics/generic_const_exprs/issue-90847.rs
 tests/ui/const-generics/issues/issue-88119.rs
+tests/ui/const-generics/min_const_generics/macro.rs
 tests/ui/consts/const-eval/issue-55541.rs
 tests/ui/consts/const-labeled-break.rs
+tests/ui/consts/issue-65348.rs
+tests/ui/consts/promotion.rs
+tests/ui/consts/references.rs
 tests/ui/consts/trait_specialization.rs
 tests/ui/coroutine/addassign-yield.rs
 tests/ui/coroutine/async-gen-deduce-yield.rs
 tests/ui/coroutine/async-gen-yield-ty-is-unit.rs
 tests/ui/coroutine/async_gen_fn_iter.rs
 tests/ui/coroutine/borrow-in-tail-expr.rs
+tests/ui/coroutine/clone-rpit.rs
 tests/ui/coroutine/conditional-drop.rs
 tests/ui/coroutine/coroutine-resume-after-panic.rs
 tests/ui/coroutine/derived-drop-parent-expr.rs
+tests/ui/coroutine/discriminant.rs
 tests/ui/coroutine/drop-and-replace.rs
 tests/ui/coroutine/drop-control-flow.rs
 tests/ui/coroutine/drop-env.rs
 tests/ui/coroutine/drop-track-addassign-yield.rs
 tests/ui/coroutine/drop-tracking-yielding-in-match-guards.rs
 tests/ui/coroutine/issue-110929-coroutine-conflict-error-ice.rs
+tests/ui/coroutine/issue-44197.rs
+tests/ui/coroutine/issue-52304.rs
+tests/ui/coroutine/issue-52398.rs
+tests/ui/coroutine/issue-57084.rs
+tests/ui/coroutine/issue-58888.rs
 tests/ui/coroutine/issue-61442-stmt-expr-with-drop.rs
+tests/ui/coroutine/issue-69017.rs
+tests/ui/coroutine/issue-69039.rs
+tests/ui/coroutine/issue-87142.rs
+tests/ui/coroutine/iterator-count.rs
 tests/ui/coroutine/live-upvar-across-yield.rs
+tests/ui/coroutine/match-bindings.rs
 tests/ui/coroutine/nested_coroutine.rs
 tests/ui/coroutine/niche-in-coroutine.rs
+tests/ui/coroutine/other-attribute-on-gen.rs
 tests/ui/coroutine/overlap-locals.rs
 tests/ui/coroutine/panic-drops-resume.rs
 tests/ui/coroutine/panic-drops.rs
 tests/ui/coroutine/panic-safe.rs
+tests/ui/coroutine/reborrow-mut-upvar.rs
 tests/ui/coroutine/reinit-in-match-guard.rs
 tests/ui/coroutine/resume-after-return.rs
 tests/ui/coroutine/resume-arg-size.rs
 tests/ui/coroutine/resume-live-across-yield.rs
 tests/ui/coroutine/return-types-diverge.rs
+tests/ui/coroutine/size-moved-locals.rs
 tests/ui/coroutine/smoke-resume-args.rs
 tests/ui/coroutine/static-coroutine.rs
 tests/ui/coroutine/static-mut-reference-across-yield.rs
+tests/ui/coroutine/static-reference-across-yield.rs
+tests/ui/coroutine/too-live-local-in-immovable-gen.rs
 tests/ui/coroutine/uninhabited-field.rs
+tests/ui/coroutine/yield-in-args-rev.rs
+tests/ui/coroutine/yield-in-initializer.rs
+tests/ui/coroutine/yield-subtype.rs
+tests/ui/cross/cross-crate-macro-backtrace/auxiliary/extern_macro_crate.rs
+tests/ui/debuginfo/debuginfo-inline-callsite-location-macro-1.rs
 tests/ui/definition-reachable/private-non-types.rs
 tests/ui/definition-reachable/private-types.rs
 tests/ui/delegation/body-identity-glob.rs
@@ -150,6 +198,7 @@
 tests/ui/derives/derive-hygiene.rs
 tests/ui/destructuring-assignment/struct_destructure.rs
 tests/ui/drop/dynamic-drop.rs
+tests/ui/drop/lint-if-let-rescope.rs
 tests/ui/dyn-compatibility/assoc_const_bounds.rs
 tests/ui/dyn-compatibility/assoc_const_bounds_sized.rs
 tests/ui/dyn-star/box.rs
@@ -173,8 +222,11 @@
 tests/ui/explicit-tail-calls/ctfe-collatz-multi-rec.rs
 tests/ui/explicit-tail-calls/return-lifetime-sub.rs
 tests/ui/explicit-tail-calls/two-phase.rs
+tests/ui/expr/if/attrs/builtin-if-attr.rs
+tests/ui/feature-gates/feature-gate-asm_experimental_arch.rs
 tests/ui/feature-gates/feature-gate-trivial_bounds-lint.rs
 tests/ui/for-loop-while/loop-break-value.rs
+tests/ui/for-loop-while/loop-label-shadowing.rs
 tests/ui/generic-associated-types/collections.rs
 tests/ui/generic-const-items/assoc-const-AnonConst-ice-108220.rs
 tests/ui/generic-const-items/associated-const-equality.rs
@@ -185,37 +237,62 @@
 tests/ui/half-open-range-patterns/range_pat_interactions0.rs
 tests/ui/higher-ranked/trait-bounds/issue-95230.rs
 tests/ui/hygiene/assoc_ty_bindings.rs
+tests/ui/hygiene/eager-from-opaque-2.rs
 tests/ui/hygiene/generic_params.rs
 tests/ui/hygiene/issue-44128.rs
 tests/ui/hygiene/items.rs
 tests/ui/hygiene/lexical.rs
+tests/ui/hygiene/macro-metavars-transparent.rs
 tests/ui/hygiene/specialization.rs
 tests/ui/hygiene/stdlib-prelude-from-opaque-late.rs
 tests/ui/hygiene/trait_items-2.rs
 tests/ui/hygiene/traits-in-scope.rs
 tests/ui/impl-trait/equality-rpass.rs
+tests/ui/impl-trait/lifetimes.rs
+tests/ui/impl-trait/recursive-coroutine-boxed.rs
 tests/ui/imports/local-modularized-tricky-pass-2.rs
 tests/ui/inline-const/const-match-pat-inference.rs
 tests/ui/inline-const/const-match-pat-range.rs
 tests/ui/inline-const/pat-unsafe.rs
+tests/ui/issues/issue-30530.rs
 tests/ui/issues/issue-36116.rs
 tests/ui/issues/issue-37051.rs
 tests/ui/issues/issue-37733.rs
 tests/ui/issues/issue-43692.rs
 tests/ui/issues/issue-49632.rs
 tests/ui/issues/issue-61475.rs
+tests/ui/iterators/into-iter-on-arrays-lint.rs
+tests/ui/iterators/into-iter-on-boxed-slices-lint.rs
 tests/ui/lifetimes/elided-lifetime-in-param-pat.rs
+tests/ui/lint/auxiliary/lint_stability.rs
 tests/ui/lint/dead-code/anon-const-in-pat.rs
+tests/ui/lint/expansion-time.rs
+tests/ui/lint/inert-attr-macro.rs
 tests/ui/lint/invalid-nan-comparison-suggestion.rs
+tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs
+tests/ui/lint/lint-level-macro-def-mod.rs
+tests/ui/lint/lint-level-macro-def.rs
+tests/ui/lint/lints-on-stmt-not-overridden-130142.rs
+tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs
+tests/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.rs
+tests/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.rs
+tests/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_early_lints.rs
 tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.rs
 tests/ui/lint/unused/trait-alias-supertrait.rs
 tests/ui/liveness/liveness-upvars.rs
+tests/ui/macros/auxiliary/issue-19163.rs
+tests/ui/macros/auxiliary/macro_with_super_1.rs
+tests/ui/macros/auxiliary/or-pattern.rs
 tests/ui/macros/edition-macro-pats.rs
 tests/ui/macros/expr_2021.rs
+tests/ui/macros/issue-33185.rs
 tests/ui/macros/issue-44127.rs
+tests/ui/macros/issue-52169.rs
 tests/ui/macros/issue-63102.rs
 tests/ui/macros/issue-77475.rs
+tests/ui/macros/macro-crate-use.rs
 tests/ui/macros/macro-in-fn.rs
+tests/ui/macros/macro-literal.rs
 tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs
 tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs
 tests/ui/macros/macro-nested_expr.rs
@@ -229,10 +306,14 @@
 tests/ui/match/postfix-match/pf-match-chain.rs
 tests/ui/mir/issue-68841.rs
 tests/ui/nll/assign-while-to-immutable.rs
+tests/ui/nll/coroutine-distinct-lifetime.rs
 tests/ui/nll/empty-type-predicate.rs
+tests/ui/nll/extra-unused-mut.rs
 tests/ui/nll/issue-112604-closure-output-normalize.rs
+tests/ui/nll/issue-48623-coroutine.rs
 tests/ui/packed/packed-struct-drop-aligned.rs
 tests/ui/parser/impls-nested-within-fns-semantic-1.rs
+tests/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs
 tests/ui/parser/issues/issue-88583-union-as-ident.rs
 tests/ui/parser/keyword-union-as-identifier.rs
 tests/ui/parser/parser-unicode-whitespace.rs
@@ -245,7 +326,10 @@
 tests/ui/rfcs/rfc-0000-never_patterns/diverges.rs
 tests/ui/rfcs/rfc-0000-never_patterns/use-bindings.rs
 tests/ui/rfcs/rfc-2008-non-exhaustive/structs_same_crate.rs
+tests/ui/rfcs/rfc-2091-track-caller/macro-declaration.rs
 tests/ui/rfcs/rfc-2091-track-caller/tracked-closure.rs
+tests/ui/rfcs/rfc-2294-if-let-guard/run-pass.rs
+tests/ui/rfcs/rfc-2396-target_feature-11/issue-108655-inline-always-closure.rs
 tests/ui/rust-2018/uniform-paths/from-decl-macro.rs
 tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs
 tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs
@@ -261,6 +345,7 @@
 tests/ui/specialization/specialization-projection-alias.rs
 tests/ui/specialization/specialization-projection.rs
 tests/ui/specialization/transmute-specialization.rs
+tests/ui/stability-attribute/auxiliary/lint-stability.rs
 tests/ui/structs-enums/numeric-fields.rs
 tests/ui/structs-enums/struct-aliases.rs
 tests/ui/structs/default-field-values/use-normalized-ty-for-default-struct-value.rs
@@ -328,7 +413,9 @@
 tests/ui/trivial-bounds/trivial-bounds-object.rs
 tests/ui/try-trait/yeet-for-option.rs
 tests/ui/try-trait/yeet-for-result.rs
+tests/ui/type-alias-impl-trait/issue-53678-coroutine-and-const-fn.rs
 tests/ui/type-alias-impl-trait/issue-57611-trait-alias.rs
+tests/ui/type-alias-impl-trait/issue-58662-coroutine-with-lifetime.rs
 tests/ui/type_length_limit.rs
 tests/ui/typeck/auxiliary/tdticc_coherence_lib.rs
 tests/ui/underscore-imports/hygiene-2.rs

@wetneb
Copy link
Author

wetneb commented Mar 19, 2025

@m4rch3n1ng amazing, thank you so much for having a look!

It seems that there are quite a few more places where outer annotations are allowed. Your method of comparing to rustc's behaviour is obviously much more fruitful than my attempt at following the specs.

I am keen to adjust my PR accordingly to add outer attributes in more places as needed. Do you think that's a sensible approach, or would you avoid this move for other reasons?

@m4rch3n1ng
Copy link
Contributor

m4rch3n1ng commented Mar 19, 2025

i think it should be doable, but i don't know if it's "worth it". it's not really tree-sitters job to like check if the rust code is completely valid, it should just be able to highlight it.

i could see it being useful for #246, where it could potentially help with associating doc comments with the types they belong to, but even that use case is very niche and i think should only be considered if someone actually needs it for something, and i don't even really see an an actual use case for this pr.

especially because it will make highlighting worse in some cases: if i write a #[cfg(feature = ...)] in "anticipation" of writing the corresponding function declaration next, then i still want that attribute to be highlighted, even if it is not yet valid.

for this reason there is quite a bit more syntax that tree-sitter accepts, that is also not valid rust syntax where i don't think it would be of any benefit to restrict them exactly to where they would be valid, because those errors make more sense to be reported by rust-analyzer instead of trying to handle them in tree-sitter and breaking syntax highlighting.

@wetneb
Copy link
Author

wetneb commented Mar 20, 2025

Sure, I can expand on the motivation for being more faithful to the Rust syntax. It is mostly useful outside of the highlighting use case indeed. For instance, ast-grep uses tree-sitter to do syntax aware search and replace on source code, letting search patterns capture entire AST nodes. In this context, being able to reliably capture an entire declaration with the outer attributes that are attached to it would be quite useful.
My motivation for this PR is mergiraf, a syntax aware merge driver for Git. For instance, to be able to merge two additions of different methods in an impl block in the same location, it is useful to be able to properly attach all the attributes that are attached to each method. And similarly for docstrings.

I do recognize that tree-sitter is primarily developed for syntax highlighting, so perhaps fidelity of the AST structures isn't actually a priority. Perhaps there's a case for forking this grammar to tweak it for those different use cases where fidelity is more important.

especially because it will make highlighting worse in some cases: if i write a #[cfg(feature = ...)] in "anticipation" of writing the corresponding function declaration next, then i still want that attribute to be highlighted, even if it is not yet valid.

Isn't that something that happens all the time when parsing incomplete input though? I'd expect it to parse the attribute declaration generate a MISSING error token after, which isn't disruptive for the user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug: Incorrect parsing of attributes
2 participants