aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock23
-rw-r--r--Cargo.toml1
-rw-r--r--crates/base_db/src/fixture.rs2
-rw-r--r--crates/base_db/src/lib.rs3
-rw-r--r--crates/cfg/src/cfg_expr.rs2
-rw-r--r--crates/cfg/src/lib.rs2
-rw-r--r--crates/hir/Cargo.toml1
-rw-r--r--crates/hir/src/diagnostics.rs297
-rw-r--r--crates/hir/src/lib.rs407
-rw-r--r--crates/hir/src/semantics.rs109
-rw-r--r--crates/hir/src/semantics/source_to_def.rs121
-rw-r--r--crates/hir/src/source_analyzer.rs6
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/attr.rs32
-rw-r--r--crates/hir_def/src/body/lower.rs8
-rw-r--r--crates/hir_def/src/body/scope.rs2
-rw-r--r--crates/hir_def/src/body/tests.rs149
-rw-r--r--crates/hir_def/src/body/tests/block.rs10
-rw-r--r--crates/hir_def/src/builtin_attr.rs38
-rw-r--r--crates/hir_def/src/child_by_source.rs4
-rw-r--r--crates/hir_def/src/data.rs17
-rw-r--r--crates/hir_def/src/db.rs3
-rw-r--r--crates/hir_def/src/generics.rs4
-rw-r--r--crates/hir_def/src/import_map.rs169
-rw-r--r--crates/hir_def/src/item_scope.rs19
-rw-r--r--crates/hir_def/src/item_tree/lower.rs17
-rw-r--r--crates/hir_def/src/item_tree/pretty.rs2
-rw-r--r--crates/hir_def/src/item_tree/tests.rs38
-rw-r--r--crates/hir_def/src/keys.rs3
-rw-r--r--crates/hir_def/src/lang_item.rs1
-rw-r--r--crates/hir_def/src/lib.rs86
-rw-r--r--crates/hir_def/src/nameres/collector.rs82
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs12
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs229
-rw-r--r--crates/hir_def/src/path/lower.rs4
-rw-r--r--crates/hir_def/src/per_ns.rs2
-rw-r--r--crates/hir_def/src/resolver.rs10
-rw-r--r--crates/hir_def/src/test_db.rs161
-rw-r--r--crates/hir_def/src/type_ref.rs16
-rw-r--r--crates/hir_expand/src/builtin_attr.rs67
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs14
-rw-r--r--crates/hir_expand/src/db.rs27
-rw-r--r--crates/hir_expand/src/eager.rs20
-rw-r--r--crates/hir_expand/src/hygiene.rs1
-rw-r--r--crates/hir_expand/src/input.rs25
-rw-r--r--crates/hir_expand/src/lib.rs30
-rw-r--r--crates/hir_expand/src/name.rs9
-rw-r--r--crates/hir_expand/src/proc_macro.rs8
-rw-r--r--crates/hir_ty/Cargo.toml2
-rw-r--r--crates/hir_ty/src/builder.rs2
-rw-r--r--crates/hir_ty/src/chalk_db.rs39
-rw-r--r--crates/hir_ty/src/consteval.rs2
-rw-r--r--crates/hir_ty/src/db.rs7
-rw-r--r--crates/hir_ty/src/diagnostics.rs772
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs357
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs507
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs955
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/usefulness.rs13
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs151
-rw-r--r--crates/hir_ty/src/diagnostics_sink.rs109
-rw-r--r--crates/hir_ty/src/infer.rs98
-rw-r--r--crates/hir_ty/src/infer/coerce.rs8
-rw-r--r--crates/hir_ty/src/infer/expr.rs23
-rw-r--r--crates/hir_ty/src/infer/pat.rs15
-rw-r--r--crates/hir_ty/src/infer/path.rs6
-rw-r--r--crates/hir_ty/src/interner.rs6
-rw-r--r--crates/hir_ty/src/lib.rs3
-rw-r--r--crates/hir_ty/src/lower.rs10
-rw-r--r--crates/hir_ty/src/method_resolution.rs98
-rw-r--r--crates/hir_ty/src/test_db.rs10
-rw-r--r--crates/hir_ty/src/tests.rs98
-rw-r--r--crates/hir_ty/src/tests/coercion.rs43
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs49
-rw-r--r--crates/hir_ty/src/tests/patterns.rs104
-rw-r--r--crates/hir_ty/src/tests/traits.rs67
-rw-r--r--crates/ide/Cargo.toml3
-rw-r--r--crates/ide/src/diagnostics.rs544
-rw-r--r--crates/ide/src/diagnostics/break_outside_of_loop.rs30
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs33
-rw-r--r--crates/ide/src/diagnostics/fixes.rs31
-rw-r--r--crates/ide/src/diagnostics/fixes/change_case.rs155
-rw-r--r--crates/ide/src/diagnostics/fixes/fill_missing_fields.rs217
-rw-r--r--crates/ide/src/diagnostics/fixes/remove_semicolon.rs41
-rw-r--r--crates/ide/src/diagnostics/fixes/replace_with_find_map.rs84
-rw-r--r--crates/ide/src/diagnostics/inactive_code.rs119
-rw-r--r--crates/ide/src/diagnostics/incorrect_case.rs488
-rw-r--r--crates/ide/src/diagnostics/macro_error.rs173
-rw-r--r--crates/ide/src/diagnostics/mismatched_arg_count.rs272
-rw-r--r--crates/ide/src/diagnostics/missing_fields.rs327
-rw-r--r--crates/ide/src/diagnostics/missing_match_arms.rs929
-rw-r--r--crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs (renamed from crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs)65
-rw-r--r--crates/ide/src/diagnostics/missing_unsafe.rs101
-rw-r--r--crates/ide/src/diagnostics/no_such_field.rs (renamed from crates/ide/src/diagnostics/fixes/create_field.rs)164
-rw-r--r--crates/ide/src/diagnostics/remove_this_semicolon.rs64
-rw-r--r--crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs182
-rw-r--r--crates/ide/src/diagnostics/unimplemented_builtin_macro.rs19
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs259
-rw-r--r--crates/ide/src/diagnostics/unresolved_extern_crate.rs49
-rw-r--r--crates/ide/src/diagnostics/unresolved_import.rs90
-rw-r--r--crates/ide/src/diagnostics/unresolved_macro_call.rs84
-rw-r--r--crates/ide/src/diagnostics/unresolved_module.rs (renamed from crates/ide/src/diagnostics/fixes/unresolved_module.rs)88
-rw-r--r--crates/ide/src/diagnostics/unresolved_proc_macro.rs30
-rw-r--r--crates/ide/src/doc_links.rs22
-rw-r--r--crates/ide/src/expand_macro.rs32
-rw-r--r--crates/ide/src/extend_selection.rs2
-rw-r--r--crates/ide/src/fixture.rs12
-rw-r--r--crates/ide/src/goto_definition.rs153
-rw-r--r--crates/ide/src/goto_implementation.rs6
-rw-r--r--crates/ide/src/hover.rs184
-rw-r--r--crates/ide/src/inlay_hints.rs1131
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/lib.rs12
-rw-r--r--crates/ide/src/prime_caches.rs7
-rw-r--r--crates/ide/src/references.rs22
-rw-r--r--crates/ide/src/references/rename.rs208
-rw-r--r--crates/ide/src/runnables.rs8
-rw-r--r--crates/ide/src/syntax_highlighting.rs34
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs15
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html9
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs9
-rw-r--r--crates/ide/src/typing/on_enter.rs6
-rw-r--r--crates/ide_assists/Cargo.toml2
-rw-r--r--crates/ide_assists/src/assist_context.rs3
-rw-r--r--crates/ide_assists/src/handlers/apply_demorgan.rs4
-rw-r--r--crates/ide_assists/src/handlers/change_visibility.rs11
-rw-r--r--crates/ide_assists/src/handlers/convert_comment_block.rs4
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs352
-rw-r--r--crates/ide_assists/src/handlers/early_return.rs4
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs10
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs169
-rw-r--r--crates/ide_assists/src/handlers/extract_type_alias.rs68
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs33
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs6
-rw-r--r--crates/ide_assists/src/handlers/fix_visibility.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_is_method.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_projection_method.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs8
-rw-r--r--crates/ide_assists/src/handlers/generate_getter.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_new.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_setter.rs2
-rw-r--r--crates/ide_assists/src/handlers/inline_local_variable.rs17
-rw-r--r--crates/ide_assists/src/handlers/remove_dbg.rs2
-rw-r--r--crates/ide_assists/src/handlers/remove_unused_param.rs30
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_if_let_with_match.rs2
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs2
-rw-r--r--crates/ide_assists/src/lib.rs8
-rw-r--r--crates/ide_assists/src/tests.rs2
-rw-r--r--crates/ide_assists/src/utils.rs2
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs2
-rw-r--r--crates/ide_completion/Cargo.toml2
-rw-r--r--crates/ide_completion/src/completions.rs62
-rw-r--r--crates/ide_completion/src/completions/attribute.rs28
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs80
-rw-r--r--crates/ide_completion/src/completions/attribute/lint.rs139
-rw-r--r--crates/ide_completion/src/completions/dot.rs8
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs27
-rw-r--r--crates/ide_completion/src/completions/keyword.rs25
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs48
-rw-r--r--crates/ide_completion/src/completions/pattern.rs24
-rw-r--r--crates/ide_completion/src/completions/postfix.rs22
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs22
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs84
-rw-r--r--crates/ide_completion/src/completions/snippet.rs14
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs18
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs130
-rw-r--r--crates/ide_completion/src/context.rs174
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/patterns.rs30
-rw-r--r--crates/ide_completion/src/render.rs400
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs12
-rw-r--r--crates/ide_completion/src/render/function.rs26
-rw-r--r--crates/ide_completion/src/render/macro_.rs4
-rw-r--r--crates/ide_completion/src/render/pattern.rs6
-rw-r--r--crates/ide_completion/src/render/type_alias.rs23
-rw-r--r--crates/ide_db/Cargo.toml2
-rw-r--r--crates/ide_db/src/call_info.rs5
-rw-r--r--crates/ide_db/src/defs.rs10
-rw-r--r--crates/ide_db/src/helpers.rs1
-rw-r--r--crates/ide_db/src/helpers/generated_lints.rs (renamed from crates/ide_completion/src/generated_lint_completions.rs)8108
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs4
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs18
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs15
-rw-r--r--crates/ide_db/src/helpers/merge_imports.rs2
-rw-r--r--crates/ide_db/src/lib.rs1
-rw-r--r--crates/ide_db/src/search.rs61
-rw-r--r--crates/ide_db/src/symbol_index.rs2
-rw-r--r--crates/ide_ssr/Cargo.toml2
-rw-r--r--crates/ide_ssr/src/matching.rs6
-rw-r--r--crates/ide_ssr/src/replacing.rs10
-rw-r--r--crates/ide_ssr/src/resolving.rs2
-rw-r--r--crates/ide_ssr/src/search.rs2
-rw-r--r--crates/ide_ssr/src/tests.rs4
-rw-r--r--crates/mbe/Cargo.toml2
-rw-r--r--crates/mbe/src/benchmark.rs2
-rw-r--r--crates/mbe/src/expander/matcher.rs41
-rw-r--r--crates/mbe/src/expander/transcriber.rs8
-rw-r--r--crates/mbe/src/lib.rs16
-rw-r--r--crates/mbe/src/parser.rs16
-rw-r--r--crates/mbe/src/subtree_source.rs6
-rw-r--r--crates/mbe/src/syntax_bridge.rs7
-rw-r--r--crates/mbe/src/tests/expand.rs44
-rw-r--r--crates/mbe/src/tt_iter.rs4
-rw-r--r--crates/parser/src/grammar/attributes.rs3
-rw-r--r--crates/parser/src/grammar/expressions.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs8
-rw-r--r--crates/parser/src/syntax_kind/generated.rs2
-rw-r--r--crates/paths/src/lib.rs2
-rw-r--r--crates/proc_macro_api/src/msg.rs4
-rw-r--r--crates/proc_macro_api/src/process.rs4
-rw-r--r--crates/proc_macro_api/src/version.rs2
-rw-r--r--crates/proc_macro_srv/src/dylib.rs11
-rw-r--r--crates/proc_macro_srv/src/lib.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/client.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/closure.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/handle.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/mod.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/server.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/diagnostic.rs11
-rw-r--r--crates/proc_macro_srv/src/proc_macro/mod.rs2
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs4
-rw-r--r--crates/proc_macro_srv/src/tests/utils.rs29
-rw-r--r--crates/proc_macro_test/Cargo.toml6
-rw-r--r--crates/proc_macro_test/build.rs48
-rw-r--r--crates/proc_macro_test/imp/.gitignore2
-rw-r--r--crates/proc_macro_test/imp/Cargo.toml17
-rw-r--r--crates/proc_macro_test/imp/src/lib.rs48
-rw-r--r--crates/proc_macro_test/src/lib.rs48
-rw-r--r--crates/profile/Cargo.toml3
-rw-r--r--crates/profile/src/lib.rs6
-rw-r--r--crates/profile/src/memory_usage.rs51
-rw-r--r--crates/project_model/src/build_data.rs2
-rw-r--r--crates/project_model/src/cargo_workspace.rs2
-rw-r--r--crates/project_model/src/sysroot.rs4
-rw-r--r--crates/project_model/src/workspace.rs12
-rw-r--r--crates/rust-analyzer/src/bin/flags.rs24
-rw-r--r--crates/rust-analyzer/src/bin/main.rs6
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs2
-rw-r--r--crates/rust-analyzer/src/cli.rs1
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs9
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs8
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs23
-rw-r--r--crates/rust-analyzer/src/cli/ssr.rs9
-rw-r--r--crates/rust-analyzer/src/config.rs11
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs6
-rw-r--r--crates/rust-analyzer/src/dispatch.rs4
-rw-r--r--crates/rust-analyzer/src/global_state.rs19
-rw-r--r--crates/rust-analyzer/src/handlers.rs52
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs4
-rw-r--r--crates/rust-analyzer/src/reload.rs6
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs10
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs6
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs2
-rw-r--r--crates/stdx/src/panic_context.rs2
-rw-r--r--crates/stdx/src/process.rs4
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/edit.rs4
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs31
-rw-r--r--crates/syntax/src/ast/make.rs5
-rw-r--r--crates/syntax/src/ast/node_ext.rs24
-rw-r--r--crates/syntax/src/ast/token_ext.rs2
-rw-r--r--crates/syntax/src/parsing.rs4
-rw-r--r--crates/syntax/src/parsing/lexer.rs2
-rw-r--r--crates/syntax/src/parsing/reparsing.rs14
-rw-r--r--crates/syntax/src/tests.rs6
-rw-r--r--crates/syntax/test_data/parser/err/0005_attribute_recover.rast66
-rw-r--r--crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast68
-rw-r--r--crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast17
-rw-r--r--crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast17
-rw-r--r--crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast18
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast25
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast25
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast17
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast51
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast125
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast34
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast17
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast45
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast18
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast9
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast9
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast9
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast23
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast17
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast9
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast17
-rw-r--r--crates/syntax/test_data/parser/ok/0006_inner_attributes.rast258
-rw-r--r--crates/syntax/test_data/parser/ok/0008_mod_item.rast9
-rw-r--r--crates/syntax/test_data/parser/ok/0011_outer_attribute.rast45
-rw-r--r--crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast19
-rw-r--r--crates/syntax/test_data/parser/ok/0035_weird_exprs.rast87
-rw-r--r--crates/syntax/test_data/parser/ok/0044_let_attrs.rast25
-rw-r--r--crates/syntax/test_data/parser/ok/0045_block_attrs.rast102
-rw-r--r--crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast17
-rw-r--r--crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast135
-rw-r--r--crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast9
-rw-r--r--crates/syntax/test_data/parser/ok/0062_macro_2.0.rast9
-rw-r--r--crates/syntax/test_data/parser/ok/0063_variadic_fun.rast17
-rw-r--r--crates/test_utils/src/lib.rs58
-rw-r--r--crates/tt/src/buffer.rs2
-rw-r--r--crates/tt/src/lib.rs4
-rw-r--r--crates/vfs/src/file_set.rs2
-rw-r--r--docs/dev/README.md16
-rw-r--r--docs/dev/architecture.md2
-rw-r--r--docs/user/generated_config.adoc12
-rw-r--r--docs/user/manual.adoc14
-rw-r--r--editors/code/package.json12
-rw-r--r--editors/code/src/config.ts1
-rw-r--r--editors/code/src/ctx.ts1
-rw-r--r--xtask/Cargo.toml2
-rw-r--r--xtask/src/ast_src.rs2
-rw-r--r--xtask/src/codegen/gen_lint_completions.rs94
-rw-r--r--xtask/src/codegen/gen_syntax.rs26
-rw-r--r--xtask/src/main.rs2
-rw-r--r--xtask/src/metrics.rs7
-rw-r--r--xtask/src/release/changelog.rs4
-rw-r--r--xtask/src/tidy.rs70
330 files changed, 14039 insertions, 11266 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 505263c64..e47b87964 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -245,9 +245,9 @@ dependencies = [
245 245
246[[package]] 246[[package]]
247name = "cov-mark" 247name = "cov-mark"
248version = "1.1.0" 248version = "2.0.0-pre.1"
249source = "registry+https://github.com/rust-lang/crates.io-index" 249source = "registry+https://github.com/rust-lang/crates.io-index"
250checksum = "9ffa3d3e0138386cd4361f63537765cac7ee40698028844635a54495a92f67f3" 250checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a"
251 251
252[[package]] 252[[package]]
253name = "crc32fast" 253name = "crc32fast"
@@ -482,6 +482,7 @@ dependencies = [
482 "hir_ty", 482 "hir_ty",
483 "itertools", 483 "itertools",
484 "log", 484 "log",
485 "once_cell",
485 "profile", 486 "profile",
486 "rustc-hash", 487 "rustc-hash",
487 "smallvec", 488 "smallvec",
@@ -765,9 +766,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
765 766
766[[package]] 767[[package]]
767name = "libc" 768name = "libc"
768version = "0.2.95" 769version = "0.2.97"
769source = "registry+https://github.com/rust-lang/crates.io-index" 770source = "registry+https://github.com/rust-lang/crates.io-index"
770checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" 771checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
771 772
772[[package]] 773[[package]]
773name = "libloading" 774name = "libloading"
@@ -1158,6 +1159,15 @@ dependencies = [
1158[[package]] 1159[[package]]
1159name = "proc_macro_test" 1160name = "proc_macro_test"
1160version = "0.0.0" 1161version = "0.0.0"
1162dependencies = [
1163 "cargo_metadata",
1164 "proc_macro_test_impl",
1165 "toolchain",
1166]
1167
1168[[package]]
1169name = "proc_macro_test_impl"
1170version = "0.0.0"
1161 1171
1162[[package]] 1172[[package]]
1163name = "profile" 1173name = "profile"
@@ -1170,6 +1180,7 @@ dependencies = [
1170 "once_cell", 1180 "once_cell",
1171 "perf-event", 1181 "perf-event",
1172 "tikv-jemalloc-ctl", 1182 "tikv-jemalloc-ctl",
1183 "winapi",
1173] 1184]
1174 1185
1175[[package]] 1186[[package]]
@@ -1792,9 +1803,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
1792 1803
1793[[package]] 1804[[package]]
1794name = "ungrammar" 1805name = "ungrammar"
1795version = "1.13.0" 1806version = "1.14.0"
1796source = "registry+https://github.com/rust-lang/crates.io-index" 1807source = "registry+https://github.com/rust-lang/crates.io-index"
1797checksum = "76760314176cc2b94047af2f921b92c39f11a34dc05c43a3c2b0fc91cb22959f" 1808checksum = "50ef6d7335c77ec3e4a7c4be74c2b9e4642569e94a4004c836f8cca71fede3a7"
1798 1809
1799[[package]] 1810[[package]]
1800name = "unicase" 1811name = "unicase"
diff --git a/Cargo.toml b/Cargo.toml
index 32ba3923b..4d6908fa9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,7 @@
1[workspace] 1[workspace]
2resolver = "2" 2resolver = "2"
3members = ["xtask/", "lib/*", "crates/*"] 3members = ["xtask/", "lib/*", "crates/*"]
4exclude = ["crates/proc_macro_test/imp"]
4 5
5[profile.dev] 6[profile.dev]
6# Disabling debug info speeds up builds a bunch, 7# Disabling debug info speeds up builds a bunch,
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 69ceba735..da4afb5eb 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -190,7 +190,7 @@ impl From<Fixture> for FileMeta {
190 edition: f 190 edition: f
191 .edition 191 .edition
192 .as_ref() 192 .as_ref()
193 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), 193 .map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap()),
194 env: f.env.into_iter().collect(), 194 env: f.env.into_iter().collect(),
195 introduce_new_source_root: f.introduce_new_source_root, 195 introduce_new_source_root: f.introduce_new_source_root,
196 } 196 }
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs
index 62bf2a4b2..d26f8f180 100644
--- a/crates/base_db/src/lib.rs
+++ b/crates/base_db/src/lib.rs
@@ -42,7 +42,7 @@ pub struct FilePosition {
42 pub offset: TextSize, 42 pub offset: TextSize,
43} 43}
44 44
45#[derive(Clone, Copy, Debug, Eq, PartialEq)] 45#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
46pub struct FileRange { 46pub struct FileRange {
47 pub file_id: FileId, 47 pub file_id: FileId,
48 pub range: TextRange, 48 pub range: TextRange,
@@ -120,6 +120,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
120 } 120 }
121 121
122 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { 122 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
123 let _p = profile::span("relevant_crates");
123 let source_root = self.0.file_source_root(file_id); 124 let source_root = self.0.file_source_root(file_id);
124 self.0.source_root_crates(source_root) 125 self.0.source_root_crates(source_root)
125 } 126 }
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs
index 069fc01d0..8a1a51e6e 100644
--- a/crates/cfg/src/cfg_expr.rs
+++ b/crates/cfg/src/cfg_expr.rs
@@ -1,6 +1,6 @@
1//! The condition expression used in `#[cfg(..)]` attributes. 1//! The condition expression used in `#[cfg(..)]` attributes.
2//! 2//!
3//! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation 3//! See: <https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation>
4 4
5use std::{fmt, slice::Iter as SliceIter}; 5use std::{fmt, slice::Iter as SliceIter};
6 6
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index 59fd38880..03b8dd767 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -22,7 +22,7 @@ pub use dnf::DnfExpr;
22/// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple 22/// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple
23/// of key and value in `key_values`. 23/// of key and value in `key_values`.
24/// 24///
25/// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options 25/// See: <https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options>
26#[derive(Debug, Clone, PartialEq, Eq, Default)] 26#[derive(Debug, Clone, PartialEq, Eq, Default)]
27pub struct CfgOptions { 27pub struct CfgOptions {
28 enabled: FxHashSet<CfgAtom>, 28 enabled: FxHashSet<CfgAtom>,
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 560b15238..7c148fd40 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -16,6 +16,7 @@ either = "1.5.3"
16arrayvec = "0.7" 16arrayvec = "0.7"
17itertools = "0.10.0" 17itertools = "0.10.0"
18smallvec = "1.4.0" 18smallvec = "1.4.0"
19once_cell = "1"
19 20
20stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
21syntax = { path = "../syntax", version = "0.0.0" } 22syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 2cdbd172a..b4c505898 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -3,251 +3,152 @@
3//! 3//!
4//! This probably isn't the best way to do this -- ideally, diagnistics should 4//! This probably isn't the best way to do this -- ideally, diagnistics should
5//! be expressed in terms of hir types themselves. 5//! be expressed in terms of hir types themselves.
6use std::any::Any; 6use cfg::{CfgExpr, CfgOptions};
7 7use either::Either;
8use cfg::{CfgExpr, CfgOptions, DnfExpr};
9use hir_def::path::ModPath; 8use hir_def::path::ModPath;
10use hir_expand::{HirFileId, InFile}; 9use hir_expand::{name::Name, HirFileId, InFile};
11use stdx::format_to;
12use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; 10use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
13 11
14pub use hir_ty::{ 12macro_rules! diagnostics {
15 diagnostics::{ 13 ($($diag:ident,)*) => {
16 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, 14 pub enum AnyDiagnostic {$(
17 MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon, 15 $diag(Box<$diag>),
18 ReplaceFilterMapNextWithFindMap, 16 )*}
19 }, 17
20 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, 18 $(
21}; 19 impl From<$diag> for AnyDiagnostic {
22 20 fn from(d: $diag) -> AnyDiagnostic {
23// Diagnostic: unresolved-module 21 AnyDiagnostic::$diag(Box::new(d))
24// 22 }
25// This diagnostic is triggered if rust-analyzer is unable to discover referred module. 23 }
24 )*
25 };
26}
27
28diagnostics![
29 BreakOutsideOfLoop,
30 InactiveCode,
31 IncorrectCase,
32 MacroError,
33 MismatchedArgCount,
34 MissingFields,
35 MissingMatchArms,
36 MissingOkOrSomeInTailExpr,
37 MissingUnsafe,
38 NoSuchField,
39 RemoveThisSemicolon,
40 ReplaceFilterMapNextWithFindMap,
41 UnimplementedBuiltinMacro,
42 UnresolvedExternCrate,
43 UnresolvedImport,
44 UnresolvedMacroCall,
45 UnresolvedModule,
46 UnresolvedProcMacro,
47];
48
26#[derive(Debug)] 49#[derive(Debug)]
27pub struct UnresolvedModule { 50pub struct UnresolvedModule {
28 pub file: HirFileId, 51 pub decl: InFile<AstPtr<ast::Module>>,
29 pub decl: AstPtr<ast::Module>,
30 pub candidate: String, 52 pub candidate: String,
31} 53}
32 54
33impl Diagnostic for UnresolvedModule {
34 fn code(&self) -> DiagnosticCode {
35 DiagnosticCode("unresolved-module")
36 }
37 fn message(&self) -> String {
38 "unresolved module".to_string()
39 }
40 fn display_source(&self) -> InFile<SyntaxNodePtr> {
41 InFile::new(self.file, self.decl.clone().into())
42 }
43 fn as_any(&self) -> &(dyn Any + Send + 'static) {
44 self
45 }
46}
47
48// Diagnostic: unresolved-extern-crate
49//
50// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
51#[derive(Debug)] 55#[derive(Debug)]
52pub struct UnresolvedExternCrate { 56pub struct UnresolvedExternCrate {
53 pub file: HirFileId, 57 pub decl: InFile<AstPtr<ast::ExternCrate>>,
54 pub item: AstPtr<ast::ExternCrate>,
55}
56
57impl Diagnostic for UnresolvedExternCrate {
58 fn code(&self) -> DiagnosticCode {
59 DiagnosticCode("unresolved-extern-crate")
60 }
61 fn message(&self) -> String {
62 "unresolved extern crate".to_string()
63 }
64 fn display_source(&self) -> InFile<SyntaxNodePtr> {
65 InFile::new(self.file, self.item.clone().into())
66 }
67 fn as_any(&self) -> &(dyn Any + Send + 'static) {
68 self
69 }
70} 58}
71 59
72#[derive(Debug)] 60#[derive(Debug)]
73pub struct UnresolvedImport { 61pub struct UnresolvedImport {
74 pub file: HirFileId, 62 pub decl: InFile<AstPtr<ast::UseTree>>,
75 pub node: AstPtr<ast::UseTree>, 63}
76} 64
77
78impl Diagnostic for UnresolvedImport {
79 fn code(&self) -> DiagnosticCode {
80 DiagnosticCode("unresolved-import")
81 }
82 fn message(&self) -> String {
83 "unresolved import".to_string()
84 }
85 fn display_source(&self) -> InFile<SyntaxNodePtr> {
86 InFile::new(self.file, self.node.clone().into())
87 }
88 fn as_any(&self) -> &(dyn Any + Send + 'static) {
89 self
90 }
91 fn is_experimental(&self) -> bool {
92 // This currently results in false positives in the following cases:
93 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
94 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
95 // - proc macros and/or proc macro generated code
96 true
97 }
98}
99
100// Diagnostic: unresolved-macro-call
101//
102// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
103// macro in a macro invocation.
104#[derive(Debug, Clone, Eq, PartialEq)] 65#[derive(Debug, Clone, Eq, PartialEq)]
105pub struct UnresolvedMacroCall { 66pub struct UnresolvedMacroCall {
106 pub file: HirFileId, 67 pub macro_call: InFile<AstPtr<ast::MacroCall>>,
107 pub node: AstPtr<ast::MacroCall>,
108 pub path: ModPath, 68 pub path: ModPath,
109} 69}
110 70
111impl Diagnostic for UnresolvedMacroCall {
112 fn code(&self) -> DiagnosticCode {
113 DiagnosticCode("unresolved-macro-call")
114 }
115 fn message(&self) -> String {
116 format!("unresolved macro `{}!`", self.path)
117 }
118 fn display_source(&self) -> InFile<SyntaxNodePtr> {
119 InFile::new(self.file, self.node.clone().into())
120 }
121 fn as_any(&self) -> &(dyn Any + Send + 'static) {
122 self
123 }
124 fn is_experimental(&self) -> bool {
125 true
126 }
127}
128
129// Diagnostic: inactive-code
130//
131// This diagnostic is shown for code with inactive `#[cfg]` attributes.
132#[derive(Debug, Clone, Eq, PartialEq)] 71#[derive(Debug, Clone, Eq, PartialEq)]
133pub struct InactiveCode { 72pub struct InactiveCode {
134 pub file: HirFileId, 73 pub node: InFile<SyntaxNodePtr>,
135 pub node: SyntaxNodePtr,
136 pub cfg: CfgExpr, 74 pub cfg: CfgExpr,
137 pub opts: CfgOptions, 75 pub opts: CfgOptions,
138} 76}
139 77
140impl Diagnostic for InactiveCode {
141 fn code(&self) -> DiagnosticCode {
142 DiagnosticCode("inactive-code")
143 }
144 fn message(&self) -> String {
145 let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
146 let mut buf = "code is inactive due to #[cfg] directives".to_string();
147
148 if let Some(inactive) = inactive {
149 format_to!(buf, ": {}", inactive);
150 }
151
152 buf
153 }
154 fn display_source(&self) -> InFile<SyntaxNodePtr> {
155 InFile::new(self.file, self.node.clone())
156 }
157 fn as_any(&self) -> &(dyn Any + Send + 'static) {
158 self
159 }
160}
161
162// Diagnostic: unresolved-proc-macro
163//
164// This diagnostic is shown when a procedural macro can not be found. This usually means that
165// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
166// but can also indicate project setup problems.
167//
168// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
169// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
170// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
171#[derive(Debug, Clone, Eq, PartialEq)] 78#[derive(Debug, Clone, Eq, PartialEq)]
172pub struct UnresolvedProcMacro { 79pub struct UnresolvedProcMacro {
173 pub file: HirFileId, 80 pub node: InFile<SyntaxNodePtr>,
174 pub node: SyntaxNodePtr,
175 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` 81 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
176 /// to use instead. 82 /// to use instead.
177 pub precise_location: Option<TextRange>, 83 pub precise_location: Option<TextRange>,
178 pub macro_name: Option<String>, 84 pub macro_name: Option<String>,
179} 85}
180 86
181impl Diagnostic for UnresolvedProcMacro { 87#[derive(Debug, Clone, Eq, PartialEq)]
182 fn code(&self) -> DiagnosticCode { 88pub struct MacroError {
183 DiagnosticCode("unresolved-proc-macro") 89 pub node: InFile<SyntaxNodePtr>,
184 } 90 pub message: String,
91}
185 92
186 fn message(&self) -> String { 93#[derive(Debug)]
187 match &self.macro_name { 94pub struct UnimplementedBuiltinMacro {
188 Some(name) => format!("proc macro `{}` not expanded", name), 95 pub node: InFile<SyntaxNodePtr>,
189 None => "proc macro not expanded".to_string(), 96}
190 }
191 }
192 97
193 fn display_source(&self) -> InFile<SyntaxNodePtr> { 98#[derive(Debug)]
194 InFile::new(self.file, self.node.clone()) 99pub struct NoSuchField {
195 } 100 pub field: InFile<AstPtr<ast::RecordExprField>>,
101}
196 102
197 fn as_any(&self) -> &(dyn Any + Send + 'static) { 103#[derive(Debug)]
198 self 104pub struct BreakOutsideOfLoop {
199 } 105 pub expr: InFile<AstPtr<ast::Expr>>,
200} 106}
201 107
202// Diagnostic: macro-error 108#[derive(Debug)]
203// 109pub struct MissingUnsafe {
204// This diagnostic is shown for macro expansion errors. 110 pub expr: InFile<AstPtr<ast::Expr>>,
205#[derive(Debug, Clone, Eq, PartialEq)]
206pub struct MacroError {
207 pub file: HirFileId,
208 pub node: SyntaxNodePtr,
209 pub message: String,
210} 111}
211 112
212impl Diagnostic for MacroError { 113#[derive(Debug)]
213 fn code(&self) -> DiagnosticCode { 114pub struct MissingFields {
214 DiagnosticCode("macro-error") 115 pub file: HirFileId,
215 } 116 pub field_list_parent: Either<AstPtr<ast::RecordExpr>, AstPtr<ast::RecordPat>>,
216 fn message(&self) -> String { 117 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
217 self.message.clone() 118 pub missed_fields: Vec<Name>,
218 }
219 fn display_source(&self) -> InFile<SyntaxNodePtr> {
220 InFile::new(self.file, self.node.clone())
221 }
222 fn as_any(&self) -> &(dyn Any + Send + 'static) {
223 self
224 }
225 fn is_experimental(&self) -> bool {
226 // Newly added and not very well-tested, might contain false positives.
227 true
228 }
229} 119}
230 120
231#[derive(Debug)] 121#[derive(Debug)]
232pub struct UnimplementedBuiltinMacro { 122pub struct ReplaceFilterMapNextWithFindMap {
233 pub file: HirFileId, 123 pub file: HirFileId,
234 pub node: SyntaxNodePtr, 124 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
125 pub next_expr: AstPtr<ast::Expr>,
235} 126}
236 127
237impl Diagnostic for UnimplementedBuiltinMacro { 128#[derive(Debug)]
238 fn code(&self) -> DiagnosticCode { 129pub struct MismatchedArgCount {
239 DiagnosticCode("unimplemented-builtin-macro") 130 pub call_expr: InFile<AstPtr<ast::Expr>>,
240 } 131 pub expected: usize,
132 pub found: usize,
133}
241 134
242 fn message(&self) -> String { 135#[derive(Debug)]
243 "unimplemented built-in macro".to_string() 136pub struct RemoveThisSemicolon {
244 } 137 pub expr: InFile<AstPtr<ast::Expr>>,
138}
245 139
246 fn display_source(&self) -> InFile<SyntaxNodePtr> { 140#[derive(Debug)]
247 InFile::new(self.file, self.node.clone()) 141pub struct MissingOkOrSomeInTailExpr {
248 } 142 pub expr: InFile<AstPtr<ast::Expr>>,
143 // `Some` or `Ok` depending on whether the return type is Result or Option
144 pub required: String,
145}
249 146
250 fn as_any(&self) -> &(dyn Any + Send + 'static) { 147#[derive(Debug)]
251 self 148pub struct MissingMatchArms {
252 } 149 pub file: HirFileId,
150 pub match_expr: AstPtr<ast::Expr>,
151 pub arms: AstPtr<ast::MatchArmList>,
253} 152}
153
154pub use hir_ty::diagnostics::IncorrectCase;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index d3ef29db4..5bc0b2338 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -15,7 +15,7 @@
15//! 15//!
16//! `hir` is what insulates the "we don't know how to actually write an incremental compiler" 16//! `hir` is what insulates the "we don't know how to actually write an incremental compiler"
17//! from the ide with completions, hovers, etc. It is a (soft, internal) boundary: 17//! from the ide with completions, hovers, etc. It is a (soft, internal) boundary:
18//! https://www.tedinski.com/2018/02/06/system-boundaries.html. 18//! <https://www.tedinski.com/2018/02/06/system-boundaries.html>.
19 19
20#![recursion_limit = "512"] 20#![recursion_limit = "512"]
21 21
@@ -35,14 +35,10 @@ use std::{iter, sync::Arc};
35 35
36use arrayvec::ArrayVec; 36use arrayvec::ArrayVec;
37use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 37use base_db::{CrateDisplayName, CrateId, Edition, FileId};
38use diagnostics::{
39 InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport,
40 UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
41};
42use either::Either; 38use either::Either;
43use hir_def::{ 39use hir_def::{
44 adt::{ReprKind, VariantData}, 40 adt::{ReprKind, VariantData},
45 body::BodyDiagnostic, 41 body::{BodyDiagnostic, SyntheticSyntax},
46 expr::{BindingAnnotation, LabelId, Pat, PatId}, 42 expr::{BindingAnnotation, LabelId, Pat, PatId},
47 item_tree::ItemTreeNode, 43 item_tree::ItemTreeNode,
48 lang_item::LangItemTarget, 44 lang_item::LangItemTarget,
@@ -50,7 +46,6 @@ use hir_def::{
50 per_ns::PerNs, 46 per_ns::PerNs,
51 resolver::{HasResolver, Resolver}, 47 resolver::{HasResolver, Resolver},
52 src::HasSource as _, 48 src::HasSource as _,
53 type_ref::TraitRef,
54 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, 49 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
55 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, 50 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
56 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, 51 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
@@ -61,8 +56,8 @@ use hir_ty::{
61 autoderef, 56 autoderef,
62 consteval::ConstExt, 57 consteval::ConstExt,
63 could_unify, 58 could_unify,
64 diagnostics_sink::DiagnosticSink, 59 diagnostics::BodyValidationDiagnostic,
65 method_resolution::{self, def_crates, TyFingerprint}, 60 method_resolution::{self, TyFingerprint},
66 primitive::UintTy, 61 primitive::UintTy,
67 subst_prefix, 62 subst_prefix,
68 traits::FnTrait, 63 traits::FnTrait,
@@ -73,6 +68,7 @@ use hir_ty::{
73}; 68};
74use itertools::Itertools; 69use itertools::Itertools;
75use nameres::diagnostics::DefDiagnosticKind; 70use nameres::diagnostics::DefDiagnosticKind;
71use once_cell::unsync::Lazy;
76use rustc_hash::FxHashSet; 72use rustc_hash::FxHashSet;
77use stdx::{format_to, impl_from}; 73use stdx::{format_to, impl_from};
78use syntax::{ 74use syntax::{
@@ -85,6 +81,13 @@ use crate::db::{DefDatabase, HirDatabase};
85 81
86pub use crate::{ 82pub use crate::{
87 attrs::{HasAttrs, Namespace}, 83 attrs::{HasAttrs, Namespace},
84 diagnostics::{
85 AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, MacroError,
86 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
87 MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
88 UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
89 UnresolvedModule, UnresolvedProcMacro,
90 },
88 has_source::HasSource, 91 has_source::HasSource,
89 semantics::{PathResolution, Semantics, SemanticsScope}, 92 semantics::{PathResolution, Semantics, SemanticsScope},
90}; 93};
@@ -192,6 +195,7 @@ impl Crate {
192 db: &dyn DefDatabase, 195 db: &dyn DefDatabase,
193 query: import_map::Query, 196 query: import_map::Query,
194 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 197 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
198 let _p = profile::span("query_external_importables");
195 import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { 199 import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
196 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), 200 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
197 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), 201 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
@@ -332,7 +336,7 @@ impl ModuleDef {
332 } 336 }
333 } 337 }
334 338
335 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 339 pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec<AnyDiagnostic> {
336 let id = match self { 340 let id = match self {
337 ModuleDef::Adt(it) => match it { 341 ModuleDef::Adt(it) => match it {
338 Adt::Struct(it) => it.id.into(), 342 Adt::Struct(it) => it.id.into(),
@@ -345,15 +349,19 @@ impl ModuleDef {
345 ModuleDef::Module(it) => it.id.into(), 349 ModuleDef::Module(it) => it.id.into(),
346 ModuleDef::Const(it) => it.id.into(), 350 ModuleDef::Const(it) => it.id.into(),
347 ModuleDef::Static(it) => it.id.into(), 351 ModuleDef::Static(it) => it.id.into(),
348 _ => return, 352 _ => return Vec::new(),
349 }; 353 };
350 354
351 let module = match self.module(db) { 355 let module = match self.module(db) {
352 Some(it) => it, 356 Some(it) => it,
353 None => return, 357 None => return Vec::new(),
354 }; 358 };
355 359
356 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) 360 let mut acc = Vec::new();
361 for diag in hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id) {
362 acc.push(diag.into())
363 }
364 acc
357 } 365 }
358} 366}
359 367
@@ -442,10 +450,10 @@ impl Module {
442 } 450 }
443 451
444 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { 452 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
445 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) 453 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of((*def).into())
446 } 454 }
447 455
448 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 456 pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
449 let _p = profile::span("Module::diagnostics").detail(|| { 457 let _p = profile::span("Module::diagnostics").detail(|| {
450 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) 458 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
451 }); 459 });
@@ -458,18 +466,22 @@ impl Module {
458 match &diag.kind { 466 match &diag.kind {
459 DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => { 467 DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => {
460 let decl = declaration.to_node(db.upcast()); 468 let decl = declaration.to_node(db.upcast());
461 sink.push(UnresolvedModule { 469 acc.push(
462 file: declaration.file_id, 470 UnresolvedModule {
463 decl: AstPtr::new(&decl), 471 decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
464 candidate: candidate.clone(), 472 candidate: candidate.clone(),
465 }) 473 }
474 .into(),
475 )
466 } 476 }
467 DefDiagnosticKind::UnresolvedExternCrate { ast } => { 477 DefDiagnosticKind::UnresolvedExternCrate { ast } => {
468 let item = ast.to_node(db.upcast()); 478 let item = ast.to_node(db.upcast());
469 sink.push(UnresolvedExternCrate { 479 acc.push(
470 file: ast.file_id, 480 UnresolvedExternCrate {
471 item: AstPtr::new(&item), 481 decl: InFile::new(ast.file_id, AstPtr::new(&item)),
472 }); 482 }
483 .into(),
484 );
473 } 485 }
474 486
475 DefDiagnosticKind::UnresolvedImport { id, index } => { 487 DefDiagnosticKind::UnresolvedImport { id, index } => {
@@ -478,25 +490,30 @@ impl Module {
478 let import = &item_tree[id.value]; 490 let import = &item_tree[id.value];
479 491
480 let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index); 492 let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
481 sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) }); 493 acc.push(
494 UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }
495 .into(),
496 );
482 } 497 }
483 498
484 DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { 499 DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
485 let item = ast.to_node(db.upcast()); 500 let item = ast.to_node(db.upcast());
486 sink.push(InactiveCode { 501 acc.push(
487 file: ast.file_id, 502 InactiveCode {
488 node: AstPtr::new(&item).into(), 503 node: ast.with_value(AstPtr::new(&item).into()),
489 cfg: cfg.clone(), 504 cfg: cfg.clone(),
490 opts: opts.clone(), 505 opts: opts.clone(),
491 }); 506 }
507 .into(),
508 );
492 } 509 }
493 510
494 DefDiagnosticKind::UnresolvedProcMacro { ast } => { 511 DefDiagnosticKind::UnresolvedProcMacro { ast } => {
495 let mut precise_location = None; 512 let mut precise_location = None;
496 let (file, ast, name) = match ast { 513 let (node, name) = match ast {
497 MacroCallKind::FnLike { ast_id, .. } => { 514 MacroCallKind::FnLike { ast_id, .. } => {
498 let node = ast_id.to_node(db.upcast()); 515 let node = ast_id.to_node(db.upcast());
499 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) 516 (ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), None)
500 } 517 }
501 MacroCallKind::Derive { ast_id, derive_name, .. } => { 518 MacroCallKind::Derive { ast_id, derive_name, .. } => {
502 let node = ast_id.to_node(db.upcast()); 519 let node = ast_id.to_node(db.upcast());
@@ -529,71 +546,84 @@ impl Module {
529 } 546 }
530 547
531 ( 548 (
532 ast_id.file_id, 549 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
533 SyntaxNodePtr::from(AstPtr::new(&node)),
534 Some(derive_name.clone()), 550 Some(derive_name.clone()),
535 ) 551 )
536 } 552 }
553 MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => {
554 let node = ast_id.to_node(db.upcast());
555 let attr =
556 node.attrs().nth((*invoc_attr_index) as usize).unwrap_or_else(
557 || panic!("cannot find attribute #{}", invoc_attr_index),
558 );
559 (
560 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
561 Some(attr_name.clone()),
562 )
563 }
537 }; 564 };
538 sink.push(UnresolvedProcMacro { 565 acc.push(
539 file, 566 UnresolvedProcMacro { node, precise_location, macro_name: name }.into(),
540 node: ast, 567 );
541 precise_location,
542 macro_name: name,
543 });
544 } 568 }
545 569
546 DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { 570 DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
547 let node = ast.to_node(db.upcast()); 571 let node = ast.to_node(db.upcast());
548 sink.push(UnresolvedMacroCall { 572 acc.push(
549 file: ast.file_id, 573 UnresolvedMacroCall {
550 node: AstPtr::new(&node), 574 macro_call: InFile::new(ast.file_id, AstPtr::new(&node)),
551 path: path.clone(), 575 path: path.clone(),
552 }); 576 }
577 .into(),
578 );
553 } 579 }
554 580
555 DefDiagnosticKind::MacroError { ast, message } => { 581 DefDiagnosticKind::MacroError { ast, message } => {
556 let (file, ast) = match ast { 582 let node = match ast {
557 MacroCallKind::FnLike { ast_id, .. } => { 583 MacroCallKind::FnLike { ast_id, .. } => {
558 let node = ast_id.to_node(db.upcast()); 584 let node = ast_id.to_node(db.upcast());
559 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) 585 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node)))
560 } 586 }
561 MacroCallKind::Derive { ast_id, .. } => { 587 MacroCallKind::Derive { ast_id, .. }
588 | MacroCallKind::Attr { ast_id, .. } => {
589 // FIXME: point to the attribute instead, this creates very large diagnostics
562 let node = ast_id.to_node(db.upcast()); 590 let node = ast_id.to_node(db.upcast());
563 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) 591 ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node)))
564 } 592 }
565 }; 593 };
566 sink.push(MacroError { file, node: ast, message: message.clone() }); 594 acc.push(MacroError { node, message: message.clone() }.into());
567 } 595 }
568 596
569 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { 597 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
570 let node = ast.to_node(db.upcast()); 598 let node = ast.to_node(db.upcast());
571 // Must have a name, otherwise we wouldn't emit it. 599 // Must have a name, otherwise we wouldn't emit it.
572 let name = node.name().expect("unimplemented builtin macro with no name"); 600 let name = node.name().expect("unimplemented builtin macro with no name");
573 let ptr = SyntaxNodePtr::from(AstPtr::new(&name)); 601 acc.push(
574 sink.push(UnimplementedBuiltinMacro { file: ast.file_id, node: ptr }); 602 UnimplementedBuiltinMacro {
603 node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&name))),
604 }
605 .into(),
606 );
575 } 607 }
576 } 608 }
577 } 609 }
578 for decl in self.declarations(db) { 610 for decl in self.declarations(db) {
579 match decl { 611 match decl {
580 crate::ModuleDef::Function(f) => f.diagnostics(db, sink), 612 ModuleDef::Function(f) => f.diagnostics(db, acc),
581 crate::ModuleDef::Module(m) => { 613 ModuleDef::Module(m) => {
582 // Only add diagnostics from inline modules 614 // Only add diagnostics from inline modules
583 if def_map[m.id.local_id].origin.is_inline() { 615 if def_map[m.id.local_id].origin.is_inline() {
584 m.diagnostics(db, sink) 616 m.diagnostics(db, acc)
585 } 617 }
586 } 618 }
587 _ => { 619 _ => acc.extend(decl.diagnostics(db)),
588 decl.diagnostics(db, sink);
589 }
590 } 620 }
591 } 621 }
592 622
593 for impl_def in self.impl_defs(db) { 623 for impl_def in self.impl_defs(db) {
594 for item in impl_def.items(db) { 624 for item in impl_def.items(db) {
595 if let AssocItem::Function(f) = item { 625 if let AssocItem::Function(f) = item {
596 f.diagnostics(db, sink); 626 f.diagnostics(db, acc);
597 } 627 }
598 } 628 }
599 } 629 }
@@ -995,41 +1025,191 @@ impl Function {
995 db.function_data(self.id).is_async() 1025 db.function_data(self.id).is_async()
996 } 1026 }
997 1027
998 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 1028 pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
999 let krate = self.module(db).id.krate(); 1029 let krate = self.module(db).id.krate();
1000 1030
1001 let source_map = db.body_with_source_map(self.id.into()).1; 1031 let source_map = db.body_with_source_map(self.id.into()).1;
1002 for diag in source_map.diagnostics() { 1032 for diag in source_map.diagnostics() {
1003 match diag { 1033 match diag {
1004 BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode { 1034 BodyDiagnostic::InactiveCode { node, cfg, opts } => acc.push(
1005 file: node.file_id, 1035 InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() }
1006 node: node.value.clone(), 1036 .into(),
1007 cfg: cfg.clone(), 1037 ),
1008 opts: opts.clone(), 1038 BodyDiagnostic::MacroError { node, message } => acc.push(
1009 }), 1039 MacroError {
1010 BodyDiagnostic::MacroError { node, message } => sink.push(MacroError { 1040 node: node.clone().map(|it| it.into()),
1011 file: node.file_id, 1041 message: message.to_string(),
1012 node: node.value.clone().into(), 1042 }
1013 message: message.to_string(), 1043 .into(),
1014 }), 1044 ),
1015 BodyDiagnostic::UnresolvedProcMacro { node } => sink.push(UnresolvedProcMacro { 1045 BodyDiagnostic::UnresolvedProcMacro { node } => acc.push(
1016 file: node.file_id, 1046 UnresolvedProcMacro {
1017 node: node.value.clone().into(), 1047 node: node.clone().map(|it| it.into()),
1018 precise_location: None, 1048 precise_location: None,
1019 macro_name: None, 1049 macro_name: None,
1020 }), 1050 }
1021 BodyDiagnostic::UnresolvedMacroCall { node, path } => { 1051 .into(),
1022 sink.push(UnresolvedMacroCall { 1052 ),
1023 file: node.file_id, 1053 BodyDiagnostic::UnresolvedMacroCall { node, path } => acc.push(
1024 node: node.value.clone(), 1054 UnresolvedMacroCall { macro_call: node.clone(), path: path.clone() }.into(),
1025 path: path.clone(), 1055 ),
1026 }) 1056 }
1057 }
1058
1059 let infer = db.infer(self.id.into());
1060 let source_map = Lazy::new(|| db.body_with_source_map(self.id.into()).1);
1061 for d in &infer.diagnostics {
1062 match d {
1063 hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
1064 let field = source_map.field_syntax(*expr);
1065 acc.push(NoSuchField { field }.into())
1066 }
1067 hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
1068 let expr = source_map
1069 .expr_syntax(*expr)
1070 .expect("break outside of loop in synthetic syntax");
1071 acc.push(BreakOutsideOfLoop { expr }.into())
1072 }
1073 }
1074 }
1075
1076 for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) {
1077 match source_map.expr_syntax(expr) {
1078 Ok(expr) => acc.push(MissingUnsafe { expr }.into()),
1079 Err(SyntheticSyntax) => {
1080 // FIXME: Here and eslwhere in this file, the `expr` was
1081 // desugared, report or assert that this doesn't happen.
1082 }
1083 }
1084 }
1085
1086 for diagnostic in BodyValidationDiagnostic::collect(db, self.id.into()) {
1087 match diagnostic {
1088 BodyValidationDiagnostic::RecordMissingFields {
1089 record,
1090 variant,
1091 missed_fields,
1092 } => {
1093 let variant_data = variant.variant_data(db.upcast());
1094 let missed_fields = missed_fields
1095 .into_iter()
1096 .map(|idx| variant_data.fields()[idx].name.clone())
1097 .collect();
1098
1099 match record {
1100 Either::Left(record_expr) => match source_map.expr_syntax(record_expr) {
1101 Ok(source_ptr) => {
1102 let root = source_ptr.file_syntax(db.upcast());
1103 if let ast::Expr::RecordExpr(record_expr) =
1104 &source_ptr.value.to_node(&root)
1105 {
1106 if let Some(_) = record_expr.record_expr_field_list() {
1107 acc.push(
1108 MissingFields {
1109 file: source_ptr.file_id,
1110 field_list_parent: Either::Left(AstPtr::new(
1111 record_expr,
1112 )),
1113 field_list_parent_path: record_expr
1114 .path()
1115 .map(|path| AstPtr::new(&path)),
1116 missed_fields,
1117 }
1118 .into(),
1119 )
1120 }
1121 }
1122 }
1123 Err(SyntheticSyntax) => (),
1124 },
1125 Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
1126 Ok(source_ptr) => {
1127 if let Some(expr) = source_ptr.value.as_ref().left() {
1128 let root = source_ptr.file_syntax(db.upcast());
1129 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
1130 if let Some(_) = record_pat.record_pat_field_list() {
1131 acc.push(
1132 MissingFields {
1133 file: source_ptr.file_id,
1134 field_list_parent: Either::Right(AstPtr::new(
1135 &record_pat,
1136 )),
1137 field_list_parent_path: record_pat
1138 .path()
1139 .map(|path| AstPtr::new(&path)),
1140 missed_fields,
1141 }
1142 .into(),
1143 )
1144 }
1145 }
1146 }
1147 }
1148 Err(SyntheticSyntax) => (),
1149 },
1150 }
1151 }
1152 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
1153 if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
1154 acc.push(
1155 ReplaceFilterMapNextWithFindMap {
1156 file: next_source_ptr.file_id,
1157 next_expr: next_source_ptr.value,
1158 }
1159 .into(),
1160 );
1161 }
1162 }
1163 BodyValidationDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
1164 match source_map.expr_syntax(call_expr) {
1165 Ok(source_ptr) => acc.push(
1166 MismatchedArgCount { call_expr: source_ptr, expected, found }.into(),
1167 ),
1168 Err(SyntheticSyntax) => (),
1169 }
1170 }
1171 BodyValidationDiagnostic::RemoveThisSemicolon { expr } => {
1172 match source_map.expr_syntax(expr) {
1173 Ok(expr) => acc.push(RemoveThisSemicolon { expr }.into()),
1174 Err(SyntheticSyntax) => (),
1175 }
1176 }
1177 BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr, required } => {
1178 match source_map.expr_syntax(expr) {
1179 Ok(expr) => acc.push(MissingOkOrSomeInTailExpr { expr, required }.into()),
1180 Err(SyntheticSyntax) => (),
1181 }
1182 }
1183 BodyValidationDiagnostic::MissingMatchArms { match_expr } => {
1184 match source_map.expr_syntax(match_expr) {
1185 Ok(source_ptr) => {
1186 let root = source_ptr.file_syntax(db.upcast());
1187 if let ast::Expr::MatchExpr(match_expr) =
1188 &source_ptr.value.to_node(&root)
1189 {
1190 if let (Some(match_expr), Some(arms)) =
1191 (match_expr.expr(), match_expr.match_arm_list())
1192 {
1193 acc.push(
1194 MissingMatchArms {
1195 file: source_ptr.file_id,
1196 match_expr: AstPtr::new(&match_expr),
1197 arms: AstPtr::new(&arms),
1198 }
1199 .into(),
1200 )
1201 }
1202 }
1203 }
1204 Err(SyntheticSyntax) => (),
1205 }
1027 } 1206 }
1028 } 1207 }
1029 } 1208 }
1030 1209
1031 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); 1210 for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
1032 hir_ty::diagnostics::validate_body(db, self.id.into(), sink); 1211 acc.push(diag.into())
1212 }
1033 } 1213 }
1034 1214
1035 /// Whether this function declaration has a definition. 1215 /// Whether this function declaration has a definition.
@@ -1331,6 +1511,7 @@ impl MacroDef {
1331 MacroDefKind::Declarative(_) => MacroKind::Declarative, 1511 MacroDefKind::Declarative(_) => MacroKind::Declarative,
1332 MacroDefKind::BuiltIn(_, _) | MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn, 1512 MacroDefKind::BuiltIn(_, _) | MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn,
1333 MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive, 1513 MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive,
1514 MacroDefKind::BuiltInAttr(_, _) => MacroKind::Attr,
1334 MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::CustomDerive, _) => { 1515 MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::CustomDerive, _) => {
1335 MacroKind::Derive 1516 MacroKind::Derive
1336 } 1517 }
@@ -1338,6 +1519,13 @@ impl MacroDef {
1338 MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro, 1519 MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro,
1339 } 1520 }
1340 } 1521 }
1522
1523 pub fn is_fn_like(&self) -> bool {
1524 match self.kind() {
1525 MacroKind::Declarative | MacroKind::BuiltIn | MacroKind::ProcMacro => true,
1526 MacroKind::Attr | MacroKind::Derive => false,
1527 }
1528 }
1341} 1529}
1342 1530
1343/// Invariant: `inner.as_assoc_item(db).is_some()` 1531/// Invariant: `inner.as_assoc_item(db).is_some()`
@@ -1429,6 +1617,20 @@ impl AssocItem {
1429 _ => None, 1617 _ => None,
1430 } 1618 }
1431 } 1619 }
1620
1621 pub fn containing_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
1622 match self.container(db) {
1623 AssocItemContainer::Impl(i) => i.trait_(db),
1624 _ => None,
1625 }
1626 }
1627
1628 pub fn containing_trait_or_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
1629 match self.container(db) {
1630 AssocItemContainer::Trait(t) => Some(t),
1631 AssocItemContainer::Impl(i) => i.trait_(db),
1632 }
1633 }
1432} 1634}
1433 1635
1434impl HasVisibility for AssocItem { 1636impl HasVisibility for AssocItem {
@@ -1726,7 +1928,7 @@ impl Impl {
1726 } 1928 }
1727 1929
1728 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { 1930 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> {
1729 let def_crates = match def_crates(db, &ty, krate) { 1931 let def_crates = match method_resolution::def_crates(db, &ty, krate) {
1730 Some(def_crates) => def_crates, 1932 Some(def_crates) => def_crates,
1731 None => return Vec::new(), 1933 None => return Vec::new(),
1732 }; 1934 };
@@ -1783,9 +1985,11 @@ impl Impl {
1783 } 1985 }
1784 1986
1785 // FIXME: the return type is wrong. This should be a hir version of 1987 // FIXME: the return type is wrong. This should be a hir version of
1786 // `TraitRef` (ie, resolved `TypeRef`). 1988 // `TraitRef` (to account for parameters and qualifiers)
1787 pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> { 1989 pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> {
1788 db.impl_data(self.id).target_trait.as_deref().cloned() 1990 let trait_ref = db.impl_trait(self.id)?.skip_binders().clone();
1991 let id = hir_ty::from_chalk_trait_id(trait_ref.trait_id);
1992 Some(Trait { id })
1789 } 1993 }
1790 1994
1791 pub fn self_ty(self, db: &dyn HirDatabase) -> Type { 1995 pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
@@ -2130,7 +2334,7 @@ impl Type {
2130 krate: Crate, 2334 krate: Crate,
2131 mut callback: impl FnMut(AssocItem) -> Option<T>, 2335 mut callback: impl FnMut(AssocItem) -> Option<T>,
2132 ) -> Option<T> { 2336 ) -> Option<T> {
2133 for krate in def_crates(db, &self.ty, krate.id)? { 2337 for krate in method_resolution::def_crates(db, &self.ty, krate.id)? {
2134 let impls = db.inherent_impls_in_crate(krate); 2338 let impls = db.inherent_impls_in_crate(krate);
2135 2339
2136 for impl_def in impls.for_self_ty(&self.ty) { 2340 for impl_def in impls.for_self_ty(&self.ty) {
@@ -2162,6 +2366,7 @@ impl Type {
2162 name: Option<&Name>, 2366 name: Option<&Name>,
2163 mut callback: impl FnMut(&Ty, Function) -> Option<T>, 2367 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
2164 ) -> Option<T> { 2368 ) -> Option<T> {
2369 let _p = profile::span("iterate_method_candidates");
2165 // There should be no inference vars in types passed here 2370 // There should be no inference vars in types passed here
2166 // FIXME check that? 2371 // FIXME check that?
2167 // FIXME replace Unknown by bound vars here 2372 // FIXME replace Unknown by bound vars here
@@ -2195,6 +2400,7 @@ impl Type {
2195 name: Option<&Name>, 2400 name: Option<&Name>,
2196 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, 2401 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
2197 ) -> Option<T> { 2402 ) -> Option<T> {
2403 let _p = profile::span("iterate_path_candidates");
2198 let canonical = hir_ty::replace_errors_with_variables(&self.ty); 2404 let canonical = hir_ty::replace_errors_with_variables(&self.ty);
2199 2405
2200 let env = self.env.clone(); 2406 let env = self.env.clone();
@@ -2232,6 +2438,7 @@ impl Type {
2232 &'a self, 2438 &'a self,
2233 db: &'a dyn HirDatabase, 2439 db: &'a dyn HirDatabase,
2234 ) -> impl Iterator<Item = Trait> + 'a { 2440 ) -> impl Iterator<Item = Trait> + 'a {
2441 let _p = profile::span("applicable_inherent_traits");
2235 self.autoderef(db) 2442 self.autoderef(db)
2236 .filter_map(|derefed_type| derefed_type.ty.dyn_trait()) 2443 .filter_map(|derefed_type| derefed_type.ty.dyn_trait())
2237 .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) 2444 .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
@@ -2304,13 +2511,13 @@ impl Type {
2304 match ty.kind(&Interner) { 2511 match ty.kind(&Interner) {
2305 TyKind::Adt(_, substs) => { 2512 TyKind::Adt(_, substs) => {
2306 cb(type_.derived(ty.clone())); 2513 cb(type_.derived(ty.clone()));
2307 walk_substs(db, type_, &substs, cb); 2514 walk_substs(db, type_, substs, cb);
2308 } 2515 }
2309 TyKind::AssociatedType(_, substs) => { 2516 TyKind::AssociatedType(_, substs) => {
2310 if let Some(_) = ty.associated_type_parent_trait(db) { 2517 if let Some(_) = ty.associated_type_parent_trait(db) {
2311 cb(type_.derived(ty.clone())); 2518 cb(type_.derived(ty.clone()));
2312 } 2519 }
2313 walk_substs(db, type_, &substs, cb); 2520 walk_substs(db, type_, substs, cb);
2314 } 2521 }
2315 TyKind::OpaqueType(_, subst) => { 2522 TyKind::OpaqueType(_, subst) => {
2316 if let Some(bounds) = ty.impl_trait_bounds(db) { 2523 if let Some(bounds) = ty.impl_trait_bounds(db) {
@@ -2350,7 +2557,7 @@ impl Type {
2350 TyKind::FnDef(_, substs) 2557 TyKind::FnDef(_, substs)
2351 | TyKind::Tuple(_, substs) 2558 | TyKind::Tuple(_, substs)
2352 | TyKind::Closure(.., substs) => { 2559 | TyKind::Closure(.., substs) => {
2353 walk_substs(db, type_, &substs, cb); 2560 walk_substs(db, type_, substs, cb);
2354 } 2561 }
2355 TyKind::Function(hir_ty::FnPointer { substitution, .. }) => { 2562 TyKind::Function(hir_ty::FnPointer { substitution, .. }) => {
2356 walk_substs(db, type_, &substitution.0, cb); 2563 walk_substs(db, type_, &substitution.0, cb);
@@ -2481,6 +2688,18 @@ impl ScopeDef {
2481 2688
2482 items 2689 items
2483 } 2690 }
2691
2692 pub fn is_value_def(&self) -> bool {
2693 matches!(
2694 self,
2695 ScopeDef::ModuleDef(ModuleDef::Function(_))
2696 | ScopeDef::ModuleDef(ModuleDef::Variant(_))
2697 | ScopeDef::ModuleDef(ModuleDef::Const(_))
2698 | ScopeDef::ModuleDef(ModuleDef::Static(_))
2699 | ScopeDef::GenericParam(GenericParam::ConstParam(_))
2700 | ScopeDef::Local(_)
2701 )
2702 }
2484} 2703}
2485 2704
2486impl From<ItemInNs> for ScopeDef { 2705impl From<ItemInNs> for ScopeDef {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index c7f2c02e4..613266e07 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -17,7 +17,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
17use syntax::{ 17use syntax::{
18 algo::find_node_at_offset, 18 algo::find_node_at_offset,
19 ast::{self, GenericParamsOwner, LoopBodyOwner}, 19 ast::{self, GenericParamsOwner, LoopBodyOwner},
20 match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize, 20 match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
21}; 21};
22 22
23use crate::{ 23use crate::{
@@ -35,8 +35,9 @@ pub enum PathResolution {
35 Def(ModuleDef), 35 Def(ModuleDef),
36 /// A local binding (only value namespace) 36 /// A local binding (only value namespace)
37 Local(Local), 37 Local(Local),
38 /// A generic parameter 38 /// A type parameter
39 TypeParam(TypeParam), 39 TypeParam(TypeParam),
40 /// A const parameter
40 ConstParam(ConstParam), 41 ConstParam(ConstParam),
41 SelfType(Impl), 42 SelfType(Impl),
42 Macro(MacroDef), 43 Macro(MacroDef),
@@ -117,6 +118,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
117 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { 118 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
118 self.imp.expand(macro_call) 119 self.imp.expand(macro_call)
119 } 120 }
121
122 /// If `item` has an attribute macro attached to it, expands it.
123 pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
124 self.imp.expand_attr_macro(item)
125 }
126
127 pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
128 self.imp.is_attr_macro_call(item)
129 }
130
120 pub fn speculative_expand( 131 pub fn speculative_expand(
121 &self, 132 &self,
122 actual_macro_call: &ast::MacroCall, 133 actual_macro_call: &ast::MacroCall,
@@ -181,7 +192,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
181 node: &SyntaxNode, 192 node: &SyntaxNode,
182 offset: TextSize, 193 offset: TextSize,
183 ) -> Option<N> { 194 ) -> Option<N> {
184 if let Some(it) = find_node_at_offset(&node, offset) { 195 if let Some(it) = find_node_at_offset(node, offset) {
185 return Some(it); 196 return Some(it);
186 } 197 }
187 198
@@ -332,6 +343,22 @@ impl<'db> SemanticsImpl<'db> {
332 Some(node) 343 Some(node)
333 } 344 }
334 345
346 fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
347 let sa = self.analyze(item.syntax());
348 let src = InFile::new(sa.file_id, item.clone());
349 let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
350 let file_id = macro_call_id.as_file();
351 let node = self.db.parse_or_expand(file_id)?;
352 self.cache(node.clone(), file_id);
353 Some(node)
354 }
355
356 fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
357 let sa = self.analyze(item.syntax());
358 let src = InFile::new(sa.file_id, item.clone());
359 self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
360 }
361
335 fn speculative_expand( 362 fn speculative_expand(
336 &self, 363 &self,
337 actual_macro_call: &ast::MacroCall, 364 actual_macro_call: &ast::MacroCall,
@@ -362,25 +389,65 @@ impl<'db> SemanticsImpl<'db> {
362 389
363 let token = successors(Some(InFile::new(sa.file_id, token)), |token| { 390 let token = successors(Some(InFile::new(sa.file_id, token)), |token| {
364 self.db.unwind_if_cancelled(); 391 self.db.unwind_if_cancelled();
365 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; 392
366 let tt = macro_call.token_tree()?; 393 for node in token.value.ancestors() {
367 if !tt.syntax().text_range().contains_range(token.value.text_range()) { 394 match_ast! {
368 return None; 395 match node {
369 } 396 ast::MacroCall(macro_call) => {
370 let file_id = sa.expand(self.db, token.with_value(&macro_call))?; 397 let tt = macro_call.token_tree()?;
371 let token = self 398 let l_delim = match tt.left_delimiter_token() {
372 .expansion_info_cache 399 Some(it) => it.text_range().end(),
373 .borrow_mut() 400 None => tt.syntax().text_range().start()
374 .entry(file_id) 401 };
375 .or_insert_with(|| file_id.expansion_info(self.db.upcast())) 402 let r_delim = match tt.right_delimiter_token() {
376 .as_ref()? 403 Some(it) => it.text_range().start(),
377 .map_token_down(token.as_ref())?; 404 None => tt.syntax().text_range().end()
378 405 };
379 if let Some(parent) = token.value.parent() { 406 if !TextRange::new(l_delim, r_delim).contains_range(token.value.text_range()) {
380 self.cache(find_root(&parent), token.file_id); 407 return None;
408 }
409 let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
410 let token = self
411 .expansion_info_cache
412 .borrow_mut()
413 .entry(file_id)
414 .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
415 .as_ref()?
416 .map_token_down(token.as_ref())?;
417
418 if let Some(parent) = token.value.parent() {
419 self.cache(find_root(&parent), token.file_id);
420 }
421
422 return Some(token);
423 },
424 ast::Item(item) => {
425 match self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item))) {
426 Some(call_id) => {
427 let file_id = call_id.as_file();
428 let token = self
429 .expansion_info_cache
430 .borrow_mut()
431 .entry(file_id)
432 .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
433 .as_ref()?
434 .map_token_down(token.as_ref())?;
435
436 if let Some(parent) = token.value.parent() {
437 self.cache(find_root(&parent), token.file_id);
438 }
439
440 return Some(token);
441 }
442 None => {}
443 }
444 },
445 _ => {}
446 }
447 }
381 } 448 }
382 449
383 Some(token) 450 None
384 }) 451 })
385 .last() 452 .last()
386 .unwrap(); 453 .unwrap();
@@ -677,7 +744,7 @@ impl<'db> SemanticsImpl<'db> {
677 return None; 744 return None;
678 } 745 }
679 746
680 let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; 747 let func = self.resolve_method_call(method_call_expr).map(Function::from)?;
681 let res = match func.self_param(self.db)?.access(self.db) { 748 let res = match func.self_param(self.db)?.access(self.db) {
682 Access::Shared | Access::Exclusive => true, 749 Access::Shared | Access::Exclusive => true,
683 Access::Owned => false, 750 Access::Owned => false,
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 9a5a2255f..e8c2ed48e 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -10,7 +10,7 @@ use hir_def::{
10 ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 10 ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
11 UnionId, VariantId, 11 UnionId, VariantId,
12}; 12};
13use hir_expand::{name::AsName, AstId, MacroDefKind}; 13use hir_expand::{name::AsName, AstId, MacroCallId, MacroDefKind};
14use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
15use smallvec::SmallVec; 15use smallvec::SmallVec;
16use stdx::impl_from; 16use stdx::impl_from;
@@ -145,16 +145,25 @@ impl SourceToDefCtx<'_, '_> {
145 Some((container, label_id)) 145 Some((container, label_id))
146 } 146 }
147 147
148 pub(super) fn item_to_macro_call(&mut self, src: InFile<ast::Item>) -> Option<MacroCallId> {
149 let map = self.dyn_map(src.as_ref())?;
150 map[keys::ATTR_MACRO].get(&src).copied()
151 }
152
148 fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( 153 fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
149 &mut self, 154 &mut self,
150 src: InFile<Ast>, 155 src: InFile<Ast>,
151 key: Key<Ast, ID>, 156 key: Key<Ast, ID>,
152 ) -> Option<ID> { 157 ) -> Option<ID> {
153 let container = self.find_container(src.as_ref().map(|it| it.syntax()))?; 158 self.dyn_map(src.as_ref())?[key].get(&src).copied()
159 }
160
161 fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
162 let container = self.find_container(src.map(|it| it.syntax()))?;
154 let db = self.db; 163 let db = self.db;
155 let dyn_map = 164 let dyn_map =
156 &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db)); 165 &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
157 dyn_map[key].get(&src).copied() 166 Some(dyn_map)
158 } 167 }
159 168
160 pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> { 169 pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> {
@@ -202,62 +211,68 @@ impl SourceToDefCtx<'_, '_> {
202 211
203 pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { 212 pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
204 for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { 213 for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
205 let res: ChildContainer = match_ast! { 214 if let Some(res) = self.container_to_def(container) {
206 match (container.value) { 215 return Some(res);
207 ast::Module(it) => { 216 }
208 let def = self.module_to_def(container.with_value(it))?;
209 def.into()
210 },
211 ast::Trait(it) => {
212 let def = self.trait_to_def(container.with_value(it))?;
213 def.into()
214 },
215 ast::Impl(it) => {
216 let def = self.impl_to_def(container.with_value(it))?;
217 def.into()
218 },
219 ast::Fn(it) => {
220 let def = self.fn_to_def(container.with_value(it))?;
221 DefWithBodyId::from(def).into()
222 },
223 ast::Struct(it) => {
224 let def = self.struct_to_def(container.with_value(it))?;
225 VariantId::from(def).into()
226 },
227 ast::Enum(it) => {
228 let def = self.enum_to_def(container.with_value(it))?;
229 def.into()
230 },
231 ast::Union(it) => {
232 let def = self.union_to_def(container.with_value(it))?;
233 VariantId::from(def).into()
234 },
235 ast::Static(it) => {
236 let def = self.static_to_def(container.with_value(it))?;
237 DefWithBodyId::from(def).into()
238 },
239 ast::Const(it) => {
240 let def = self.const_to_def(container.with_value(it))?;
241 DefWithBodyId::from(def).into()
242 },
243 ast::TypeAlias(it) => {
244 let def = self.type_alias_to_def(container.with_value(it))?;
245 def.into()
246 },
247 ast::Variant(it) => {
248 let def = self.enum_variant_to_def(container.with_value(it))?;
249 VariantId::from(def).into()
250 },
251 _ => continue,
252 }
253 };
254 return Some(res);
255 } 217 }
256 218
257 let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?; 219 let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?;
258 Some(def.into()) 220 Some(def.into())
259 } 221 }
260 222
223 fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<ChildContainer> {
224 let cont = match_ast! {
225 match (container.value) {
226 ast::Module(it) => {
227 let def = self.module_to_def(container.with_value(it))?;
228 def.into()
229 },
230 ast::Trait(it) => {
231 let def = self.trait_to_def(container.with_value(it))?;
232 def.into()
233 },
234 ast::Impl(it) => {
235 let def = self.impl_to_def(container.with_value(it))?;
236 def.into()
237 },
238 ast::Fn(it) => {
239 let def = self.fn_to_def(container.with_value(it))?;
240 DefWithBodyId::from(def).into()
241 },
242 ast::Struct(it) => {
243 let def = self.struct_to_def(container.with_value(it))?;
244 VariantId::from(def).into()
245 },
246 ast::Enum(it) => {
247 let def = self.enum_to_def(container.with_value(it))?;
248 def.into()
249 },
250 ast::Union(it) => {
251 let def = self.union_to_def(container.with_value(it))?;
252 VariantId::from(def).into()
253 },
254 ast::Static(it) => {
255 let def = self.static_to_def(container.with_value(it))?;
256 DefWithBodyId::from(def).into()
257 },
258 ast::Const(it) => {
259 let def = self.const_to_def(container.with_value(it))?;
260 DefWithBodyId::from(def).into()
261 },
262 ast::TypeAlias(it) => {
263 let def = self.type_alias_to_def(container.with_value(it))?;
264 def.into()
265 },
266 ast::Variant(it) => {
267 let def = self.enum_variant_to_def(container.with_value(it))?;
268 VariantId::from(def).into()
269 },
270 _ => return None,
271 }
272 };
273 Some(cont)
274 }
275
261 fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> { 276 fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
262 for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { 277 for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
263 let res: GenericDefId = match_ast! { 278 let res: GenericDefId = match_ast! {
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 37a050415..c9744d81d 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -222,7 +222,7 @@ impl SourceAnalyzer {
222 Pat::Path(path) => path, 222 Pat::Path(path) => path,
223 _ => return None, 223 _ => return None,
224 }; 224 };
225 let res = resolve_hir_path(db, &self.resolver, &path)?; 225 let res = resolve_hir_path(db, &self.resolver, path)?;
226 match res { 226 match res {
227 PathResolution::Def(def) => Some(def), 227 PathResolution::Def(def) => Some(def),
228 _ => None, 228 _ => None,
@@ -329,7 +329,7 @@ impl SourceAnalyzer {
329 329
330 let (variant, missing_fields, _exhaustive) = 330 let (variant, missing_fields, _exhaustive) =
331 record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; 331 record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
332 let res = self.missing_fields(db, krate, &substs, variant, missing_fields); 332 let res = self.missing_fields(db, krate, substs, variant, missing_fields);
333 Some(res) 333 Some(res)
334 } 334 }
335 335
@@ -347,7 +347,7 @@ impl SourceAnalyzer {
347 347
348 let (variant, missing_fields, _exhaustive) = 348 let (variant, missing_fields, _exhaustive) =
349 record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; 349 record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
350 let res = self.missing_fields(db, krate, &substs, variant, missing_fields); 350 let res = self.missing_fields(db, krate, substs, variant, missing_fields);
351 Some(res) 351 Some(res)
352 } 352 }
353 353
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 43324d8d9..bb86f6a73 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14dashmap = { version = "4.0.2", features = ["raw-api"] } 14dashmap = { version = "4.0.2", features = ["raw-api"] }
15log = "0.4.8" 15log = "0.4.8"
16once_cell = "1.3.1" 16once_cell = "1.3.1"
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 385ba8c80..d07adb084 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -36,6 +36,10 @@ use crate::{
36pub struct Documentation(String); 36pub struct Documentation(String);
37 37
38impl Documentation { 38impl Documentation {
39 pub fn new(s: String) -> Self {
40 Documentation(s)
41 }
42
39 pub fn as_str(&self) -> &str { 43 pub fn as_str(&self) -> &str {
40 &self.0 44 &self.0
41 } 45 }
@@ -102,7 +106,9 @@ impl RawAttrs {
102 ) -> Self { 106 ) -> Self {
103 let entries = collect_attrs(owner) 107 let entries = collect_attrs(owner)
104 .flat_map(|(id, attr)| match attr { 108 .flat_map(|(id, attr)| match attr {
105 Either::Left(attr) => Attr::from_src(db, attr, hygiene, id), 109 Either::Left(attr) => {
110 attr.meta().and_then(|meta| Attr::from_src(db, meta, hygiene, id))
111 }
106 Either::Right(comment) => comment.doc_comment().map(|doc| Attr { 112 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
107 id, 113 id,
108 input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), 114 input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
@@ -168,10 +174,9 @@ impl RawAttrs {
168 let index = attr.id; 174 let index = attr.id;
169 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { 175 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
170 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; 176 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
171 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
172 // FIXME hygiene 177 // FIXME hygiene
173 let hygiene = Hygiene::new_unhygienic(); 178 let hygiene = Hygiene::new_unhygienic();
174 Attr::from_src(db, attr, &hygiene, index) 179 Attr::from_tt(db, &tree, &hygiene, index)
175 }); 180 });
176 181
177 let cfg_options = &crate_graph[krate].cfg_options; 182 let cfg_options = &crate_graph[krate].cfg_options;
@@ -578,13 +583,13 @@ impl AttrSourceMap {
578 .get(id.ast_index as usize) 583 .get(id.ast_index as usize)
579 .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id)) 584 .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
580 .clone() 585 .clone()
581 .map(|attr| Either::Right(attr)) 586 .map(Either::Right)
582 } else { 587 } else {
583 self.attrs 588 self.attrs
584 .get(id.ast_index as usize) 589 .get(id.ast_index as usize)
585 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id)) 590 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
586 .clone() 591 .clone()
587 .map(|attr| Either::Left(attr)) 592 .map(Either::Left)
588 } 593 }
589 } 594 }
590} 595}
@@ -601,7 +606,7 @@ pub struct DocsRangeMap {
601impl DocsRangeMap { 606impl DocsRangeMap {
602 pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { 607 pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
603 let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; 608 let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
604 let (line_docs_range, idx, original_line_src_range) = self.mapping[found].clone(); 609 let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
605 if !line_docs_range.contains_range(range) { 610 if !line_docs_range.contains_range(range) {
606 return None; 611 return None;
607 } 612 }
@@ -660,7 +665,7 @@ impl fmt::Display for AttrInput {
660impl Attr { 665impl Attr {
661 fn from_src( 666 fn from_src(
662 db: &dyn DefDatabase, 667 db: &dyn DefDatabase,
663 ast: ast::Attr, 668 ast: ast::Meta,
664 hygiene: &Hygiene, 669 hygiene: &Hygiene,
665 id: AttrId, 670 id: AttrId,
666 ) -> Option<Attr> { 671 ) -> Option<Attr> {
@@ -679,6 +684,19 @@ impl Attr {
679 Some(Attr { id, path, input }) 684 Some(Attr { id, path, input })
680 } 685 }
681 686
687 fn from_tt(
688 db: &dyn DefDatabase,
689 tt: &tt::Subtree,
690 hygiene: &Hygiene,
691 id: AttrId,
692 ) -> Option<Attr> {
693 let (parse, _) =
694 mbe::token_tree_to_syntax_node(tt, hir_expand::FragmentKind::MetaItem).ok()?;
695 let ast = ast::Meta::cast(parse.syntax_node())?;
696
697 Self::from_src(db, ast, hygiene, id)
698 }
699
682 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths 700 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
683 /// to derive macros. 701 /// to derive macros.
684 /// 702 ///
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index da1fdac33..bed4c4994 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1000,18 +1000,18 @@ impl From<ast::LiteralKind> for Literal {
1000 // FIXME: these should have actual values filled in, but unsure on perf impact 1000 // FIXME: these should have actual values filled in, but unsure on perf impact
1001 LiteralKind::IntNumber(lit) => { 1001 LiteralKind::IntNumber(lit) => {
1002 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { 1002 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
1003 return Literal::Float(Default::default(), builtin); 1003 Literal::Float(Default::default(), builtin)
1004 } else if let builtin @ Some(_) = 1004 } else if let builtin @ Some(_) =
1005 lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) 1005 lit.suffix().and_then(|it| BuiltinInt::from_suffix(it))
1006 { 1006 {
1007 Literal::Int(lit.value().unwrap_or(0) as i128, builtin) 1007 Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
1008 } else { 1008 } else {
1009 let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); 1009 let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(it));
1010 Literal::Uint(lit.value().unwrap_or(0), builtin) 1010 Literal::Uint(lit.value().unwrap_or(0), builtin)
1011 } 1011 }
1012 } 1012 }
1013 LiteralKind::FloatNumber(lit) => { 1013 LiteralKind::FloatNumber(lit) => {
1014 let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); 1014 let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(it));
1015 Literal::Float(Default::default(), ty) 1015 Literal::Float(Default::default(), ty)
1016 } 1016 }
1017 LiteralKind::ByteString(bs) => { 1017 LiteralKind::ByteString(bs) => {
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 6764de3a7..58a1fc81c 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -198,7 +198,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
198 } 198 }
199 Expr::Lambda { args, body: body_expr, .. } => { 199 Expr::Lambda { args, body: body_expr, .. } => {
200 let scope = scopes.new_scope(scope); 200 let scope = scopes.new_scope(scope);
201 scopes.add_params_bindings(body, scope, &args); 201 scopes.add_params_bindings(body, scope, args);
202 compute_expr_scopes(*body_expr, body, scopes, scope); 202 compute_expr_scopes(*body_expr, body, scopes, scope);
203 } 203 }
204 Expr::Match { expr, arms } => { 204 Expr::Match { expr, arms } => {
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index d4fae05a6..27d837d47 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -3,7 +3,7 @@ mod block;
3use base_db::{fixture::WithFixture, SourceDatabase}; 3use base_db::{fixture::WithFixture, SourceDatabase};
4use expect_test::Expect; 4use expect_test::Expect;
5 5
6use crate::{test_db::TestDB, ModuleDefId}; 6use crate::ModuleDefId;
7 7
8use super::*; 8use super::*;
9 9
@@ -28,11 +28,6 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
28 db.body(fn_def.unwrap().into()) 28 db.body(fn_def.unwrap().into())
29} 29}
30 30
31fn check_diagnostics(ra_fixture: &str) {
32 let db: TestDB = TestDB::with_files(ra_fixture);
33 db.check_diagnostics();
34}
35
36fn block_def_map_at(ra_fixture: &str) -> String { 31fn block_def_map_at(ra_fixture: &str) -> String {
37 let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); 32 let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
38 33
@@ -57,7 +52,7 @@ fn check_at(ra_fixture: &str, expect: Expect) {
57fn your_stack_belongs_to_me() { 52fn your_stack_belongs_to_me() {
58 cov_mark::check!(your_stack_belongs_to_me); 53 cov_mark::check!(your_stack_belongs_to_me);
59 lower( 54 lower(
60 " 55 r#"
61macro_rules! n_nuple { 56macro_rules! n_nuple {
62 ($e:tt) => (); 57 ($e:tt) => ();
63 ($($rest:tt)*) => {{ 58 ($($rest:tt)*) => {{
@@ -65,7 +60,7 @@ macro_rules! n_nuple {
65 }}; 60 }};
66} 61}
67fn main() { n_nuple!(1,2,3); } 62fn main() { n_nuple!(1,2,3); }
68", 63"#,
69 ); 64 );
70} 65}
71 66
@@ -73,7 +68,7 @@ fn main() { n_nuple!(1,2,3); }
73fn macro_resolve() { 68fn macro_resolve() {
74 // Regression test for a path resolution bug introduced with inner item handling. 69 // Regression test for a path resolution bug introduced with inner item handling.
75 lower( 70 lower(
76 r" 71 r#"
77macro_rules! vec { 72macro_rules! vec {
78 () => { () }; 73 () => { () };
79 ($elem:expr; $n:expr) => { () }; 74 ($elem:expr; $n:expr) => { () };
@@ -84,140 +79,6 @@ mod m {
84 let _ = vec![FileSet::default(); self.len()]; 79 let _ = vec![FileSet::default(); self.len()];
85 } 80 }
86} 81}
87 ", 82"#,
88 );
89}
90
91#[test]
92fn cfg_diagnostics() {
93 check_diagnostics(
94 r"
95fn f() {
96 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
97
98 #[cfg(a)] fn f() {} // Item statement
99 //^^^^^^^^^^^^^^^^^^^ InactiveCode
100 #[cfg(a)] {} // Expression statement
101 //^^^^^^^^^^^^ InactiveCode
102 #[cfg(a)] let x = 0; // let statement
103 //^^^^^^^^^^^^^^^^^^^^ InactiveCode
104
105 abc(#[cfg(a)] 0);
106 //^^^^^^^^^^^ InactiveCode
107 let x = Struct {
108 #[cfg(a)] f: 0,
109 //^^^^^^^^^^^^^^ InactiveCode
110 };
111 match () {
112 () => (),
113 #[cfg(a)] () => (),
114 //^^^^^^^^^^^^^^^^^^ InactiveCode
115 }
116
117 #[cfg(a)] 0 // Trailing expression of block
118 //^^^^^^^^^^^ InactiveCode
119}
120 ",
121 );
122}
123
124#[test]
125fn macro_diag_builtin() {
126 check_diagnostics(
127 r#"
128#[rustc_builtin_macro]
129macro_rules! env {}
130
131#[rustc_builtin_macro]
132macro_rules! include {}
133
134#[rustc_builtin_macro]
135macro_rules! compile_error {}
136
137#[rustc_builtin_macro]
138macro_rules! format_args {
139 () => {}
140}
141
142fn f() {
143 // Test a handful of built-in (eager) macros:
144
145 include!(invalid);
146 //^^^^^^^^^^^^^^^^^ could not convert tokens
147 include!("does not exist");
148 //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist`
149
150 env!(invalid);
151 //^^^^^^^^^^^^^ could not convert tokens
152
153 env!("OUT_DIR");
154 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
155
156 compile_error!("compile_error works");
157 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
158
159 // Lazy:
160
161 format_args!();
162 //^^^^^^^^^^^^^^ no rule matches input tokens
163}
164 "#,
165 );
166}
167
168#[test]
169fn macro_rules_diag() {
170 check_diagnostics(
171 r#"
172macro_rules! m {
173 () => {};
174}
175fn f() {
176 m!();
177
178 m!(hi);
179 //^^^^^^ leftover tokens
180}
181 "#,
182 ); 83 );
183} 84}
184
185#[test]
186fn unresolved_macro_diag() {
187 check_diagnostics(
188 r#"
189fn f() {
190 m!();
191 //^^^^ UnresolvedMacroCall
192}
193 "#,
194 );
195}
196
197#[test]
198fn dollar_crate_in_builtin_macro() {
199 check_diagnostics(
200 r#"
201#[macro_export]
202#[rustc_builtin_macro]
203macro_rules! format_args {}
204
205#[macro_export]
206macro_rules! arg {
207 () => {}
208}
209
210#[macro_export]
211macro_rules! outer {
212 () => {
213 $crate::format_args!( "", $crate::arg!(1) )
214 };
215}
216
217fn f() {
218 outer!();
219 //^^^^^^^^ leftover tokens
220}
221 "#,
222 )
223}
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs
index bc3d0f138..15c10d053 100644
--- a/crates/hir_def/src/body/tests/block.rs
+++ b/crates/hir_def/src/body/tests/block.rs
@@ -163,14 +163,14 @@ fn legacy_macro_items() {
163 // correctly. 163 // correctly.
164 check_at( 164 check_at(
165 r#" 165 r#"
166macro_rules! hit { 166macro_rules! mark {
167 () => { 167 () => {
168 struct Hit {} 168 struct Hit {}
169 } 169 }
170} 170}
171 171
172fn f() { 172fn f() {
173 hit!(); 173 mark!();
174 $0 174 $0
175} 175}
176"#, 176"#,
@@ -193,20 +193,20 @@ use core::cov_mark;
193 193
194fn f() { 194fn f() {
195 fn nested() { 195 fn nested() {
196 cov_mark::hit!(Hit); 196 cov_mark::mark!(Hit);
197 $0 197 $0
198 } 198 }
199} 199}
200//- /core.rs crate:core 200//- /core.rs crate:core
201pub mod cov_mark { 201pub mod cov_mark {
202 #[macro_export] 202 #[macro_export]
203 macro_rules! _hit { 203 macro_rules! _mark {
204 ($name:ident) => { 204 ($name:ident) => {
205 struct $name {} 205 struct $name {}
206 } 206 }
207 } 207 }
208 208
209 pub use crate::_hit as hit; 209 pub use crate::_mark as mark;
210} 210}
211"#, 211"#,
212 expect![[r#" 212 expect![[r#"
diff --git a/crates/hir_def/src/builtin_attr.rs b/crates/hir_def/src/builtin_attr.rs
index d5d7f0f47..39c7f84f7 100644
--- a/crates/hir_def/src/builtin_attr.rs
+++ b/crates/hir_def/src/builtin_attr.rs
@@ -2,7 +2,7 @@
2//! 2//!
3//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. 3//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
4//! 4//!
5//! It was last synchronized with upstream commit 2225ee1b62ff089917434aefd9b2bf509cfa087f. 5//! It was last synchronized with upstream commit 835150e70288535bc57bb624792229b9dc94991d.
6//! 6//!
7//! The macros were adjusted to only expand to the attribute name, since that is all we need to do 7//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
8//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to 8//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
@@ -58,7 +58,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
58 ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")), 58 ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")),
59 59
60 // Macros: 60 // Macros:
61 ungated!(derive, Normal, template!(List: "Trait1, Trait2, ...")),
62 ungated!(automatically_derived, Normal, template!(Word)), 61 ungated!(automatically_derived, Normal, template!(Word)),
63 // FIXME(#14407) 62 // FIXME(#14407)
64 ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")), 63 ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")),
@@ -98,8 +97,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
98 template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), 97 template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
99 ), 98 ),
100 ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")), 99 ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")),
101 ungated!(no_link, Normal, template!(Word)), 100 ungated!(no_link, AssumedUsed, template!(Word)),
102 ungated!(repr, Normal, template!(List: "C")), 101 ungated!(repr, AssumedUsed, template!(List: "C")),
103 ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")), 102 ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")),
104 ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")), 103 ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")),
105 ungated!(no_mangle, AssumedUsed, template!(Word)), 104 ungated!(no_mangle, AssumedUsed, template!(Word)),
@@ -112,6 +111,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
112 const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit, 111 const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
113 experimental!(const_eval_limit) 112 experimental!(const_eval_limit)
114 ), 113 ),
114 gated!(
115 move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments,
116 experimental!(move_size_limit)
117 ),
115 118
116 // Entry point: 119 // Entry point:
117 ungated!(main, Normal, template!(Word)), 120 ungated!(main, Normal, template!(Word)),
@@ -140,6 +143,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
140 template!(List: "address, memory, thread"), 143 template!(List: "address, memory, thread"),
141 experimental!(no_sanitize) 144 experimental!(no_sanitize)
142 ), 145 ),
146 gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),
143 147
144 // FIXME: #14408 assume docs are used since rustdoc looks at them. 148 // FIXME: #14408 assume docs are used since rustdoc looks at them.
145 ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")), 149 ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
@@ -151,11 +155,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
151 // Linking: 155 // Linking:
152 gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)), 156 gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)),
153 gated!( 157 gated!(
154 link_args, Normal, template!(NameValueStr: "args"),
155 "the `link_args` attribute is experimental and not portable across platforms, \
156 it is recommended to use `#[link(name = \"foo\")] instead",
157 ),
158 gated!(
159 link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib, 158 link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib,
160 experimental!(link_ordinal) 159 experimental!(link_ordinal)
161 ), 160 ),
@@ -172,7 +171,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
172 "custom test frameworks are an unstable feature", 171 "custom test frameworks are an unstable feature",
173 ), 172 ),
174 // RFC #1268 173 // RFC #1268
175 gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)), 174 gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)),
176 gated!( 175 gated!(
177 thread_local, AssumedUsed, template!(Word), 176 thread_local, AssumedUsed, template!(Word),
178 "`#[thread_local]` is an experimental feature, and does not currently handle destructors", 177 "`#[thread_local]` is an experimental feature, and does not currently handle destructors",
@@ -291,7 +290,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
291 // Internal attributes, Macro related: 290 // Internal attributes, Macro related:
292 // ========================================================================== 291 // ==========================================================================
293 292
294 rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word), IMPL_DETAIL), 293 rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL),
295 rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), 294 rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
296 rustc_attr!( 295 rustc_attr!(
297 rustc_macro_transparency, AssumedUsed, 296 rustc_macro_transparency, AssumedUsed,
@@ -319,7 +318,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
319 // ========================================================================== 318 // ==========================================================================
320 319
321 rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL), 320 rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
322 rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), 321 rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
323 322
324 // ========================================================================== 323 // ==========================================================================
325 // Internal attributes, Layout related: 324 // Internal attributes, Layout related:
@@ -380,6 +379,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
380 rustc_specialization_trait, Normal, template!(Word), 379 rustc_specialization_trait, Normal, template!(Word),
381 "the `#[rustc_specialization_trait]` attribute is used to check specializations" 380 "the `#[rustc_specialization_trait]` attribute is used to check specializations"
382 ), 381 ),
382 rustc_attr!(
383 rustc_main, Normal, template!(Word),
384 "the `#[rustc_main]` attribute is used internally to specify test entry point function",
385 ),
386 rustc_attr!(
387 rustc_skip_array_during_method_dispatch, Normal, template!(Word),
388 "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
389 from method dispatch when the receiver is an array, for compatibility in editions < 2021."
390 ),
383 391
384 // ========================================================================== 392 // ==========================================================================
385 // Internal attributes, Testing: 393 // Internal attributes, Testing:
@@ -387,6 +395,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
387 395
388 rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), 396 rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
389 rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), 397 rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)),
398 rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word)),
390 rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), 399 rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
391 rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), 400 rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
392 rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), 401 rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),
@@ -395,13 +404,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
395 template!(Word, List: "delay_span_bug_from_inside_query") 404 template!(Word, List: "delay_span_bug_from_inside_query")
396 ), 405 ),
397 rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)), 406 rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)),
407 rustc_attr!(TEST, rustc_evaluate_where_clauses, AssumedUsed, template!(Word)),
398 rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")), 408 rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")),
399 rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")), 409 rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")),
400 rustc_attr!( 410 rustc_attr!(
401 TEST, rustc_dirty, AssumedUsed,
402 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
403 ),
404 rustc_attr!(
405 TEST, rustc_clean, AssumedUsed, 411 TEST, rustc_clean, AssumedUsed,
406 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), 412 template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
407 ), 413 ),
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index f2e809ca9..f22383e22 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -85,6 +85,10 @@ impl ChildBySource for ItemScope {
85 res[keys::CONST].insert(src, konst); 85 res[keys::CONST].insert(src, konst);
86 }); 86 });
87 self.impls().for_each(|imp| add_impl(db, res, imp)); 87 self.impls().for_each(|imp| add_impl(db, res, imp));
88 self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
89 let item = ast_id.with_value(ast_id.to_node(db.upcast()));
90 res[keys::ATTR_MACRO].insert(item, call_id);
91 });
88 92
89 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { 93 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
90 match item { 94 match item {
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index d2bb381be..52cb7777b 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -143,6 +143,10 @@ pub struct TraitData {
143 pub is_auto: bool, 143 pub is_auto: bool,
144 pub is_unsafe: bool, 144 pub is_unsafe: bool,
145 pub visibility: RawVisibility, 145 pub visibility: RawVisibility,
146 /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
147 /// method calls to this trait's methods when the receiver is an array and the crate edition is
148 /// 2015 or 2018.
149 pub skip_array_during_method_dispatch: bool,
146} 150}
147 151
148impl TraitData { 152impl TraitData {
@@ -157,6 +161,10 @@ impl TraitData {
157 let container = AssocContainerId::TraitId(tr); 161 let container = AssocContainerId::TraitId(tr);
158 let visibility = item_tree[tr_def.visibility].clone(); 162 let visibility = item_tree[tr_def.visibility].clone();
159 let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); 163 let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
164 let skip_array_during_method_dispatch = item_tree
165 .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into())
166 .by_key("rustc_skip_array_during_method_dispatch")
167 .exists();
160 168
161 let items = collect_items( 169 let items = collect_items(
162 db, 170 db,
@@ -168,7 +176,14 @@ impl TraitData {
168 100, 176 100,
169 ); 177 );
170 178
171 Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility }) 179 Arc::new(TraitData {
180 name,
181 items,
182 is_auto,
183 is_unsafe,
184 visibility,
185 skip_array_during_method_dispatch,
186 })
172 } 187 }
173 188
174 pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ { 189 pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 7eadc8e0d..c977971cd 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -51,6 +51,9 @@ pub trait InternDatabase: SourceDatabase {
51 51
52#[salsa::query_group(DefDatabaseStorage)] 52#[salsa::query_group(DefDatabaseStorage)]
53pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { 53pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
54 #[salsa::input]
55 fn enable_proc_attr_macros(&self) -> bool;
56
54 #[salsa::invoke(ItemTree::file_item_tree_query)] 57 #[salsa::invoke(ItemTree::file_item_tree_query)]
55 fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; 58 fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
56 59
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 44d22b918..6933f6e3c 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -280,7 +280,7 @@ impl GenericParams {
280 sm.type_params.insert(param_id, Either::Right(type_param.clone())); 280 sm.type_params.insert(param_id, Either::Right(type_param.clone()));
281 281
282 let type_ref = TypeRef::Path(name.into()); 282 let type_ref = TypeRef::Path(name.into());
283 self.fill_bounds(&lower_ctx, &type_param, Either::Left(type_ref)); 283 self.fill_bounds(lower_ctx, &type_param, Either::Left(type_ref));
284 } 284 }
285 for lifetime_param in params.lifetime_params() { 285 for lifetime_param in params.lifetime_params() {
286 let name = 286 let name =
@@ -289,7 +289,7 @@ impl GenericParams {
289 let param_id = self.lifetimes.alloc(param); 289 let param_id = self.lifetimes.alloc(param);
290 sm.lifetime_params.insert(param_id, lifetime_param.clone()); 290 sm.lifetime_params.insert(param_id, lifetime_param.clone());
291 let lifetime_ref = LifetimeRef::new_name(name); 291 let lifetime_ref = LifetimeRef::new_name(name);
292 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); 292 self.fill_bounds(lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
293 } 293 }
294 for const_param in params.const_params() { 294 for const_param in params.const_params() {
295 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); 295 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 960cabb5f..404e3e153 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -1,6 +1,6 @@
1//! A map of all publicly exported items in a crate. 1//! A map of all publicly exported items in a crate.
2 2
3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; 3use std::{fmt, hash::BuildHasherDefault, sync::Arc};
4 4
5use base_db::CrateId; 5use base_db::CrateId;
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
@@ -69,81 +69,11 @@ pub struct ImportMap {
69impl ImportMap { 69impl ImportMap {
70 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { 70 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
71 let _p = profile::span("import_map_query"); 71 let _p = profile::span("import_map_query");
72 let def_map = db.crate_def_map(krate);
73 let mut import_map = Self::default();
74
75 // We look only into modules that are public(ly reexported), starting with the crate root.
76 let empty = ImportPath { segments: vec![] };
77 let root = def_map.module_id(def_map.root());
78 let mut worklist = vec![(root, empty)];
79 while let Some((module, mod_path)) = worklist.pop() {
80 let ext_def_map;
81 let mod_data = if module.krate == krate {
82 &def_map[module.local_id]
83 } else {
84 // The crate might reexport a module defined in another crate.
85 ext_def_map = module.def_map(db);
86 &ext_def_map[module.local_id]
87 };
88
89 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
90 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
91 if per_ns.is_none() {
92 None
93 } else {
94 Some((name, per_ns))
95 }
96 });
97
98 for (name, per_ns) in visible_items {
99 let mk_path = || {
100 let mut path = mod_path.clone();
101 path.segments.push(name.clone());
102 path
103 };
104
105 for item in per_ns.iter_items() {
106 let path = mk_path();
107 let path_len = path.len();
108 let import_info =
109 ImportInfo { path, container: module, is_trait_assoc_item: false };
110
111 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
112 import_map.collect_trait_assoc_items(
113 db,
114 tr,
115 matches!(item, ItemInNs::Types(_)),
116 &import_info,
117 );
118 }
119 72
120 match import_map.map.entry(item) { 73 let mut import_map = collect_import_map(db, krate);
121 Entry::Vacant(entry) => {
122 entry.insert(import_info);
123 }
124 Entry::Occupied(mut entry) => {
125 // If the new path is shorter, prefer that one.
126 if path_len < entry.get().path.len() {
127 *entry.get_mut() = import_info;
128 } else {
129 continue;
130 }
131 }
132 }
133
134 // If we've just added a path to a module, descend into it. We might traverse
135 // modules multiple times, but only if the new path to it is shorter than the
136 // first (else we `continue` above).
137 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
138 worklist.push((mod_id, mk_path()));
139 }
140 }
141 }
142 }
143 74
144 let mut importables = import_map.map.iter().collect::<Vec<_>>(); 75 let mut importables = import_map.map.iter().collect::<Vec<_>>();
145 76 importables.sort_by_cached_key(|(_, import_info)| fst_path(&import_info.path));
146 importables.sort_by(cmp);
147 77
148 // Build the FST, taking care not to insert duplicate values. 78 // Build the FST, taking care not to insert duplicate values.
149 79
@@ -151,13 +81,13 @@ impl ImportMap {
151 let mut last_batch_start = 0; 81 let mut last_batch_start = 0;
152 82
153 for idx in 0..importables.len() { 83 for idx in 0..importables.len() {
154 if let Some(next_item) = importables.get(idx + 1) { 84 let key = fst_path(&importables[last_batch_start].1.path);
155 if cmp(&importables[last_batch_start], next_item) == Ordering::Equal { 85 if let Some((_, next_import_info)) = importables.get(idx + 1) {
86 if key == fst_path(&next_import_info.path) {
156 continue; 87 continue;
157 } 88 }
158 } 89 }
159 90
160 let key = fst_path(&importables[last_batch_start].1.path);
161 builder.insert(key, last_batch_start as u64).unwrap(); 91 builder.insert(key, last_batch_start as u64).unwrap();
162 92
163 last_batch_start = idx + 1; 93 last_batch_start = idx + 1;
@@ -185,6 +115,7 @@ impl ImportMap {
185 is_type_in_ns: bool, 115 is_type_in_ns: bool,
186 original_import_info: &ImportInfo, 116 original_import_info: &ImportInfo,
187 ) { 117 ) {
118 let _p = profile::span("collect_trait_assoc_items");
188 for (assoc_item_name, item) in &db.trait_data(tr).items { 119 for (assoc_item_name, item) in &db.trait_data(tr).items {
189 let module_def_id = match item { 120 let module_def_id = match item {
190 AssocItemId::FunctionId(f) => ModuleDefId::from(*f), 121 AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
@@ -210,6 +141,84 @@ impl ImportMap {
210 } 141 }
211} 142}
212 143
144fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
145 let _p = profile::span("collect_import_map");
146
147 let def_map = db.crate_def_map(krate);
148 let mut import_map = ImportMap::default();
149
150 // We look only into modules that are public(ly reexported), starting with the crate root.
151 let empty = ImportPath { segments: vec![] };
152 let root = def_map.module_id(def_map.root());
153 let mut worklist = vec![(root, empty)];
154 while let Some((module, mod_path)) = worklist.pop() {
155 let ext_def_map;
156 let mod_data = if module.krate == krate {
157 &def_map[module.local_id]
158 } else {
159 // The crate might reexport a module defined in another crate.
160 ext_def_map = module.def_map(db);
161 &ext_def_map[module.local_id]
162 };
163
164 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
165 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
166 if per_ns.is_none() {
167 None
168 } else {
169 Some((name, per_ns))
170 }
171 });
172
173 for (name, per_ns) in visible_items {
174 let mk_path = || {
175 let mut path = mod_path.clone();
176 path.segments.push(name.clone());
177 path
178 };
179
180 for item in per_ns.iter_items() {
181 let path = mk_path();
182 let path_len = path.len();
183 let import_info =
184 ImportInfo { path, container: module, is_trait_assoc_item: false };
185
186 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
187 import_map.collect_trait_assoc_items(
188 db,
189 tr,
190 matches!(item, ItemInNs::Types(_)),
191 &import_info,
192 );
193 }
194
195 match import_map.map.entry(item) {
196 Entry::Vacant(entry) => {
197 entry.insert(import_info);
198 }
199 Entry::Occupied(mut entry) => {
200 // If the new path is shorter, prefer that one.
201 if path_len < entry.get().path.len() {
202 *entry.get_mut() = import_info;
203 } else {
204 continue;
205 }
206 }
207 }
208
209 // If we've just added a path to a module, descend into it. We might traverse
210 // modules multiple times, but only if the new path to it is shorter than the
211 // first (else we `continue` above).
212 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
213 worklist.push((mod_id, mk_path()));
214 }
215 }
216 }
217 }
218
219 import_map
220}
221
213impl PartialEq for ImportMap { 222impl PartialEq for ImportMap {
214 fn eq(&self, other: &Self) -> bool { 223 fn eq(&self, other: &Self) -> bool {
215 // `fst` and `importables` are built from `map`, so we don't need to compare them. 224 // `fst` and `importables` are built from `map`, so we don't need to compare them.
@@ -240,17 +249,12 @@ impl fmt::Debug for ImportMap {
240} 249}
241 250
242fn fst_path(path: &ImportPath) -> String { 251fn fst_path(path: &ImportPath) -> String {
252 let _p = profile::span("fst_path");
243 let mut s = path.to_string(); 253 let mut s = path.to_string();
244 s.make_ascii_lowercase(); 254 s.make_ascii_lowercase();
245 s 255 s
246} 256}
247 257
248fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
249 let lhs_str = fst_path(&lhs.path);
250 let rhs_str = fst_path(&rhs.path);
251 lhs_str.cmp(&rhs_str)
252}
253
254#[derive(Debug, Eq, PartialEq, Hash)] 258#[derive(Debug, Eq, PartialEq, Hash)]
255pub enum ImportKind { 259pub enum ImportKind {
256 Module, 260 Module,
@@ -338,6 +342,7 @@ impl Query {
338 } 342 }
339 343
340 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { 344 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
345 let _p = profile::span("import_map::Query::import_matches");
341 if import.is_trait_assoc_item { 346 if import.is_trait_assoc_item {
342 if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { 347 if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
343 return false; 348 return false;
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 9014468ea..08407ebfa 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -4,11 +4,11 @@
4use std::collections::hash_map::Entry; 4use std::collections::hash_map::Entry;
5 5
6use base_db::CrateId; 6use base_db::CrateId;
7use hir_expand::name::Name; 7use hir_expand::{name::Name, AstId, MacroCallId, MacroDefKind};
8use hir_expand::MacroDefKind;
9use once_cell::sync::Lazy; 8use once_cell::sync::Lazy;
10use rustc_hash::{FxHashMap, FxHashSet}; 9use rustc_hash::{FxHashMap, FxHashSet};
11use stdx::format_to; 10use stdx::format_to;
11use syntax::ast;
12 12
13use crate::{ 13use crate::{
14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
@@ -53,12 +53,13 @@ pub struct ItemScope {
53 // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will 53 // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
54 // be all resolved to the last one defined if shadowing happens. 54 // be all resolved to the last one defined if shadowing happens.
55 legacy_macros: FxHashMap<Name, MacroDefId>, 55 legacy_macros: FxHashMap<Name, MacroDefId>,
56 attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
56} 57}
57 58
58pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { 59pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
59 BuiltinType::ALL 60 BuiltinType::ALL
60 .iter() 61 .iter()
61 .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) 62 .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public)))
62 .collect() 63 .collect()
63}); 64});
64 65
@@ -169,6 +170,16 @@ impl ItemScope {
169 self.legacy_macros.insert(name, mac); 170 self.legacy_macros.insert(name, mac);
170 } 171 }
171 172
173 pub(crate) fn add_attr_macro_invoc(&mut self, item: AstId<ast::Item>, call: MacroCallId) {
174 self.attr_macros.insert(item, call);
175 }
176
177 pub(crate) fn attr_macro_invocs(
178 &self,
179 ) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
180 self.attr_macros.iter().map(|(k, v)| (*k, *v))
181 }
182
172 pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> { 183 pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
173 self.unnamed_trait_imports.get(&tr).copied() 184 self.unnamed_trait_imports.get(&tr).copied()
174 } 185 }
@@ -307,6 +318,7 @@ impl ItemScope {
307 unnamed_consts, 318 unnamed_consts,
308 unnamed_trait_imports, 319 unnamed_trait_imports,
309 legacy_macros, 320 legacy_macros,
321 attr_macros,
310 } = self; 322 } = self;
311 types.shrink_to_fit(); 323 types.shrink_to_fit();
312 values.shrink_to_fit(); 324 values.shrink_to_fit();
@@ -317,6 +329,7 @@ impl ItemScope {
317 unnamed_consts.shrink_to_fit(); 329 unnamed_consts.shrink_to_fit();
318 unnamed_trait_imports.shrink_to_fit(); 330 unnamed_trait_imports.shrink_to_fit();
319 legacy_macros.shrink_to_fit(); 331 legacy_macros.shrink_to_fit();
332 attr_macros.shrink_to_fit();
320 } 333 }
321} 334}
322 335
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 6208facd5..3f90bda74 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -130,7 +130,7 @@ impl<'a> Ctx<'a> {
130 ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(), 130 ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
131 }; 131 };
132 132
133 self.add_attrs(item.into(), attrs.clone()); 133 self.add_attrs(item.into(), attrs);
134 134
135 Some(item) 135 Some(item)
136 } 136 }
@@ -276,10 +276,11 @@ impl<'a> Ctx<'a> {
276 let visibility = self.lower_visibility(enum_); 276 let visibility = self.lower_visibility(enum_);
277 let name = enum_.name()?.as_name(); 277 let name = enum_.name()?.as_name();
278 let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_); 278 let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
279 let variants = match &enum_.variant_list() { 279 let variants =
280 Some(variant_list) => self.lower_variants(variant_list), 280 self.with_inherited_visibility(visibility, |this| match &enum_.variant_list() {
281 None => IdRange::new(self.next_variant_idx()..self.next_variant_idx()), 281 Some(variant_list) => this.lower_variants(variant_list),
282 }; 282 None => IdRange::new(this.next_variant_idx()..this.next_variant_idx()),
283 });
283 let ast_id = self.source_ast_id_map.ast_id(enum_); 284 let ast_id = self.source_ast_id_map.ast_id(enum_);
284 let res = Enum { name, visibility, generic_params, variants, ast_id }; 285 let res = Enum { name, visibility, generic_params, variants, ast_id };
285 Some(id(self.data().enums.alloc(res))) 286 Some(id(self.data().enums.alloc(res)))
@@ -822,7 +823,7 @@ fn is_intrinsic_fn_unsafe(name: &Name) -> bool {
822 known::type_name, 823 known::type_name,
823 known::variant_count, 824 known::variant_count,
824 ] 825 ]
825 .contains(&name) 826 .contains(name)
826} 827}
827 828
828fn lower_abi(abi: ast::Abi) -> Interned<str> { 829fn lower_abi(abi: ast::Abi) -> Interned<str> {
@@ -854,7 +855,7 @@ impl UseTreeLowering<'_> {
854 // E.g. `use something::{inner}` (prefix is `None`, path is `something`) 855 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
855 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) 856 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
856 Some(path) => { 857 Some(path) => {
857 match ModPath::from_src(self.db, path, &self.hygiene) { 858 match ModPath::from_src(self.db, path, self.hygiene) {
858 Some(it) => Some(it), 859 Some(it) => Some(it),
859 None => return None, // FIXME: report errors somewhere 860 None => return None, // FIXME: report errors somewhere
860 } 861 }
@@ -873,7 +874,7 @@ impl UseTreeLowering<'_> {
873 } else { 874 } else {
874 let is_glob = tree.star_token().is_some(); 875 let is_glob = tree.star_token().is_some();
875 let path = match tree.path() { 876 let path = match tree.path() {
876 Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?), 877 Some(path) => Some(ModPath::from_src(self.db, path, self.hygiene)?),
877 None => None, 878 None => None,
878 }; 879 };
879 let alias = tree.rename().map(|a| { 880 let alias = tree.rename().map(|a| {
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index cc9944a22..b1e1b70d0 100644
--- a/crates/hir_def/src/item_tree/pretty.rs
+++ b/crates/hir_def/src/item_tree/pretty.rs
@@ -426,7 +426,7 @@ impl<'a> Printer<'a> {
426 w!(self, " {{"); 426 w!(self, " {{");
427 self.indented(|this| { 427 self.indented(|this| {
428 for item in &**items { 428 for item in &**items {
429 this.print_mod_item((*item).into()); 429 this.print_mod_item(*item);
430 } 430 }
431 }); 431 });
432 wln!(self, "}}"); 432 wln!(self, "}}");
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
index b362add5c..57686dc6e 100644
--- a/crates/hir_def/src/item_tree/tests.rs
+++ b/crates/hir_def/src/item_tree/tests.rs
@@ -359,3 +359,41 @@ trait Tr<'a, T: 'a>: Super {}
359 "#]], 359 "#]],
360 ) 360 )
361} 361}
362
363#[test]
364fn inherit_visibility() {
365 check(
366 r#"
367pub(crate) enum En {
368 Var1(u8),
369 Var2 {
370 fld: u8,
371 },
372}
373
374pub(crate) trait Tr {
375 fn f();
376 fn method(&self) {}
377}
378 "#,
379 expect![[r#"
380 pub(crate) enum En {
381 Var1(
382 pub(crate) 0: u8,
383 ),
384 Var2 {
385 pub(crate) fld: u8,
386 },
387 }
388
389 pub(crate) trait Tr<Self> {
390 pub(crate) fn f() -> ();
391
392 // flags = 0x3
393 pub(crate) fn method(
394 _: &Self,
395 ) -> ();
396 }
397 "#]],
398 )
399}
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index 89b3ed868..688cd9fcf 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -2,7 +2,7 @@
2 2
3use std::marker::PhantomData; 3use std::marker::PhantomData;
4 4
5use hir_expand::{InFile, MacroDefId}; 5use hir_expand::{InFile, MacroCallId, MacroDefId};
6use rustc_hash::FxHashMap; 6use rustc_hash::FxHashMap;
7use syntax::{ast, AstNode, AstPtr}; 7use syntax::{ast, AstNode, AstPtr};
8 8
@@ -32,6 +32,7 @@ pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
32pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new(); 32pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
33 33
34pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); 34pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
35pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
35 36
36/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are 37/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
37/// equal if they point to exactly the same object. 38/// equal if they point to exactly the same object.
diff --git a/crates/hir_def/src/lang_item.rs b/crates/hir_def/src/lang_item.rs
index 9e90f745c..3a45cbfa1 100644
--- a/crates/hir_def/src/lang_item.rs
+++ b/crates/hir_def/src/lang_item.rs
@@ -141,6 +141,7 @@ impl LangItems {
141 ) where 141 ) where
142 T: Into<AttrDefId> + Copy, 142 T: Into<AttrDefId> + Copy,
143 { 143 {
144 let _p = profile::span("collect_lang_item");
144 if let Some(lang_item_name) = lang_attr(db, item) { 145 if let Some(lang_item_name) = lang_attr(db, item) {
145 self.items.entry(lang_item_name).or_insert_with(|| constructor(item)); 146 self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
146 } 147 }
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 9aa95720a..bb174aec8 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -55,6 +55,7 @@ use std::{
55 sync::Arc, 55 sync::Arc,
56}; 56};
57 57
58use attr::Attr;
58use base_db::{impl_intern_key, salsa, CrateId}; 59use base_db::{impl_intern_key, salsa, CrateId};
59use hir_expand::{ 60use hir_expand::{
60 ast_id_map::FileAstId, 61 ast_id_map::FileAstId,
@@ -111,6 +112,10 @@ impl ModuleId {
111 self.def_map(db).containing_module(self.local_id) 112 self.def_map(db).containing_module(self.local_id)
112 } 113 }
113 114
115 pub fn containing_block(&self) -> Option<BlockId> {
116 self.block
117 }
118
114 /// Returns `true` if this module represents a block expression. 119 /// Returns `true` if this module represents a block expression.
115 /// 120 ///
116 /// Returns `false` if this module is a submodule *inside* a block expression 121 /// Returns `false` if this module is a submodule *inside* a block expression
@@ -580,6 +585,18 @@ impl HasModule for GenericDefId {
580 } 585 }
581} 586}
582 587
588impl HasModule for TypeAliasId {
589 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
590 self.lookup(db).module(db)
591 }
592}
593
594impl HasModule for TraitId {
595 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
596 self.lookup(db).container
597 }
598}
599
583impl HasModule for StaticLoc { 600impl HasModule for StaticLoc {
584 fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { 601 fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
585 self.container 602 self.container
@@ -730,13 +747,11 @@ fn macro_call_as_call_id(
730 ) 747 )
731 .map(MacroCallId::from) 748 .map(MacroCallId::from)
732 } else { 749 } else {
733 Ok(def 750 Ok(def.as_lazy_macro(
734 .as_lazy_macro( 751 db.upcast(),
735 db.upcast(), 752 krate,
736 krate, 753 MacroCallKind::FnLike { ast_id: call.ast_id, fragment },
737 MacroCallKind::FnLike { ast_id: call.ast_id, fragment }, 754 ))
738 )
739 .into())
740 }; 755 };
741 Ok(res) 756 Ok(res)
742} 757}
@@ -755,16 +770,51 @@ fn derive_macro_as_call_id(
755 .segments() 770 .segments()
756 .last() 771 .last()
757 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; 772 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
758 let res = def 773 let res = def.as_lazy_macro(
759 .as_lazy_macro( 774 db.upcast(),
760 db.upcast(), 775 krate,
761 krate, 776 MacroCallKind::Derive {
762 MacroCallKind::Derive { 777 ast_id: item_attr.ast_id,
763 ast_id: item_attr.ast_id, 778 derive_name: last_segment.to_string(),
764 derive_name: last_segment.to_string(), 779 derive_attr_index: derive_attr.ast_index,
765 derive_attr_index: derive_attr.ast_index, 780 },
766 }, 781 );
767 ) 782 Ok(res)
768 .into(); 783}
784
785fn attr_macro_as_call_id(
786 item_attr: &AstIdWithPath<ast::Item>,
787 macro_attr: &Attr,
788 db: &dyn db::DefDatabase,
789 krate: CrateId,
790 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
791) -> Result<MacroCallId, UnresolvedMacro> {
792 let def: MacroDefId = resolver(item_attr.path.clone())
793 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
794 let last_segment = item_attr
795 .path
796 .segments()
797 .last()
798 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
799 let mut arg = match &macro_attr.input {
800 Some(input) => match &**input {
801 attr::AttrInput::Literal(_) => tt::Subtree::default(),
802 attr::AttrInput::TokenTree(tt) => tt.clone(),
803 },
804 None => tt::Subtree::default(),
805 };
806 // The parentheses are always disposed here.
807 arg.delimiter = None;
808
809 let res = def.as_lazy_macro(
810 db.upcast(),
811 krate,
812 MacroCallKind::Attr {
813 ast_id: item_attr.ast_id,
814 attr_name: last_segment.to_string(),
815 attr_args: arg,
816 invoc_attr_index: macro_attr.id.ast_index,
817 },
818 );
769 Ok(res) 819 Ok(res)
770} 820}
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 6b41921ae..6fab58f15 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -9,6 +9,7 @@ use base_db::{CrateId, Edition, FileId, ProcMacroId};
9use cfg::{CfgExpr, CfgOptions}; 9use cfg::{CfgExpr, CfgOptions};
10use hir_expand::{ 10use hir_expand::{
11 ast_id_map::FileAstId, 11 ast_id_map::FileAstId,
12 builtin_attr::find_builtin_attr,
12 builtin_derive::find_builtin_derive, 13 builtin_derive::find_builtin_derive,
13 builtin_macro::find_builtin_macro, 14 builtin_macro::find_builtin_macro,
14 name::{name, AsName, Name}, 15 name::{name, AsName, Name},
@@ -23,7 +24,7 @@ use syntax::ast;
23 24
24use crate::{ 25use crate::{
25 attr::{Attr, AttrId, AttrInput, Attrs}, 26 attr::{Attr, AttrId, AttrInput, Attrs},
26 builtin_attr, 27 attr_macro_as_call_id, builtin_attr,
27 db::DefDatabase, 28 db::DefDatabase,
28 derive_macro_as_call_id, 29 derive_macro_as_call_id,
29 intern::Interned, 30 intern::Interned,
@@ -223,7 +224,7 @@ struct MacroDirective {
223enum MacroDirectiveKind { 224enum MacroDirectiveKind {
224 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, 225 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
225 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, 226 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
226 Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem }, 227 Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem },
227} 228}
228 229
229struct DefData<'a> { 230struct DefData<'a> {
@@ -419,7 +420,7 @@ impl DefCollector<'_> {
419 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); 420 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
420 let pos = unresolved_macros.iter().position(|directive| { 421 let pos = unresolved_macros.iter().position(|directive| {
421 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { 422 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
422 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr); 423 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id);
423 424
424 let file_id = ast_id.ast_id.file_id; 425 let file_id = ast_id.ast_id.file_id;
425 let item_tree = self.db.file_item_tree(file_id); 426 let item_tree = self.db.file_item_tree(file_id);
@@ -499,7 +500,7 @@ impl DefCollector<'_> {
499 let (per_ns, _) = self.def_map.resolve_path( 500 let (per_ns, _) = self.def_map.resolve_path(
500 self.db, 501 self.db,
501 self.def_map.root, 502 self.def_map.root,
502 &path, 503 path,
503 BuiltinShadowMode::Other, 504 BuiltinShadowMode::Other,
504 ); 505 );
505 506
@@ -721,7 +722,7 @@ impl DefCollector<'_> {
721 if import.is_extern_crate { 722 if import.is_extern_crate {
722 let res = self.def_map.resolve_name_in_extern_prelude( 723 let res = self.def_map.resolve_name_in_extern_prelude(
723 self.db, 724 self.db,
724 &import 725 import
725 .path 726 .path
726 .as_ident() 727 .as_ident()
727 .expect("extern crate should have been desugared to one-element path"), 728 .expect("extern crate should have been desugared to one-element path"),
@@ -1050,7 +1051,7 @@ impl DefCollector<'_> {
1050 let file_id = ast_id.ast_id.file_id; 1051 let file_id = ast_id.ast_id.file_id;
1051 let item_tree = self.db.file_item_tree(file_id); 1052 let item_tree = self.db.file_item_tree(file_id);
1052 let mod_dir = self.mod_dirs[&directive.module_id].clone(); 1053 let mod_dir = self.mod_dirs[&directive.module_id].clone();
1053 self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr); 1054 self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
1054 ModCollector { 1055 ModCollector {
1055 def_collector: &mut *self, 1056 def_collector: &mut *self,
1056 macro_depth: directive.depth, 1057 macro_depth: directive.depth,
@@ -1062,13 +1063,67 @@ impl DefCollector<'_> {
1062 .collect(&[*mod_item]); 1063 .collect(&[*mod_item]);
1063 1064
1064 // Remove the original directive since we resolved it. 1065 // Remove the original directive since we resolved it.
1066 res = ReachedFixedPoint::No;
1065 return false; 1067 return false;
1066 } 1068 }
1067 } 1069 }
1068 } 1070 }
1069 1071
1072 if !self.db.enable_proc_attr_macros() {
1073 return true;
1074 }
1075
1070 // Not resolved to a derive helper, so try to resolve as a macro. 1076 // Not resolved to a derive helper, so try to resolve as a macro.
1071 // FIXME: not yet :) 1077 match attr_macro_as_call_id(
1078 ast_id,
1079 attr,
1080 self.db,
1081 self.def_map.krate,
1082 &resolver,
1083 ) {
1084 Ok(call_id) => {
1085 let loc: MacroCallLoc = self.db.lookup_intern_macro(call_id);
1086 if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind {
1087 if exp.is_dummy() {
1088 // Proc macros that cannot be expanded are treated as not
1089 // resolved, in order to fall back later.
1090 self.def_map.diagnostics.push(
1091 DefDiagnostic::unresolved_proc_macro(
1092 directive.module_id,
1093 loc.kind,
1094 ),
1095 );
1096
1097 let file_id = ast_id.ast_id.file_id;
1098 let item_tree = self.db.file_item_tree(file_id);
1099 let mod_dir = self.mod_dirs[&directive.module_id].clone();
1100 self.skip_attrs
1101 .insert(InFile::new(file_id, *mod_item), attr.id);
1102 ModCollector {
1103 def_collector: &mut *self,
1104 macro_depth: directive.depth,
1105 module_id: directive.module_id,
1106 file_id,
1107 item_tree: &item_tree,
1108 mod_dir,
1109 }
1110 .collect(&[*mod_item]);
1111
1112 // Remove the macro directive.
1113 return false;
1114 }
1115 }
1116
1117 self.def_map.modules[directive.module_id]
1118 .scope
1119 .add_attr_macro_invoc(ast_id.ast_id, call_id);
1120
1121 resolved.push((directive.module_id, call_id, directive.depth));
1122 res = ReachedFixedPoint::No;
1123 return false;
1124 }
1125 Err(UnresolvedMacro { .. }) => (),
1126 }
1072 } 1127 }
1073 } 1128 }
1074 1129
@@ -1296,7 +1351,7 @@ impl ModCollector<'_, '_> {
1296 let imports = Import::from_use( 1351 let imports = Import::from_use(
1297 self.def_collector.db, 1352 self.def_collector.db,
1298 krate, 1353 krate,
1299 &self.item_tree, 1354 self.item_tree,
1300 ItemTreeId::new(self.file_id, import_id), 1355 ItemTreeId::new(self.file_id, import_id),
1301 ); 1356 );
1302 self.def_collector.unresolved_imports.extend(imports.into_iter().map( 1357 self.def_collector.unresolved_imports.extend(imports.into_iter().map(
@@ -1313,7 +1368,7 @@ impl ModCollector<'_, '_> {
1313 import: Import::from_extern_crate( 1368 import: Import::from_extern_crate(
1314 self.def_collector.db, 1369 self.def_collector.db,
1315 krate, 1370 krate,
1316 &self.item_tree, 1371 self.item_tree,
1317 ItemTreeId::new(self.file_id, import_id), 1372 ItemTreeId::new(self.file_id, import_id),
1318 ), 1373 ),
1319 status: PartialResolvedImport::Unresolved, 1374 status: PartialResolvedImport::Unresolved,
@@ -1628,7 +1683,7 @@ impl ModCollector<'_, '_> {
1628 self.def_collector.unresolved_macros.push(MacroDirective { 1683 self.def_collector.unresolved_macros.push(MacroDirective {
1629 module_id: self.module_id, 1684 module_id: self.module_id,
1630 depth: self.macro_depth + 1, 1685 depth: self.macro_depth + 1,
1631 kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item }, 1686 kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
1632 }); 1687 });
1633 1688
1634 return Err(()); 1689 return Err(());
@@ -1782,7 +1837,8 @@ impl ModCollector<'_, '_> {
1782 let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into()); 1837 let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
1783 if attrs.by_key("rustc_builtin_macro").exists() { 1838 if attrs.by_key("rustc_builtin_macro").exists() {
1784 let macro_id = find_builtin_macro(&mac.name, krate, ast_id) 1839 let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
1785 .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); 1840 .or_else(|| find_builtin_derive(&mac.name, krate, ast_id))
1841 .or_else(|| find_builtin_attr(&mac.name, krate, ast_id));
1786 1842
1787 match macro_id { 1843 match macro_id {
1788 Some(macro_id) => { 1844 Some(macro_id) => {
@@ -1833,7 +1889,7 @@ impl ModCollector<'_, '_> {
1833 self.def_collector.def_map.with_ancestor_maps( 1889 self.def_collector.def_map.with_ancestor_maps(
1834 self.def_collector.db, 1890 self.def_collector.db,
1835 self.module_id, 1891 self.module_id,
1836 &mut |map, module| map[module].scope.get_legacy_macro(&name), 1892 &mut |map, module| map[module].scope.get_legacy_macro(name),
1837 ) 1893 )
1838 }) 1894 })
1839 }, 1895 },
@@ -1937,7 +1993,7 @@ mod tests {
1937 } 1993 }
1938 1994
1939 fn do_resolve(code: &str) -> DefMap { 1995 fn do_resolve(code: &str) -> DefMap {
1940 let (db, _file_id) = TestDB::with_single_file(&code); 1996 let (db, _file_id) = TestDB::with_single_file(code);
1941 let krate = db.test_crate(); 1997 let krate = db.test_crate();
1942 1998
1943 let edition = db.crate_graph()[krate].edition; 1999 let edition = db.crate_graph()[krate].edition;
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index c984148c3..629bc7952 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -93,7 +93,7 @@ impl DefMap {
93 let mut vis = match visibility { 93 let mut vis = match visibility {
94 RawVisibility::Module(path) => { 94 RawVisibility::Module(path) => {
95 let (result, remaining) = 95 let (result, remaining) =
96 self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module); 96 self.resolve_path(db, original_module, path, BuiltinShadowMode::Module);
97 if remaining.is_some() { 97 if remaining.is_some() {
98 return None; 98 return None;
99 } 99 }
@@ -205,7 +205,7 @@ impl DefMap {
205 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 205 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
206 }; 206 };
207 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); 207 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
208 self.resolve_name_in_crate_root_or_extern_prelude(db, &segment) 208 self.resolve_name_in_crate_root_or_extern_prelude(db, segment)
209 } 209 }
210 PathKind::Plain => { 210 PathKind::Plain => {
211 let (_, segment) = match segments.next() { 211 let (_, segment) = match segments.next() {
@@ -222,7 +222,7 @@ impl DefMap {
222 if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; 222 if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
223 223
224 log::debug!("resolving {:?} in module", segment); 224 log::debug!("resolving {:?} in module", segment);
225 self.resolve_name_in_module(db, original_module, &segment, prefer_module) 225 self.resolve_name_in_module(db, original_module, segment, prefer_module)
226 } 226 }
227 PathKind::Super(lvl) => { 227 PathKind::Super(lvl) => {
228 let mut module = original_module; 228 let mut module = original_module;
@@ -269,7 +269,7 @@ impl DefMap {
269 Some((_, segment)) => segment, 269 Some((_, segment)) => segment,
270 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 270 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
271 }; 271 };
272 if let Some(def) = self.extern_prelude.get(&segment) { 272 if let Some(def) = self.extern_prelude.get(segment) {
273 log::debug!("absolute path {:?} resolved to crate {:?}", path, def); 273 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
274 PerNs::types(*def, Visibility::Public) 274 PerNs::types(*def, Visibility::Public)
275 } else { 275 } else {
@@ -319,13 +319,13 @@ impl DefMap {
319 }; 319 };
320 320
321 // Since it is a qualified path here, it should not contains legacy macros 321 // Since it is a qualified path here, it should not contains legacy macros
322 module_data.scope.get(&segment) 322 module_data.scope.get(segment)
323 } 323 }
324 ModuleDefId::AdtId(AdtId::EnumId(e)) => { 324 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
325 // enum variant 325 // enum variant
326 cov_mark::hit!(can_import_enum_variant); 326 cov_mark::hit!(can_import_enum_variant);
327 let enum_data = db.enum_data(e); 327 let enum_data = db.enum_data(e);
328 match enum_data.variant(&segment) { 328 match enum_data.variant(segment) {
329 Some(local_id) => { 329 Some(local_id) => {
330 let variant = EnumVariantId { parent: e, local_id }; 330 let variant = EnumVariantId { parent: e, local_id };
331 match &*enum_data.variants[local_id].variant_data { 331 match &*enum_data.variants[local_id].variant_data {
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 58c01354a..cf43f2a96 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -2,7 +2,6 @@ mod globs;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
6mod primitives; 5mod primitives;
7 6
8use std::sync::Arc; 7use std::sync::Arc;
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
deleted file mode 100644
index ec6670952..000000000
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ /dev/null
@@ -1,229 +0,0 @@
1use base_db::fixture::WithFixture;
2
3use crate::test_db::TestDB;
4
5fn check_diagnostics(ra_fixture: &str) {
6 let db: TestDB = TestDB::with_files(ra_fixture);
7 db.check_diagnostics();
8}
9
10fn check_no_diagnostics(ra_fixture: &str) {
11 let db: TestDB = TestDB::with_files(ra_fixture);
12 db.check_no_diagnostics();
13}
14
15#[test]
16fn unresolved_import() {
17 check_diagnostics(
18 r"
19 use does_exist;
20 use does_not_exist;
21 //^^^^^^^^^^^^^^^^^^^ UnresolvedImport
22
23 mod does_exist {}
24 ",
25 );
26}
27
28#[test]
29fn unresolved_extern_crate() {
30 check_diagnostics(
31 r"
32 //- /main.rs crate:main deps:core
33 extern crate core;
34 extern crate doesnotexist;
35 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
36 //- /lib.rs crate:core
37 ",
38 );
39}
40
41#[test]
42fn extern_crate_self_as() {
43 cov_mark::check!(extern_crate_self_as);
44 check_diagnostics(
45 r"
46 //- /lib.rs
47 extern crate doesnotexist;
48 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
49 // Should not error.
50 extern crate self as foo;
51 struct Foo;
52 use foo::Foo as Bar;
53 ",
54 );
55}
56
57#[test]
58fn dedup_unresolved_import_from_unresolved_crate() {
59 check_diagnostics(
60 r"
61 //- /main.rs crate:main
62 mod a {
63 extern crate doesnotexist;
64 //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate
65
66 // Should not error, since we already errored for the missing crate.
67 use doesnotexist::{self, bla, *};
68
69 use crate::doesnotexist;
70 //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
71 }
72
73 mod m {
74 use super::doesnotexist;
75 //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport
76 }
77 ",
78 );
79}
80
81#[test]
82fn unresolved_module() {
83 check_diagnostics(
84 r"
85 //- /lib.rs
86 mod foo;
87 mod bar;
88 //^^^^^^^^ UnresolvedModule
89 mod baz {}
90 //- /foo.rs
91 ",
92 );
93}
94
95#[test]
96fn inactive_item() {
97 // Additional tests in `cfg` crate. This only tests disabled cfgs.
98
99 check_diagnostics(
100 r#"
101 //- /lib.rs
102 #[cfg(no)] pub fn f() {}
103 //^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
104
105 #[cfg(no)] #[cfg(no2)] mod m;
106 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
107
108 #[cfg(all(not(a), b))] enum E {}
109 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
110
111 #[cfg(feature = "std")] use std;
112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
113 "#,
114 );
115}
116
117/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
118#[test]
119fn inactive_via_cfg_attr() {
120 cov_mark::check!(cfg_attr_active);
121 check_diagnostics(
122 r#"
123 //- /lib.rs
124 #[cfg_attr(not(never), cfg(no))] fn f() {}
125 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
126
127 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
128
129 #[cfg_attr(never, cfg(no))] fn g() {}
130
131 #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
132 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode
133 "#,
134 );
135}
136
137#[test]
138fn unresolved_legacy_scope_macro() {
139 check_diagnostics(
140 r#"
141 //- /lib.rs
142 macro_rules! m { () => {} }
143
144 m!();
145 m2!();
146 //^^^^^^ UnresolvedMacroCall
147 "#,
148 );
149}
150
151#[test]
152fn unresolved_module_scope_macro() {
153 check_diagnostics(
154 r#"
155 //- /lib.rs
156 mod mac {
157 #[macro_export]
158 macro_rules! m { () => {} }
159 }
160
161 self::m!();
162 self::m2!();
163 //^^^^^^^^^^^^ UnresolvedMacroCall
164 "#,
165 );
166}
167
168#[test]
169fn builtin_macro_fails_expansion() {
170 check_diagnostics(
171 r#"
172 //- /lib.rs
173 #[rustc_builtin_macro]
174 macro_rules! include { () => {} }
175
176 include!("doesntexist");
177 //^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist`
178 "#,
179 );
180}
181
182#[test]
183fn include_macro_should_allow_empty_content() {
184 check_no_diagnostics(
185 r#"
186 //- /lib.rs
187 #[rustc_builtin_macro]
188 macro_rules! include { () => {} }
189
190 include!("bar.rs");
191 //- /bar.rs
192 // empty
193 "#,
194 );
195}
196
197#[test]
198fn good_out_dir_diagnostic() {
199 check_diagnostics(
200 r#"
201 #[rustc_builtin_macro]
202 macro_rules! include { () => {} }
203 #[rustc_builtin_macro]
204 macro_rules! env { () => {} }
205 #[rustc_builtin_macro]
206 macro_rules! concat { () => {} }
207
208 include!(concat!(env!("OUT_DIR"), "/out.rs"));
209 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
210 "#,
211 );
212}
213
214#[test]
215fn register_attr_and_tool() {
216 cov_mark::check!(register_attr);
217 cov_mark::check!(register_tool);
218 check_no_diagnostics(
219 r#"
220#![register_tool(tool)]
221#![register_attr(attr)]
222
223#[tool::path]
224#[attr]
225struct S;
226 "#,
227 );
228 // NB: we don't currently emit diagnostics here
229}
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index f6220aa92..27345d07c 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -208,13 +208,13 @@ fn lower_generic_args_from_fn_path(
208 let params = params?; 208 let params = params?;
209 let mut param_types = Vec::new(); 209 let mut param_types = Vec::new();
210 for param in params.params() { 210 for param in params.params() {
211 let type_ref = TypeRef::from_ast_opt(&ctx, param.ty()); 211 let type_ref = TypeRef::from_ast_opt(ctx, param.ty());
212 param_types.push(type_ref); 212 param_types.push(type_ref);
213 } 213 }
214 let arg = GenericArg::Type(TypeRef::Tuple(param_types)); 214 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
215 args.push(arg); 215 args.push(arg);
216 if let Some(ret_type) = ret_type { 216 if let Some(ret_type) = ret_type {
217 let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty()); 217 let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
218 bindings.push(AssociatedTypeBinding { 218 bindings.push(AssociatedTypeBinding {
219 name: name![Output], 219 name: name![Output],
220 type_ref: Some(type_ref), 220 type_ref: Some(type_ref),
diff --git a/crates/hir_def/src/per_ns.rs b/crates/hir_def/src/per_ns.rs
index a594afce6..a9f13cb82 100644
--- a/crates/hir_def/src/per_ns.rs
+++ b/crates/hir_def/src/per_ns.rs
@@ -62,6 +62,7 @@ impl PerNs {
62 } 62 }
63 63
64 pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { 64 pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
65 let _p = profile::span("PerNs::filter_visibility");
65 PerNs { 66 PerNs {
66 types: self.types.filter(|(_, v)| f(*v)), 67 types: self.types.filter(|(_, v)| f(*v)),
67 values: self.values.filter(|(_, v)| f(*v)), 68 values: self.values.filter(|(_, v)| f(*v)),
@@ -86,6 +87,7 @@ impl PerNs {
86 } 87 }
87 88
88 pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> { 89 pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
90 let _p = profile::span("PerNs::iter_items");
89 self.types 91 self.types
90 .map(|it| ItemInNs::Types(it.0)) 92 .map(|it| ItemInNs::Types(it.0))
91 .into_iter() 93 .into_iter()
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index fb8a6f260..d4681fa3e 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -133,7 +133,7 @@ impl Resolver {
133 Some(it) => it, 133 Some(it) => it,
134 None => return PerNs::none(), 134 None => return PerNs::none(),
135 }; 135 };
136 let (module_res, segment_index) = item_map.resolve_path(db, module, &path, shadow); 136 let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow);
137 if segment_index.is_some() { 137 if segment_index.is_some() {
138 return PerNs::none(); 138 return PerNs::none();
139 } 139 }
@@ -150,7 +150,7 @@ impl Resolver {
150 path: &ModPath, 150 path: &ModPath,
151 ) -> Option<TraitId> { 151 ) -> Option<TraitId> {
152 let (item_map, module) = self.module_scope()?; 152 let (item_map, module) = self.module_scope()?;
153 let (module_res, ..) = item_map.resolve_path(db, module, &path, BuiltinShadowMode::Module); 153 let (module_res, ..) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module);
154 match module_res.take_types()? { 154 match module_res.take_types()? {
155 ModuleDefId::TraitId(it) => Some(it), 155 ModuleDefId::TraitId(it) => Some(it),
156 _ => None, 156 _ => None,
@@ -325,7 +325,7 @@ impl Resolver {
325 path: &ModPath, 325 path: &ModPath,
326 ) -> Option<MacroDefId> { 326 ) -> Option<MacroDefId> {
327 let (item_map, module) = self.module_scope()?; 327 let (item_map, module) = self.module_scope()?;
328 item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() 328 item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
329 } 329 }
330 330
331 pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { 331 pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
@@ -561,7 +561,7 @@ impl ModuleItemMap {
561 path: &ModPath, 561 path: &ModPath,
562 ) -> Option<ResolveValueResult> { 562 ) -> Option<ResolveValueResult> {
563 let (module_def, idx) = 563 let (module_def, idx) =
564 self.def_map.resolve_path_locally(db, self.module_id, &path, BuiltinShadowMode::Other); 564 self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
565 match idx { 565 match idx {
566 None => { 566 None => {
567 let value = to_value_ns(module_def)?; 567 let value = to_value_ns(module_def)?;
@@ -591,7 +591,7 @@ impl ModuleItemMap {
591 path: &ModPath, 591 path: &ModPath,
592 ) -> Option<(TypeNs, Option<usize>)> { 592 ) -> Option<(TypeNs, Option<usize>)> {
593 let (module_def, idx) = 593 let (module_def, idx) =
594 self.def_map.resolve_path_locally(db, self.module_id, &path, BuiltinShadowMode::Other); 594 self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
595 let res = to_type_ns(module_def)?; 595 let res = to_type_ns(module_def)?;
596 Some((res, idx)) 596 Some((res, idx))
597 } 597 }
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index e840fe5e8..2635b556e 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -6,19 +6,16 @@ use std::{
6}; 6};
7 7
8use base_db::{ 8use base_db::{
9 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, FileRange, Upcast, 9 salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition,
10 SourceDatabase, Upcast,
10}; 11};
11use base_db::{AnchoredPath, SourceDatabase};
12use hir_expand::{db::AstDatabase, InFile}; 12use hir_expand::{db::AstDatabase, InFile};
13use rustc_hash::FxHashMap;
14use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
15use syntax::{algo, ast, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; 14use syntax::{algo, ast, AstNode};
16use test_utils::extract_annotations;
17 15
18use crate::{ 16use crate::{
19 body::BodyDiagnostic,
20 db::DefDatabase, 17 db::DefDatabase,
21 nameres::{diagnostics::DefDiagnosticKind, DefMap, ModuleSource}, 18 nameres::{DefMap, ModuleSource},
22 src::HasSource, 19 src::HasSource,
23 LocalModuleId, Lookup, ModuleDefId, ModuleId, 20 LocalModuleId, Lookup, ModuleDefId, ModuleId,
24}; 21};
@@ -30,12 +27,19 @@ use crate::{
30 crate::db::InternDatabaseStorage, 27 crate::db::InternDatabaseStorage,
31 crate::db::DefDatabaseStorage 28 crate::db::DefDatabaseStorage
32)] 29)]
33#[derive(Default)]
34pub(crate) struct TestDB { 30pub(crate) struct TestDB {
35 storage: salsa::Storage<TestDB>, 31 storage: salsa::Storage<TestDB>,
36 events: Mutex<Option<Vec<salsa::Event>>>, 32 events: Mutex<Option<Vec<salsa::Event>>>,
37} 33}
38 34
35impl Default for TestDB {
36 fn default() -> Self {
37 let mut this = Self { storage: Default::default(), events: Default::default() };
38 this.set_enable_proc_attr_macros(true);
39 this
40 }
41}
42
39impl Upcast<dyn AstDatabase> for TestDB { 43impl Upcast<dyn AstDatabase> for TestDB {
40 fn upcast(&self) -> &(dyn AstDatabase + 'static) { 44 fn upcast(&self) -> &(dyn AstDatabase + 'static) {
41 &*self 45 &*self
@@ -238,145 +242,4 @@ impl TestDB {
238 }) 242 })
239 .collect() 243 .collect()
240 } 244 }
241
242 pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
243 let mut files = Vec::new();
244 let crate_graph = self.crate_graph();
245 for krate in crate_graph.iter() {
246 let crate_def_map = self.crate_def_map(krate);
247 for (module_id, _) in crate_def_map.modules() {
248 let file_id = crate_def_map[module_id].origin.file_id();
249 files.extend(file_id)
250 }
251 }
252 assert!(!files.is_empty());
253 files
254 .into_iter()
255 .filter_map(|file_id| {
256 let text = self.file_text(file_id);
257 let annotations = extract_annotations(&text);
258 if annotations.is_empty() {
259 return None;
260 }
261 Some((file_id, annotations))
262 })
263 .collect()
264 }
265
266 pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) {
267 let crate_graph = self.crate_graph();
268 for krate in crate_graph.iter() {
269 let crate_def_map = self.crate_def_map(krate);
270
271 for diag in crate_def_map.diagnostics() {
272 let (node, message): (InFile<SyntaxNode>, &str) = match &diag.kind {
273 DefDiagnosticKind::UnresolvedModule { ast, .. } => {
274 let node = ast.to_node(self.upcast());
275 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedModule")
276 }
277 DefDiagnosticKind::UnresolvedExternCrate { ast, .. } => {
278 let node = ast.to_node(self.upcast());
279 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
280 }
281 DefDiagnosticKind::UnresolvedImport { id, .. } => {
282 let item_tree = id.item_tree(self.upcast());
283 let import = &item_tree[id.value];
284 let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast());
285 (InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport")
286 }
287 DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
288 let node = ast.to_node(self.upcast());
289 (InFile::new(ast.file_id, node.syntax().clone()), "UnconfiguredCode")
290 }
291 DefDiagnosticKind::UnresolvedProcMacro { ast, .. } => {
292 (ast.to_node(self.upcast()), "UnresolvedProcMacro")
293 }
294 DefDiagnosticKind::UnresolvedMacroCall { ast, .. } => {
295 let node = ast.to_node(self.upcast());
296 (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedMacroCall")
297 }
298 DefDiagnosticKind::MacroError { ast, message } => {
299 (ast.to_node(self.upcast()), message.as_str())
300 }
301 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
302 let node = ast.to_node(self.upcast());
303 (
304 InFile::new(ast.file_id, node.syntax().clone()),
305 "UnimplementedBuiltinMacro",
306 )
307 }
308 };
309
310 let frange = node.as_ref().original_file_range(self);
311 cb(frange, message.to_string())
312 }
313
314 for (_module_id, module) in crate_def_map.modules() {
315 for decl in module.scope.declarations() {
316 if let ModuleDefId::FunctionId(it) = decl {
317 let source_map = self.body_with_source_map(it.into()).1;
318 for diag in source_map.diagnostics() {
319 let (ptr, message): (InFile<SyntaxNodePtr>, &str) = match diag {
320 BodyDiagnostic::InactiveCode { node, .. } => {
321 (node.clone().map(|it| it.into()), "InactiveCode")
322 }
323 BodyDiagnostic::MacroError { node, message } => {
324 (node.clone().map(|it| it.into()), message.as_str())
325 }
326 BodyDiagnostic::UnresolvedProcMacro { node } => {
327 (node.clone().map(|it| it.into()), "UnresolvedProcMacro")
328 }
329 BodyDiagnostic::UnresolvedMacroCall { node, .. } => {
330 (node.clone().map(|it| it.into()), "UnresolvedMacroCall")
331 }
332 };
333
334 let root = self.parse_or_expand(ptr.file_id).unwrap();
335 let node = ptr.map(|ptr| ptr.to_node(&root));
336 let frange = node.as_ref().original_file_range(self);
337 cb(frange, message.to_string())
338 }
339 }
340 }
341 }
342 }
343 }
344
345 pub(crate) fn check_diagnostics(&self) {
346 let db: &TestDB = self;
347 let annotations = db.extract_annotations();
348 assert!(!annotations.is_empty());
349
350 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
351 db.diagnostics(&mut |frange, message| {
352 actual.entry(frange.file_id).or_default().push((frange.range, message));
353 });
354
355 for (file_id, diags) in actual.iter_mut() {
356 diags.sort_by_key(|it| it.0.start());
357 let text = db.file_text(*file_id);
358 // For multiline spans, place them on line start
359 for (range, content) in diags {
360 if text[*range].contains('\n') {
361 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
362 *content = format!("... {}", content);
363 }
364 }
365 }
366
367 assert_eq!(annotations, actual);
368 }
369
370 pub(crate) fn check_no_diagnostics(&self) {
371 let db: &TestDB = self;
372 let annotations = db.extract_annotations();
373 assert!(annotations.is_empty());
374
375 let mut has_diagnostics = false;
376 db.diagnostics(&mut |_, _| {
377 has_diagnostics = true;
378 });
379
380 assert!(!has_diagnostics);
381 }
382} 245}
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index cbde6b940..ffe499973 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -128,7 +128,7 @@ impl TypeRef {
128 /// Converts an `ast::TypeRef` to a `hir::TypeRef`. 128 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
129 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { 129 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
130 match node { 130 match node {
131 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), 131 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
132 ast::Type::TupleType(inner) => { 132 ast::Type::TupleType(inner) => {
133 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect()) 133 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
134 } 134 }
@@ -142,7 +142,7 @@ impl TypeRef {
142 .unwrap_or(TypeRef::Error) 142 .unwrap_or(TypeRef::Error)
143 } 143 }
144 ast::Type::PtrType(inner) => { 144 ast::Type::PtrType(inner) => {
145 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty()); 145 let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
146 let mutability = Mutability::from_mutable(inner.mut_token().is_some()); 146 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
147 TypeRef::RawPtr(Box::new(inner_ty), mutability) 147 TypeRef::RawPtr(Box::new(inner_ty), mutability)
148 } 148 }
@@ -156,13 +156,13 @@ impl TypeRef {
156 .map(ConstScalar::usize_from_literal_expr) 156 .map(ConstScalar::usize_from_literal_expr)
157 .unwrap_or(ConstScalar::Unknown); 157 .unwrap_or(ConstScalar::Unknown);
158 158
159 TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len) 159 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
160 } 160 }
161 ast::Type::SliceType(inner) => { 161 ast::Type::SliceType(inner) => {
162 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) 162 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
163 } 163 }
164 ast::Type::RefType(inner) => { 164 ast::Type::RefType(inner) => {
165 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty()); 165 let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
166 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt)); 166 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
167 let mutability = Mutability::from_mutable(inner.mut_token().is_some()); 167 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
168 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability) 168 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
@@ -180,7 +180,7 @@ impl TypeRef {
180 is_varargs = param.dotdotdot_token().is_some(); 180 is_varargs = param.dotdotdot_token().is_some();
181 } 181 }
182 182
183 pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect() 183 pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(ctx, it)).collect()
184 } else { 184 } else {
185 Vec::new() 185 Vec::new()
186 }; 186 };
@@ -188,7 +188,7 @@ impl TypeRef {
188 TypeRef::Fn(params, is_varargs) 188 TypeRef::Fn(params, is_varargs)
189 } 189 }
190 // for types are close enough for our purposes to the inner type for now... 190 // for types are close enough for our purposes to the inner type for now...
191 ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), 191 ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
192 ast::Type::ImplTraitType(inner) => { 192 ast::Type::ImplTraitType(inner) => {
193 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) 193 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
194 } 194 }
@@ -229,7 +229,7 @@ impl TypeRef {
229 TypeRef::RawPtr(type_ref, _) 229 TypeRef::RawPtr(type_ref, _)
230 | TypeRef::Reference(type_ref, ..) 230 | TypeRef::Reference(type_ref, ..)
231 | TypeRef::Array(type_ref, _) 231 | TypeRef::Array(type_ref, _)
232 | TypeRef::Slice(type_ref) => go(&type_ref, f), 232 | TypeRef::Slice(type_ref) => go(type_ref, f),
233 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { 233 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
234 for bound in bounds { 234 for bound in bounds {
235 match bound.as_ref() { 235 match bound.as_ref() {
diff --git a/crates/hir_expand/src/builtin_attr.rs b/crates/hir_expand/src/builtin_attr.rs
new file mode 100644
index 000000000..c8432005e
--- /dev/null
+++ b/crates/hir_expand/src/builtin_attr.rs
@@ -0,0 +1,67 @@
1//! Builtin derives.
2
3use syntax::ast;
4
5use crate::{db::AstDatabase, name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
6
7macro_rules! register_builtin {
8 ( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
9 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10 pub enum BuiltinAttrExpander {
11 $($variant),*
12 }
13
14 impl BuiltinAttrExpander {
15 pub fn expand(
16 &self,
17 db: &dyn AstDatabase,
18 id: MacroCallId,
19 tt: &tt::Subtree,
20 ) -> Result<tt::Subtree, mbe::ExpandError> {
21 let expander = match *self {
22 $( BuiltinAttrExpander::$variant => $expand, )*
23 };
24 expander(db, id, tt)
25 }
26
27 fn find_by_name(name: &name::Name) -> Option<Self> {
28 match name {
29 $( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )*
30 _ => None,
31 }
32 }
33 }
34
35 };
36}
37
38register_builtin! {
39 (bench, Bench) => dummy_attr_expand,
40 (cfg_accessible, CfgAccessible) => dummy_attr_expand,
41 (cfg_eval, CfgEval) => dummy_attr_expand,
42 (derive, Derive) => dummy_attr_expand,
43 (global_allocator, GlobalAllocator) => dummy_attr_expand,
44 (test, Test) => dummy_attr_expand,
45 (test_case, TestCase) => dummy_attr_expand
46}
47
48pub fn find_builtin_attr(
49 ident: &name::Name,
50 krate: CrateId,
51 ast_id: AstId<ast::Macro>,
52) -> Option<MacroDefId> {
53 let expander = BuiltinAttrExpander::find_by_name(ident)?;
54 Some(MacroDefId {
55 krate,
56 kind: MacroDefKind::BuiltInAttr(expander, ast_id),
57 local_inner: false,
58 })
59}
60
61fn dummy_attr_expand(
62 _db: &dyn AstDatabase,
63 _id: MacroCallId,
64 tt: &tt::Subtree,
65) -> Result<tt::Subtree, mbe::ExpandError> {
66 Ok(tt.clone())
67}
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index fe9497b50..4610f6f91 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -325,7 +325,7 @@ $0
325 }, 325 },
326 }; 326 };
327 327
328 let id: MacroCallId = db.intern_macro(loc).into(); 328 let id: MacroCallId = db.intern_macro(loc);
329 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 329 let parsed = db.parse_or_expand(id.as_file()).unwrap();
330 330
331 // FIXME text() for syntax nodes parsed from token tree looks weird 331 // FIXME text() for syntax nodes parsed from token tree looks weird
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 0b310ba2f..f24d1d919 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -354,7 +354,7 @@ fn concat_expand(
354 // concat works with string and char literals, so remove any quotes. 354 // concat works with string and char literals, so remove any quotes.
355 // It also works with integer, float and boolean literals, so just use the rest 355 // It also works with integer, float and boolean literals, so just use the rest
356 // as-is. 356 // as-is.
357 let component = unquote_str(&it).unwrap_or_else(|| it.text.to_string()); 357 let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
358 text.push_str(&component); 358 text.push_str(&component);
359 } 359 }
360 // handle boolean literals 360 // handle boolean literals
@@ -417,7 +417,7 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
417 tt.token_trees 417 tt.token_trees
418 .get(0) 418 .get(0)
419 .and_then(|tt| match tt { 419 .and_then(|tt| match tt {
420 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it), 420 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
421 _ => None, 421 _ => None,
422 }) 422 })
423 .ok_or_else(|| mbe::ExpandError::ConversionError) 423 .ok_or_else(|| mbe::ExpandError::ConversionError)
@@ -430,7 +430,7 @@ fn include_expand(
430) -> ExpandResult<Option<ExpandedEager>> { 430) -> ExpandResult<Option<ExpandedEager>> {
431 let res = (|| { 431 let res = (|| {
432 let path = parse_string(tt)?; 432 let path = parse_string(tt)?;
433 let file_id = relative_file(db, arg_id.into(), &path, false)?; 433 let file_id = relative_file(db, arg_id, &path, false)?;
434 434
435 let subtree = parse_to_token_tree(&db.file_text(file_id)) 435 let subtree = parse_to_token_tree(&db.file_text(file_id))
436 .ok_or_else(|| mbe::ExpandError::ConversionError)? 436 .ok_or_else(|| mbe::ExpandError::ConversionError)?
@@ -480,7 +480,7 @@ fn include_str_expand(
480 // it's unusual to `include_str!` a Rust file), but we can return an empty string. 480 // it's unusual to `include_str!` a Rust file), but we can return an empty string.
481 // Ideally, we'd be able to offer a precise expansion if the user asks for macro 481 // Ideally, we'd be able to offer a precise expansion if the user asks for macro
482 // expansion. 482 // expansion.
483 let file_id = match relative_file(db, arg_id.into(), &path, true) { 483 let file_id = match relative_file(db, arg_id, &path, true) {
484 Ok(file_id) => file_id, 484 Ok(file_id) => file_id,
485 Err(_) => { 485 Err(_) => {
486 return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); 486 return ExpandResult::ok(Some(ExpandedEager::new(quote!(""))));
@@ -561,7 +561,7 @@ mod tests {
561 use syntax::ast::NameOwner; 561 use syntax::ast::NameOwner;
562 562
563 fn expand_builtin_macro(ra_fixture: &str) -> String { 563 fn expand_builtin_macro(ra_fixture: &str) -> String {
564 let (db, file_id) = TestDB::with_single_file(&ra_fixture); 564 let (db, file_id) = TestDB::with_single_file(ra_fixture);
565 let parsed = db.parse(file_id); 565 let parsed = db.parse(file_id);
566 let mut macro_rules: Vec<_> = 566 let mut macro_rules: Vec<_> =
567 parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); 567 parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect();
@@ -598,7 +598,7 @@ mod tests {
598 }, 598 },
599 }; 599 };
600 600
601 let id: MacroCallId = db.intern_macro(loc).into(); 601 let id: MacroCallId = db.intern_macro(loc);
602 id.as_file() 602 id.as_file()
603 } 603 }
604 Either::Right(expander) => { 604 Either::Right(expander) => {
@@ -635,7 +635,7 @@ mod tests {
635 kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, 635 kind: MacroCallKind::FnLike { ast_id: call_id, fragment },
636 }; 636 };
637 637
638 let id: MacroCallId = db.intern_macro(loc).into(); 638 let id: MacroCallId = db.intern_macro(loc);
639 id.as_file() 639 id.as_file()
640 } 640 }
641 }; 641 };
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index e8f4af309..66f44202b 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -12,9 +12,9 @@ use syntax::{
12}; 12};
13 13
14use crate::{ 14use crate::{
15 ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, 15 ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinAttrExpander,
16 BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId, 16 BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId,
17 MacroDefKind, MacroFile, ProcMacroExpander, 17 MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
18}; 18};
19 19
20/// Total limit on the number of tokens produced by any macro invocation. 20/// Total limit on the number of tokens produced by any macro invocation.
@@ -31,6 +31,8 @@ pub enum TokenExpander {
31 MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap }, 31 MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap },
32 /// Stuff like `line!` and `file!`. 32 /// Stuff like `line!` and `file!`.
33 Builtin(BuiltinFnLikeExpander), 33 Builtin(BuiltinFnLikeExpander),
34 /// `global_allocator` and such.
35 BuiltinAttr(BuiltinAttrExpander),
34 /// `derive(Copy)` and such. 36 /// `derive(Copy)` and such.
35 BuiltinDerive(BuiltinDeriveExpander), 37 BuiltinDerive(BuiltinDeriveExpander),
36 /// The thing we love the most here in rust-analyzer -- procedural macros. 38 /// The thing we love the most here in rust-analyzer -- procedural macros.
@@ -49,12 +51,13 @@ impl TokenExpander {
49 TokenExpander::MacroDef { mac, .. } => mac.expand(tt), 51 TokenExpander::MacroDef { mac, .. } => mac.expand(tt),
50 TokenExpander::Builtin(it) => it.expand(db, id, tt), 52 TokenExpander::Builtin(it) => it.expand(db, id, tt),
51 // FIXME switch these to ExpandResult as well 53 // FIXME switch these to ExpandResult as well
54 TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt).into(),
52 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 55 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
53 TokenExpander::ProcMacro(_) => { 56 TokenExpander::ProcMacro(_) => {
54 // We store the result in salsa db to prevent non-deterministic behavior in 57 // We store the result in salsa db to prevent non-deterministic behavior in
55 // some proc-macro implementation 58 // some proc-macro implementation
56 // See #4315 for details 59 // See #4315 for details
57 db.expand_proc_macro(id.into()).into() 60 db.expand_proc_macro(id).into()
58 } 61 }
59 } 62 }
60 } 63 }
@@ -64,6 +67,7 @@ impl TokenExpander {
64 TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id), 67 TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id),
65 TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id), 68 TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id),
66 TokenExpander::Builtin(..) 69 TokenExpander::Builtin(..)
70 | TokenExpander::BuiltinAttr(..)
67 | TokenExpander::BuiltinDerive(..) 71 | TokenExpander::BuiltinDerive(..)
68 | TokenExpander::ProcMacro(..) => id, 72 | TokenExpander::ProcMacro(..) => id,
69 } 73 }
@@ -74,6 +78,7 @@ impl TokenExpander {
74 TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id), 78 TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id),
75 TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id), 79 TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id),
76 TokenExpander::Builtin(..) 80 TokenExpander::Builtin(..)
81 | TokenExpander::BuiltinAttr(..)
77 | TokenExpander::BuiltinDerive(..) 82 | TokenExpander::BuiltinDerive(..)
78 | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), 83 | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
79 } 84 }
@@ -85,7 +90,7 @@ impl TokenExpander {
85pub trait AstDatabase: SourceDatabase { 90pub trait AstDatabase: SourceDatabase {
86 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; 91 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
87 92
88 /// Main public API -- parsis a hir file, not caring whether it's a real 93 /// Main public API -- parses a hir file, not caring whether it's a real
89 /// file or a macro expansion. 94 /// file or a macro expansion.
90 #[salsa::transparent] 95 #[salsa::transparent]
91 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; 96 fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
@@ -236,7 +241,7 @@ fn parse_macro_expansion(
236 } 241 }
237 }; 242 };
238 if is_self_replicating(&node, &call_node.value) { 243 if is_self_replicating(&node, &call_node.value) {
239 return ExpandResult::only_err(err); 244 ExpandResult::only_err(err)
240 } else { 245 } else {
241 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } 246 ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
242 } 247 }
@@ -299,6 +304,9 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>>
299 } 304 }
300 }, 305 },
301 MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))), 306 MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))),
307 MacroDefKind::BuiltInAttr(expander, _) => {
308 Some(Arc::new(TokenExpander::BuiltinAttr(expander)))
309 }
302 MacroDefKind::BuiltInDerive(expander, _) => { 310 MacroDefKind::BuiltInDerive(expander, _) => {
303 Some(Arc::new(TokenExpander::BuiltinDerive(expander))) 311 Some(Arc::new(TokenExpander::BuiltinDerive(expander)))
304 } 312 }
@@ -377,7 +385,12 @@ fn expand_proc_macro(
377 _ => unreachable!(), 385 _ => unreachable!(),
378 }; 386 };
379 387
380 expander.expand(db, loc.krate, &macro_arg.0) 388 let attr_arg = match &loc.kind {
389 MacroCallKind::Attr { attr_args, .. } => Some(attr_args),
390 _ => None,
391 };
392
393 expander.expand(db, loc.krate, &macro_arg.0, attr_arg)
381} 394}
382 395
383fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { 396fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool {
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 14af628a1..584ddcf9f 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -17,7 +17,7 @@
17//! > and we need to live with it because it's available on stable and widely relied upon. 17//! > and we need to live with it because it's available on stable and widely relied upon.
18//! 18//!
19//! 19//!
20//! See the full discussion : https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros 20//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
21 21
22use crate::{ 22use crate::{
23 ast::{self, AstNode}, 23 ast::{self, AstNode},
@@ -128,7 +128,7 @@ pub fn expand_eager_macro(
128 }), 128 }),
129 kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr }, 129 kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr },
130 }); 130 });
131 let arg_file_id: MacroCallId = arg_id.into(); 131 let arg_file_id: MacroCallId = arg_id;
132 132
133 let parsed_args = 133 let parsed_args =
134 diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0; 134 diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0;
@@ -177,13 +177,11 @@ fn lazy_expand(
177 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 177 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
178 178
179 let fragment = crate::to_fragment_kind(&macro_call.value); 179 let fragment = crate::to_fragment_kind(&macro_call.value);
180 let id: MacroCallId = def 180 let id: MacroCallId = def.as_lazy_macro(
181 .as_lazy_macro( 181 db,
182 db, 182 krate,
183 krate, 183 MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment },
184 MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment }, 184 );
185 )
186 .into();
187 185
188 let err = db.macro_expand_error(id); 186 let err = db.macro_expand_error(id);
189 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); 187 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
@@ -216,14 +214,14 @@ fn eager_macro_recur(
216 def, 214 def,
217 macro_resolver, 215 macro_resolver,
218 diagnostic_sink, 216 diagnostic_sink,
219 )? 217 )?;
220 .into();
221 db.parse_or_expand(id.as_file()) 218 db.parse_or_expand(id.as_file())
222 .expect("successful macro expansion should be parseable") 219 .expect("successful macro expansion should be parseable")
223 .clone_for_update() 220 .clone_for_update()
224 } 221 }
225 MacroDefKind::Declarative(_) 222 MacroDefKind::Declarative(_)
226 | MacroDefKind::BuiltIn(..) 223 | MacroDefKind::BuiltIn(..)
224 | MacroDefKind::BuiltInAttr(..)
227 | MacroDefKind::BuiltInDerive(..) 225 | MacroDefKind::BuiltInDerive(..)
228 | MacroDefKind::ProcMacro(..) => { 226 | MacroDefKind::ProcMacro(..) => {
229 let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); 227 let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index d98913907..05c6c3fb1 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -192,6 +192,7 @@ impl HygieneFrame {
192 (info, Some(loc.def.krate), loc.def.local_inner) 192 (info, Some(loc.def.krate), loc.def.local_inner)
193 } 193 }
194 MacroDefKind::BuiltIn(..) => (info, Some(loc.def.krate), false), 194 MacroDefKind::BuiltIn(..) => (info, Some(loc.def.krate), false),
195 MacroDefKind::BuiltInAttr(..) => (info, None, false),
195 MacroDefKind::BuiltInDerive(..) => (info, None, false), 196 MacroDefKind::BuiltInDerive(..) => (info, None, false),
196 MacroDefKind::BuiltInEager(..) => (info, None, false), 197 MacroDefKind::BuiltInEager(..) => (info, None, false),
197 MacroDefKind::ProcMacro(..) => (info, None, false), 198 MacroDefKind::ProcMacro(..) => (info, None, false),
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs
index fe4790e7b..bc3ecc593 100644
--- a/crates/hir_expand/src/input.rs
+++ b/crates/hir_expand/src/input.rs
@@ -1,7 +1,7 @@
1//! Macro input conditioning. 1//! Macro input conditioning.
2 2
3use syntax::{ 3use syntax::{
4 ast::{self, AttrsOwner}, 4 ast::{self, make, AttrsOwner},
5 AstNode, SyntaxNode, 5 AstNode, SyntaxNode,
6}; 6};
7 7
@@ -28,6 +28,14 @@ pub(crate) fn process_macro_input(
28 28
29 remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() 29 remove_derives_up_to(item, derive_attr_index as usize).syntax().clone()
30 } 30 }
31 MacroCallKind::Attr { invoc_attr_index, .. } => {
32 let item = match ast::Item::cast(node.clone()) {
33 Some(item) => item,
34 None => return node,
35 };
36
37 remove_attr_invoc(item, invoc_attr_index as usize).syntax().clone()
38 }
31 } 39 }
32} 40}
33 41
@@ -46,6 +54,19 @@ fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item {
46 item 54 item
47} 55}
48 56
57/// Removes the attribute invoking an attribute macro from `item`.
58fn remove_attr_invoc(item: ast::Item, attr_index: usize) -> ast::Item {
59 let item = item.clone_for_update();
60 let attr = item
61 .attrs()
62 .nth(attr_index)
63 .unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index));
64 let syntax_index = attr.syntax().index();
65 let ws = make::tokens::whitespace(&" ".repeat(u32::from(attr.syntax().text().len()) as usize));
66 item.syntax().splice_children(syntax_index..syntax_index + 1, vec![ws.into()]);
67 item
68}
69
49#[cfg(test)] 70#[cfg(test)]
50mod tests { 71mod tests {
51 use base_db::fixture::WithFixture; 72 use base_db::fixture::WithFixture;
@@ -57,7 +78,7 @@ mod tests {
57 use super::*; 78 use super::*;
58 79
59 fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) { 80 fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) {
60 let (db, file_id) = TestDB::with_single_file(&ra_fixture); 81 let (db, file_id) = TestDB::with_single_file(ra_fixture);
61 let parsed = db.parse(file_id); 82 let parsed = db.parse(file_id);
62 83
63 let mut items: Vec<_> = 84 let mut items: Vec<_> =
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 90d8ae240..33107aa24 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -8,6 +8,7 @@ pub mod db;
8pub mod ast_id_map; 8pub mod ast_id_map;
9pub mod name; 9pub mod name;
10pub mod hygiene; 10pub mod hygiene;
11pub mod builtin_attr;
11pub mod builtin_derive; 12pub mod builtin_derive;
12pub mod builtin_macro; 13pub mod builtin_macro;
13pub mod proc_macro; 14pub mod proc_macro;
@@ -32,6 +33,7 @@ use syntax::{
32}; 33};
33 34
34use crate::ast_id_map::FileAstId; 35use crate::ast_id_map::FileAstId;
36use crate::builtin_attr::BuiltinAttrExpander;
35use crate::builtin_derive::BuiltinDeriveExpander; 37use crate::builtin_derive::BuiltinDeriveExpander;
36use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; 38use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander};
37use crate::proc_macro::ProcMacroExpander; 39use crate::proc_macro::ProcMacroExpander;
@@ -51,7 +53,7 @@ mod test_db;
51/// this is a recursive definition! However, the size_of of `HirFileId` is 53/// this is a recursive definition! However, the size_of of `HirFileId` is
52/// finite (because everything bottoms out at the real `FileId`) and small 54/// finite (because everything bottoms out at the real `FileId`) and small
53/// (`MacroCallId` uses the location interning. You can check details here: 55/// (`MacroCallId` uses the location interning. You can check details here:
54/// https://en.wikipedia.org/wiki/String_interning). 56/// <https://en.wikipedia.org/wiki/String_interning>).
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub struct HirFileId(HirFileIdRepr); 58pub struct HirFileId(HirFileIdRepr);
57 59
@@ -206,6 +208,7 @@ impl MacroDefId {
206 let id = match &self.kind { 208 let id = match &self.kind {
207 MacroDefKind::Declarative(id) => id, 209 MacroDefKind::Declarative(id) => id,
208 MacroDefKind::BuiltIn(_, id) => id, 210 MacroDefKind::BuiltIn(_, id) => id,
211 MacroDefKind::BuiltInAttr(_, id) => id,
209 MacroDefKind::BuiltInDerive(_, id) => id, 212 MacroDefKind::BuiltInDerive(_, id) => id,
210 MacroDefKind::BuiltInEager(_, id) => id, 213 MacroDefKind::BuiltInEager(_, id) => id,
211 MacroDefKind::ProcMacro(.., id) => return Either::Right(*id), 214 MacroDefKind::ProcMacro(.., id) => return Either::Right(*id),
@@ -223,6 +226,7 @@ pub enum MacroDefKind {
223 Declarative(AstId<ast::Macro>), 226 Declarative(AstId<ast::Macro>),
224 BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>), 227 BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>),
225 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander 228 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
229 BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>),
226 BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), 230 BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>),
227 BuiltInEager(EagerExpander, AstId<ast::Macro>), 231 BuiltInEager(EagerExpander, AstId<ast::Macro>),
228 ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>), 232 ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>),
@@ -258,14 +262,29 @@ pub enum MacroCallKind {
258 /// out-of-line modules, which may have attributes spread across 2 files! 262 /// out-of-line modules, which may have attributes spread across 2 files!
259 derive_attr_index: u32, 263 derive_attr_index: u32,
260 }, 264 },
265 Attr {
266 ast_id: AstId<ast::Item>,
267 attr_name: String,
268 attr_args: tt::Subtree,
269 /// Syntactical index of the invoking `#[attribute]`.
270 ///
271 /// Outer attributes are counted first, then inner attributes. This does not support
272 /// out-of-line modules, which may have attributes spread across 2 files!
273 invoc_attr_index: u32,
274 },
261} 275}
262 276
277// FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole
278// `cfg_attr` instead of just one of the attributes it expands to
279
263impl MacroCallKind { 280impl MacroCallKind {
264 /// Returns the file containing the macro invocation. 281 /// Returns the file containing the macro invocation.
265 fn file_id(&self) -> HirFileId { 282 fn file_id(&self) -> HirFileId {
266 match self { 283 match self {
267 MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, 284 MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id,
268 MacroCallKind::Derive { ast_id, .. } => ast_id.file_id, 285 MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
286 ast_id.file_id
287 }
269 } 288 }
270 } 289 }
271 290
@@ -274,7 +293,7 @@ impl MacroCallKind {
274 MacroCallKind::FnLike { ast_id, .. } => { 293 MacroCallKind::FnLike { ast_id, .. } => {
275 ast_id.with_value(ast_id.to_node(db).syntax().clone()) 294 ast_id.with_value(ast_id.to_node(db).syntax().clone())
276 } 295 }
277 MacroCallKind::Derive { ast_id, .. } => { 296 MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
278 ast_id.with_value(ast_id.to_node(db).syntax().clone()) 297 ast_id.with_value(ast_id.to_node(db).syntax().clone())
279 } 298 }
280 } 299 }
@@ -285,7 +304,9 @@ impl MacroCallKind {
285 MacroCallKind::FnLike { ast_id, .. } => { 304 MacroCallKind::FnLike { ast_id, .. } => {
286 Some(ast_id.to_node(db).token_tree()?.syntax().clone()) 305 Some(ast_id.to_node(db).token_tree()?.syntax().clone())
287 } 306 }
288 MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), 307 MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => {
308 Some(ast_id.to_node(db).syntax().clone())
309 }
289 } 310 }
290 } 311 }
291 312
@@ -293,6 +314,7 @@ impl MacroCallKind {
293 match self { 314 match self {
294 MacroCallKind::FnLike { fragment, .. } => *fragment, 315 MacroCallKind::FnLike { fragment, .. } => *fragment,
295 MacroCallKind::Derive { .. } => FragmentKind::Items, 316 MacroCallKind::Derive { .. } => FragmentKind::Items,
317 MacroCallKind::Attr { .. } => FragmentKind::Items, // is this always correct?
296 } 318 }
297 } 319 }
298} 320}
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 00b8adc1e..376fe130f 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -160,7 +160,6 @@ pub mod known {
160 str, 160 str,
161 // Special names 161 // Special names
162 macro_rules, 162 macro_rules,
163 derive,
164 doc, 163 doc,
165 cfg, 164 cfg,
166 cfg_attr, 165 cfg_attr,
@@ -240,6 +239,14 @@ pub mod known {
240 PartialOrd, 239 PartialOrd,
241 Eq, 240 Eq,
242 PartialEq, 241 PartialEq,
242 // Builtin attributes
243 bench,
244 cfg_accessible,
245 cfg_eval,
246 derive,
247 global_allocator,
248 test,
249 test_case,
243 // Safe intrinsics 250 // Safe intrinsics
244 abort, 251 abort,
245 size_of, 252 size_of,
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index d5643393a..025e10239 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -28,11 +28,16 @@ impl ProcMacroExpander {
28 Self { krate, proc_macro_id: None } 28 Self { krate, proc_macro_id: None }
29 } 29 }
30 30
31 pub fn is_dummy(&self) -> bool {
32 self.proc_macro_id.is_none()
33 }
34
31 pub fn expand( 35 pub fn expand(
32 self, 36 self,
33 db: &dyn AstDatabase, 37 db: &dyn AstDatabase,
34 calling_crate: CrateId, 38 calling_crate: CrateId,
35 tt: &tt::Subtree, 39 tt: &tt::Subtree,
40 attr_arg: Option<&tt::Subtree>,
36 ) -> Result<tt::Subtree, mbe::ExpandError> { 41 ) -> Result<tt::Subtree, mbe::ExpandError> {
37 match self.proc_macro_id { 42 match self.proc_macro_id {
38 Some(id) => { 43 Some(id) => {
@@ -40,13 +45,12 @@ impl ProcMacroExpander {
40 let proc_macro = krate_graph[self.krate] 45 let proc_macro = krate_graph[self.krate]
41 .proc_macro 46 .proc_macro
42 .get(id.0 as usize) 47 .get(id.0 as usize)
43 .clone()
44 .ok_or_else(|| err!("No derive macro found."))?; 48 .ok_or_else(|| err!("No derive macro found."))?;
45 49
46 // Proc macros have access to the environment variables of the invoking crate. 50 // Proc macros have access to the environment variables of the invoking crate.
47 let env = &krate_graph[calling_crate].env; 51 let env = &krate_graph[calling_crate].env;
48 52
49 proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from) 53 proc_macro.expander.expand(tt, attr_arg, env).map_err(mbe::ExpandError::from)
50 } 54 }
51 None => Err(mbe::ExpandError::UnresolvedProcMacro), 55 None => Err(mbe::ExpandError::UnresolvedProcMacro),
52 } 56 }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 4b714c6d8..a1894e8d8 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14itertools = "0.10.0" 14itertools = "0.10.0"
15arrayvec = "0.7" 15arrayvec = "0.7"
16smallvec = "1.2.0" 16smallvec = "1.2.0"
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index 893e727c2..bb9d84246 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -202,7 +202,7 @@ impl<T: HasInterner<Interner = Interner> + Fold<Interner>> TyBuilder<Binders<T>>
202 202
203impl TyBuilder<Binders<Ty>> { 203impl TyBuilder<Binders<Ty>> {
204 pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> { 204 pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> {
205 TyBuilder::subst_binders(db.ty(def.into())) 205 TyBuilder::subst_binders(db.ty(def))
206 } 206 }
207 207
208 pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> { 208 pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs
index 4e042bf42..a4c09c742 100644
--- a/crates/hir_ty/src/chalk_db.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -10,16 +10,16 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
10use base_db::CrateId; 10use base_db::CrateId;
11use hir_def::{ 11use hir_def::{
12 lang_item::{lang_attr, LangItemTarget}, 12 lang_item::{lang_attr, LangItemTarget},
13 AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId, 13 AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, ModuleId, TypeAliasId,
14}; 14};
15use hir_expand::name::name; 15use hir_expand::name::name;
16 16
17use crate::{ 17use crate::{
18 db::HirDatabase, 18 db::HirDatabase,
19 display::HirDisplay, 19 display::HirDisplay,
20 from_assoc_type_id, from_chalk_trait_id, make_only_type_binders, 20 from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_only_type_binders,
21 mapping::{from_chalk, ToChalk, TypeAliasAsValue}, 21 mapping::{from_chalk, ToChalk, TypeAliasAsValue},
22 method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, 22 method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
23 to_assoc_type_id, to_chalk_trait_id, 23 to_assoc_type_id, to_chalk_trait_id,
24 traits::ChalkContext, 24 traits::ChalkContext,
25 utils::generics, 25 utils::generics,
@@ -105,12 +105,30 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
105 _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), 105 _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
106 }; 106 };
107 107
108 fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> {
109 db.trait_impls_in_block(module.containing_block()?)
110 }
111
108 // Note: Since we're using impls_for_trait, only impls where the trait 112 // Note: Since we're using impls_for_trait, only impls where the trait
109 // can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that 113 // can be resolved should ever reach Chalk. impl_datum relies on that
110 // and will panic if the trait can't be resolved. 114 // and will panic if the trait can't be resolved.
111 let in_deps = self.db.trait_impls_in_deps(self.krate); 115 let in_deps = self.db.trait_impls_in_deps(self.krate);
112 let in_self = self.db.trait_impls_in_crate(self.krate); 116 let in_self = self.db.trait_impls_in_crate(self.krate);
113 let impl_maps = [in_deps, in_self]; 117 let trait_module = trait_.module(self.db.upcast());
118 let type_module = match self_ty_fp {
119 Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
120 Some(TyFingerprint::ForeignType(type_id)) => {
121 Some(from_foreign_def_id(type_id).module(self.db.upcast()))
122 }
123 Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
124 _ => None,
125 };
126 let impl_maps = [
127 Some(in_deps),
128 Some(in_self),
129 local_impls(self.db, trait_module),
130 type_module.and_then(|m| local_impls(self.db, m)),
131 ];
114 132
115 let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); 133 let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
116 134
@@ -118,14 +136,16 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
118 debug!("Unrestricted search for {:?} impls...", trait_); 136 debug!("Unrestricted search for {:?} impls...", trait_);
119 impl_maps 137 impl_maps
120 .iter() 138 .iter()
121 .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk)) 139 .filter_map(|o| o.as_ref())
140 .flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk))
122 .collect() 141 .collect()
123 } else { 142 } else {
124 impl_maps 143 impl_maps
125 .iter() 144 .iter()
126 .flat_map(|crate_impl_defs| { 145 .filter_map(|o| o.as_ref())
146 .flat_map(|impls| {
127 fps.iter().flat_map(move |fp| { 147 fps.iter().flat_map(move |fp| {
128 crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) 148 impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
129 }) 149 })
130 }) 150 })
131 .collect() 151 .collect()
@@ -430,8 +450,7 @@ pub(crate) fn trait_datum_query(
430 fundamental: false, 450 fundamental: false,
431 }; 451 };
432 let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); 452 let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
433 let associated_ty_ids = 453 let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
434 trait_data.associated_types().map(|type_alias| to_assoc_type_id(type_alias)).collect();
435 let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; 454 let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
436 let well_known = 455 let well_known =
437 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); 456 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs
index e3ceb3d62..6f0bf8f8c 100644
--- a/crates/hir_ty/src/consteval.rs
+++ b/crates/hir_ty/src/consteval.rs
@@ -49,7 +49,7 @@ pub fn usize_const(value: Option<u64>) -> Const {
49 ConstData { 49 ConstData {
50 ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), 50 ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner),
51 value: ConstValue::Concrete(chalk_ir::ConcreteConst { 51 value: ConstValue::Concrete(chalk_ir::ConcreteConst {
52 interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown), 52 interned: value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown),
53 }), 53 }),
54 } 54 }
55 .intern(&Interner) 55 .intern(&Interner)
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index be5b9110e..b9003c413 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -5,8 +5,8 @@ use std::sync::Arc;
5 5
6use base_db::{impl_intern_key, salsa, CrateId, Upcast}; 6use base_db::{impl_intern_key, salsa, CrateId, Upcast};
7use hir_def::{ 7use hir_def::{
8 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, 8 db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId,
9 LifetimeParamId, LocalFieldId, TypeParamId, VariantId, 9 ImplId, LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
10}; 10};
11use la_arena::ArenaMap; 11use la_arena::ArenaMap;
12 12
@@ -79,6 +79,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
79 #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] 79 #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
80 fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; 80 fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
81 81
82 #[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
83 fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>;
84
82 #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] 85 #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
83 fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>; 86 fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
84 87
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 283894704..6339c9687 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -4,325 +4,31 @@ mod match_check;
4mod unsafe_check; 4mod unsafe_check;
5mod decl_check; 5mod decl_check;
6 6
7use std::{any::Any, fmt}; 7use std::fmt;
8 8
9use base_db::CrateId; 9use base_db::CrateId;
10use hir_def::{DefWithBodyId, ModuleDefId}; 10use hir_def::ModuleDefId;
11use hir_expand::{name::Name, HirFileId, InFile}; 11use hir_expand::HirFileId;
12use stdx::format_to; 12use syntax::{ast, AstPtr};
13use syntax::{ast, AstPtr, SyntaxNodePtr};
14 13
15use crate::{ 14use crate::db::HirDatabase;
16 db::HirDatabase,
17 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
18};
19 15
20pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; 16pub use crate::diagnostics::{
17 expr::{
18 record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
19 },
20 unsafe_check::missing_unsafe,
21};
21 22
22pub fn validate_module_item( 23pub fn validate_module_item(
23 db: &dyn HirDatabase, 24 db: &dyn HirDatabase,
24 krate: CrateId, 25 krate: CrateId,
25 owner: ModuleDefId, 26 owner: ModuleDefId,
26 sink: &mut DiagnosticSink<'_>, 27) -> Vec<IncorrectCase> {
27) {
28 let _p = profile::span("validate_module_item"); 28 let _p = profile::span("validate_module_item");
29 let mut validator = decl_check::DeclValidator::new(db, krate, sink); 29 let mut validator = decl_check::DeclValidator::new(db, krate);
30 validator.validate_item(owner); 30 validator.validate_item(owner);
31} 31 validator.sink
32
33pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
34 let _p = profile::span("validate_body");
35 let infer = db.infer(owner);
36 infer.add_diagnostics(db, owner, sink);
37 let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
38 validator.validate_body(db);
39 let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
40 validator.validate_body(db);
41}
42
43// Diagnostic: no-such-field
44//
45// This diagnostic is triggered if created structure does not have field provided in record.
46#[derive(Debug)]
47pub struct NoSuchField {
48 pub file: HirFileId,
49 pub field: AstPtr<ast::RecordExprField>,
50}
51
52impl Diagnostic for NoSuchField {
53 fn code(&self) -> DiagnosticCode {
54 DiagnosticCode("no-such-field")
55 }
56
57 fn message(&self) -> String {
58 "no such field".to_string()
59 }
60
61 fn display_source(&self) -> InFile<SyntaxNodePtr> {
62 InFile::new(self.file, self.field.clone().into())
63 }
64
65 fn as_any(&self) -> &(dyn Any + Send + 'static) {
66 self
67 }
68}
69
70// Diagnostic: missing-structure-fields
71//
72// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
73//
74// Example:
75//
76// ```rust
77// struct A { a: u8, b: u8 }
78//
79// let a = A { a: 10 };
80// ```
81#[derive(Debug)]
82pub struct MissingFields {
83 pub file: HirFileId,
84 pub field_list_parent: AstPtr<ast::RecordExpr>,
85 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
86 pub missed_fields: Vec<Name>,
87}
88
89impl Diagnostic for MissingFields {
90 fn code(&self) -> DiagnosticCode {
91 DiagnosticCode("missing-structure-fields")
92 }
93 fn message(&self) -> String {
94 let mut buf = String::from("Missing structure fields:\n");
95 for field in &self.missed_fields {
96 format_to!(buf, "- {}\n", field);
97 }
98 buf
99 }
100
101 fn display_source(&self) -> InFile<SyntaxNodePtr> {
102 InFile {
103 file_id: self.file,
104 value: self
105 .field_list_parent_path
106 .clone()
107 .map(SyntaxNodePtr::from)
108 .unwrap_or_else(|| self.field_list_parent.clone().into()),
109 }
110 }
111
112 fn as_any(&self) -> &(dyn Any + Send + 'static) {
113 self
114 }
115}
116
117// Diagnostic: missing-pat-fields
118//
119// This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure.
120//
121// Example:
122//
123// ```rust
124// struct A { a: u8, b: u8 }
125//
126// let a = A { a: 10, b: 20 };
127//
128// if let A { a } = a {
129// // ...
130// }
131// ```
132#[derive(Debug)]
133pub struct MissingPatFields {
134 pub file: HirFileId,
135 pub field_list_parent: AstPtr<ast::RecordPat>,
136 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
137 pub missed_fields: Vec<Name>,
138}
139
140impl Diagnostic for MissingPatFields {
141 fn code(&self) -> DiagnosticCode {
142 DiagnosticCode("missing-pat-fields")
143 }
144 fn message(&self) -> String {
145 let mut buf = String::from("Missing structure fields:\n");
146 for field in &self.missed_fields {
147 format_to!(buf, "- {}\n", field);
148 }
149 buf
150 }
151 fn display_source(&self) -> InFile<SyntaxNodePtr> {
152 InFile {
153 file_id: self.file,
154 value: self
155 .field_list_parent_path
156 .clone()
157 .map(SyntaxNodePtr::from)
158 .unwrap_or_else(|| self.field_list_parent.clone().into()),
159 }
160 }
161 fn as_any(&self) -> &(dyn Any + Send + 'static) {
162 self
163 }
164}
165
166// Diagnostic: missing-match-arm
167//
168// This diagnostic is triggered if `match` block is missing one or more match arms.
169#[derive(Debug)]
170pub struct MissingMatchArms {
171 pub file: HirFileId,
172 pub match_expr: AstPtr<ast::Expr>,
173 pub arms: AstPtr<ast::MatchArmList>,
174}
175
176impl Diagnostic for MissingMatchArms {
177 fn code(&self) -> DiagnosticCode {
178 DiagnosticCode("missing-match-arm")
179 }
180 fn message(&self) -> String {
181 String::from("Missing match arm")
182 }
183 fn display_source(&self) -> InFile<SyntaxNodePtr> {
184 InFile { file_id: self.file, value: self.match_expr.clone().into() }
185 }
186 fn as_any(&self) -> &(dyn Any + Send + 'static) {
187 self
188 }
189}
190
191// Diagnostic: missing-ok-or-some-in-tail-expr
192//
193// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
194// or if a block that should return `Option` returns a value not wrapped in `Some`.
195//
196// Example:
197//
198// ```rust
199// fn foo() -> Result<u8, ()> {
200// 10
201// }
202// ```
203#[derive(Debug)]
204pub struct MissingOkOrSomeInTailExpr {
205 pub file: HirFileId,
206 pub expr: AstPtr<ast::Expr>,
207 // `Some` or `Ok` depending on whether the return type is Result or Option
208 pub required: String,
209}
210
211impl Diagnostic for MissingOkOrSomeInTailExpr {
212 fn code(&self) -> DiagnosticCode {
213 DiagnosticCode("missing-ok-or-some-in-tail-expr")
214 }
215 fn message(&self) -> String {
216 format!("wrap return expression in {}", self.required)
217 }
218 fn display_source(&self) -> InFile<SyntaxNodePtr> {
219 InFile { file_id: self.file, value: self.expr.clone().into() }
220 }
221 fn as_any(&self) -> &(dyn Any + Send + 'static) {
222 self
223 }
224}
225
226#[derive(Debug)]
227pub struct RemoveThisSemicolon {
228 pub file: HirFileId,
229 pub expr: AstPtr<ast::Expr>,
230}
231
232impl Diagnostic for RemoveThisSemicolon {
233 fn code(&self) -> DiagnosticCode {
234 DiagnosticCode("remove-this-semicolon")
235 }
236
237 fn message(&self) -> String {
238 "Remove this semicolon".to_string()
239 }
240
241 fn display_source(&self) -> InFile<SyntaxNodePtr> {
242 InFile { file_id: self.file, value: self.expr.clone().into() }
243 }
244
245 fn as_any(&self) -> &(dyn Any + Send + 'static) {
246 self
247 }
248}
249
250// Diagnostic: break-outside-of-loop
251//
252// This diagnostic is triggered if the `break` keyword is used outside of a loop.
253#[derive(Debug)]
254pub struct BreakOutsideOfLoop {
255 pub file: HirFileId,
256 pub expr: AstPtr<ast::Expr>,
257}
258
259impl Diagnostic for BreakOutsideOfLoop {
260 fn code(&self) -> DiagnosticCode {
261 DiagnosticCode("break-outside-of-loop")
262 }
263 fn message(&self) -> String {
264 "break outside of loop".to_string()
265 }
266 fn display_source(&self) -> InFile<SyntaxNodePtr> {
267 InFile { file_id: self.file, value: self.expr.clone().into() }
268 }
269 fn as_any(&self) -> &(dyn Any + Send + 'static) {
270 self
271 }
272}
273
274// Diagnostic: missing-unsafe
275//
276// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
277#[derive(Debug)]
278pub struct MissingUnsafe {
279 pub file: HirFileId,
280 pub expr: AstPtr<ast::Expr>,
281}
282
283impl Diagnostic for MissingUnsafe {
284 fn code(&self) -> DiagnosticCode {
285 DiagnosticCode("missing-unsafe")
286 }
287 fn message(&self) -> String {
288 format!("This operation is unsafe and requires an unsafe function or block")
289 }
290 fn display_source(&self) -> InFile<SyntaxNodePtr> {
291 InFile { file_id: self.file, value: self.expr.clone().into() }
292 }
293 fn as_any(&self) -> &(dyn Any + Send + 'static) {
294 self
295 }
296}
297
298// Diagnostic: mismatched-arg-count
299//
300// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
301#[derive(Debug)]
302pub struct MismatchedArgCount {
303 pub file: HirFileId,
304 pub call_expr: AstPtr<ast::Expr>,
305 pub expected: usize,
306 pub found: usize,
307}
308
309impl Diagnostic for MismatchedArgCount {
310 fn code(&self) -> DiagnosticCode {
311 DiagnosticCode("mismatched-arg-count")
312 }
313 fn message(&self) -> String {
314 let s = if self.expected == 1 { "" } else { "s" };
315 format!("Expected {} argument{}, found {}", self.expected, s, self.found)
316 }
317 fn display_source(&self) -> InFile<SyntaxNodePtr> {
318 InFile { file_id: self.file, value: self.call_expr.clone().into() }
319 }
320 fn as_any(&self) -> &(dyn Any + Send + 'static) {
321 self
322 }
323 fn is_experimental(&self) -> bool {
324 true
325 }
326} 32}
327 33
328#[derive(Debug)] 34#[derive(Debug)]
@@ -378,9 +84,6 @@ impl fmt::Display for IdentType {
378 } 84 }
379} 85}
380 86
381// Diagnostic: incorrect-ident-case
382//
383// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
384#[derive(Debug)] 87#[derive(Debug)]
385pub struct IncorrectCase { 88pub struct IncorrectCase {
386 pub file: HirFileId, 89 pub file: HirFileId,
@@ -390,450 +93,3 @@ pub struct IncorrectCase {
390 pub ident_text: String, 93 pub ident_text: String,
391 pub suggested_text: String, 94 pub suggested_text: String,
392} 95}
393
394impl Diagnostic for IncorrectCase {
395 fn code(&self) -> DiagnosticCode {
396 DiagnosticCode("incorrect-ident-case")
397 }
398
399 fn message(&self) -> String {
400 format!(
401 "{} `{}` should have {} name, e.g. `{}`",
402 self.ident_type,
403 self.ident_text,
404 self.expected_case.to_string(),
405 self.suggested_text
406 )
407 }
408
409 fn display_source(&self) -> InFile<SyntaxNodePtr> {
410 InFile::new(self.file, self.ident.clone().into())
411 }
412
413 fn as_any(&self) -> &(dyn Any + Send + 'static) {
414 self
415 }
416
417 fn is_experimental(&self) -> bool {
418 true
419 }
420}
421
422// Diagnostic: replace-filter-map-next-with-find-map
423//
424// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
425#[derive(Debug)]
426pub struct ReplaceFilterMapNextWithFindMap {
427 pub file: HirFileId,
428 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
429 pub next_expr: AstPtr<ast::Expr>,
430}
431
432impl Diagnostic for ReplaceFilterMapNextWithFindMap {
433 fn code(&self) -> DiagnosticCode {
434 DiagnosticCode("replace-filter-map-next-with-find-map")
435 }
436 fn message(&self) -> String {
437 "replace filter_map(..).next() with find_map(..)".to_string()
438 }
439 fn display_source(&self) -> InFile<SyntaxNodePtr> {
440 InFile { file_id: self.file, value: self.next_expr.clone().into() }
441 }
442 fn as_any(&self) -> &(dyn Any + Send + 'static) {
443 self
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
450 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
451 use hir_expand::db::AstDatabase;
452 use rustc_hash::FxHashMap;
453 use syntax::{TextRange, TextSize};
454
455 use crate::{
456 diagnostics::{validate_body, validate_module_item},
457 diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder},
458 test_db::TestDB,
459 };
460
461 impl TestDB {
462 fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
463 let crate_graph = self.crate_graph();
464 for krate in crate_graph.iter() {
465 let crate_def_map = self.crate_def_map(krate);
466
467 let mut fns = Vec::new();
468 for (module_id, _) in crate_def_map.modules() {
469 for decl in crate_def_map[module_id].scope.declarations() {
470 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
471 validate_module_item(self, krate, decl, &mut sink);
472
473 if let ModuleDefId::FunctionId(f) = decl {
474 fns.push(f)
475 }
476 }
477
478 for impl_id in crate_def_map[module_id].scope.impls() {
479 let impl_data = self.impl_data(impl_id);
480 for item in impl_data.items.iter() {
481 if let AssocItemId::FunctionId(f) = item {
482 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
483 validate_module_item(
484 self,
485 krate,
486 ModuleDefId::FunctionId(*f),
487 &mut sink,
488 );
489 fns.push(*f)
490 }
491 }
492 }
493 }
494
495 for f in fns {
496 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
497 validate_body(self, f.into(), &mut sink);
498 }
499 }
500 }
501 }
502
503 pub(crate) fn check_diagnostics(ra_fixture: &str) {
504 let db = TestDB::with_files(ra_fixture);
505 let annotations = db.extract_annotations();
506
507 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
508 db.diagnostics(|d| {
509 let src = d.display_source();
510 let root = db.parse_or_expand(src.file_id).unwrap();
511 // FIXME: macros...
512 let file_id = src.file_id.original_file(&db);
513 let range = src.value.to_node(&root).text_range();
514 let message = d.message();
515 actual.entry(file_id).or_default().push((range, message));
516 });
517
518 for (file_id, diags) in actual.iter_mut() {
519 diags.sort_by_key(|it| it.0.start());
520 let text = db.file_text(*file_id);
521 // For multiline spans, place them on line start
522 for (range, content) in diags {
523 if text[*range].contains('\n') {
524 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
525 *content = format!("... {}", content);
526 }
527 }
528 }
529
530 assert_eq!(annotations, actual);
531 }
532
533 #[test]
534 fn no_such_field_diagnostics() {
535 check_diagnostics(
536 r#"
537struct S { foo: i32, bar: () }
538impl S {
539 fn new() -> S {
540 S {
541 //^ Missing structure fields:
542 //| - bar
543 foo: 92,
544 baz: 62,
545 //^^^^^^^ no such field
546 }
547 }
548}
549"#,
550 );
551 }
552 #[test]
553 fn no_such_field_with_feature_flag_diagnostics() {
554 check_diagnostics(
555 r#"
556//- /lib.rs crate:foo cfg:feature=foo
557struct MyStruct {
558 my_val: usize,
559 #[cfg(feature = "foo")]
560 bar: bool,
561}
562
563impl MyStruct {
564 #[cfg(feature = "foo")]
565 pub(crate) fn new(my_val: usize, bar: bool) -> Self {
566 Self { my_val, bar }
567 }
568 #[cfg(not(feature = "foo"))]
569 pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
570 Self { my_val }
571 }
572}
573"#,
574 );
575 }
576
577 #[test]
578 fn no_such_field_enum_with_feature_flag_diagnostics() {
579 check_diagnostics(
580 r#"
581//- /lib.rs crate:foo cfg:feature=foo
582enum Foo {
583 #[cfg(not(feature = "foo"))]
584 Buz,
585 #[cfg(feature = "foo")]
586 Bar,
587 Baz
588}
589
590fn test_fn(f: Foo) {
591 match f {
592 Foo::Bar => {},
593 Foo::Baz => {},
594 }
595}
596"#,
597 );
598 }
599
600 #[test]
601 fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
602 check_diagnostics(
603 r#"
604//- /lib.rs crate:foo cfg:feature=foo
605struct S {
606 #[cfg(feature = "foo")]
607 foo: u32,
608 #[cfg(not(feature = "foo"))]
609 bar: u32,
610}
611
612impl S {
613 #[cfg(feature = "foo")]
614 fn new(foo: u32) -> Self {
615 Self { foo }
616 }
617 #[cfg(not(feature = "foo"))]
618 fn new(bar: u32) -> Self {
619 Self { bar }
620 }
621 fn new2(bar: u32) -> Self {
622 #[cfg(feature = "foo")]
623 { Self { foo: bar } }
624 #[cfg(not(feature = "foo"))]
625 { Self { bar } }
626 }
627 fn new2(val: u32) -> Self {
628 Self {
629 #[cfg(feature = "foo")]
630 foo: val,
631 #[cfg(not(feature = "foo"))]
632 bar: val,
633 }
634 }
635}
636"#,
637 );
638 }
639
640 #[test]
641 fn no_such_field_with_type_macro() {
642 check_diagnostics(
643 r#"
644macro_rules! Type { () => { u32 }; }
645struct Foo { bar: Type![] }
646
647impl Foo {
648 fn new() -> Self {
649 Foo { bar: 0 }
650 }
651}
652"#,
653 );
654 }
655
656 #[test]
657 fn missing_record_pat_field_diagnostic() {
658 check_diagnostics(
659 r#"
660struct S { foo: i32, bar: () }
661fn baz(s: S) {
662 let S { foo: _ } = s;
663 //^ Missing structure fields:
664 //| - bar
665}
666"#,
667 );
668 }
669
670 #[test]
671 fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
672 check_diagnostics(
673 r"
674struct S { foo: i32, bar: () }
675fn baz(s: S) -> i32 {
676 match s {
677 S { foo, .. } => foo,
678 }
679}
680",
681 )
682 }
683
684 #[test]
685 fn missing_record_pat_field_box() {
686 check_diagnostics(
687 r"
688struct S { s: Box<u32> }
689fn x(a: S) {
690 let S { box s } = a;
691}
692",
693 )
694 }
695
696 #[test]
697 fn missing_record_pat_field_ref() {
698 check_diagnostics(
699 r"
700struct S { s: u32 }
701fn x(a: S) {
702 let S { ref s } = a;
703}
704",
705 )
706 }
707
708 #[test]
709 fn import_extern_crate_clash_with_inner_item() {
710 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
711
712 check_diagnostics(
713 r#"
714//- /lib.rs crate:lib deps:jwt
715mod permissions;
716
717use permissions::jwt;
718
719fn f() {
720 fn inner() {}
721 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
722}
723
724//- /permissions.rs
725pub mod jwt {
726 pub struct Claims {}
727}
728
729//- /jwt/lib.rs crate:jwt
730pub struct Claims {
731 field: u8,
732}
733 "#,
734 );
735 }
736
737 #[test]
738 fn break_outside_of_loop() {
739 check_diagnostics(
740 r#"
741fn foo() { break; }
742 //^^^^^ break outside of loop
743"#,
744 );
745 }
746
747 #[test]
748 fn missing_semicolon() {
749 check_diagnostics(
750 r#"
751 fn test() -> i32 { 123; }
752 //^^^ Remove this semicolon
753 "#,
754 );
755 }
756
757 // Register the required standard library types to make the tests work
758 fn add_filter_map_with_find_next_boilerplate(body: &str) -> String {
759 let prefix = r#"
760 //- /main.rs crate:main deps:core
761 use core::iter::Iterator;
762 use core::option::Option::{self, Some, None};
763 "#;
764 let suffix = r#"
765 //- /core/lib.rs crate:core
766 pub mod option {
767 pub enum Option<T> { Some(T), None }
768 }
769 pub mod iter {
770 pub trait Iterator {
771 type Item;
772 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
773 fn next(&mut self) -> Option<Self::Item>;
774 }
775 pub struct FilterMap {}
776 impl Iterator for FilterMap {
777 type Item = i32;
778 fn next(&mut self) -> i32 { 7 }
779 }
780 }
781 "#;
782 format!("{}{}{}", prefix, body, suffix)
783 }
784
785 #[test]
786 fn replace_filter_map_next_with_find_map2() {
787 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
788 r#"
789 fn foo() {
790 let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
791 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..)
792 }
793 "#,
794 ));
795 }
796
797 #[test]
798 fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
799 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
800 r#"
801 fn foo() {
802 let m = [1, 2, 3]
803 .iter()
804 .filter_map(|x| if *x == 2 { Some (4) } else { None })
805 .len();
806 }
807 "#,
808 ));
809 }
810
811 #[test]
812 fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
813 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
814 r#"
815 fn foo() {
816 let m = [1, 2, 3]
817 .iter()
818 .filter_map(|x| if *x == 2 { Some (4) } else { None })
819 .map(|x| x + 2)
820 .len();
821 }
822 "#,
823 ));
824 }
825
826 #[test]
827 fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
828 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
829 r#"
830 fn foo() {
831 let m = [1, 2, 3]
832 .iter()
833 .filter_map(|x| if *x == 2 { Some (4) } else { None });
834 let n = m.next();
835 }
836 "#,
837 ));
838 }
839}
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index cfb5d7320..f26150b77 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -29,7 +29,6 @@ use syntax::{
29use crate::{ 29use crate::{
30 db::HirDatabase, 30 db::HirDatabase,
31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, 31 diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase},
32 diagnostics_sink::DiagnosticSink,
33}; 32};
34 33
35mod allow { 34mod allow {
@@ -40,10 +39,10 @@ mod allow {
40 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; 39 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
41} 40}
42 41
43pub(super) struct DeclValidator<'a, 'b> { 42pub(super) struct DeclValidator<'a> {
44 db: &'a dyn HirDatabase, 43 db: &'a dyn HirDatabase,
45 krate: CrateId, 44 krate: CrateId,
46 sink: &'a mut DiagnosticSink<'b>, 45 pub(super) sink: Vec<IncorrectCase>,
47} 46}
48 47
49#[derive(Debug)] 48#[derive(Debug)]
@@ -53,13 +52,9 @@ struct Replacement {
53 expected_case: CaseType, 52 expected_case: CaseType,
54} 53}
55 54
56impl<'a, 'b> DeclValidator<'a, 'b> { 55impl<'a> DeclValidator<'a> {
57 pub(super) fn new( 56 pub(super) fn new(db: &'a dyn HirDatabase, krate: CrateId) -> DeclValidator<'a> {
58 db: &'a dyn HirDatabase, 57 DeclValidator { db, krate, sink: Vec::new() }
59 krate: CrateId,
60 sink: &'a mut DiagnosticSink<'b>,
61 ) -> DeclValidator<'a, 'b> {
62 DeclValidator { db, krate, sink }
63 } 58 }
64 59
65 pub(super) fn validate_item(&mut self, item: ModuleDefId) { 60 pub(super) fn validate_item(&mut self, item: ModuleDefId) {
@@ -131,7 +126,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
131 for (_, block_def_map) in body.blocks(self.db.upcast()) { 126 for (_, block_def_map) in body.blocks(self.db.upcast()) {
132 for (_, module) in block_def_map.modules() { 127 for (_, module) in block_def_map.modules() {
133 for def_id in module.scope.declarations() { 128 for def_id in module.scope.declarations() {
134 let mut validator = DeclValidator::new(self.db, self.krate, self.sink); 129 let mut validator = DeclValidator::new(self.db, self.krate);
135 validator.validate_item(def_id); 130 validator.validate_item(def_id);
136 } 131 }
137 } 132 }
@@ -623,343 +618,3 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
623 self.sink.push(diagnostic); 618 self.sink.push(diagnostic);
624 } 619 }
625} 620}
626
627#[cfg(test)]
628mod tests {
629 use crate::diagnostics::tests::check_diagnostics;
630
631 #[test]
632 fn incorrect_function_name() {
633 check_diagnostics(
634 r#"
635fn NonSnakeCaseName() {}
636// ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
637"#,
638 );
639 }
640
641 #[test]
642 fn incorrect_function_params() {
643 check_diagnostics(
644 r#"
645fn foo(SomeParam: u8) {}
646 // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param`
647
648fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
649 // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
650"#,
651 );
652 }
653
654 #[test]
655 fn incorrect_variable_names() {
656 check_diagnostics(
657 r#"
658fn foo() {
659 let SOME_VALUE = 10;
660 // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
661 let AnotherValue = 20;
662 // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value`
663}
664"#,
665 );
666 }
667
668 #[test]
669 fn incorrect_struct_names() {
670 check_diagnostics(
671 r#"
672struct non_camel_case_name {}
673 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
674
675struct SCREAMING_CASE {}
676 // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
677"#,
678 );
679 }
680
681 #[test]
682 fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() {
683 check_diagnostics(
684 r#"
685struct AABB {}
686"#,
687 );
688 }
689
690 #[test]
691 fn incorrect_struct_field() {
692 check_diagnostics(
693 r#"
694struct SomeStruct { SomeField: u8 }
695 // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field`
696"#,
697 );
698 }
699
700 #[test]
701 fn incorrect_enum_names() {
702 check_diagnostics(
703 r#"
704enum some_enum { Val(u8) }
705 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
706
707enum SOME_ENUM
708 // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
709"#,
710 );
711 }
712
713 #[test]
714 fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() {
715 check_diagnostics(
716 r#"
717enum AABB {}
718"#,
719 );
720 }
721
722 #[test]
723 fn incorrect_enum_variant_name() {
724 check_diagnostics(
725 r#"
726enum SomeEnum { SOME_VARIANT(u8) }
727 // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
728"#,
729 );
730 }
731
732 #[test]
733 fn incorrect_const_name() {
734 check_diagnostics(
735 r#"
736const some_weird_const: u8 = 10;
737 // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
738
739fn func() {
740 const someConstInFunc: &str = "hi there";
741 // ^^^^^^^^^^^^^^^ Constant `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
742
743}
744"#,
745 );
746 }
747
748 #[test]
749 fn incorrect_static_name() {
750 check_diagnostics(
751 r#"
752static some_weird_const: u8 = 10;
753 // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
754
755fn func() {
756 static someConstInFunc: &str = "hi there";
757 // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
758}
759"#,
760 );
761 }
762
763 #[test]
764 fn fn_inside_impl_struct() {
765 check_diagnostics(
766 r#"
767struct someStruct;
768 // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
769
770impl someStruct {
771 fn SomeFunc(&self) {
772 // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func`
773 static someConstInFunc: &str = "hi there";
774 // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC`
775 let WHY_VAR_IS_CAPS = 10;
776 // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
777 }
778}
779"#,
780 );
781 }
782
783 #[test]
784 fn no_diagnostic_for_enum_varinats() {
785 check_diagnostics(
786 r#"
787enum Option { Some, None }
788
789fn main() {
790 match Option::None {
791 None => (),
792 Some => (),
793 }
794}
795"#,
796 );
797 }
798
799 #[test]
800 fn non_let_bind() {
801 check_diagnostics(
802 r#"
803enum Option { Some, None }
804
805fn main() {
806 match Option::None {
807 SOME_VAR @ None => (),
808 // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
809 Some => (),
810 }
811}
812"#,
813 );
814 }
815
816 #[test]
817 fn allow_attributes() {
818 check_diagnostics(
819 r#"
820#[allow(non_snake_case)]
821fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
822 // cov_flags generated output from elsewhere in this file
823 extern "C" {
824 #[no_mangle]
825 static lower_case: u8;
826 }
827
828 let OtherVar = SOME_VAR + 1;
829 OtherVar
830}
831
832#[allow(nonstandard_style)]
833mod CheckNonstandardStyle {
834 fn HiImABadFnName() {}
835}
836
837#[allow(bad_style)]
838mod CheckBadStyle {
839 fn HiImABadFnName() {}
840}
841
842mod F {
843 #![allow(non_snake_case)]
844 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
845}
846
847#[allow(non_snake_case, non_camel_case_types)]
848pub struct some_type {
849 SOME_FIELD: u8,
850 SomeField: u16,
851}
852
853#[allow(non_upper_case_globals)]
854pub const some_const: u8 = 10;
855
856#[allow(non_upper_case_globals)]
857pub static SomeStatic: u8 = 10;
858 "#,
859 );
860 }
861
862 #[test]
863 fn allow_attributes_crate_attr() {
864 check_diagnostics(
865 r#"
866#![allow(non_snake_case)]
867
868mod F {
869 fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
870}
871 "#,
872 );
873 }
874
875 #[test]
876 #[ignore]
877 fn bug_trait_inside_fn() {
878 // FIXME:
879 // This is broken, and in fact, should not even be looked at by this
880 // lint in the first place. There's weird stuff going on in the
881 // collection phase.
882 // It's currently being brought in by:
883 // * validate_func on `a` recursing into modules
884 // * then it finds the trait and then the function while iterating
885 // through modules
886 // * then validate_func is called on Dirty
887 // * ... which then proceeds to look at some unknown module taking no
888 // attrs from either the impl or the fn a, and then finally to the root
889 // module
890 //
891 // It should find the attribute on the trait, but it *doesn't even see
892 // the trait* as far as I can tell.
893
894 check_diagnostics(
895 r#"
896trait T { fn a(); }
897struct U {}
898impl T for U {
899 fn a() {
900 // this comes out of bitflags, mostly
901 #[allow(non_snake_case)]
902 trait __BitFlags {
903 const HiImAlsoBad: u8 = 2;
904 #[inline]
905 fn Dirty(&self) -> bool {
906 false
907 }
908 }
909
910 }
911}
912 "#,
913 );
914 }
915
916 #[test]
917 #[ignore]
918 fn bug_traits_arent_checked() {
919 // FIXME: Traits and functions in traits aren't currently checked by
920 // r-a, even though rustc will complain about them.
921 check_diagnostics(
922 r#"
923trait BAD_TRAIT {
924 // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
925 fn BAD_FUNCTION();
926 // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
927 fn BadFunction();
928 // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function`
929}
930 "#,
931 );
932 }
933
934 #[test]
935 fn ignores_extern_items() {
936 cov_mark::check!(extern_func_incorrect_case_ignored);
937 cov_mark::check!(extern_static_incorrect_case_ignored);
938 check_diagnostics(
939 r#"
940extern {
941 fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
942 pub static SomeStatic: u8 = 10;
943}
944 "#,
945 );
946 }
947
948 #[test]
949 fn infinite_loop_inner_items() {
950 check_diagnostics(
951 r#"
952fn qualify() {
953 mod foo {
954 use super::*;
955 }
956}
957 "#,
958 )
959 }
960
961 #[test] // Issue #8809.
962 fn parenthesized_parameter() {
963 check_diagnostics(r#"fn f((O): _) {}"#)
964 }
965}
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 3efbce773..b809b96a0 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -8,20 +8,15 @@ use hir_def::{
8 expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, 8 expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
9}; 9};
10use hir_expand::name; 10use hir_expand::name;
11use itertools::Either;
11use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
12use syntax::{ast, AstPtr};
13 13
14use crate::{ 14use crate::{
15 db::HirDatabase, 15 db::HirDatabase,
16 diagnostics::{ 16 diagnostics::match_check::{
17 match_check::{ 17 self,
18 self, 18 usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
19 usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
20 },
21 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
22 MissingPatFields, RemoveThisSemicolon,
23 }, 19 },
24 diagnostics_sink::DiagnosticSink,
25 AdtId, InferenceResult, Interner, TyExt, TyKind, 20 AdtId, InferenceResult, Interner, TyExt, TyKind,
26}; 21};
27 22
@@ -31,38 +26,67 @@ pub(crate) use hir_def::{
31 LocalFieldId, VariantId, 26 LocalFieldId, VariantId,
32}; 27};
33 28
34use super::ReplaceFilterMapNextWithFindMap; 29pub enum BodyValidationDiagnostic {
30 RecordMissingFields {
31 record: Either<ExprId, PatId>,
32 variant: VariantId,
33 missed_fields: Vec<LocalFieldId>,
34 },
35 ReplaceFilterMapNextWithFindMap {
36 method_call_expr: ExprId,
37 },
38 MismatchedArgCount {
39 call_expr: ExprId,
40 expected: usize,
41 found: usize,
42 },
43 RemoveThisSemicolon {
44 expr: ExprId,
45 },
46 MissingOkOrSomeInTailExpr {
47 expr: ExprId,
48 required: String,
49 },
50 MissingMatchArms {
51 match_expr: ExprId,
52 },
53}
54
55impl BodyValidationDiagnostic {
56 pub fn collect(db: &dyn HirDatabase, owner: DefWithBodyId) -> Vec<BodyValidationDiagnostic> {
57 let _p = profile::span("BodyValidationDiagnostic::collect");
58 let infer = db.infer(owner);
59 let mut validator = ExprValidator::new(owner, infer.clone());
60 validator.validate_body(db);
61 validator.diagnostics
62 }
63}
35 64
36pub(super) struct ExprValidator<'a, 'b: 'a> { 65struct ExprValidator {
37 owner: DefWithBodyId, 66 owner: DefWithBodyId,
38 infer: Arc<InferenceResult>, 67 infer: Arc<InferenceResult>,
39 sink: &'a mut DiagnosticSink<'b>, 68 pub(super) diagnostics: Vec<BodyValidationDiagnostic>,
40} 69}
41 70
42impl<'a, 'b> ExprValidator<'a, 'b> { 71impl ExprValidator {
43 pub(super) fn new( 72 fn new(owner: DefWithBodyId, infer: Arc<InferenceResult>) -> ExprValidator {
44 owner: DefWithBodyId, 73 ExprValidator { owner, infer, diagnostics: Vec::new() }
45 infer: Arc<InferenceResult>,
46 sink: &'a mut DiagnosticSink<'b>,
47 ) -> ExprValidator<'a, 'b> {
48 ExprValidator { owner, infer, sink }
49 } 74 }
50 75
51 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 76 fn validate_body(&mut self, db: &dyn HirDatabase) {
52 self.check_for_filter_map_next(db); 77 self.check_for_filter_map_next(db);
53 78
54 let body = db.body(self.owner); 79 let body = db.body(self.owner);
55 80
56 for (id, expr) in body.exprs.iter() { 81 for (id, expr) in body.exprs.iter() {
57 if let Some((variant_def, missed_fields, true)) = 82 if let Some((variant, missed_fields, true)) =
58 record_literal_missing_fields(db, &self.infer, id, expr) 83 record_literal_missing_fields(db, &self.infer, id, expr)
59 { 84 {
60 self.create_record_literal_missing_fields_diagnostic( 85 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
61 id, 86 record: Either::Left(id),
62 db, 87 variant,
63 variant_def,
64 missed_fields, 88 missed_fields,
65 ); 89 });
66 } 90 }
67 91
68 match expr { 92 match expr {
@@ -76,15 +100,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
76 } 100 }
77 } 101 }
78 for (id, pat) in body.pats.iter() { 102 for (id, pat) in body.pats.iter() {
79 if let Some((variant_def, missed_fields, true)) = 103 if let Some((variant, missed_fields, true)) =
80 record_pattern_missing_fields(db, &self.infer, id, pat) 104 record_pattern_missing_fields(db, &self.infer, id, pat)
81 { 105 {
82 self.create_record_pattern_missing_fields_diagnostic( 106 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
83 id, 107 record: Either::Right(id),
84 db, 108 variant,
85 variant_def,
86 missed_fields, 109 missed_fields,
87 ); 110 });
88 } 111 }
89 } 112 }
90 let body_expr = &body[body.body_expr]; 113 let body_expr = &body[body.body_expr];
@@ -92,71 +115,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
92 if let Some(t) = tail { 115 if let Some(t) = tail {
93 self.validate_results_in_tail_expr(body.body_expr, *t, db); 116 self.validate_results_in_tail_expr(body.body_expr, *t, db);
94 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { 117 } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() {
95 self.validate_missing_tail_expr(body.body_expr, *id, db); 118 self.validate_missing_tail_expr(body.body_expr, *id);
96 }
97 }
98 }
99
100 fn create_record_literal_missing_fields_diagnostic(
101 &mut self,
102 id: ExprId,
103 db: &dyn HirDatabase,
104 variant_def: VariantId,
105 missed_fields: Vec<LocalFieldId>,
106 ) {
107 // XXX: only look at source_map if we do have missing fields
108 let (_, source_map) = db.body_with_source_map(self.owner);
109
110 if let Ok(source_ptr) = source_map.expr_syntax(id) {
111 let root = source_ptr.file_syntax(db.upcast());
112 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) {
113 if let Some(_) = record_expr.record_expr_field_list() {
114 let variant_data = variant_def.variant_data(db.upcast());
115 let missed_fields = missed_fields
116 .into_iter()
117 .map(|idx| variant_data.fields()[idx].name.clone())
118 .collect();
119 self.sink.push(MissingFields {
120 file: source_ptr.file_id,
121 field_list_parent: AstPtr::new(&record_expr),
122 field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)),
123 missed_fields,
124 })
125 }
126 }
127 }
128 }
129
130 fn create_record_pattern_missing_fields_diagnostic(
131 &mut self,
132 id: PatId,
133 db: &dyn HirDatabase,
134 variant_def: VariantId,
135 missed_fields: Vec<LocalFieldId>,
136 ) {
137 // XXX: only look at source_map if we do have missing fields
138 let (_, source_map) = db.body_with_source_map(self.owner);
139
140 if let Ok(source_ptr) = source_map.pat_syntax(id) {
141 if let Some(expr) = source_ptr.value.as_ref().left() {
142 let root = source_ptr.file_syntax(db.upcast());
143 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
144 if let Some(_) = record_pat.record_pat_field_list() {
145 let variant_data = variant_def.variant_data(db.upcast());
146 let missed_fields = missed_fields
147 .into_iter()
148 .map(|idx| variant_data.fields()[idx].name.clone())
149 .collect();
150 self.sink.push(MissingPatFields {
151 file: source_ptr.file_id,
152 field_list_parent: AstPtr::new(&record_pat),
153 field_list_parent_path: record_pat
154 .path()
155 .map(|path| AstPtr::new(&path)),
156 missed_fields,
157 })
158 }
159 }
160 } 119 }
161 } 120 }
162 } 121 }
@@ -199,13 +158,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
199 if function_id == *next_function_id { 158 if function_id == *next_function_id {
200 if let Some(filter_map_id) = prev { 159 if let Some(filter_map_id) = prev {
201 if *receiver == filter_map_id { 160 if *receiver == filter_map_id {
202 let (_, source_map) = db.body_with_source_map(self.owner); 161 self.diagnostics.push(
203 if let Ok(next_source_ptr) = source_map.expr_syntax(id) { 162 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
204 self.sink.push(ReplaceFilterMapNextWithFindMap { 163 method_call_expr: id,
205 file: next_source_ptr.file_id, 164 },
206 next_expr: next_source_ptr.value, 165 );
207 });
208 }
209 } 166 }
210 } 167 }
211 } 168 }
@@ -266,19 +223,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
266 let mut arg_count = args.len(); 223 let mut arg_count = args.len();
267 224
268 if arg_count != param_count { 225 if arg_count != param_count {
269 let (_, source_map) = db.body_with_source_map(self.owner); 226 if is_method_call {
270 if let Ok(source_ptr) = source_map.expr_syntax(call_id) { 227 param_count -= 1;
271 if is_method_call { 228 arg_count -= 1;
272 param_count -= 1;
273 arg_count -= 1;
274 }
275 self.sink.push(MismatchedArgCount {
276 file: source_ptr.file_id,
277 call_expr: source_ptr.value,
278 expected: param_count,
279 found: arg_count,
280 });
281 } 229 }
230 self.diagnostics.push(BodyValidationDiagnostic::MismatchedArgCount {
231 call_expr: call_id,
232 expected: param_count,
233 found: arg_count,
234 });
282 } 235 }
283 } 236 }
284 237
@@ -346,8 +299,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
346 // fit the match expression, we skip this diagnostic. Skipping the entire 299 // fit the match expression, we skip this diagnostic. Skipping the entire
347 // diagnostic rather than just not including this match arm is preferred 300 // diagnostic rather than just not including this match arm is preferred
348 // to avoid the chance of false positives. 301 // to avoid the chance of false positives.
349 #[cfg(test)] 302 cov_mark::hit!(validate_match_bailed_out);
350 match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink);
351 return; 303 return;
352 } 304 }
353 305
@@ -357,17 +309,20 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
357 infer: &infer, 309 infer: &infer,
358 db, 310 db,
359 pattern_arena: &pattern_arena, 311 pattern_arena: &pattern_arena,
360 eprint_panic_context: &|| { 312 panic_context: &|| {
361 use syntax::AstNode; 313 use syntax::AstNode;
362 if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) { 314 let match_expr_text = source_map
363 let root = scrutinee_sptr.file_syntax(db.upcast()); 315 .expr_syntax(match_expr)
364 if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() { 316 .ok()
365 eprintln!( 317 .and_then(|scrutinee_sptr| {
366 "Match checking is about to panic on this expression:\n{}", 318 let root = scrutinee_sptr.file_syntax(db.upcast());
367 match_ast.to_string(), 319 scrutinee_sptr.value.to_node(&root).syntax().parent()
368 ); 320 })
369 } 321 .map(|node| node.to_string());
370 } 322 format!(
323 "expression:\n{}",
324 match_expr_text.as_deref().unwrap_or("<synthesized expr>")
325 )
371 }, 326 },
372 }; 327 };
373 let report = compute_match_usefulness(&cx, &m_arms); 328 let report = compute_match_usefulness(&cx, &m_arms);
@@ -379,20 +334,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
379 // FIXME Report witnesses 334 // FIXME Report witnesses
380 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); 335 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
381 if !witnesses.is_empty() { 336 if !witnesses.is_empty() {
382 if let Ok(source_ptr) = source_map.expr_syntax(id) { 337 self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id });
383 let root = source_ptr.file_syntax(db.upcast());
384 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
385 if let (Some(match_expr), Some(arms)) =
386 (match_expr.expr(), match_expr.match_arm_list())
387 {
388 self.sink.push(MissingMatchArms {
389 file: source_ptr.file_id,
390 match_expr: AstPtr::new(&match_expr),
391 arms: AstPtr::new(&arms),
392 })
393 }
394 }
395 }
396 } 338 }
397 } 339 }
398 340
@@ -450,24 +392,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
450 if params.len(&Interner) > 0 392 if params.len(&Interner) > 0
451 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) 393 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual)
452 { 394 {
453 let (_, source_map) = db.body_with_source_map(self.owner); 395 self.diagnostics
454 396 .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required });
455 if let Ok(source_ptr) = source_map.expr_syntax(id) {
456 self.sink.push(MissingOkOrSomeInTailExpr {
457 file: source_ptr.file_id,
458 expr: source_ptr.value,
459 required,
460 });
461 }
462 } 397 }
463 } 398 }
464 399
465 fn validate_missing_tail_expr( 400 fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) {
466 &mut self,
467 body_id: ExprId,
468 possible_tail_id: ExprId,
469 db: &dyn HirDatabase,
470 ) {
471 let mismatch = match self.infer.type_mismatch_for_expr(body_id) { 401 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
472 Some(m) => m, 402 Some(m) => m,
473 None => return, 403 None => return,
@@ -482,12 +412,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
482 return; 412 return;
483 } 413 }
484 414
485 let (_, source_map) = db.body_with_source_map(self.owner); 415 self.diagnostics
486 416 .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id });
487 if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) {
488 self.sink
489 .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value });
490 }
491 } 417 }
492} 418}
493 419
@@ -565,258 +491,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
565 walk(pat, body, infer, &mut has_type_mismatches); 491 walk(pat, body, infer, &mut has_type_mismatches);
566 !has_type_mismatches 492 !has_type_mismatches
567} 493}
568
569#[cfg(test)]
570mod tests {
571 use crate::diagnostics::tests::check_diagnostics;
572
573 #[test]
574 fn simple_free_fn_zero() {
575 check_diagnostics(
576 r#"
577fn zero() {}
578fn f() { zero(1); }
579 //^^^^^^^ Expected 0 arguments, found 1
580"#,
581 );
582
583 check_diagnostics(
584 r#"
585fn zero() {}
586fn f() { zero(); }
587"#,
588 );
589 }
590
591 #[test]
592 fn simple_free_fn_one() {
593 check_diagnostics(
594 r#"
595fn one(arg: u8) {}
596fn f() { one(); }
597 //^^^^^ Expected 1 argument, found 0
598"#,
599 );
600
601 check_diagnostics(
602 r#"
603fn one(arg: u8) {}
604fn f() { one(1); }
605"#,
606 );
607 }
608
609 #[test]
610 fn method_as_fn() {
611 check_diagnostics(
612 r#"
613struct S;
614impl S { fn method(&self) {} }
615
616fn f() {
617 S::method();
618} //^^^^^^^^^^^ Expected 1 argument, found 0
619"#,
620 );
621
622 check_diagnostics(
623 r#"
624struct S;
625impl S { fn method(&self) {} }
626
627fn f() {
628 S::method(&S);
629 S.method();
630}
631"#,
632 );
633 }
634
635 #[test]
636 fn method_with_arg() {
637 check_diagnostics(
638 r#"
639struct S;
640impl S { fn method(&self, arg: u8) {} }
641
642 fn f() {
643 S.method();
644 } //^^^^^^^^^^ Expected 1 argument, found 0
645 "#,
646 );
647
648 check_diagnostics(
649 r#"
650struct S;
651impl S { fn method(&self, arg: u8) {} }
652
653fn f() {
654 S::method(&S, 0);
655 S.method(1);
656}
657"#,
658 );
659 }
660
661 #[test]
662 fn method_unknown_receiver() {
663 // note: this is incorrect code, so there might be errors on this in the
664 // future, but we shouldn't emit an argument count diagnostic here
665 check_diagnostics(
666 r#"
667trait Foo { fn method(&self, arg: usize) {} }
668
669fn f() {
670 let x;
671 x.method();
672}
673"#,
674 );
675 }
676
677 #[test]
678 fn tuple_struct() {
679 check_diagnostics(
680 r#"
681struct Tup(u8, u16);
682fn f() {
683 Tup(0);
684} //^^^^^^ Expected 2 arguments, found 1
685"#,
686 )
687 }
688
689 #[test]
690 fn enum_variant() {
691 check_diagnostics(
692 r#"
693enum En { Variant(u8, u16), }
694fn f() {
695 En::Variant(0);
696} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1
697"#,
698 )
699 }
700
701 #[test]
702 fn enum_variant_type_macro() {
703 check_diagnostics(
704 r#"
705macro_rules! Type {
706 () => { u32 };
707}
708enum Foo {
709 Bar(Type![])
710}
711impl Foo {
712 fn new() {
713 Foo::Bar(0);
714 Foo::Bar(0, 1);
715 //^^^^^^^^^^^^^^ Expected 1 argument, found 2
716 Foo::Bar();
717 //^^^^^^^^^^ Expected 1 argument, found 0
718 }
719}
720 "#,
721 );
722 }
723
724 #[test]
725 fn varargs() {
726 check_diagnostics(
727 r#"
728extern "C" {
729 fn fixed(fixed: u8);
730 fn varargs(fixed: u8, ...);
731 fn varargs2(...);
732}
733
734fn f() {
735 unsafe {
736 fixed(0);
737 fixed(0, 1);
738 //^^^^^^^^^^^ Expected 1 argument, found 2
739 varargs(0);
740 varargs(0, 1);
741 varargs2();
742 varargs2(0);
743 varargs2(0, 1);
744 }
745}
746 "#,
747 )
748 }
749
750 #[test]
751 fn arg_count_lambda() {
752 check_diagnostics(
753 r#"
754fn main() {
755 let f = |()| ();
756 f();
757 //^^^ Expected 1 argument, found 0
758 f(());
759 f((), ());
760 //^^^^^^^^^ Expected 1 argument, found 2
761}
762"#,
763 )
764 }
765
766 #[test]
767 fn cfgd_out_call_arguments() {
768 check_diagnostics(
769 r#"
770struct C(#[cfg(FALSE)] ());
771impl C {
772 fn new() -> Self {
773 Self(
774 #[cfg(FALSE)]
775 (),
776 )
777 }
778
779 fn method(&self) {}
780}
781
782fn main() {
783 C::new().method(#[cfg(FALSE)] 0);
784}
785 "#,
786 );
787 }
788
789 #[test]
790 fn cfgd_out_fn_params() {
791 check_diagnostics(
792 r#"
793fn foo(#[cfg(NEVER)] x: ()) {}
794
795struct S;
796
797impl S {
798 fn method(#[cfg(NEVER)] self) {}
799 fn method2(#[cfg(NEVER)] self, arg: u8) {}
800 fn method3(self, #[cfg(NEVER)] arg: u8) {}
801}
802
803extern "C" {
804 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
805 fn varargs(#[cfg(not(NEVER))] ...);
806}
807
808fn main() {
809 foo();
810 S::method();
811 S::method2(0);
812 S::method3(S);
813 S.method3();
814 unsafe {
815 fixed(0);
816 varargs(1, 2, 3);
817 }
818}
819 "#,
820 )
821 }
822}
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index a9a99f57a..a30e42699 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> {
100 } 100 }
101 101
102 pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { 102 pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat {
103 // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics") 103 // XXX(iDawer): Collecting pattern adjustments feels imprecise to me.
104 // When lowering of & and box patterns are implemented this should be tested
105 // in a manner of `match_ergonomics_issue_9095` test.
106 // Pattern adjustment is part of RFC 2005-match-ergonomics.
104 // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 107 // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
105 let unadjusted_pat = self.lower_pattern_unadjusted(pat); 108 let unadjusted_pat = self.lower_pattern_unadjusted(pat);
106 unadjusted_pat 109 self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
110 unadjusted_pat,
111 |subpattern, ref_ty| Pat {
112 ty: ref_ty.clone(),
113 kind: Box::new(PatKind::Deref { subpattern }),
114 },
115 )
107 } 116 }
108 117
109 fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { 118 fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat {
@@ -355,945 +364,3 @@ impl PatternFoldable for PatKind {
355 } 364 }
356 } 365 }
357} 366}
358
359#[cfg(test)]
360pub(super) mod tests {
361 mod report {
362 use std::any::Any;
363
364 use hir_def::{expr::PatId, DefWithBodyId};
365 use hir_expand::{HirFileId, InFile};
366 use syntax::SyntaxNodePtr;
367
368 use crate::{
369 db::HirDatabase,
370 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
371 };
372
373 /// In tests, match check bails out loudly.
374 /// This helps to catch incorrect tests that pass due to false negatives.
375 pub(crate) fn report_bail_out(
376 db: &dyn HirDatabase,
377 def: DefWithBodyId,
378 pat: PatId,
379 sink: &mut DiagnosticSink,
380 ) {
381 let (_, source_map) = db.body_with_source_map(def);
382 if let Ok(source_ptr) = source_map.pat_syntax(pat) {
383 let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into);
384 sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr });
385 }
386 }
387
388 #[derive(Debug)]
389 struct BailedOut {
390 file: HirFileId,
391 pat_syntax_ptr: SyntaxNodePtr,
392 }
393
394 impl Diagnostic for BailedOut {
395 fn code(&self) -> DiagnosticCode {
396 DiagnosticCode("internal:match-check-bailed-out")
397 }
398 fn message(&self) -> String {
399 format!("Internal: match check bailed out")
400 }
401 fn display_source(&self) -> InFile<SyntaxNodePtr> {
402 InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() }
403 }
404 fn as_any(&self) -> &(dyn Any + Send + 'static) {
405 self
406 }
407 }
408 }
409
410 use crate::diagnostics::tests::check_diagnostics;
411
412 pub(crate) use self::report::report_bail_out;
413
414 #[test]
415 fn empty_tuple() {
416 check_diagnostics(
417 r#"
418fn main() {
419 match () { }
420 //^^ Missing match arm
421 match (()) { }
422 //^^^^ Missing match arm
423
424 match () { _ => (), }
425 match () { () => (), }
426 match (()) { (()) => (), }
427}
428"#,
429 );
430 }
431
432 #[test]
433 fn tuple_of_two_empty_tuple() {
434 check_diagnostics(
435 r#"
436fn main() {
437 match ((), ()) { }
438 //^^^^^^^^ Missing match arm
439
440 match ((), ()) { ((), ()) => (), }
441}
442"#,
443 );
444 }
445
446 #[test]
447 fn boolean() {
448 check_diagnostics(
449 r#"
450fn test_main() {
451 match false { }
452 //^^^^^ Missing match arm
453 match false { true => (), }
454 //^^^^^ Missing match arm
455 match (false, true) {}
456 //^^^^^^^^^^^^^ Missing match arm
457 match (false, true) { (true, true) => (), }
458 //^^^^^^^^^^^^^ Missing match arm
459 match (false, true) {
460 //^^^^^^^^^^^^^ Missing match arm
461 (false, true) => (),
462 (false, false) => (),
463 (true, false) => (),
464 }
465 match (false, true) { (true, _x) => (), }
466 //^^^^^^^^^^^^^ Missing match arm
467
468 match false { true => (), false => (), }
469 match (false, true) {
470 (false, _) => (),
471 (true, false) => (),
472 (_, true) => (),
473 }
474 match (false, true) {
475 (true, true) => (),
476 (true, false) => (),
477 (false, true) => (),
478 (false, false) => (),
479 }
480 match (false, true) {
481 (true, _x) => (),
482 (false, true) => (),
483 (false, false) => (),
484 }
485 match (false, true, false) {
486 (false, ..) => (),
487 (true, ..) => (),
488 }
489 match (false, true, false) {
490 (.., false) => (),
491 (.., true) => (),
492 }
493 match (false, true, false) { (..) => (), }
494}
495"#,
496 );
497 }
498
499 #[test]
500 fn tuple_of_tuple_and_bools() {
501 check_diagnostics(
502 r#"
503fn main() {
504 match (false, ((), false)) {}
505 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
506 match (false, ((), false)) { (true, ((), true)) => (), }
507 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
508 match (false, ((), false)) { (true, _) => (), }
509 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
510
511 match (false, ((), false)) {
512 (true, ((), true)) => (),
513 (true, ((), false)) => (),
514 (false, ((), true)) => (),
515 (false, ((), false)) => (),
516 }
517 match (false, ((), false)) {
518 (true, ((), true)) => (),
519 (true, ((), false)) => (),
520 (false, _) => (),
521 }
522}
523"#,
524 );
525 }
526
527 #[test]
528 fn enums() {
529 check_diagnostics(
530 r#"
531enum Either { A, B, }
532
533fn main() {
534 match Either::A { }
535 //^^^^^^^^^ Missing match arm
536 match Either::B { Either::A => (), }
537 //^^^^^^^^^ Missing match arm
538
539 match &Either::B {
540 //^^^^^^^^^^ Missing match arm
541 Either::A => (),
542 }
543
544 match Either::B {
545 Either::A => (), Either::B => (),
546 }
547 match &Either::B {
548 Either::A => (), Either::B => (),
549 }
550}
551"#,
552 );
553 }
554
555 #[test]
556 fn enum_containing_bool() {
557 check_diagnostics(
558 r#"
559enum Either { A(bool), B }
560
561fn main() {
562 match Either::B { }
563 //^^^^^^^^^ Missing match arm
564 match Either::B {
565 //^^^^^^^^^ Missing match arm
566 Either::A(true) => (), Either::B => ()
567 }
568
569 match Either::B {
570 Either::A(true) => (),
571 Either::A(false) => (),
572 Either::B => (),
573 }
574 match Either::B {
575 Either::B => (),
576 _ => (),
577 }
578 match Either::B {
579 Either::A(_) => (),
580 Either::B => (),
581 }
582
583}
584 "#,
585 );
586 }
587
588 #[test]
589 fn enum_different_sizes() {
590 check_diagnostics(
591 r#"
592enum Either { A(bool), B(bool, bool) }
593
594fn main() {
595 match Either::A(false) {
596 //^^^^^^^^^^^^^^^^ Missing match arm
597 Either::A(_) => (),
598 Either::B(false, _) => (),
599 }
600
601 match Either::A(false) {
602 Either::A(_) => (),
603 Either::B(true, _) => (),
604 Either::B(false, _) => (),
605 }
606 match Either::A(false) {
607 Either::A(true) | Either::A(false) => (),
608 Either::B(true, _) => (),
609 Either::B(false, _) => (),
610 }
611}
612"#,
613 );
614 }
615
616 #[test]
617 fn tuple_of_enum_no_diagnostic() {
618 check_diagnostics(
619 r#"
620enum Either { A(bool), B(bool, bool) }
621enum Either2 { C, D }
622
623fn main() {
624 match (Either::A(false), Either2::C) {
625 (Either::A(true), _) | (Either::A(false), _) => (),
626 (Either::B(true, _), Either2::C) => (),
627 (Either::B(false, _), Either2::C) => (),
628 (Either::B(_, _), Either2::D) => (),
629 }
630}
631"#,
632 );
633 }
634
635 #[test]
636 fn or_pattern_no_diagnostic() {
637 check_diagnostics(
638 r#"
639enum Either {A, B}
640
641fn main() {
642 match (Either::A, Either::B) {
643 (Either::A | Either::B, _) => (),
644 }
645}"#,
646 )
647 }
648
649 #[test]
650 fn mismatched_types() {
651 // Match statements with arms that don't match the
652 // expression pattern do not fire this diagnostic.
653 check_diagnostics(
654 r#"
655enum Either { A, B }
656enum Either2 { C, D }
657
658fn main() {
659 match Either::A {
660 Either2::C => (),
661 // ^^^^^^^^^^ Internal: match check bailed out
662 Either2::D => (),
663 }
664 match (true, false) {
665 (true, false, true) => (),
666 // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out
667 (true) => (),
668 }
669 match (true, false) { (true,) => {} }
670 // ^^^^^^^ Internal: match check bailed out
671 match (0) { () => () }
672 // ^^ Internal: match check bailed out
673 match Unresolved::Bar { Unresolved::Baz => () }
674}
675 "#,
676 );
677 }
678
679 #[test]
680 fn mismatched_types_in_or_patterns() {
681 check_diagnostics(
682 r#"
683fn main() {
684 match false { true | () => {} }
685 // ^^^^^^^^^ Internal: match check bailed out
686 match (false,) { (true | (),) => {} }
687 // ^^^^^^^^^^^^ Internal: match check bailed out
688}
689"#,
690 );
691 }
692
693 #[test]
694 fn malformed_match_arm_tuple_enum_missing_pattern() {
695 // We are testing to be sure we don't panic here when the match
696 // arm `Either::B` is missing its pattern.
697 check_diagnostics(
698 r#"
699enum Either { A, B(u32) }
700
701fn main() {
702 match Either::A {
703 Either::A => (),
704 Either::B() => (),
705 }
706}
707"#,
708 );
709 }
710
711 #[test]
712 fn malformed_match_arm_extra_fields() {
713 check_diagnostics(
714 r#"
715enum A { B(isize, isize), C }
716fn main() {
717 match A::B(1, 2) {
718 A::B(_, _, _) => (),
719 // ^^^^^^^^^^^^^ Internal: match check bailed out
720 }
721 match A::B(1, 2) {
722 A::C(_) => (),
723 // ^^^^^^^ Internal: match check bailed out
724 }
725}
726"#,
727 );
728 }
729
730 #[test]
731 fn expr_diverges() {
732 check_diagnostics(
733 r#"
734enum Either { A, B }
735
736fn main() {
737 match loop {} {
738 Either::A => (),
739 // ^^^^^^^^^ Internal: match check bailed out
740 Either::B => (),
741 }
742 match loop {} {
743 Either::A => (),
744 // ^^^^^^^^^ Internal: match check bailed out
745 }
746 match loop { break Foo::A } {
747 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
748 Either::A => (),
749 }
750 match loop { break Foo::A } {
751 Either::A => (),
752 Either::B => (),
753 }
754}
755"#,
756 );
757 }
758
759 #[test]
760 fn expr_partially_diverges() {
761 check_diagnostics(
762 r#"
763enum Either<T> { A(T), B }
764
765fn foo() -> Either<!> { Either::B }
766fn main() -> u32 {
767 match foo() {
768 Either::A(val) => val,
769 Either::B => 0,
770 }
771}
772"#,
773 );
774 }
775
776 #[test]
777 fn enum_record() {
778 check_diagnostics(
779 r#"
780enum Either { A { foo: bool }, B }
781
782fn main() {
783 let a = Either::A { foo: true };
784 match a { }
785 //^ Missing match arm
786 match a { Either::A { foo: true } => () }
787 //^ Missing match arm
788 match a {
789 Either::A { } => (),
790 //^^^^^^^^^ Missing structure fields:
791 // | - foo
792 Either::B => (),
793 }
794 match a {
795 //^ Missing match arm
796 Either::A { } => (),
797 } //^^^^^^^^^ Missing structure fields:
798 // | - foo
799
800 match a {
801 Either::A { foo: true } => (),
802 Either::A { foo: false } => (),
803 Either::B => (),
804 }
805 match a {
806 Either::A { foo: _ } => (),
807 Either::B => (),
808 }
809}
810"#,
811 );
812 }
813
814 #[test]
815 fn enum_record_fields_out_of_order() {
816 check_diagnostics(
817 r#"
818enum Either {
819 A { foo: bool, bar: () },
820 B,
821}
822
823fn main() {
824 let a = Either::A { foo: true, bar: () };
825 match a {
826 //^ Missing match arm
827 Either::A { bar: (), foo: false } => (),
828 Either::A { foo: true, bar: () } => (),
829 }
830
831 match a {
832 Either::A { bar: (), foo: false } => (),
833 Either::A { foo: true, bar: () } => (),
834 Either::B => (),
835 }
836}
837"#,
838 );
839 }
840
841 #[test]
842 fn enum_record_ellipsis() {
843 check_diagnostics(
844 r#"
845enum Either {
846 A { foo: bool, bar: bool },
847 B,
848}
849
850fn main() {
851 let a = Either::B;
852 match a {
853 //^ Missing match arm
854 Either::A { foo: true, .. } => (),
855 Either::B => (),
856 }
857 match a {
858 //^ Missing match arm
859 Either::A { .. } => (),
860 }
861
862 match a {
863 Either::A { foo: true, .. } => (),
864 Either::A { foo: false, .. } => (),
865 Either::B => (),
866 }
867
868 match a {
869 Either::A { .. } => (),
870 Either::B => (),
871 }
872}
873"#,
874 );
875 }
876
877 #[test]
878 fn enum_tuple_partial_ellipsis() {
879 check_diagnostics(
880 r#"
881enum Either {
882 A(bool, bool, bool, bool),
883 B,
884}
885
886fn main() {
887 match Either::B {
888 //^^^^^^^^^ Missing match arm
889 Either::A(true, .., true) => (),
890 Either::A(true, .., false) => (),
891 Either::A(false, .., false) => (),
892 Either::B => (),
893 }
894 match Either::B {
895 //^^^^^^^^^ Missing match arm
896 Either::A(true, .., true) => (),
897 Either::A(true, .., false) => (),
898 Either::A(.., true) => (),
899 Either::B => (),
900 }
901
902 match Either::B {
903 Either::A(true, .., true) => (),
904 Either::A(true, .., false) => (),
905 Either::A(false, .., true) => (),
906 Either::A(false, .., false) => (),
907 Either::B => (),
908 }
909 match Either::B {
910 Either::A(true, .., true) => (),
911 Either::A(true, .., false) => (),
912 Either::A(.., true) => (),
913 Either::A(.., false) => (),
914 Either::B => (),
915 }
916}
917"#,
918 );
919 }
920
921 #[test]
922 fn never() {
923 check_diagnostics(
924 r#"
925enum Never {}
926
927fn enum_(never: Never) {
928 match never {}
929}
930fn enum_ref(never: &Never) {
931 match never {}
932 //^^^^^ Missing match arm
933}
934fn bang(never: !) {
935 match never {}
936}
937"#,
938 );
939 }
940
941 #[test]
942 fn unknown_type() {
943 check_diagnostics(
944 r#"
945enum Option<T> { Some(T), None }
946
947fn main() {
948 // `Never` is deliberately not defined so that it's an uninferred type.
949 match Option::<Never>::None {
950 None => (),
951 Some(never) => match never {},
952 // ^^^^^^^^^^^ Internal: match check bailed out
953 }
954 match Option::<Never>::None {
955 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
956 Option::Some(_never) => {},
957 }
958}
959"#,
960 );
961 }
962
963 #[test]
964 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
965 check_diagnostics(
966 r#"
967fn main() {
968 match (false, true, false) {
969 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
970 (false, ..) => (),
971 }
972}"#,
973 );
974 }
975
976 #[test]
977 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
978 check_diagnostics(
979 r#"
980fn main() {
981 match (false, true, false) {
982 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
983 (.., false) => (),
984 }
985}"#,
986 );
987 }
988
989 #[test]
990 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
991 check_diagnostics(
992 r#"
993fn main() {
994 match (false, true, false) {
995 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
996 (true, .., false) => (),
997 }
998}"#,
999 );
1000 }
1001
1002 #[test]
1003 fn record_struct() {
1004 check_diagnostics(
1005 r#"struct Foo { a: bool }
1006fn main(f: Foo) {
1007 match f {}
1008 //^ Missing match arm
1009 match f { Foo { a: true } => () }
1010 //^ Missing match arm
1011 match &f { Foo { a: true } => () }
1012 //^^ Missing match arm
1013 match f { Foo { a: _ } => () }
1014 match f {
1015 Foo { a: true } => (),
1016 Foo { a: false } => (),
1017 }
1018 match &f {
1019 Foo { a: true } => (),
1020 Foo { a: false } => (),
1021 }
1022}
1023"#,
1024 );
1025 }
1026
1027 #[test]
1028 fn tuple_struct() {
1029 check_diagnostics(
1030 r#"struct Foo(bool);
1031fn main(f: Foo) {
1032 match f {}
1033 //^ Missing match arm
1034 match f { Foo(true) => () }
1035 //^ Missing match arm
1036 match f {
1037 Foo(true) => (),
1038 Foo(false) => (),
1039 }
1040}
1041"#,
1042 );
1043 }
1044
1045 #[test]
1046 fn unit_struct() {
1047 check_diagnostics(
1048 r#"struct Foo;
1049fn main(f: Foo) {
1050 match f {}
1051 //^ Missing match arm
1052 match f { Foo => () }
1053}
1054"#,
1055 );
1056 }
1057
1058 #[test]
1059 fn record_struct_ellipsis() {
1060 check_diagnostics(
1061 r#"struct Foo { foo: bool, bar: bool }
1062fn main(f: Foo) {
1063 match f { Foo { foo: true, .. } => () }
1064 //^ Missing match arm
1065 match f {
1066 //^ Missing match arm
1067 Foo { foo: true, .. } => (),
1068 Foo { bar: false, .. } => ()
1069 }
1070 match f { Foo { .. } => () }
1071 match f {
1072 Foo { foo: true, .. } => (),
1073 Foo { foo: false, .. } => ()
1074 }
1075}
1076"#,
1077 );
1078 }
1079
1080 #[test]
1081 fn internal_or() {
1082 check_diagnostics(
1083 r#"
1084fn main() {
1085 enum Either { A(bool), B }
1086 match Either::B {
1087 //^^^^^^^^^ Missing match arm
1088 Either::A(true | false) => (),
1089 }
1090}
1091"#,
1092 );
1093 }
1094
1095 #[test]
1096 fn no_panic_at_unimplemented_subpattern_type() {
1097 check_diagnostics(
1098 r#"
1099struct S { a: char}
1100fn main(v: S) {
1101 match v { S{ a } => {} }
1102 match v { S{ a: _x } => {} }
1103 match v { S{ a: 'a' } => {} }
1104 //^^^^^^^^^^^ Internal: match check bailed out
1105 match v { S{..} => {} }
1106 match v { _ => {} }
1107 match v { }
1108 //^ Missing match arm
1109}
1110"#,
1111 );
1112 }
1113
1114 #[test]
1115 fn binding() {
1116 check_diagnostics(
1117 r#"
1118fn main() {
1119 match true {
1120 _x @ true => {}
1121 false => {}
1122 }
1123 match true { _x @ true => {} }
1124 //^^^^ Missing match arm
1125}
1126"#,
1127 );
1128 }
1129
1130 #[test]
1131 fn binding_ref_has_correct_type() {
1132 // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
1133 // If that's not true match checking will panic with "incompatible constructors"
1134 // FIXME: make facilities to test this directly like `tests::check_infer(..)`
1135 check_diagnostics(
1136 r#"
1137enum Foo { A }
1138fn main() {
1139 // FIXME: this should not bail out but current behavior is such as the old algorithm.
1140 // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
1141 match Foo::A {
1142 ref _x => {}
1143 // ^^^^^^ Internal: match check bailed out
1144 Foo::A => {}
1145 }
1146 match (true,) {
1147 (ref _x,) => {}
1148 (true,) => {}
1149 }
1150}
1151"#,
1152 );
1153 }
1154
1155 #[test]
1156 fn enum_non_exhaustive() {
1157 check_diagnostics(
1158 r#"
1159//- /lib.rs crate:lib
1160#[non_exhaustive]
1161pub enum E { A, B }
1162fn _local() {
1163 match E::A { _ => {} }
1164 match E::A {
1165 E::A => {}
1166 E::B => {}
1167 }
1168 match E::A {
1169 E::A | E::B => {}
1170 }
1171}
1172
1173//- /main.rs crate:main deps:lib
1174use lib::E;
1175fn main() {
1176 match E::A { _ => {} }
1177 match E::A {
1178 //^^^^ Missing match arm
1179 E::A => {}
1180 E::B => {}
1181 }
1182 match E::A {
1183 //^^^^ Missing match arm
1184 E::A | E::B => {}
1185 }
1186}
1187"#,
1188 );
1189 }
1190
1191 #[test]
1192 fn match_guard() {
1193 check_diagnostics(
1194 r#"
1195fn main() {
1196 match true {
1197 true if false => {}
1198 true => {}
1199 false => {}
1200 }
1201 match true {
1202 //^^^^ Missing match arm
1203 true if false => {}
1204 false => {}
1205}
1206"#,
1207 );
1208 }
1209
1210 #[test]
1211 fn pattern_type_is_of_substitution() {
1212 cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
1213 check_diagnostics(
1214 r#"
1215struct Foo<T>(T);
1216struct Bar;
1217fn main() {
1218 match Foo(Bar) {
1219 _ | Foo(Bar) => {}
1220 }
1221}
1222"#,
1223 );
1224 }
1225
1226 #[test]
1227 fn record_struct_no_such_field() {
1228 check_diagnostics(
1229 r#"
1230struct Foo { }
1231fn main(f: Foo) {
1232 match f { Foo { bar } => () }
1233 // ^^^^^^^^^^^ Internal: match check bailed out
1234}
1235"#,
1236 );
1237 }
1238
1239 mod false_negatives {
1240 //! The implementation of match checking here is a work in progress. As we roll this out, we
1241 //! prefer false negatives to false positives (ideally there would be no false positives). This
1242 //! test module should document known false negatives. Eventually we will have a complete
1243 //! implementation of match checking and this module will be empty.
1244 //!
1245 //! The reasons for documenting known false negatives:
1246 //!
1247 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
1248 //! 2. It ensures the code doesn't panic when handling these cases.
1249 use super::*;
1250
1251 #[test]
1252 fn integers() {
1253 // We don't currently check integer exhaustiveness.
1254 check_diagnostics(
1255 r#"
1256fn main() {
1257 match 5 {
1258 10 => (),
1259 // ^^ Internal: match check bailed out
1260 11..20 => (),
1261 }
1262}
1263"#,
1264 );
1265 }
1266
1267 #[test]
1268 fn reference_patterns_at_top_level() {
1269 check_diagnostics(
1270 r#"
1271fn main() {
1272 match &false {
1273 &true => {}
1274 // ^^^^^ Internal: match check bailed out
1275 }
1276}
1277 "#,
1278 );
1279 }
1280
1281 #[test]
1282 fn reference_patterns_in_fields() {
1283 check_diagnostics(
1284 r#"
1285fn main() {
1286 match (&false,) {
1287 (true,) => {}
1288 // ^^^^^^^ Internal: match check bailed out
1289 }
1290 match (&false,) {
1291 (&true,) => {}
1292 // ^^^^^^^^ Internal: match check bailed out
1293 }
1294}
1295 "#,
1296 );
1297 }
1298 }
1299}
diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
index 1f4219b42..471cd4921 100644
--- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -528,7 +528,7 @@ impl SplitWildcard {
528 smallvec![NonExhaustive] 528 smallvec![NonExhaustive]
529 } 529 }
530 TyKind::Never => SmallVec::new(), 530 TyKind::Never => SmallVec::new(),
531 _ if cx.is_uninhabited(&pcx.ty) => SmallVec::new(), 531 _ if cx.is_uninhabited(pcx.ty) => SmallVec::new(),
532 TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], 532 TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single],
533 // This type is one for which we cannot list constructors, like `str` or `f64`. 533 // This type is one for which we cannot list constructors, like `str` or `f64`.
534 _ => smallvec![NonExhaustive], 534 _ => smallvec![NonExhaustive],
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
index 83b094a89..8451f9df5 100644
--- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
@@ -1,5 +1,5 @@
1//! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) 1//! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22)
2//! https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs 2//! <https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs>
3//! 3//!
4//! ----- 4//! -----
5//! 5//!
@@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> {
295 pub(crate) db: &'a dyn HirDatabase, 295 pub(crate) db: &'a dyn HirDatabase,
296 /// Lowered patterns from arms plus generated by the check. 296 /// Lowered patterns from arms plus generated by the check.
297 pub(crate) pattern_arena: &'a RefCell<PatternArena>, 297 pub(crate) pattern_arena: &'a RefCell<PatternArena>,
298 pub(crate) eprint_panic_context: &'a dyn Fn(), 298 pub(crate) panic_context: &'a dyn Fn() -> String,
299} 299}
300 300
301impl<'a> MatchCheckCtx<'a> { 301impl<'a> MatchCheckCtx<'a> {
@@ -331,8 +331,7 @@ impl<'a> MatchCheckCtx<'a> {
331 331
332 #[track_caller] 332 #[track_caller]
333 pub(super) fn bug(&self, info: &str) -> ! { 333 pub(super) fn bug(&self, info: &str) -> ! {
334 (self.eprint_panic_context)(); 334 panic!("bug: {}\n{}", info, (self.panic_context)());
335 panic!("bug: {}", info);
336 } 335 }
337} 336}
338 337
@@ -646,7 +645,7 @@ impl SubPatSet {
646 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { 645 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => {
647 s_set.retain(|i, s_sub_set| { 646 s_set.retain(|i, s_sub_set| {
648 // Missing entries count as full. 647 // Missing entries count as full.
649 let o_sub_set = o_set.remove(&i).unwrap_or(Full); 648 let o_sub_set = o_set.remove(i).unwrap_or(Full);
650 s_sub_set.union(o_sub_set); 649 s_sub_set.union(o_sub_set);
651 // We drop full entries. 650 // We drop full entries.
652 !s_sub_set.is_full() 651 !s_sub_set.is_full()
@@ -657,7 +656,7 @@ impl SubPatSet {
657 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { 656 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => {
658 s_set.retain(|i, s_sub_set| { 657 s_set.retain(|i, s_sub_set| {
659 // Missing entries count as empty. 658 // Missing entries count as empty.
660 let o_sub_set = o_set.remove(&i).unwrap_or(Empty); 659 let o_sub_set = o_set.remove(i).unwrap_or(Empty);
661 s_sub_set.union(o_sub_set); 660 s_sub_set.union(o_sub_set);
662 // We drop empty entries. 661 // We drop empty entries.
663 !s_sub_set.is_empty() 662 !s_sub_set.is_empty()
@@ -899,7 +898,7 @@ impl Usefulness {
899 } else { 898 } else {
900 witnesses 899 witnesses
901 .into_iter() 900 .into_iter()
902 .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) 901 .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns))
903 .collect() 902 .collect()
904 }; 903 };
905 WithWitnesses(new_witnesses) 904 WithWitnesses(new_witnesses)
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index c3c483425..777f347b8 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -1,8 +1,6 @@
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing 1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks. 2//! unsafe blocks.
3 3
4use std::sync::Arc;
5
6use hir_def::{ 4use hir_def::{
7 body::Body, 5 body::Body,
8 expr::{Expr, ExprId, UnaryOp}, 6 expr::{Expr, ExprId, UnaryOp},
@@ -10,60 +8,32 @@ use hir_def::{
10 DefWithBodyId, 8 DefWithBodyId,
11}; 9};
12 10
13use crate::{ 11use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16};
17 12
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 13pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
19 owner: DefWithBodyId, 14 let infer = db.infer(def);
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23 15
24impl<'a, 'b> UnsafeValidator<'a, 'b> { 16 let is_unsafe = match def {
25 pub(super) fn new( 17 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
26 owner: DefWithBodyId, 18 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
27 infer: Arc<InferenceResult>, 19 };
28 sink: &'a mut DiagnosticSink<'b>, 20 if is_unsafe {
29 ) -> UnsafeValidator<'a, 'b> { 21 return Vec::new();
30 UnsafeValidator { owner, infer, sink }
31 } 22 }
32 23
33 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 24 unsafe_expressions(db, &infer, def)
34 let def = self.owner; 25 .into_iter()
35 let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); 26 .filter(|it| !it.inside_unsafe_block)
36 let is_unsafe = match self.owner { 27 .map(|it| it.expr)
37 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), 28 .collect()
38 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
39 };
40 if is_unsafe
41 || unsafe_expressions
42 .iter()
43 .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block)
44 .count()
45 == 0
46 {
47 return;
48 }
49
50 let (_, body_source) = db.body_with_source_map(def);
51 for unsafe_expr in unsafe_expressions {
52 if !unsafe_expr.inside_unsafe_block {
53 if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) {
54 self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
55 }
56 }
57 }
58 }
59} 29}
60 30
61pub(crate) struct UnsafeExpr { 31struct UnsafeExpr {
62 pub(crate) expr: ExprId, 32 pub(crate) expr: ExprId,
63 pub(crate) inside_unsafe_block: bool, 33 pub(crate) inside_unsafe_block: bool,
64} 34}
65 35
66pub(crate) fn unsafe_expressions( 36fn unsafe_expressions(
67 db: &dyn HirDatabase, 37 db: &dyn HirDatabase,
68 infer: &InferenceResult, 38 infer: &InferenceResult,
69 def: DefWithBodyId, 39 def: DefWithBodyId,
@@ -126,92 +96,3 @@ fn walk_unsafe(
126 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); 96 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
127 }); 97 });
128} 98}
129
130#[cfg(test)]
131mod tests {
132 use crate::diagnostics::tests::check_diagnostics;
133
134 #[test]
135 fn missing_unsafe_diagnostic_with_raw_ptr() {
136 check_diagnostics(
137 r#"
138fn main() {
139 let x = &5 as *const usize;
140 unsafe { let y = *x; }
141 let z = *x;
142} //^^ This operation is unsafe and requires an unsafe function or block
143"#,
144 )
145 }
146
147 #[test]
148 fn missing_unsafe_diagnostic_with_unsafe_call() {
149 check_diagnostics(
150 r#"
151struct HasUnsafe;
152
153impl HasUnsafe {
154 unsafe fn unsafe_fn(&self) {
155 let x = &5 as *const usize;
156 let y = *x;
157 }
158}
159
160unsafe fn unsafe_fn() {
161 let x = &5 as *const usize;
162 let y = *x;
163}
164
165fn main() {
166 unsafe_fn();
167 //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
168 HasUnsafe.unsafe_fn();
169 //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
170 unsafe {
171 unsafe_fn();
172 HasUnsafe.unsafe_fn();
173 }
174}
175"#,
176 );
177 }
178
179 #[test]
180 fn missing_unsafe_diagnostic_with_static_mut() {
181 check_diagnostics(
182 r#"
183struct Ty {
184 a: u8,
185}
186
187static mut STATIC_MUT: Ty = Ty { a: 0 };
188
189fn main() {
190 let x = STATIC_MUT.a;
191 //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
192 unsafe {
193 let x = STATIC_MUT.a;
194 }
195}
196"#,
197 );
198 }
199
200 #[test]
201 fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
202 check_diagnostics(
203 r#"
204extern "rust-intrinsic" {
205 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
206 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
207}
208
209fn main() {
210 let _ = bitreverse(12);
211 let _ = floorf32(12.0);
212 //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
213}
214"#,
215 );
216 }
217}
diff --git a/crates/hir_ty/src/diagnostics_sink.rs b/crates/hir_ty/src/diagnostics_sink.rs
deleted file mode 100644
index 084fa8b06..000000000
--- a/crates/hir_ty/src/diagnostics_sink.rs
+++ /dev/null
@@ -1,109 +0,0 @@
1//! Semantic errors and warnings.
2//!
3//! The `Diagnostic` trait defines a trait object which can represent any
4//! diagnostic.
5//!
6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating
7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
8//! Diagnostic` or to any concrete diagnostic (downcasting is used internally).
9//!
10//! Because diagnostics store file offsets, it's a bad idea to store them
11//! directly in salsa. For this reason, every hir subsytem defines it's own
12//! strongly-typed closed set of diagnostics which use hir ids internally, are
13//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a
14//! subsystem provides a separate, non-query-based API which can walk all stored
15//! values and transform them into instances of `Diagnostic`.
16
17use std::{any::Any, fmt};
18
19use hir_expand::InFile;
20use syntax::SyntaxNodePtr;
21
22#[derive(Copy, Clone, Debug, PartialEq)]
23pub struct DiagnosticCode(pub &'static str);
24
25impl DiagnosticCode {
26 pub fn as_str(&self) -> &str {
27 self.0
28 }
29}
30
31pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
32 fn code(&self) -> DiagnosticCode;
33 fn message(&self) -> String;
34 /// Source element that triggered the diagnostics.
35 ///
36 /// Note that this should reflect "semantics", rather than specific span we
37 /// want to highlight. When rendering the diagnostics into an error message,
38 /// the IDE will fetch the `SyntaxNode` and will narrow the span
39 /// appropriately.
40 fn display_source(&self) -> InFile<SyntaxNodePtr>;
41 fn as_any(&self) -> &(dyn Any + Send + 'static);
42 fn is_experimental(&self) -> bool {
43 false
44 }
45}
46
47pub struct DiagnosticSink<'a> {
48 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
49 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
50 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
51}
52
53impl<'a> DiagnosticSink<'a> {
54 pub fn push(&mut self, d: impl Diagnostic) {
55 let d: &dyn Diagnostic = &d;
56 self._push(d);
57 }
58
59 fn _push(&mut self, d: &dyn Diagnostic) {
60 for filter in &mut self.filters {
61 if !filter(d) {
62 return;
63 }
64 }
65 for cb in &mut self.callbacks {
66 match cb(d) {
67 Ok(()) => return,
68 Err(()) => (),
69 }
70 }
71 (self.default_callback)(d)
72 }
73}
74
75pub struct DiagnosticSinkBuilder<'a> {
76 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
77 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
78}
79
80impl<'a> DiagnosticSinkBuilder<'a> {
81 pub fn new() -> Self {
82 Self { callbacks: Vec::new(), filters: Vec::new() }
83 }
84
85 pub fn filter<F: FnMut(&dyn Diagnostic) -> bool + 'a>(mut self, cb: F) -> Self {
86 self.filters.push(Box::new(cb));
87 self
88 }
89
90 pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
91 let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() {
92 Some(d) => {
93 cb(d);
94 Ok(())
95 }
96 None => Err(()),
97 };
98 self.callbacks.push(Box::new(cb));
99 self
100 }
101
102 pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> {
103 DiagnosticSink {
104 callbacks: self.callbacks,
105 filters: self.filters,
106 default_callback: Box::new(default_callback),
107 }
108 }
109}
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 7a4268819..63f37c0ab 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -35,11 +35,9 @@ use stdx::impl_from;
35use syntax::SmolStr; 35use syntax::SmolStr;
36 36
37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; 37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
38use crate::diagnostics_sink::DiagnosticSink;
39use crate::{ 38use crate::{
40 db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, 39 db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy,
41 lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, 40 Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
42 TyBuilder, TyExt, TyKind,
43}; 41};
44 42
45// This lint has a false positive here. See the link below for details. 43// This lint has a false positive here. See the link below for details.
@@ -80,7 +78,7 @@ enum ExprOrPatId {
80impl_from!(ExprId, PatId for ExprOrPatId); 78impl_from!(ExprId, PatId for ExprOrPatId);
81 79
82/// Binding modes inferred for patterns. 80/// Binding modes inferred for patterns.
83/// https://doc.rust-lang.org/reference/patterns.html#binding-modes 81/// <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
84#[derive(Copy, Clone, Debug, Eq, PartialEq)] 82#[derive(Copy, Clone, Debug, Eq, PartialEq)]
85enum BindingMode { 83enum BindingMode {
86 Move, 84 Move,
@@ -111,6 +109,12 @@ pub(crate) struct InferOk {
111pub(crate) struct TypeError; 109pub(crate) struct TypeError;
112pub(crate) type InferResult = Result<InferOk, TypeError>; 110pub(crate) type InferResult = Result<InferOk, TypeError>;
113 111
112#[derive(Debug, PartialEq, Eq, Clone)]
113pub enum InferenceDiagnostic {
114 NoSuchField { expr: ExprId },
115 BreakOutsideOfLoop { expr: ExprId },
116}
117
114/// A mismatch between an expected and an inferred type. 118/// A mismatch between an expected and an inferred type.
115#[derive(Clone, PartialEq, Eq, Debug, Hash)] 119#[derive(Clone, PartialEq, Eq, Debug, Hash)]
116pub struct TypeMismatch { 120pub struct TypeMismatch {
@@ -140,7 +144,7 @@ pub struct InferenceResult {
140 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, 144 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
141 /// For each associated item record what it resolves to 145 /// For each associated item record what it resolves to
142 assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, 146 assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
143 diagnostics: Vec<InferenceDiagnostic>, 147 pub diagnostics: Vec<InferenceDiagnostic>,
144 pub type_of_expr: ArenaMap<ExprId, Ty>, 148 pub type_of_expr: ArenaMap<ExprId, Ty>,
145 /// For each pattern record the type it resolves to. 149 /// For each pattern record the type it resolves to.
146 /// 150 ///
@@ -150,6 +154,8 @@ pub struct InferenceResult {
150 type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, 154 type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
151 /// Interned Unknown to return references to. 155 /// Interned Unknown to return references to.
152 standard_types: InternedStandardTypes, 156 standard_types: InternedStandardTypes,
157 /// Stores the types which were implicitly dereferenced in pattern binding modes.
158 pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
153} 159}
154 160
155impl InferenceResult { 161impl InferenceResult {
@@ -189,14 +195,6 @@ impl InferenceResult {
189 _ => None, 195 _ => None,
190 }) 196 })
191 } 197 }
192 pub fn add_diagnostics(
193 &self,
194 db: &dyn HirDatabase,
195 owner: DefWithBodyId,
196 sink: &mut DiagnosticSink,
197 ) {
198 self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink))
199 }
200} 198}
201 199
202impl Index<ExprId> for InferenceResult { 200impl Index<ExprId> for InferenceResult {
@@ -763,6 +761,38 @@ impl Expectation {
763 Expectation::RValueLikeUnsized(_) | Expectation::None => None, 761 Expectation::RValueLikeUnsized(_) | Expectation::None => None,
764 } 762 }
765 } 763 }
764
765 /// Comment copied from rustc:
766 /// Disregard "castable to" expectations because they
767 /// can lead us astray. Consider for example `if cond
768 /// {22} else {c} as u8` -- if we propagate the
769 /// "castable to u8" constraint to 22, it will pick the
770 /// type 22u8, which is overly constrained (c might not
771 /// be a u8). In effect, the problem is that the
772 /// "castable to" expectation is not the tightest thing
773 /// we can say, so we want to drop it in this case.
774 /// The tightest thing we can say is "must unify with
775 /// else branch". Note that in the case of a "has type"
776 /// constraint, this limitation does not hold.
777 ///
778 /// If the expected type is just a type variable, then don't use
779 /// an expected type. Otherwise, we might write parts of the type
780 /// when checking the 'then' block which are incompatible with the
781 /// 'else' branch.
782 fn adjust_for_branches(&self, table: &mut unify::InferenceTable) -> Expectation {
783 match self {
784 Expectation::HasType(ety) => {
785 let ety = table.resolve_ty_shallow(ety);
786 if !ety.is_ty_var() {
787 Expectation::HasType(ety)
788 } else {
789 Expectation::None
790 }
791 }
792 Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()),
793 _ => Expectation::None,
794 }
795 }
766} 796}
767 797
768#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 798#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -802,43 +832,3 @@ impl std::ops::BitOrAssign for Diverges {
802 *self = *self | other; 832 *self = *self | other;
803 } 833 }
804} 834}
805
806mod diagnostics {
807 use hir_def::{expr::ExprId, DefWithBodyId};
808
809 use crate::{
810 db::HirDatabase,
811 diagnostics::{BreakOutsideOfLoop, NoSuchField},
812 diagnostics_sink::DiagnosticSink,
813 };
814
815 #[derive(Debug, PartialEq, Eq, Clone)]
816 pub(super) enum InferenceDiagnostic {
817 NoSuchField { expr: ExprId },
818 BreakOutsideOfLoop { expr: ExprId },
819 }
820
821 impl InferenceDiagnostic {
822 pub(super) fn add_to(
823 &self,
824 db: &dyn HirDatabase,
825 owner: DefWithBodyId,
826 sink: &mut DiagnosticSink,
827 ) {
828 match self {
829 InferenceDiagnostic::NoSuchField { expr } => {
830 let (_, source_map) = db.body_with_source_map(owner);
831 let field = source_map.field_syntax(*expr);
832 sink.push(NoSuchField { file: field.file_id, field: field.value })
833 }
834 InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
835 let (_, source_map) = db.body_with_source_map(owner);
836 let ptr = source_map
837 .expr_syntax(*expr)
838 .expect("break outside of loop in synthetic syntax");
839 sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
840 }
841 }
842 }
843 }
844}
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index 03b97e7db..4b7f31521 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -2,8 +2,8 @@
2//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions 2//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions
3//! like going from `&Vec<T>` to `&[T]`. 3//! like going from `&Vec<T>` to `&[T]`.
4//! 4//!
5//! See https://doc.rust-lang.org/nomicon/coercions.html and 5//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
6//! librustc_typeck/check/coercion.rs. 6//! `librustc_typeck/check/coercion.rs`.
7 7
8use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; 8use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
9use hir_def::{expr::ExprId, lang_item::LangItemTarget}; 9use hir_def::{expr::ExprId, lang_item::LangItemTarget};
@@ -109,7 +109,7 @@ impl<'a> InferenceContext<'a> {
109 } 109 }
110 110
111 // Consider coercing the subtype to a DST 111 // Consider coercing the subtype to a DST
112 if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { 112 if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) {
113 return Ok(ret); 113 return Ok(ret);
114 } 114 }
115 115
@@ -331,7 +331,7 @@ impl<'a> InferenceContext<'a> {
331 331
332 /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` 332 /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
333 /// 333 ///
334 /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html 334 /// See: <https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html>
335 fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { 335 fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult {
336 // These 'if' statements require some explanation. 336 // These 'if' statements require some explanation.
337 // The `CoerceUnsized` trait is special - it is only 337 // The `CoerceUnsized` trait is special - it is only
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index f73bf43b2..5ea2e5934 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -54,7 +54,7 @@ impl<'a> InferenceContext<'a> {
54 /// Infer type of expression with possibly implicit coerce to the expected type. 54 /// Infer type of expression with possibly implicit coerce to the expected type.
55 /// Return the type after possible coercion. 55 /// Return the type after possible coercion.
56 pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { 56 pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
57 let ty = self.infer_expr_inner(expr, &expected); 57 let ty = self.infer_expr_inner(expr, expected);
58 let ty = if let Some(target) = expected.only_has_type(&mut self.table) { 58 let ty = if let Some(target) = expected.only_has_type(&mut self.table) {
59 if !self.coerce(&ty, &target) { 59 if !self.coerce(&ty, &target) {
60 self.result 60 self.result
@@ -135,11 +135,11 @@ impl<'a> InferenceContext<'a> {
135 let mut both_arms_diverge = Diverges::Always; 135 let mut both_arms_diverge = Diverges::Always;
136 136
137 let mut result_ty = self.table.new_type_var(); 137 let mut result_ty = self.table.new_type_var();
138 let then_ty = self.infer_expr_inner(*then_branch, &expected); 138 let then_ty = self.infer_expr_inner(*then_branch, expected);
139 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); 139 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
140 result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); 140 result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty);
141 let else_ty = match else_branch { 141 let else_ty = match else_branch {
142 Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), 142 Some(else_branch) => self.infer_expr_inner(*else_branch, expected),
143 None => TyBuilder::unit(), 143 None => TyBuilder::unit(),
144 }; 144 };
145 both_arms_diverge &= self.diverges; 145 both_arms_diverge &= self.diverges;
@@ -327,20 +327,19 @@ impl<'a> InferenceContext<'a> {
327 self.normalize_associated_types_in(ret_ty) 327 self.normalize_associated_types_in(ret_ty)
328 } 328 }
329 Expr::MethodCall { receiver, args, method_name, generic_args } => self 329 Expr::MethodCall { receiver, args, method_name, generic_args } => self
330 .infer_method_call( 330 .infer_method_call(tgt_expr, *receiver, args, method_name, generic_args.as_deref()),
331 tgt_expr,
332 *receiver,
333 &args,
334 &method_name,
335 generic_args.as_deref(),
336 ),
337 Expr::Match { expr, arms } => { 331 Expr::Match { expr, arms } => {
338 let input_ty = self.infer_expr(*expr, &Expectation::none()); 332 let input_ty = self.infer_expr(*expr, &Expectation::none());
339 333
334 let expected = expected.adjust_for_branches(&mut self.table);
335
340 let mut result_ty = if arms.is_empty() { 336 let mut result_ty = if arms.is_empty() {
341 TyKind::Never.intern(&Interner) 337 TyKind::Never.intern(&Interner)
342 } else { 338 } else {
343 self.table.new_type_var() 339 match &expected {
340 Expectation::HasType(ty) => ty.clone(),
341 _ => self.table.new_type_var(),
342 }
344 }; 343 };
345 344
346 let matchee_diverges = self.diverges; 345 let matchee_diverges = self.diverges;
@@ -988,7 +987,7 @@ impl<'a> InferenceContext<'a> {
988 } 987 }
989 988
990 fn register_obligations_for_call(&mut self, callable_ty: &Ty) { 989 fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
991 let callable_ty = self.resolve_ty_shallow(&callable_ty); 990 let callable_ty = self.resolve_ty_shallow(callable_ty);
992 if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { 991 if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) {
993 let def: CallableDefId = from_chalk(self.db, *fn_def); 992 let def: CallableDefId = from_chalk(self.db, *fn_def);
994 let generic_predicates = self.db.generic_predicates(def.into()); 993 let generic_predicates = self.db.generic_predicates(def.into());
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 83e0a7a9e..035f4ded6 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -101,7 +101,9 @@ impl<'a> InferenceContext<'a> {
101 let mut expected = self.resolve_ty_shallow(expected); 101 let mut expected = self.resolve_ty_shallow(expected);
102 102
103 if is_non_ref_pat(&body, pat) { 103 if is_non_ref_pat(&body, pat) {
104 let mut pat_adjustments = Vec::new();
104 while let Some((inner, _lifetime, mutability)) = expected.as_reference() { 105 while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
106 pat_adjustments.push(expected.clone());
105 expected = self.resolve_ty_shallow(inner); 107 expected = self.resolve_ty_shallow(inner);
106 default_bm = match default_bm { 108 default_bm = match default_bm {
107 BindingMode::Move => BindingMode::Ref(mutability), 109 BindingMode::Move => BindingMode::Ref(mutability),
@@ -109,6 +111,11 @@ impl<'a> InferenceContext<'a> {
109 BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), 111 BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability),
110 } 112 }
111 } 113 }
114
115 if !pat_adjustments.is_empty() {
116 pat_adjustments.shrink_to_fit();
117 self.result.pat_adjustments.insert(pat, pat_adjustments);
118 }
112 } else if let Pat::Ref { .. } = &body[pat] { 119 } else if let Pat::Ref { .. } = &body[pat] {
113 cov_mark::hit!(match_ergonomics_ref); 120 cov_mark::hit!(match_ergonomics_ref);
114 // When you encounter a `&pat` pattern, reset to Move. 121 // When you encounter a `&pat` pattern, reset to Move.
@@ -185,7 +192,7 @@ impl<'a> InferenceContext<'a> {
185 Pat::Path(path) => { 192 Pat::Path(path) => {
186 // FIXME use correct resolver for the surrounding expression 193 // FIXME use correct resolver for the surrounding expression
187 let resolver = self.resolver.clone(); 194 let resolver = self.resolver.clone();
188 self.infer_path(&resolver, &path, pat.into()).unwrap_or(self.err_ty()) 195 self.infer_path(&resolver, path, pat.into()).unwrap_or(self.err_ty())
189 } 196 }
190 Pat::Bind { mode, name: _, subpat } => { 197 Pat::Bind { mode, name: _, subpat } => {
191 let mode = if mode == &BindingAnnotation::Unannotated { 198 let mode = if mode == &BindingAnnotation::Unannotated {
@@ -268,7 +275,7 @@ impl<'a> InferenceContext<'a> {
268 if !self.unify(&ty, &expected) { 275 if !self.unify(&ty, &expected) {
269 self.result 276 self.result
270 .type_mismatches 277 .type_mismatches
271 .insert(pat.into(), TypeMismatch { expected: expected, actual: ty.clone() }); 278 .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
272 } 279 }
273 self.write_pat_ty(pat, ty.clone()); 280 self.write_pat_ty(pat, ty.clone());
274 ty 281 ty
@@ -290,6 +297,10 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
290 Expr::Literal(Literal::String(..)) => false, 297 Expr::Literal(Literal::String(..)) => false,
291 _ => true, 298 _ => true,
292 }, 299 },
300 Pat::Bind { mode: BindingAnnotation::Mutable, subpat: Some(subpat), .. }
301 | Pat::Bind { mode: BindingAnnotation::Unannotated, subpat: Some(subpat), .. } => {
302 is_non_ref_pat(body, *subpat)
303 }
293 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, 304 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
294 } 305 }
295} 306}
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 14c99eafd..056cdb5d5 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -43,11 +43,11 @@ impl<'a> InferenceContext<'a> {
43 } 43 }
44 let ty = self.make_ty(type_ref); 44 let ty = self.make_ty(type_ref);
45 let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); 45 let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
46 let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver); 46 let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
47 let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); 47 let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
48 self.resolve_ty_assoc_item( 48 self.resolve_ty_assoc_item(
49 ty, 49 ty,
50 &path.segments().last().expect("path had at least one segment").name, 50 path.segments().last().expect("path had at least one segment").name,
51 id, 51 id,
52 )? 52 )?
53 } else { 53 } else {
@@ -154,7 +154,7 @@ impl<'a> InferenceContext<'a> {
154 let segment = 154 let segment =
155 remaining_segments.last().expect("there should be at least one segment here"); 155 remaining_segments.last().expect("there should be at least one segment here");
156 156
157 self.resolve_ty_assoc_item(ty, &segment.name, id) 157 self.resolve_ty_assoc_item(ty, segment.name, id)
158 } 158 }
159 } 159 }
160 } 160 }
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs
index 29ffdd9b7..5fef878e8 100644
--- a/crates/hir_ty/src/interner.rs
+++ b/crates/hir_ty/src/interner.rs
@@ -331,7 +331,7 @@ impl chalk_ir::interner::Interner for Interner {
331 &self, 331 &self,
332 clauses: &'a Self::InternedProgramClauses, 332 clauses: &'a Self::InternedProgramClauses,
333 ) -> &'a [chalk_ir::ProgramClause<Self>] { 333 ) -> &'a [chalk_ir::ProgramClause<Self>] {
334 &clauses 334 clauses
335 } 335 }
336 336
337 fn intern_quantified_where_clauses<E>( 337 fn intern_quantified_where_clauses<E>(
@@ -373,7 +373,7 @@ impl chalk_ir::interner::Interner for Interner {
373 &self, 373 &self,
374 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, 374 canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
375 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { 375 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] {
376 &canonical_var_kinds 376 canonical_var_kinds
377 } 377 }
378 378
379 fn intern_constraints<E>( 379 fn intern_constraints<E>(
@@ -413,7 +413,7 @@ impl chalk_ir::interner::Interner for Interner {
413 &self, 413 &self,
414 variances: &'a Self::InternedVariances, 414 variances: &'a Self::InternedVariances,
415 ) -> &'a [chalk_ir::Variance] { 415 ) -> &'a [chalk_ir::Variance] {
416 &variances 416 variances
417 } 417 }
418} 418}
419 419
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 50e0d6333..128cae830 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -21,7 +21,6 @@ mod utils;
21mod walk; 21mod walk;
22pub mod db; 22pub mod db;
23pub mod diagnostics; 23pub mod diagnostics;
24pub mod diagnostics_sink;
25pub mod display; 24pub mod display;
26pub mod method_resolution; 25pub mod method_resolution;
27pub mod primitive; 26pub mod primitive;
@@ -50,7 +49,7 @@ use crate::{db::HirDatabase, utils::generics};
50pub use autoderef::autoderef; 49pub use autoderef::autoderef;
51pub use builder::TyBuilder; 50pub use builder::TyBuilder;
52pub use chalk_ext::*; 51pub use chalk_ext::*;
53pub use infer::{could_unify, InferenceResult}; 52pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
54pub use interner::Interner; 53pub use interner::Interner;
55pub use lower::{ 54pub use lower::{
56 associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, 55 associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index c83933c73..817a65c20 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -238,7 +238,7 @@ impl<'a> TyLoweringContext<'a> {
238 // away instead of two. 238 // away instead of two.
239 let actual_opaque_type_data = self 239 let actual_opaque_type_data = self
240 .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { 240 .with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
241 ctx.lower_impl_trait(&bounds) 241 ctx.lower_impl_trait(bounds)
242 }); 242 });
243 self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; 243 self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
244 244
@@ -421,7 +421,7 @@ impl<'a> TyLoweringContext<'a> {
421 let found = self 421 let found = self
422 .db 422 .db
423 .trait_data(trait_ref.hir_trait_id()) 423 .trait_data(trait_ref.hir_trait_id())
424 .associated_type_by_name(&segment.name); 424 .associated_type_by_name(segment.name);
425 match found { 425 match found {
426 Some(associated_ty) => { 426 Some(associated_ty) => {
427 // FIXME handle type parameters on the segment 427 // FIXME handle type parameters on the segment
@@ -505,7 +505,7 @@ impl<'a> TyLoweringContext<'a> {
505 pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) { 505 pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) {
506 // Resolve the path (in type namespace) 506 // Resolve the path (in type namespace)
507 if let Some(type_ref) = path.type_anchor() { 507 if let Some(type_ref) = path.type_anchor() {
508 let (ty, res) = self.lower_ty_ext(&type_ref); 508 let (ty, res) = self.lower_ty_ext(type_ref);
509 return self.lower_ty_relative_path(ty, res, path.segments()); 509 return self.lower_ty_relative_path(ty, res, path.segments());
510 } 510 }
511 let (resolution, remaining_index) = 511 let (resolution, remaining_index) =
@@ -784,7 +784,7 @@ impl<'a> TyLoweringContext<'a> {
784 let trait_ref = match bound { 784 let trait_ref = match bound {
785 TypeBound::Path(path) => { 785 TypeBound::Path(path) => {
786 bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); 786 bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
787 bindings.clone().map(WhereClause::Implemented).map(|b| crate::wrap_empty_binders(b)) 787 bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
788 } 788 }
789 TypeBound::Lifetime(_) => None, 789 TypeBound::Lifetime(_) => None,
790 TypeBound::Error => None, 790 TypeBound::Error => None,
@@ -957,7 +957,7 @@ pub(crate) fn field_types_query(
957/// like `T::Item`. 957/// like `T::Item`.
958/// 958///
959/// See the analogous query in rustc and its comment: 959/// See the analogous query in rustc and its comment:
960/// https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46 960/// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46>
961/// This is a query mostly to handle cycles somewhat gracefully; e.g. the 961/// This is a query mostly to handle cycles somewhat gracefully; e.g. the
962/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but 962/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
963/// these are fine: `T: Foo<U::Item>, U: Foo<()>`. 963/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index af6b6cda7..3d233b1e2 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -5,10 +5,10 @@
5use std::{iter, sync::Arc}; 5use std::{iter, sync::Arc};
6 6
7use arrayvec::ArrayVec; 7use arrayvec::ArrayVec;
8use base_db::CrateId; 8use base_db::{CrateId, Edition};
9use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; 9use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
10use hir_def::{ 10use hir_def::{
11 lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, 11 lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, BlockId, FunctionId,
12 GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, 12 GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
13}; 13};
14use hir_expand::name::Name; 14use hir_expand::name::Name;
@@ -60,7 +60,7 @@ impl TyFingerprint {
60 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), 60 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt),
61 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), 61 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
62 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), 62 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
63 TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, 63 TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?,
64 _ => return None, 64 _ => return None,
65 }; 65 };
66 Some(fp) 66 Some(fp)
@@ -77,7 +77,7 @@ impl TyFingerprint {
77 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), 77 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt),
78 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), 78 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
79 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), 79 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
80 TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, 80 TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?,
81 TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), 81 TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty),
82 TyKind::Tuple(_, subst) => { 82 TyKind::Tuple(_, subst) => {
83 let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner)); 83 let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner));
@@ -139,35 +139,47 @@ impl TraitImpls {
139 let mut impls = Self { map: FxHashMap::default() }; 139 let mut impls = Self { map: FxHashMap::default() };
140 140
141 let crate_def_map = db.crate_def_map(krate); 141 let crate_def_map = db.crate_def_map(krate);
142 collect_def_map(db, &crate_def_map, &mut impls); 142 impls.collect_def_map(db, &crate_def_map);
143 143
144 return Arc::new(impls); 144 return Arc::new(impls);
145 }
145 146
146 fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) { 147 pub(crate) fn trait_impls_in_block_query(
147 for (_module_id, module_data) in def_map.modules() { 148 db: &dyn HirDatabase,
148 for impl_id in module_data.scope.impls() { 149 block: BlockId,
149 let target_trait = match db.impl_trait(impl_id) { 150 ) -> Option<Arc<Self>> {
150 Some(tr) => tr.skip_binders().hir_trait_id(), 151 let _p = profile::span("trait_impls_in_block_query");
151 None => continue, 152 let mut impls = Self { map: FxHashMap::default() };
152 };
153 let self_ty = db.impl_self_ty(impl_id);
154 let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
155 impls
156 .map
157 .entry(target_trait)
158 .or_default()
159 .entry(self_ty_fp)
160 .or_default()
161 .push(impl_id);
162 }
163 153
164 // To better support custom derives, collect impls in all unnamed const items. 154 let block_def_map = db.block_def_map(block)?;
165 // const _: () = { ... }; 155 impls.collect_def_map(db, &block_def_map);
166 for konst in module_data.scope.unnamed_consts() { 156
167 let body = db.body(konst.into()); 157 return Some(Arc::new(impls));
168 for (_, block_def_map) in body.blocks(db.upcast()) { 158 }
169 collect_def_map(db, &block_def_map, impls); 159
170 } 160 fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
161 for (_module_id, module_data) in def_map.modules() {
162 for impl_id in module_data.scope.impls() {
163 let target_trait = match db.impl_trait(impl_id) {
164 Some(tr) => tr.skip_binders().hir_trait_id(),
165 None => continue,
166 };
167 let self_ty = db.impl_self_ty(impl_id);
168 let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
169 self.map
170 .entry(target_trait)
171 .or_default()
172 .entry(self_ty_fp)
173 .or_default()
174 .push(impl_id);
175 }
176
177 // To better support custom derives, collect impls in all unnamed const items.
178 // const _: () = { ... };
179 for konst in module_data.scope.unnamed_consts() {
180 let body = db.body(konst.into());
181 for (_, block_def_map) in body.blocks(db.upcast()) {
182 self.collect_def_map(db, &block_def_map);
171 } 183 }
172 } 184 }
173 } 185 }
@@ -372,7 +384,7 @@ pub(crate) fn lookup_method(
372 db, 384 db,
373 env, 385 env,
374 krate, 386 krate,
375 &traits_in_scope, 387 traits_in_scope,
376 visible_from_module, 388 visible_from_module,
377 Some(name), 389 Some(name),
378 LookupMode::MethodCall, 390 LookupMode::MethodCall,
@@ -484,7 +496,7 @@ fn iterate_method_candidates_impl(
484 LookupMode::Path => { 496 LookupMode::Path => {
485 // No autoderef for path lookups 497 // No autoderef for path lookups
486 iterate_method_candidates_for_self_ty( 498 iterate_method_candidates_for_self_ty(
487 &ty, 499 ty,
488 db, 500 db,
489 env, 501 env,
490 krate, 502 krate,
@@ -513,7 +525,7 @@ fn iterate_method_candidates_with_autoref(
513 db, 525 db,
514 env.clone(), 526 env.clone(),
515 krate, 527 krate,
516 &traits_in_scope, 528 traits_in_scope,
517 visible_from_module, 529 visible_from_module,
518 name, 530 name,
519 &mut callback, 531 &mut callback,
@@ -531,7 +543,7 @@ fn iterate_method_candidates_with_autoref(
531 db, 543 db,
532 env.clone(), 544 env.clone(),
533 krate, 545 krate,
534 &traits_in_scope, 546 traits_in_scope,
535 visible_from_module, 547 visible_from_module,
536 name, 548 name,
537 &mut callback, 549 &mut callback,
@@ -549,7 +561,7 @@ fn iterate_method_candidates_with_autoref(
549 db, 561 db,
550 env, 562 env,
551 krate, 563 krate,
552 &traits_in_scope, 564 traits_in_scope,
553 visible_from_module, 565 visible_from_module,
554 name, 566 name,
555 &mut callback, 567 &mut callback,
@@ -593,7 +605,7 @@ fn iterate_method_candidates_by_receiver(
593 db, 605 db,
594 env.clone(), 606 env.clone(),
595 krate, 607 krate,
596 &traits_in_scope, 608 traits_in_scope,
597 name, 609 name,
598 Some(receiver_ty), 610 Some(receiver_ty),
599 &mut callback, 611 &mut callback,
@@ -639,6 +651,7 @@ fn iterate_trait_method_candidates(
639 receiver_ty: Option<&Canonical<Ty>>, 651 receiver_ty: Option<&Canonical<Ty>>,
640 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, 652 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
641) -> bool { 653) -> bool {
654 let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..));
642 // if ty is `dyn Trait`, the trait doesn't need to be in scope 655 // if ty is `dyn Trait`, the trait doesn't need to be in scope
643 let inherent_trait = 656 let inherent_trait =
644 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); 657 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
@@ -655,6 +668,19 @@ fn iterate_trait_method_candidates(
655 'traits: for t in traits { 668 'traits: for t in traits {
656 let data = db.trait_data(t); 669 let data = db.trait_data(t);
657 670
671 // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
672 // method resolution, if the receiver is an array, and we're compiling for editions before
673 // 2021.
674 // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
675 // arrays.
676 if data.skip_array_during_method_dispatch && receiver_is_array {
677 // FIXME: this should really be using the edition of the method name's span, in case it
678 // comes from a macro
679 if db.crate_graph()[krate].edition < Edition::Edition2021 {
680 continue;
681 }
682 }
683
658 // we'll be lazy about checking whether the type implements the 684 // we'll be lazy about checking whether the type implements the
659 // trait, but if we find out it doesn't, we'll skip the rest of the 685 // trait, but if we find out it doesn't, we'll skip the rest of the
660 // iteration 686 // iteration
@@ -856,7 +882,7 @@ fn transform_receiver_ty(
856 .fill_with_unknown() 882 .fill_with_unknown()
857 .build(), 883 .build(),
858 AssocContainerId::ImplId(impl_id) => { 884 AssocContainerId::ImplId(impl_id) => {
859 let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?; 885 let impl_substs = inherent_impl_substs(db, env, impl_id, self_ty)?;
860 TyBuilder::subst_for_def(db, function_id) 886 TyBuilder::subst_for_def(db, function_id)
861 .use_parent_substs(&impl_substs) 887 .use_parent_substs(&impl_substs)
862 .fill_with_unknown() 888 .fill_with_unknown()
diff --git a/crates/hir_ty/src/test_db.rs b/crates/hir_ty/src/test_db.rs
index 381b98ba8..4640ea821 100644
--- a/crates/hir_ty/src/test_db.rs
+++ b/crates/hir_ty/src/test_db.rs
@@ -22,11 +22,19 @@ use test_utils::extract_annotations;
22 hir_def::db::DefDatabaseStorage, 22 hir_def::db::DefDatabaseStorage,
23 crate::db::HirDatabaseStorage 23 crate::db::HirDatabaseStorage
24)] 24)]
25#[derive(Default)]
26pub(crate) struct TestDB { 25pub(crate) struct TestDB {
27 storage: salsa::Storage<TestDB>, 26 storage: salsa::Storage<TestDB>,
28 events: Mutex<Option<Vec<salsa::Event>>>, 27 events: Mutex<Option<Vec<salsa::Event>>>,
29} 28}
29
30impl Default for TestDB {
31 fn default() -> Self {
32 let mut this = Self { storage: Default::default(), events: Default::default() };
33 this.set_enable_proc_attr_macros(true);
34 this
35 }
36}
37
30impl fmt::Debug for TestDB { 38impl fmt::Debug for TestDB {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 f.debug_struct("TestDB").finish() 40 f.debug_struct("TestDB").finish()
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 9d726b024..b873585c4 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -9,7 +9,7 @@ mod macros;
9mod display_source_code; 9mod display_source_code;
10mod incremental; 10mod incremental;
11 11
12use std::{env, sync::Arc}; 12use std::{collections::HashMap, env, sync::Arc};
13 13
14use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 14use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
15use expect_test::Expect; 15use expect_test::Expect;
@@ -83,9 +83,105 @@ fn check_types_impl(ra_fixture: &str, display_source: bool) {
83 checked_one = true; 83 checked_one = true;
84 } 84 }
85 } 85 }
86
86 assert!(checked_one, "no `//^` annotations found"); 87 assert!(checked_one, "no `//^` annotations found");
87} 88}
88 89
90fn check_no_mismatches(ra_fixture: &str) {
91 check_mismatches_impl(ra_fixture, true)
92}
93
94#[allow(unused)]
95fn check_mismatches(ra_fixture: &str) {
96 check_mismatches_impl(ra_fixture, false)
97}
98
99fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) {
100 let _tracing = setup_tracing();
101 let (db, file_id) = TestDB::with_single_file(ra_fixture);
102 let module = db.module_for_file(file_id);
103 let def_map = module.def_map(&db);
104
105 let mut defs: Vec<DefWithBodyId> = Vec::new();
106 visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it));
107 defs.sort_by_key(|def| match def {
108 DefWithBodyId::FunctionId(it) => {
109 let loc = it.lookup(&db);
110 loc.source(&db).value.syntax().text_range().start()
111 }
112 DefWithBodyId::ConstId(it) => {
113 let loc = it.lookup(&db);
114 loc.source(&db).value.syntax().text_range().start()
115 }
116 DefWithBodyId::StaticId(it) => {
117 let loc = it.lookup(&db);
118 loc.source(&db).value.syntax().text_range().start()
119 }
120 });
121 let mut mismatches = HashMap::new();
122 let mut push_mismatch = |src_ptr: InFile<SyntaxNode>, mismatch: TypeMismatch| {
123 let range = src_ptr.value.text_range();
124 if src_ptr.file_id.call_node(&db).is_some() {
125 panic!("type mismatch in macro expansion");
126 }
127 let file_range = FileRange { file_id: src_ptr.file_id.original_file(&db), range };
128 let actual = format!(
129 "expected {}, got {}",
130 mismatch.expected.display_test(&db),
131 mismatch.actual.display_test(&db)
132 );
133 mismatches.insert(file_range, actual);
134 };
135 for def in defs {
136 let (_body, body_source_map) = db.body_with_source_map(def);
137 let inference_result = db.infer(def);
138 for (pat, mismatch) in inference_result.pat_type_mismatches() {
139 let syntax_ptr = match body_source_map.pat_syntax(pat) {
140 Ok(sp) => {
141 let root = db.parse_or_expand(sp.file_id).unwrap();
142 sp.map(|ptr| {
143 ptr.either(
144 |it| it.to_node(&root).syntax().clone(),
145 |it| it.to_node(&root).syntax().clone(),
146 )
147 })
148 }
149 Err(SyntheticSyntax) => continue,
150 };
151 push_mismatch(syntax_ptr, mismatch.clone());
152 }
153 for (expr, mismatch) in inference_result.expr_type_mismatches() {
154 let node = match body_source_map.expr_syntax(expr) {
155 Ok(sp) => {
156 let root = db.parse_or_expand(sp.file_id).unwrap();
157 sp.map(|ptr| ptr.to_node(&root).syntax().clone())
158 }
159 Err(SyntheticSyntax) => continue,
160 };
161 push_mismatch(node, mismatch.clone());
162 }
163 }
164 let mut checked_one = false;
165 for (file_id, annotations) in db.extract_annotations() {
166 for (range, expected) in annotations {
167 let file_range = FileRange { file_id, range };
168 if let Some(mismatch) = mismatches.remove(&file_range) {
169 assert_eq!(mismatch, expected);
170 } else {
171 assert!(false, "Expected mismatch not encountered: {}\n", expected);
172 }
173 checked_one = true;
174 }
175 }
176 let mut buf = String::new();
177 for (range, mismatch) in mismatches {
178 format_to!(buf, "{:?}: {}\n", range.range, mismatch,);
179 }
180 assert!(buf.is_empty(), "Unexpected type mismatches:\n{}", buf);
181
182 assert!(checked_one || allow_none, "no `//^` annotations found");
183}
184
89fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { 185fn type_at_range(db: &TestDB, pos: FileRange) -> Ty {
90 let file = db.parse(pos.file_id).ok().unwrap(); 186 let file = db.parse(pos.file_id).ok().unwrap();
91 let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap(); 187 let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap();
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 6dac7e103..71047703d 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_no_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_block_expr_type_mismatch() { 6fn infer_block_expr_type_mismatch() {
@@ -963,7 +963,7 @@ fn test() -> i32 {
963 963
964#[test] 964#[test]
965fn panic_macro() { 965fn panic_macro() {
966 check_infer_with_mismatches( 966 check_no_mismatches(
967 r#" 967 r#"
968mod panic { 968mod panic {
969 #[macro_export] 969 #[macro_export]
@@ -991,15 +991,34 @@ fn main() {
991 panic!() 991 panic!()
992} 992}
993 "#, 993 "#,
994 expect![[r#" 994 );
995 174..185 '{ loop {} }': ! 995}
996 176..183 'loop {}': ! 996
997 181..183 '{}': () 997#[test]
998 !0..24 '$crate...:panic': fn panic() -> ! 998fn coerce_unsize_expected_type() {
999 !0..26 '$crate...anic()': ! 999 check_no_mismatches(
1000 !0..26 '$crate...anic()': ! 1000 r#"
1001 !0..28 '$crate...015!()': ! 1001#[lang = "sized"]
1002 454..470 '{ ...c!() }': () 1002pub trait Sized {}
1003 "#]], 1003#[lang = "unsize"]
1004pub trait Unsize<T> {}
1005#[lang = "coerce_unsized"]
1006pub trait CoerceUnsized<T> {}
1007
1008impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
1009
1010fn main() {
1011 let foo: &[u32] = &[1, 2];
1012 let foo: &[u32] = match true {
1013 true => &[1, 2],
1014 false => &[1, 2, 3],
1015 };
1016 let foo: &[u32] = if true {
1017 &[1, 2]
1018 } else {
1019 &[1, 2, 3]
1020 };
1021}
1022 "#,
1004 ); 1023 );
1005} 1024}
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 058eb9129..f26b2c8a7 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1349,3 +1349,52 @@ fn f() {
1349 "#, 1349 "#,
1350 ); 1350 );
1351} 1351}
1352
1353#[test]
1354fn skip_array_during_method_dispatch() {
1355 check_types(
1356 r#"
1357//- /main2018.rs crate:main2018 deps:core
1358use core::IntoIterator;
1359
1360fn f() {
1361 let v = [4].into_iter();
1362 v;
1363 //^ &i32
1364
1365 let a = [0, 1].into_iter();
1366 a;
1367 //^ &i32
1368}
1369
1370//- /main2021.rs crate:main2021 deps:core edition:2021
1371use core::IntoIterator;
1372
1373fn f() {
1374 let v = [4].into_iter();
1375 v;
1376 //^ i32
1377
1378 let a = [0, 1].into_iter();
1379 a;
1380 //^ &i32
1381}
1382
1383//- /core.rs crate:core
1384#[rustc_skip_array_during_method_dispatch]
1385pub trait IntoIterator {
1386 type Out;
1387 fn into_iter(self) -> Self::Out;
1388}
1389
1390impl<T> IntoIterator for [T; 1] {
1391 type Out = T;
1392 fn into_iter(self) -> Self::Out {}
1393}
1394impl<'a, T> IntoIterator for &'a [T] {
1395 type Out = &'a T;
1396 fn into_iter(self) -> Self::Out {}
1397}
1398 "#,
1399 );
1400}
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index cd08b5c7a..aa513c56d 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_pattern() { 6fn infer_pattern() {
@@ -20,6 +20,8 @@ fn infer_pattern() {
20 let h = val; 20 let h = val;
21 } 21 }
22 22
23 if let x @ true = &true {}
24
23 let lambda = |a: u64, b, c: i32| { a + b; c }; 25 let lambda = |a: u64, b, c: i32| { a + b; c };
24 26
25 let ref ref_to_x = x; 27 let ref ref_to_x = x;
@@ -30,7 +32,7 @@ fn infer_pattern() {
30 "#, 32 "#,
31 expect![[r#" 33 expect![[r#"
32 8..9 'x': &i32 34 8..9 'x': &i32
33 17..368 '{ ...o_x; }': () 35 17..400 '{ ...o_x; }': ()
34 27..28 'y': &i32 36 27..28 'y': &i32
35 31..32 'x': &i32 37 31..32 'x': &i32
36 42..44 '&z': &i32 38 42..44 '&z': &i32
@@ -59,24 +61,31 @@ fn infer_pattern() {
59 176..204 '{ ... }': () 61 176..204 '{ ... }': ()
60 190..191 'h': {unknown} 62 190..191 'h': {unknown}
61 194..197 'val': {unknown} 63 194..197 'val': {unknown}
62 214..220 'lambda': |u64, u64, i32| -> i32 64 210..236 'if let...rue {}': ()
63 223..255 '|a: u6...b; c }': |u64, u64, i32| -> i32 65 217..225 'x @ true': &bool
64 224..225 'a': u64 66 221..225 'true': bool
65 232..233 'b': u64 67 221..225 'true': bool
66 235..236 'c': i32 68 228..233 '&true': &bool
67 243..255 '{ a + b; c }': i32 69 229..233 'true': bool
68 245..246 'a': u64 70 234..236 '{}': ()
69 245..250 'a + b': u64 71 246..252 'lambda': |u64, u64, i32| -> i32
70 249..250 'b': u64 72 255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
71 252..253 'c': i32 73 256..257 'a': u64
72 266..278 'ref ref_to_x': &&i32 74 264..265 'b': u64
73 281..282 'x': &i32 75 267..268 'c': i32
74 292..301 'mut mut_x': &i32 76 275..287 '{ a + b; c }': i32
75 304..305 'x': &i32 77 277..278 'a': u64
76 315..335 'ref mu...f_to_x': &mut &i32 78 277..282 'a + b': u64
77 338..339 'x': &i32 79 281..282 'b': u64
78 349..350 'k': &mut &i32 80 284..285 'c': i32
79 353..365 'mut_ref_to_x': &mut &i32 81 298..310 'ref ref_to_x': &&i32
82 313..314 'x': &i32
83 324..333 'mut mut_x': &i32
84 336..337 'x': &i32
85 347..367 'ref mu...f_to_x': &mut &i32
86 370..371 'x': &i32
87 381..382 'k': &mut &i32
88 385..397 'mut_ref_to_x': &mut &i32
80 "#]], 89 "#]],
81 ); 90 );
82} 91}
@@ -509,47 +518,24 @@ fn infer_generics_in_patterns() {
509 518
510#[test] 519#[test]
511fn infer_const_pattern() { 520fn infer_const_pattern() {
512 check_infer_with_mismatches( 521 check_mismatches(
513 r#" 522 r#"
514 enum Option<T> { None } 523enum Option<T> { None }
515 use Option::None; 524use Option::None;
516 struct Foo; 525struct Foo;
517 const Bar: usize = 1; 526const Bar: usize = 1;
518 527
519 fn test() { 528fn test() {
520 let a: Option<u32> = None; 529 let a: Option<u32> = None;
521 let b: Option<i64> = match a { 530 let b: Option<i64> = match a {
522 None => None, 531 None => None,
523 }; 532 };
524 let _: () = match () { Foo => Foo }; // Expected mismatch 533 let _: () = match () { Foo => () };
525 let _: () = match () { Bar => Bar }; // Expected mismatch 534 // ^^^ expected (), got Foo
526 } 535 let _: () = match () { Bar => () };
536 // ^^^ expected (), got usize
537}
527 "#, 538 "#,
528 expect![[r#"
529 73..74 '1': usize
530 87..309 '{ ...atch }': ()
531 97..98 'a': Option<u32>
532 114..118 'None': Option<u32>
533 128..129 'b': Option<i64>
534 145..182 'match ... }': Option<i64>
535 151..152 'a': Option<u32>
536 163..167 'None': Option<u32>
537 171..175 'None': Option<i64>
538 192..193 '_': ()
539 200..223 'match ... Foo }': Foo
540 206..208 '()': ()
541 211..214 'Foo': Foo
542 218..221 'Foo': Foo
543 254..255 '_': ()
544 262..285 'match ... Bar }': usize
545 268..270 '()': ()
546 273..276 'Bar': usize
547 280..283 'Bar': usize
548 200..223: expected (), got Foo
549 211..214: expected (), got Foo
550 262..285: expected (), got usize
551 273..276: expected (), got usize
552 "#]],
553 ); 539 );
554} 540}
555 541
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 588f0d1d4..6bcede4c4 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3740,3 +3740,70 @@ mod future {
3740"#, 3740"#,
3741 ); 3741 );
3742} 3742}
3743
3744#[test]
3745fn local_impl_1() {
3746 check_types(
3747 r#"
3748trait Trait<T> {
3749 fn foo(&self) -> T;
3750}
3751
3752fn test() {
3753 struct S;
3754 impl Trait<u32> for S {
3755 fn foo(&self) { 0 }
3756 }
3757
3758 S.foo();
3759 // ^^^^^^^ u32
3760}
3761"#,
3762 );
3763}
3764
3765#[test]
3766fn local_impl_2() {
3767 check_types(
3768 r#"
3769struct S;
3770
3771fn test() {
3772 trait Trait<T> {
3773 fn foo(&self) -> T;
3774 }
3775 impl Trait<u32> for S {
3776 fn foo(&self) { 0 }
3777 }
3778
3779 S.foo();
3780 // ^^^^^^^ u32
3781}
3782"#,
3783 );
3784}
3785
3786#[test]
3787fn local_impl_3() {
3788 check_types(
3789 r#"
3790trait Trait<T> {
3791 fn foo(&self) -> T;
3792}
3793
3794fn test() {
3795 struct S1;
3796 {
3797 struct S2;
3798
3799 impl Trait<S1> for S2 {
3800 fn foo(&self) { S1 }
3801 }
3802
3803 S2.foo();
3804 // ^^^^^^^^ S1
3805 }
3806}
3807"#,
3808 );
3809}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 88f3d09d3..f12928225 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14either = "1.5.3" 14either = "1.5.3"
15indexmap = "1.4.0" 15indexmap = "1.4.0"
16itertools = "0.10.0" 16itertools = "0.10.0"
@@ -39,4 +39,3 @@ hir = { path = "../hir", version = "0.0.0" }
39[dev-dependencies] 39[dev-dependencies]
40test_utils = { path = "../test_utils" } 40test_utils = { path = "../test_utils" }
41expect-test = "1.1" 41expect-test = "1.1"
42cov-mark = "1.1.0"
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index d5c954b8b..815a633e5 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -4,69 +4,94 @@
4//! macro-expanded files, but we need to present them to the users in terms of 4//! macro-expanded files, but we need to present them to the users in terms of
5//! original files. So we need to map the ranges. 5//! original files. So we need to map the ranges.
6 6
7mod fixes; 7mod break_outside_of_loop;
8mod field_shorthand; 8mod inactive_code;
9mod incorrect_case;
10mod macro_error;
11mod mismatched_arg_count;
12mod missing_fields;
13mod missing_match_arms;
14mod missing_ok_or_some_in_tail_expr;
15mod missing_unsafe;
16mod no_such_field;
17mod remove_this_semicolon;
18mod replace_filter_map_next_with_find_map;
19mod unimplemented_builtin_macro;
9mod unlinked_file; 20mod unlinked_file;
21mod unresolved_extern_crate;
22mod unresolved_import;
23mod unresolved_macro_call;
24mod unresolved_module;
25mod unresolved_proc_macro;
10 26
11use std::cell::RefCell; 27mod field_shorthand;
12 28
13use hir::{ 29use hir::{diagnostics::AnyDiagnostic, Semantics};
14 db::AstDatabase,
15 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
16 InFile, Semantics,
17};
18use ide_assists::AssistResolveStrategy; 30use ide_assists::AssistResolveStrategy;
19use ide_db::{base_db::SourceDatabase, RootDatabase}; 31use ide_db::{base_db::SourceDatabase, RootDatabase};
20use itertools::Itertools; 32use itertools::Itertools;
21use rustc_hash::FxHashSet; 33use rustc_hash::FxHashSet;
22use syntax::{ 34use syntax::{
23 ast::{self, AstNode}, 35 ast::{self, AstNode},
24 SyntaxNode, SyntaxNodePtr, TextRange, TextSize, 36 SyntaxNode, TextRange,
25}; 37};
26use text_edit::TextEdit; 38use text_edit::TextEdit;
27use unlinked_file::UnlinkedFile; 39use unlinked_file::UnlinkedFile;
28 40
29use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange}; 41use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};
30 42
31use self::fixes::DiagnosticWithFixes; 43#[derive(Copy, Clone, Debug, PartialEq)]
44pub struct DiagnosticCode(pub &'static str);
45
46impl DiagnosticCode {
47 pub fn as_str(&self) -> &str {
48 self.0
49 }
50}
32 51
33#[derive(Debug)] 52#[derive(Debug)]
34pub struct Diagnostic { 53pub struct Diagnostic {
35 // pub name: Option<String>, 54 pub code: DiagnosticCode,
36 pub message: String, 55 pub message: String,
37 pub range: TextRange, 56 pub range: TextRange,
38 pub severity: Severity, 57 pub severity: Severity,
39 pub fixes: Option<Vec<Assist>>,
40 pub unused: bool, 58 pub unused: bool,
41 pub code: Option<DiagnosticCode>, 59 pub experimental: bool,
60 pub fixes: Option<Vec<Assist>>,
42} 61}
43 62
44impl Diagnostic { 63impl Diagnostic {
45 fn error(range: TextRange, message: String) -> Self { 64 fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
46 Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None } 65 let message = message.into();
47 } 66 Diagnostic {
48 67 code: DiagnosticCode(code),
49 fn hint(range: TextRange, message: String) -> Self {
50 Self {
51 message, 68 message,
52 range, 69 range,
53 severity: Severity::WeakWarning, 70 severity: Severity::Error,
54 fixes: None,
55 unused: false, 71 unused: false,
56 code: None, 72 experimental: false,
73 fixes: None,
57 } 74 }
58 } 75 }
59 76
60 fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self { 77 fn experimental(mut self) -> Diagnostic {
61 Self { fixes, ..self } 78 self.experimental = true;
79 self
80 }
81
82 fn severity(mut self, severity: Severity) -> Diagnostic {
83 self.severity = severity;
84 self
62 } 85 }
63 86
64 fn with_unused(self, unused: bool) -> Self { 87 fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic {
65 Self { unused, ..self } 88 self.fixes = fixes;
89 self
66 } 90 }
67 91
68 fn with_code(self, code: Option<DiagnosticCode>) -> Self { 92 fn with_unused(mut self, unused: bool) -> Diagnostic {
69 Self { code, ..self } 93 self.unused = unused;
94 self
70 } 95 }
71} 96}
72 97
@@ -82,6 +107,12 @@ pub struct DiagnosticsConfig {
82 pub disabled: FxHashSet<String>, 107 pub disabled: FxHashSet<String>,
83} 108}
84 109
110struct DiagnosticsContext<'a> {
111 config: &'a DiagnosticsConfig,
112 sema: Semantics<'a, RootDatabase>,
113 resolve: &'a AssistResolveStrategy,
114}
115
85pub(crate) fn diagnostics( 116pub(crate) fn diagnostics(
86 db: &RootDatabase, 117 db: &RootDatabase,
87 config: &DiagnosticsConfig, 118 config: &DiagnosticsConfig,
@@ -95,144 +126,64 @@ pub(crate) fn diagnostics(
95 126
96 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. 127 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
97 res.extend( 128 res.extend(
98 parse 129 parse.errors().iter().take(128).map(|err| {
99 .errors() 130 Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range())
100 .iter() 131 }),
101 .take(128)
102 .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))),
103 ); 132 );
104 133
105 for node in parse.tree().syntax().descendants() { 134 for node in parse.tree().syntax().descendants() {
106 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); 135 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
107 field_shorthand::check(&mut res, file_id, &node); 136 field_shorthand::check(&mut res, file_id, &node);
108 } 137 }
109 let res = RefCell::new(res);
110 let sink_builder = DiagnosticSinkBuilder::new()
111 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
112 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
113 })
114 .on::<hir::diagnostics::MissingFields, _>(|d| {
115 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
116 })
117 .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
118 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
119 })
120 .on::<hir::diagnostics::NoSuchField, _>(|d| {
121 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
122 })
123 .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
124 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
125 })
126 .on::<hir::diagnostics::IncorrectCase, _>(|d| {
127 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
128 })
129 .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| {
130 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
131 })
132 .on::<hir::diagnostics::InactiveCode, _>(|d| {
133 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
134 if d.display_source().file_id.expansion_info(db).is_some() {
135 return;
136 }
137 138
138 // Override severity and mark as unused. 139 let mut diags = Vec::new();
139 res.borrow_mut().push( 140 let module = sema.to_module_def(file_id);
140 Diagnostic::hint( 141 if let Some(m) = module {
141 sema.diagnostics_display_range(d.display_source()).range, 142 m.diagnostics(db, &mut diags)
142 d.message(),
143 )
144 .with_unused(true)
145 .with_code(Some(d.code())),
146 );
147 })
148 .on::<UnlinkedFile, _>(|d| {
149 // Limit diagnostic to the first few characters in the file. This matches how VS Code
150 // renders it with the full span, but on other editors, and is less invasive.
151 let range = sema.diagnostics_display_range(d.display_source()).range;
152 let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
153
154 // Override severity and mark as unused.
155 res.borrow_mut().push(
156 Diagnostic::hint(range, d.message())
157 .with_fixes(d.fixes(&sema, resolve))
158 .with_code(Some(d.code())),
159 );
160 })
161 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
162 // Use more accurate position if available.
163 let display_range = d
164 .precise_location
165 .unwrap_or_else(|| sema.diagnostics_display_range(d.display_source()).range);
166
167 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
168 res.borrow_mut()
169 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
170 })
171 .on::<hir::diagnostics::UnresolvedMacroCall, _>(|d| {
172 let last_path_segment = sema.db.parse_or_expand(d.file).and_then(|root| {
173 d.node
174 .to_node(&root)
175 .path()
176 .and_then(|it| it.segment())
177 .and_then(|it| it.name_ref())
178 .map(|it| InFile::new(d.file, SyntaxNodePtr::new(it.syntax())))
179 });
180 let diagnostics = last_path_segment.unwrap_or_else(|| d.display_source());
181 let display_range = sema.diagnostics_display_range(diagnostics).range;
182 res.borrow_mut()
183 .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code())));
184 })
185 .on::<hir::diagnostics::UnimplementedBuiltinMacro, _>(|d| {
186 let display_range = sema.diagnostics_display_range(d.display_source()).range;
187 res.borrow_mut()
188 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
189 })
190 // Only collect experimental diagnostics when they're enabled.
191 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
192 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
193
194 // Finalize the `DiagnosticSink` building process.
195 let mut sink = sink_builder
196 // Diagnostics not handled above get no fix and default treatment.
197 .build(|d| {
198 res.borrow_mut().push(
199 Diagnostic::error(
200 sema.diagnostics_display_range(d.display_source()).range,
201 d.message(),
202 )
203 .with_code(Some(d.code())),
204 );
205 });
206
207 match sema.to_module_def(file_id) {
208 Some(m) => m.diagnostics(db, &mut sink),
209 None => {
210 sink.push(UnlinkedFile { file_id, node: SyntaxNodePtr::new(&parse.tree().syntax()) });
211 }
212 } 143 }
213 144
214 drop(sink); 145 let ctx = DiagnosticsContext { config, sema, resolve };
215 res.into_inner() 146 if module.is_none() {
216} 147 let d = UnlinkedFile { file: file_id };
148 let d = unlinked_file::unlinked_file(&ctx, &d);
149 res.push(d)
150 }
217 151
218fn diagnostic_with_fix<D: DiagnosticWithFixes>( 152 for diag in diags {
219 d: &D, 153 #[rustfmt::skip]
220 sema: &Semantics<RootDatabase>, 154 let d = match diag {
221 resolve: &AssistResolveStrategy, 155 AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d),
222) -> Diagnostic { 156 AnyDiagnostic::IncorrectCase(d) => incorrect_case::incorrect_case(&ctx, &d),
223 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) 157 AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
224 .with_fixes(d.fixes(&sema, resolve)) 158 AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
225 .with_code(Some(d.code())) 159 AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
226} 160 AnyDiagnostic::MissingMatchArms(d) => missing_match_arms::missing_match_arms(&ctx, &d),
161 AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d),
162 AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d),
163 AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d),
164 AnyDiagnostic::RemoveThisSemicolon(d) => remove_this_semicolon::remove_this_semicolon(&ctx, &d),
165 AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
166 AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
167 AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
168 AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
169 AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
170 AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
171 AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
172
173 AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) {
174 Some(it) => it,
175 None => continue,
176 }
177 };
178 res.push(d)
179 }
227 180
228fn warning_with_fix<D: DiagnosticWithFixes>( 181 res.retain(|d| {
229 d: &D, 182 !ctx.config.disabled.contains(d.code.as_str())
230 sema: &Semantics<RootDatabase>, 183 && !(ctx.config.disable_experimental && d.experimental)
231 resolve: &AssistResolveStrategy, 184 });
232) -> Diagnostic { 185
233 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) 186 res
234 .with_fixes(d.fixes(&sema, resolve))
235 .with_code(Some(d.code()))
236} 187}
237 188
238fn check_unnecessary_braces_in_use_statement( 189fn check_unnecessary_braces_in_use_statement(
@@ -260,13 +211,18 @@ fn check_unnecessary_braces_in_use_statement(
260 }); 211 });
261 212
262 acc.push( 213 acc.push(
263 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) 214 Diagnostic::new(
264 .with_fixes(Some(vec![fix( 215 "unnecessary-braces",
265 "remove_braces", 216 "Unnecessary braces in use statement".to_string(),
266 "Remove unnecessary braces", 217 use_range,
267 SourceChange::from_text_edit(file_id, edit), 218 )
268 use_range, 219 .severity(Severity::WeakWarning)
269 )])), 220 .with_fixes(Some(vec![fix(
221 "remove_braces",
222 "Remove unnecessary braces",
223 SourceChange::from_text_edit(file_id, edit),
224 use_range,
225 )])),
270 ); 226 );
271 } 227 }
272 228
@@ -344,8 +300,8 @@ mod tests {
344 ) 300 )
345 .unwrap() 301 .unwrap()
346 .pop() 302 .pop()
347 .unwrap(); 303 .expect("no diagnostics");
348 let fix = &diagnostic.fixes.unwrap()[nth]; 304 let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth];
349 let actual = { 305 let actual = {
350 let source_change = fix.source_change.as_ref().unwrap(); 306 let source_change = fix.source_change.as_ref().unwrap();
351 let file_id = *source_change.source_file_edits.keys().next().unwrap(); 307 let file_id = *source_change.source_file_edits.keys().next().unwrap();
@@ -365,8 +321,9 @@ mod tests {
365 file_position.offset 321 file_position.offset
366 ); 322 );
367 } 323 }
324
368 /// Checks that there's a diagnostic *without* fix at `$0`. 325 /// Checks that there's a diagnostic *without* fix at `$0`.
369 fn check_no_fix(ra_fixture: &str) { 326 pub(crate) fn check_no_fix(ra_fixture: &str) {
370 let (analysis, file_position) = fixture::position(ra_fixture); 327 let (analysis, file_position) = fixture::position(ra_fixture);
371 let diagnostic = analysis 328 let diagnostic = analysis
372 .diagnostics( 329 .diagnostics(
@@ -380,21 +337,6 @@ mod tests {
380 assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic); 337 assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
381 } 338 }
382 339
383 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
384 /// apply to the file containing the cursor.
385 pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
386 let (analysis, files) = fixture::files(ra_fixture);
387 let diagnostics = files
388 .into_iter()
389 .flat_map(|file_id| {
390 analysis
391 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
392 .unwrap()
393 })
394 .collect::<Vec<_>>();
395 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
396 }
397
398 pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) { 340 pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) {
399 let (analysis, file_id) = fixture::file(ra_fixture); 341 let (analysis, file_id) = fixture::file(ra_fixture);
400 let diagnostics = analysis 342 let diagnostics = analysis
@@ -403,90 +345,31 @@ mod tests {
403 expect.assert_debug_eq(&diagnostics) 345 expect.assert_debug_eq(&diagnostics)
404 } 346 }
405 347
348 #[track_caller]
406 pub(crate) fn check_diagnostics(ra_fixture: &str) { 349 pub(crate) fn check_diagnostics(ra_fixture: &str) {
407 let (analysis, file_id) = fixture::file(ra_fixture); 350 let mut config = DiagnosticsConfig::default();
408 let diagnostics = analysis 351 config.disabled.insert("inactive-code".to_string());
409 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) 352 check_diagnostics_with_config(config, ra_fixture)
410 .unwrap();
411
412 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
413 let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
414 assert_eq!(expected, actual);
415 }
416
417 #[test]
418 fn test_unresolved_macro_range() {
419 check_diagnostics(
420 r#"
421foo::bar!(92);
422 //^^^ unresolved macro `foo::bar!`
423"#,
424 );
425 }
426
427 #[test]
428 fn unresolved_import_in_use_tree() {
429 // Only the relevant part of a nested `use` item should be highlighted.
430 check_diagnostics(
431 r#"
432use does_exist::{Exists, DoesntExist};
433 //^^^^^^^^^^^ unresolved import
434
435use {does_not_exist::*, does_exist};
436 //^^^^^^^^^^^^^^^^^ unresolved import
437
438use does_not_exist::{
439 a,
440 //^ unresolved import
441 b,
442 //^ unresolved import
443 c,
444 //^ unresolved import
445};
446
447mod does_exist {
448 pub struct Exists;
449}
450"#,
451 );
452 } 353 }
453 354
454 #[test] 355 #[track_caller]
455 fn range_mapping_out_of_macros() { 356 pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
456 // FIXME: this is very wrong, but somewhat tricky to fix. 357 let (analysis, files) = fixture::files(ra_fixture);
457 check_fix( 358 for file_id in files {
458 r#" 359 let diagnostics =
459fn some() {} 360 analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
460fn items() {} 361
461fn here() {} 362 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
462 363 let mut actual =
463macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } 364 diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
464 365 actual.sort_by_key(|(range, _)| range.start());
465fn main() { 366 assert_eq!(expected, actual);
466 let _x = id![Foo { a: $042 }]; 367 }
467}
468
469pub struct Foo { pub a: i32, pub b: i32 }
470"#,
471 r#"
472fn some(, b: () ) {}
473fn items() {}
474fn here() {}
475
476macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
477
478fn main() {
479 let _x = id![Foo { a: 42 }];
480}
481
482pub struct Foo { pub a: i32, pub b: i32 }
483"#,
484 );
485 } 368 }
486 369
487 #[test] 370 #[test]
488 fn test_check_unnecessary_braces_in_use_statement() { 371 fn test_check_unnecessary_braces_in_use_statement() {
489 check_no_diagnostics( 372 check_diagnostics(
490 r#" 373 r#"
491use a; 374use a;
492use a::{c, d::e}; 375use a::{c, d::e};
@@ -499,7 +382,7 @@ mod a {
499} 382}
500"#, 383"#,
501 ); 384 );
502 check_no_diagnostics( 385 check_diagnostics(
503 r#" 386 r#"
504use a; 387use a;
505use a::{ 388use a::{
@@ -585,138 +468,31 @@ mod a {
585 } 468 }
586 469
587 #[test] 470 #[test]
588 fn unlinked_file_prepend_first_item() { 471 fn import_extern_crate_clash_with_inner_item() {
589 cov_mark::check!(unlinked_file_prepend_before_first_item); 472 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
590 // Only tests the first one for `pub mod` since the rest are the same
591 check_fixes(
592 r#"
593//- /main.rs
594fn f() {}
595//- /foo.rs
596$0
597"#,
598 vec![
599 r#"
600mod foo;
601
602fn f() {}
603"#,
604 r#"
605pub mod foo;
606
607fn f() {}
608"#,
609 ],
610 );
611 }
612
613 #[test]
614 fn unlinked_file_append_mod() {
615 cov_mark::check!(unlinked_file_append_to_existing_mods);
616 check_fix(
617 r#"
618//- /main.rs
619//! Comment on top
620
621mod preexisting;
622
623mod preexisting2;
624
625struct S;
626
627mod preexisting_bottom;)
628//- /foo.rs
629$0
630"#,
631 r#"
632//! Comment on top
633 473
634mod preexisting; 474 check_diagnostics(
635
636mod preexisting2;
637mod foo;
638
639struct S;
640
641mod preexisting_bottom;)
642"#,
643 );
644 }
645
646 #[test]
647 fn unlinked_file_insert_in_empty_file() {
648 cov_mark::check!(unlinked_file_empty_file);
649 check_fix(
650 r#"
651//- /main.rs
652//- /foo.rs
653$0
654"#,
655 r#"
656mod foo;
657"#,
658 );
659 }
660
661 #[test]
662 fn unlinked_file_old_style_modrs() {
663 check_fix(
664 r#"
665//- /main.rs
666mod submod;
667//- /submod/mod.rs
668// in mod.rs
669//- /submod/foo.rs
670$0
671"#,
672 r#"
673// in mod.rs
674mod foo;
675"#,
676 );
677 }
678
679 #[test]
680 fn unlinked_file_new_style_mod() {
681 check_fix(
682 r#"
683//- /main.rs
684mod submod;
685//- /submod.rs
686//- /submod/foo.rs
687$0
688"#,
689 r#" 475 r#"
690mod foo; 476//- /lib.rs crate:lib deps:jwt
691"#, 477mod permissions;
692 );
693 }
694 478
695 #[test] 479use permissions::jwt;
696 fn unlinked_file_with_cfg_off() {
697 cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists);
698 check_no_fix(
699 r#"
700//- /main.rs
701#[cfg(never)]
702mod foo;
703 480
704//- /foo.rs 481fn f() {
705$0 482 fn inner() {}
706"#, 483 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
707 ); 484}
708 }
709 485
710 #[test] 486//- /permissions.rs
711 fn unlinked_file_with_cfg_on() { 487pub mod jwt {
712 check_no_diagnostics( 488 pub struct Claims {}
713 r#" 489}
714//- /main.rs
715#[cfg(not(never))]
716mod foo;
717 490
718//- /foo.rs 491//- /jwt/lib.rs crate:jwt
719"#, 492pub struct Claims {
493 field: u8,
494}
495 "#,
720 ); 496 );
721 } 497 }
722} 498}
diff --git a/crates/ide/src/diagnostics/break_outside_of_loop.rs b/crates/ide/src/diagnostics/break_outside_of_loop.rs
new file mode 100644
index 000000000..80e68f3cc
--- /dev/null
+++ b/crates/ide/src/diagnostics/break_outside_of_loop.rs
@@ -0,0 +1,30 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: break-outside-of-loop
4//
5// This diagnostic is triggered if the `break` keyword is used outside of a loop.
6pub(super) fn break_outside_of_loop(
7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::BreakOutsideOfLoop,
9) -> Diagnostic {
10 Diagnostic::new(
11 "break-outside-of-loop",
12 "break outside of loop",
13 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
14 )
15}
16
17#[cfg(test)]
18mod tests {
19 use crate::diagnostics::tests::check_diagnostics;
20
21 #[test]
22 fn break_outside_of_loop() {
23 check_diagnostics(
24 r#"
25fn foo() { break; }
26 //^^^^^ break outside of loop
27"#,
28 );
29 }
30}
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
index 01bd2dba6..c7f4dab8e 100644
--- a/crates/ide/src/diagnostics/field_shorthand.rs
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange};
5use syntax::{ast, match_ast, AstNode, SyntaxNode}; 5use syntax::{ast, match_ast, AstNode, SyntaxNode};
6use text_edit::TextEdit; 6use text_edit::TextEdit;
7 7
8use crate::{diagnostics::fix, Diagnostic}; 8use crate::{diagnostics::fix, Diagnostic, Severity};
9 9
10pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { 10pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
11 match_ast! { 11 match_ast! {
@@ -46,7 +46,8 @@ fn check_expr_field_shorthand(
46 46
47 let field_range = record_field.syntax().text_range(); 47 let field_range = record_field.syntax().text_range();
48 acc.push( 48 acc.push(
49 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()) 49 Diagnostic::new("use-field-shorthand", "Shorthand struct initialization", field_range)
50 .severity(Severity::WeakWarning)
50 .with_fixes(Some(vec![fix( 51 .with_fixes(Some(vec![fix(
51 "use_expr_field_shorthand", 52 "use_expr_field_shorthand",
52 "Use struct shorthand initialization", 53 "Use struct shorthand initialization",
@@ -85,30 +86,32 @@ fn check_pat_field_shorthand(
85 let edit = edit_builder.finish(); 86 let edit = edit_builder.finish();
86 87
87 let field_range = record_pat_field.syntax().text_range(); 88 let field_range = record_pat_field.syntax().text_range();
88 acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fixes( 89 acc.push(
89 Some(vec![fix( 90 Diagnostic::new("use-field-shorthand", "Shorthand struct pattern", field_range)
90 "use_pat_field_shorthand", 91 .severity(Severity::WeakWarning)
91 "Use struct field shorthand", 92 .with_fixes(Some(vec![fix(
92 SourceChange::from_text_edit(file_id, edit), 93 "use_pat_field_shorthand",
93 field_range, 94 "Use struct field shorthand",
94 )]), 95 SourceChange::from_text_edit(file_id, edit),
95 )); 96 field_range,
97 )])),
98 );
96 } 99 }
97} 100}
98 101
99#[cfg(test)] 102#[cfg(test)]
100mod tests { 103mod tests {
101 use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; 104 use crate::diagnostics::tests::{check_diagnostics, check_fix};
102 105
103 #[test] 106 #[test]
104 fn test_check_expr_field_shorthand() { 107 fn test_check_expr_field_shorthand() {
105 check_no_diagnostics( 108 check_diagnostics(
106 r#" 109 r#"
107struct A { a: &'static str } 110struct A { a: &'static str }
108fn main() { A { a: "hello" } } 111fn main() { A { a: "hello" } }
109"#, 112"#,
110 ); 113 );
111 check_no_diagnostics( 114 check_diagnostics(
112 r#" 115 r#"
113struct A(usize); 116struct A(usize);
114fn main() { A { 0: 0 } } 117fn main() { A { 0: 0 } }
@@ -154,13 +157,13 @@ fn main() {
154 157
155 #[test] 158 #[test]
156 fn test_check_pat_field_shorthand() { 159 fn test_check_pat_field_shorthand() {
157 check_no_diagnostics( 160 check_diagnostics(
158 r#" 161 r#"
159struct A { a: &'static str } 162struct A { a: &'static str }
160fn f(a: A) { let A { a: hello } = a; } 163fn f(a: A) { let A { a: hello } = a; }
161"#, 164"#,
162 ); 165 );
163 check_no_diagnostics( 166 check_diagnostics(
164 r#" 167 r#"
165struct A(usize); 168struct A(usize);
166fn f(a: A) { let A { 0: 0 } = a; } 169fn f(a: A) { let A { 0: 0 } = a; }
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
deleted file mode 100644
index 258ac6974..000000000
--- a/crates/ide/src/diagnostics/fixes.rs
+++ /dev/null
@@ -1,31 +0,0 @@
1//! Provides a way to attach fixes to the diagnostics.
2//! The same module also has all curret custom fixes for the diagnostics implemented.
3mod change_case;
4mod create_field;
5mod fill_missing_fields;
6mod remove_semicolon;
7mod replace_with_find_map;
8mod unresolved_module;
9mod wrap_tail_expr;
10
11use hir::{diagnostics::Diagnostic, Semantics};
12use ide_assists::AssistResolveStrategy;
13use ide_db::RootDatabase;
14
15use crate::Assist;
16
17/// A [Diagnostic] that potentially has some fixes available.
18///
19/// [Diagnostic]: hir::diagnostics::Diagnostic
20pub(crate) trait DiagnosticWithFixes: Diagnostic {
21 /// `resolve` determines if the diagnostic should fill in the `edit` field
22 /// of the assist.
23 ///
24 /// If `resolve` is false, the edit will be computed later, on demand, and
25 /// can be omitted.
26 fn fixes(
27 &self,
28 sema: &Semantics<RootDatabase>,
29 _resolve: &AssistResolveStrategy,
30 ) -> Option<Vec<Assist>>;
31}
diff --git a/crates/ide/src/diagnostics/fixes/change_case.rs b/crates/ide/src/diagnostics/fixes/change_case.rs
deleted file mode 100644
index 42be3375f..000000000
--- a/crates/ide/src/diagnostics/fixes/change_case.rs
+++ /dev/null
@@ -1,155 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::IncorrectCase, InFile, Semantics};
2use ide_assists::{Assist, AssistResolveStrategy};
3use ide_db::{base_db::FilePosition, RootDatabase};
4use syntax::AstNode;
5
6use crate::{
7 diagnostics::{unresolved_fix, DiagnosticWithFixes},
8 references::rename::rename_with_semantics,
9};
10
11impl DiagnosticWithFixes for IncorrectCase {
12 fn fixes(
13 &self,
14 sema: &Semantics<RootDatabase>,
15 resolve: &AssistResolveStrategy,
16 ) -> Option<Vec<Assist>> {
17 let root = sema.db.parse_or_expand(self.file)?;
18 let name_node = self.ident.to_node(&root);
19
20 let name_node = InFile::new(self.file, name_node.syntax());
21 let frange = name_node.original_file_range(sema.db);
22 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
23
24 let label = format!("Rename to {}", self.suggested_text);
25 let mut res = unresolved_fix("change_case", &label, frange.range);
26 if resolve.should_resolve(&res.id) {
27 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
28 res.source_change = Some(source_change.ok().unwrap_or_default());
29 }
30
31 Some(vec![res])
32 }
33}
34
35#[cfg(test)]
36mod change_case {
37 use crate::{
38 diagnostics::tests::{check_fix, check_no_diagnostics},
39 fixture, AssistResolveStrategy, DiagnosticsConfig,
40 };
41
42 #[test]
43 fn test_rename_incorrect_case() {
44 check_fix(
45 r#"
46pub struct test_struct$0 { one: i32 }
47
48pub fn some_fn(val: test_struct) -> test_struct {
49 test_struct { one: val.one + 1 }
50}
51"#,
52 r#"
53pub struct TestStruct { one: i32 }
54
55pub fn some_fn(val: TestStruct) -> TestStruct {
56 TestStruct { one: val.one + 1 }
57}
58"#,
59 );
60
61 check_fix(
62 r#"
63pub fn some_fn(NonSnakeCase$0: u8) -> u8 {
64 NonSnakeCase
65}
66"#,
67 r#"
68pub fn some_fn(non_snake_case: u8) -> u8 {
69 non_snake_case
70}
71"#,
72 );
73
74 check_fix(
75 r#"
76pub fn SomeFn$0(val: u8) -> u8 {
77 if val != 0 { SomeFn(val - 1) } else { val }
78}
79"#,
80 r#"
81pub fn some_fn(val: u8) -> u8 {
82 if val != 0 { some_fn(val - 1) } else { val }
83}
84"#,
85 );
86
87 check_fix(
88 r#"
89fn some_fn() {
90 let whatAWeird_Formatting$0 = 10;
91 another_func(whatAWeird_Formatting);
92}
93"#,
94 r#"
95fn some_fn() {
96 let what_a_weird_formatting = 10;
97 another_func(what_a_weird_formatting);
98}
99"#,
100 );
101 }
102
103 #[test]
104 fn test_uppercase_const_no_diagnostics() {
105 check_no_diagnostics(
106 r#"
107fn foo() {
108 const ANOTHER_ITEM$0: &str = "some_item";
109}
110"#,
111 );
112 }
113
114 #[test]
115 fn test_rename_incorrect_case_struct_method() {
116 check_fix(
117 r#"
118pub struct TestStruct;
119
120impl TestStruct {
121 pub fn SomeFn$0() -> TestStruct {
122 TestStruct
123 }
124}
125"#,
126 r#"
127pub struct TestStruct;
128
129impl TestStruct {
130 pub fn some_fn() -> TestStruct {
131 TestStruct
132 }
133}
134"#,
135 );
136 }
137
138 #[test]
139 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
140 let input = r#"fn FOO$0() {}"#;
141 let expected = r#"fn foo() {}"#;
142
143 let (analysis, file_position) = fixture::position(input);
144 let diagnostics = analysis
145 .diagnostics(
146 &DiagnosticsConfig::default(),
147 AssistResolveStrategy::All,
148 file_position.file_id,
149 )
150 .unwrap();
151 assert_eq!(diagnostics.len(), 1);
152
153 check_fix(input, expected);
154 }
155}
diff --git a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs b/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs
deleted file mode 100644
index b5dd64c08..000000000
--- a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs
+++ /dev/null
@@ -1,217 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::MissingFields, Semantics};
2use ide_assists::AssistResolveStrategy;
3use ide_db::{source_change::SourceChange, RootDatabase};
4use syntax::{algo, ast::make, AstNode};
5use text_edit::TextEdit;
6
7use crate::{
8 diagnostics::{fix, fixes::DiagnosticWithFixes},
9 Assist,
10};
11
12impl DiagnosticWithFixes for MissingFields {
13 fn fixes(
14 &self,
15 sema: &Semantics<RootDatabase>,
16 _resolve: &AssistResolveStrategy,
17 ) -> Option<Vec<Assist>> {
18 // Note that although we could add a diagnostics to
19 // fill the missing tuple field, e.g :
20 // `struct A(usize);`
21 // `let a = A { 0: () }`
22 // but it is uncommon usage and it should not be encouraged.
23 if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
24 return None;
25 }
26
27 let root = sema.db.parse_or_expand(self.file)?;
28 let field_list_parent = self.field_list_parent.to_node(&root);
29 let old_field_list = field_list_parent.record_expr_field_list()?;
30 let new_field_list = old_field_list.clone_for_update();
31 for f in self.missed_fields.iter() {
32 let field =
33 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
34 .clone_for_update();
35 new_field_list.add_field(field);
36 }
37
38 let edit = {
39 let mut builder = TextEdit::builder();
40 algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
41 .into_text_edit(&mut builder);
42 builder.finish()
43 };
44 Some(vec![fix(
45 "fill_missing_fields",
46 "Fill struct fields",
47 SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
48 sema.original_range(&field_list_parent.syntax()).range,
49 )])
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
56
57 #[test]
58 fn test_fill_struct_fields_empty() {
59 check_fix(
60 r#"
61struct TestStruct { one: i32, two: i64 }
62
63fn test_fn() {
64 let s = TestStruct {$0};
65}
66"#,
67 r#"
68struct TestStruct { one: i32, two: i64 }
69
70fn test_fn() {
71 let s = TestStruct { one: (), two: () };
72}
73"#,
74 );
75 }
76
77 #[test]
78 fn test_fill_struct_fields_self() {
79 check_fix(
80 r#"
81struct TestStruct { one: i32 }
82
83impl TestStruct {
84 fn test_fn() { let s = Self {$0}; }
85}
86"#,
87 r#"
88struct TestStruct { one: i32 }
89
90impl TestStruct {
91 fn test_fn() { let s = Self { one: () }; }
92}
93"#,
94 );
95 }
96
97 #[test]
98 fn test_fill_struct_fields_enum() {
99 check_fix(
100 r#"
101enum Expr {
102 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
103}
104
105impl Expr {
106 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
107 Expr::Bin {$0 }
108 }
109}
110"#,
111 r#"
112enum Expr {
113 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
114}
115
116impl Expr {
117 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
118 Expr::Bin { lhs: (), rhs: () }
119 }
120}
121"#,
122 );
123 }
124
125 #[test]
126 fn test_fill_struct_fields_partial() {
127 check_fix(
128 r#"
129struct TestStruct { one: i32, two: i64 }
130
131fn test_fn() {
132 let s = TestStruct{ two: 2$0 };
133}
134"#,
135 r"
136struct TestStruct { one: i32, two: i64 }
137
138fn test_fn() {
139 let s = TestStruct{ two: 2, one: () };
140}
141",
142 );
143 }
144
145 #[test]
146 fn test_fill_struct_fields_raw_ident() {
147 check_fix(
148 r#"
149struct TestStruct { r#type: u8 }
150
151fn test_fn() {
152 TestStruct { $0 };
153}
154"#,
155 r"
156struct TestStruct { r#type: u8 }
157
158fn test_fn() {
159 TestStruct { r#type: () };
160}
161",
162 );
163 }
164
165 #[test]
166 fn test_fill_struct_fields_no_diagnostic() {
167 check_no_diagnostics(
168 r#"
169struct TestStruct { one: i32, two: i64 }
170
171fn test_fn() {
172 let one = 1;
173 let s = TestStruct{ one, two: 2 };
174}
175 "#,
176 );
177 }
178
179 #[test]
180 fn test_fill_struct_fields_no_diagnostic_on_spread() {
181 check_no_diagnostics(
182 r#"
183struct TestStruct { one: i32, two: i64 }
184
185fn test_fn() {
186 let one = 1;
187 let s = TestStruct{ ..a };
188}
189"#,
190 );
191 }
192
193 #[test]
194 fn test_fill_struct_fields_blank_line() {
195 check_fix(
196 r#"
197struct S { a: (), b: () }
198
199fn f() {
200 S {
201 $0
202 };
203}
204"#,
205 r#"
206struct S { a: (), b: () }
207
208fn f() {
209 S {
210 a: (),
211 b: (),
212 };
213}
214"#,
215 );
216 }
217}
diff --git a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs b/crates/ide/src/diagnostics/fixes/remove_semicolon.rs
deleted file mode 100644
index f1724d479..000000000
--- a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs
+++ /dev/null
@@ -1,41 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::RemoveThisSemicolon, Semantics};
2use ide_assists::{Assist, AssistResolveStrategy};
3use ide_db::{source_change::SourceChange, RootDatabase};
4use syntax::{ast, AstNode};
5use text_edit::TextEdit;
6
7use crate::diagnostics::{fix, DiagnosticWithFixes};
8
9impl DiagnosticWithFixes for RemoveThisSemicolon {
10 fn fixes(
11 &self,
12 sema: &Semantics<RootDatabase>,
13 _resolve: &AssistResolveStrategy,
14 ) -> Option<Vec<Assist>> {
15 let root = sema.db.parse_or_expand(self.file)?;
16
17 let semicolon = self
18 .expr
19 .to_node(&root)
20 .syntax()
21 .parent()
22 .and_then(ast::ExprStmt::cast)
23 .and_then(|expr| expr.semicolon_token())?
24 .text_range();
25
26 let edit = TextEdit::delete(semicolon);
27 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
28
29 Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)])
30 }
31}
32
33#[cfg(test)]
34mod tests {
35 use crate::diagnostics::tests::check_fix;
36
37 #[test]
38 fn remove_semicolon() {
39 check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
40 }
41}
diff --git a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs b/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs
deleted file mode 100644
index 444bf563b..000000000
--- a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs
+++ /dev/null
@@ -1,84 +0,0 @@
1use hir::{db::AstDatabase, diagnostics::ReplaceFilterMapNextWithFindMap, Semantics};
2use ide_assists::{Assist, AssistResolveStrategy};
3use ide_db::{source_change::SourceChange, RootDatabase};
4use syntax::{
5 ast::{self, ArgListOwner},
6 AstNode, TextRange,
7};
8use text_edit::TextEdit;
9
10use crate::diagnostics::{fix, DiagnosticWithFixes};
11
12impl DiagnosticWithFixes for ReplaceFilterMapNextWithFindMap {
13 fn fixes(
14 &self,
15 sema: &Semantics<RootDatabase>,
16 _resolve: &AssistResolveStrategy,
17 ) -> Option<Vec<Assist>> {
18 let root = sema.db.parse_or_expand(self.file)?;
19 let next_expr = self.next_expr.to_node(&root);
20 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
21
22 let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?;
23 let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range();
24 let filter_map_args = filter_map_call.arg_list()?;
25
26 let range_to_replace =
27 TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end());
28 let replacement = format!("find_map{}", filter_map_args.syntax().text());
29 let trigger_range = next_expr.syntax().text_range();
30
31 let edit = TextEdit::replace(range_to_replace, replacement);
32
33 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
34
35 Some(vec![fix(
36 "replace_with_find_map",
37 "Replace filter_map(..).next() with find_map()",
38 source_change,
39 trigger_range,
40 )])
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use crate::diagnostics::tests::check_fix;
47
48 #[test]
49 fn replace_with_wind_map() {
50 check_fix(
51 r#"
52//- /main.rs crate:main deps:core
53use core::iter::Iterator;
54use core::option::Option::{self, Some, None};
55fn foo() {
56 let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
57}
58//- /core/lib.rs crate:core
59pub mod option {
60 pub enum Option<T> { Some(T), None }
61}
62pub mod iter {
63 pub trait Iterator {
64 type Item;
65 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
66 fn next(&mut self) -> Option<Self::Item>;
67 }
68 pub struct FilterMap {}
69 impl Iterator for FilterMap {
70 type Item = i32;
71 fn next(&mut self) -> i32 { 7 }
72 }
73}
74"#,
75 r#"
76use core::iter::Iterator;
77use core::option::Option::{self, Some, None};
78fn foo() {
79 let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None });
80}
81"#,
82 )
83 }
84}
diff --git a/crates/ide/src/diagnostics/inactive_code.rs b/crates/ide/src/diagnostics/inactive_code.rs
new file mode 100644
index 000000000..d9d3e88c1
--- /dev/null
+++ b/crates/ide/src/diagnostics/inactive_code.rs
@@ -0,0 +1,119 @@
1use cfg::DnfExpr;
2use stdx::format_to;
3
4use crate::{
5 diagnostics::{Diagnostic, DiagnosticsContext},
6 Severity,
7};
8
9// Diagnostic: inactive-code
10//
11// This diagnostic is shown for code with inactive `#[cfg]` attributes.
12pub(super) fn inactive_code(
13 ctx: &DiagnosticsContext<'_>,
14 d: &hir::InactiveCode,
15) -> Option<Diagnostic> {
16 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
17 if d.node.file_id.expansion_info(ctx.sema.db).is_some() {
18 return None;
19 }
20
21 let inactive = DnfExpr::new(d.cfg.clone()).why_inactive(&d.opts);
22 let mut message = "code is inactive due to #[cfg] directives".to_string();
23
24 if let Some(inactive) = inactive {
25 format_to!(message, ": {}", inactive);
26 }
27
28 let res = Diagnostic::new(
29 "inactive-code",
30 message,
31 ctx.sema.diagnostics_display_range(d.node.clone()).range,
32 )
33 .severity(Severity::WeakWarning)
34 .with_unused(true);
35 Some(res)
36}
37
38#[cfg(test)]
39mod tests {
40 use crate::{diagnostics::tests::check_diagnostics_with_config, DiagnosticsConfig};
41
42 pub(crate) fn check(ra_fixture: &str) {
43 let config = DiagnosticsConfig::default();
44 check_diagnostics_with_config(config, ra_fixture)
45 }
46
47 #[test]
48 fn cfg_diagnostics() {
49 check(
50 r#"
51fn f() {
52 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
53
54 #[cfg(a)] fn f() {} // Item statement
55 //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
56 #[cfg(a)] {} // Expression statement
57 //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
58 #[cfg(a)] let x = 0; // let statement
59 //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
60
61 abc(#[cfg(a)] 0);
62 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
63 let x = Struct {
64 #[cfg(a)] f: 0,
65 //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
66 };
67 match () {
68 () => (),
69 #[cfg(a)] () => (),
70 //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
71 }
72
73 #[cfg(a)] 0 // Trailing expression of block
74 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
75}
76 "#,
77 );
78 }
79
80 #[test]
81 fn inactive_item() {
82 // Additional tests in `cfg` crate. This only tests disabled cfgs.
83
84 check(
85 r#"
86 #[cfg(no)] pub fn f() {}
87 //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
88
89 #[cfg(no)] #[cfg(no2)] mod m;
90 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled
91
92 #[cfg(all(not(a), b))] enum E {}
93 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled
94
95 #[cfg(feature = "std")] use std;
96 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
97"#,
98 );
99 }
100
101 /// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
102 #[test]
103 fn inactive_via_cfg_attr() {
104 cov_mark::check!(cfg_attr_active);
105 check(
106 r#"
107 #[cfg_attr(not(never), cfg(no))] fn f() {}
108 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
109
110 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
111
112 #[cfg_attr(never, cfg(no))] fn g() {}
113
114 #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
115 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
116"#,
117 );
118 }
119}
diff --git a/crates/ide/src/diagnostics/incorrect_case.rs b/crates/ide/src/diagnostics/incorrect_case.rs
new file mode 100644
index 000000000..832394400
--- /dev/null
+++ b/crates/ide/src/diagnostics/incorrect_case.rs
@@ -0,0 +1,488 @@
1use hir::{db::AstDatabase, InFile};
2use ide_assists::Assist;
3use ide_db::base_db::FilePosition;
4use syntax::AstNode;
5
6use crate::{
7 diagnostics::{unresolved_fix, Diagnostic, DiagnosticsContext},
8 references::rename::rename_with_semantics,
9 Severity,
10};
11
12// Diagnostic: incorrect-ident-case
13//
14// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
15pub(super) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
16 Diagnostic::new(
17 "incorrect-ident-case",
18 format!(
19 "{} `{}` should have {} name, e.g. `{}`",
20 d.ident_type, d.ident_text, d.expected_case, d.suggested_text
21 ),
22 ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range,
23 )
24 .severity(Severity::WeakWarning)
25 .with_fixes(fixes(ctx, d))
26}
27
28fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
29 let root = ctx.sema.db.parse_or_expand(d.file)?;
30 let name_node = d.ident.to_node(&root);
31
32 let name_node = InFile::new(d.file, name_node.syntax());
33 let frange = name_node.original_file_range(ctx.sema.db);
34 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
35
36 let label = format!("Rename to {}", d.suggested_text);
37 let mut res = unresolved_fix("change_case", &label, frange.range);
38 if ctx.resolve.should_resolve(&res.id) {
39 let source_change = rename_with_semantics(&ctx.sema, file_position, &d.suggested_text);
40 res.source_change = Some(source_change.ok().unwrap_or_default());
41 }
42
43 Some(vec![res])
44}
45
46#[cfg(test)]
47mod change_case {
48 use crate::{
49 diagnostics::tests::{check_diagnostics, check_fix},
50 fixture, AssistResolveStrategy, DiagnosticsConfig,
51 };
52
53 #[test]
54 fn test_rename_incorrect_case() {
55 check_fix(
56 r#"
57pub struct test_struct$0 { one: i32 }
58
59pub fn some_fn(val: test_struct) -> test_struct {
60 test_struct { one: val.one + 1 }
61}
62"#,
63 r#"
64pub struct TestStruct { one: i32 }
65
66pub fn some_fn(val: TestStruct) -> TestStruct {
67 TestStruct { one: val.one + 1 }
68}
69"#,
70 );
71
72 check_fix(
73 r#"
74pub fn some_fn(NonSnakeCase$0: u8) -> u8 {
75 NonSnakeCase
76}
77"#,
78 r#"
79pub fn some_fn(non_snake_case: u8) -> u8 {
80 non_snake_case
81}
82"#,
83 );
84
85 check_fix(
86 r#"
87pub fn SomeFn$0(val: u8) -> u8 {
88 if val != 0 { SomeFn(val - 1) } else { val }
89}
90"#,
91 r#"
92pub fn some_fn(val: u8) -> u8 {
93 if val != 0 { some_fn(val - 1) } else { val }
94}
95"#,
96 );
97
98 check_fix(
99 r#"
100fn some_fn() {
101 let whatAWeird_Formatting$0 = 10;
102 another_func(whatAWeird_Formatting);
103}
104"#,
105 r#"
106fn some_fn() {
107 let what_a_weird_formatting = 10;
108 another_func(what_a_weird_formatting);
109}
110"#,
111 );
112 }
113
114 #[test]
115 fn test_uppercase_const_no_diagnostics() {
116 check_diagnostics(
117 r#"
118fn foo() {
119 const ANOTHER_ITEM$0: &str = "some_item";
120}
121"#,
122 );
123 }
124
125 #[test]
126 fn test_rename_incorrect_case_struct_method() {
127 check_fix(
128 r#"
129pub struct TestStruct;
130
131impl TestStruct {
132 pub fn SomeFn$0() -> TestStruct {
133 TestStruct
134 }
135}
136"#,
137 r#"
138pub struct TestStruct;
139
140impl TestStruct {
141 pub fn some_fn() -> TestStruct {
142 TestStruct
143 }
144}
145"#,
146 );
147 }
148
149 #[test]
150 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
151 let input = r#"fn FOO$0() {}"#;
152 let expected = r#"fn foo() {}"#;
153
154 let (analysis, file_position) = fixture::position(input);
155 let diagnostics = analysis
156 .diagnostics(
157 &DiagnosticsConfig::default(),
158 AssistResolveStrategy::All,
159 file_position.file_id,
160 )
161 .unwrap();
162 assert_eq!(diagnostics.len(), 1);
163
164 check_fix(input, expected);
165 }
166
167 #[test]
168 fn incorrect_function_name() {
169 check_diagnostics(
170 r#"
171fn NonSnakeCaseName() {}
172// ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
173"#,
174 );
175 }
176
177 #[test]
178 fn incorrect_function_params() {
179 check_diagnostics(
180 r#"
181fn foo(SomeParam: u8) {}
182 // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param`
183
184fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
185 // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
186"#,
187 );
188 }
189
190 #[test]
191 fn incorrect_variable_names() {
192 check_diagnostics(
193 r#"
194fn foo() {
195 let SOME_VALUE = 10;
196 // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
197 let AnotherValue = 20;
198 // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value`
199}
200"#,
201 );
202 }
203
204 #[test]
205 fn incorrect_struct_names() {
206 check_diagnostics(
207 r#"
208struct non_camel_case_name {}
209 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
210
211struct SCREAMING_CASE {}
212 // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
213"#,
214 );
215 }
216
217 #[test]
218 fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() {
219 check_diagnostics(
220 r#"
221struct AABB {}
222"#,
223 );
224 }
225
226 #[test]
227 fn incorrect_struct_field() {
228 check_diagnostics(
229 r#"
230struct SomeStruct { SomeField: u8 }
231 // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field`
232"#,
233 );
234 }
235
236 #[test]
237 fn incorrect_enum_names() {
238 check_diagnostics(
239 r#"
240enum some_enum { Val(u8) }
241 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
242
243enum SOME_ENUM {}
244 // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
245"#,
246 );
247 }
248
249 #[test]
250 fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() {
251 check_diagnostics(
252 r#"
253enum AABB {}
254"#,
255 );
256 }
257
258 #[test]
259 fn incorrect_enum_variant_name() {
260 check_diagnostics(
261 r#"
262enum SomeEnum { SOME_VARIANT(u8) }
263 // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
264"#,
265 );
266 }
267
268 #[test]
269 fn incorrect_const_name() {
270 check_diagnostics(
271 r#"
272const some_weird_const: u8 = 10;
273 // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
274"#,
275 );
276 }
277
278 #[test]
279 fn incorrect_static_name() {
280 check_diagnostics(
281 r#"
282static some_weird_const: u8 = 10;
283 // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
284"#,
285 );
286 }
287
288 #[test]
289 fn fn_inside_impl_struct() {
290 check_diagnostics(
291 r#"
292struct someStruct;
293 // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
294
295impl someStruct {
296 fn SomeFunc(&self) {
297 // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func`
298 let WHY_VAR_IS_CAPS = 10;
299 // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
300 }
301}
302"#,
303 );
304 }
305
306 #[test]
307 fn no_diagnostic_for_enum_varinats() {
308 check_diagnostics(
309 r#"
310enum Option { Some, None }
311
312fn main() {
313 match Option::None {
314 None => (),
315 Some => (),
316 }
317}
318"#,
319 );
320 }
321
322 #[test]
323 fn non_let_bind() {
324 check_diagnostics(
325 r#"
326enum Option { Some, None }
327
328fn main() {
329 match Option::None {
330 SOME_VAR @ None => (),
331 // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
332 Some => (),
333 }
334}
335"#,
336 );
337 }
338
339 #[test]
340 fn allow_attributes_crate_attr() {
341 check_diagnostics(
342 r#"
343#![allow(non_snake_case)]
344
345mod F {
346 fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
347}
348 "#,
349 );
350 }
351
352 #[test]
353 #[ignore]
354 fn bug_trait_inside_fn() {
355 // FIXME:
356 // This is broken, and in fact, should not even be looked at by this
357 // lint in the first place. There's weird stuff going on in the
358 // collection phase.
359 // It's currently being brought in by:
360 // * validate_func on `a` recursing into modules
361 // * then it finds the trait and then the function while iterating
362 // through modules
363 // * then validate_func is called on Dirty
364 // * ... which then proceeds to look at some unknown module taking no
365 // attrs from either the impl or the fn a, and then finally to the root
366 // module
367 //
368 // It should find the attribute on the trait, but it *doesn't even see
369 // the trait* as far as I can tell.
370
371 check_diagnostics(
372 r#"
373trait T { fn a(); }
374struct U {}
375impl T for U {
376 fn a() {
377 // this comes out of bitflags, mostly
378 #[allow(non_snake_case)]
379 trait __BitFlags {
380 const HiImAlsoBad: u8 = 2;
381 #[inline]
382 fn Dirty(&self) -> bool {
383 false
384 }
385 }
386
387 }
388}
389 "#,
390 );
391 }
392
393 #[test]
394 fn infinite_loop_inner_items() {
395 check_diagnostics(
396 r#"
397fn qualify() {
398 mod foo {
399 use super::*;
400 }
401}
402 "#,
403 )
404 }
405
406 #[test] // Issue #8809.
407 fn parenthesized_parameter() {
408 check_diagnostics(r#"fn f((O): _) {}"#)
409 }
410
411 #[test]
412 fn ignores_extern_items() {
413 cov_mark::check!(extern_func_incorrect_case_ignored);
414 cov_mark::check!(extern_static_incorrect_case_ignored);
415 check_diagnostics(
416 r#"
417extern {
418 fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
419 pub static SomeStatic: u8 = 10;
420}
421 "#,
422 );
423 }
424
425 #[test]
426 #[ignore]
427 fn bug_traits_arent_checked() {
428 // FIXME: Traits and functions in traits aren't currently checked by
429 // r-a, even though rustc will complain about them.
430 check_diagnostics(
431 r#"
432trait BAD_TRAIT {
433 // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
434 fn BAD_FUNCTION();
435 // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
436 fn BadFunction();
437 // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function`
438}
439 "#,
440 );
441 }
442
443 #[test]
444 fn allow_attributes() {
445 check_diagnostics(
446 r#"
447#[allow(non_snake_case)]
448fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
449 // cov_flags generated output from elsewhere in this file
450 extern "C" {
451 #[no_mangle]
452 static lower_case: u8;
453 }
454
455 let OtherVar = SOME_VAR + 1;
456 OtherVar
457}
458
459#[allow(nonstandard_style)]
460mod CheckNonstandardStyle {
461 fn HiImABadFnName() {}
462}
463
464#[allow(bad_style)]
465mod CheckBadStyle {
466 fn HiImABadFnName() {}
467}
468
469mod F {
470 #![allow(non_snake_case)]
471 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
472}
473
474#[allow(non_snake_case, non_camel_case_types)]
475pub struct some_type {
476 SOME_FIELD: u8,
477 SomeField: u16,
478}
479
480#[allow(non_upper_case_globals)]
481pub const some_const: u8 = 10;
482
483#[allow(non_upper_case_globals)]
484pub static SomeStatic: u8 = 10;
485 "#,
486 );
487 }
488}
diff --git a/crates/ide/src/diagnostics/macro_error.rs b/crates/ide/src/diagnostics/macro_error.rs
new file mode 100644
index 000000000..5f97f190d
--- /dev/null
+++ b/crates/ide/src/diagnostics/macro_error.rs
@@ -0,0 +1,173 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: macro-error
4//
5// This diagnostic is shown for macro expansion errors.
6pub(super) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
7 Diagnostic::new(
8 "macro-error",
9 d.message.clone(),
10 ctx.sema.diagnostics_display_range(d.node.clone()).range,
11 )
12 .experimental()
13}
14
15#[cfg(test)]
16mod tests {
17 use crate::{
18 diagnostics::tests::{check_diagnostics, check_diagnostics_with_config},
19 DiagnosticsConfig,
20 };
21
22 #[test]
23 fn builtin_macro_fails_expansion() {
24 check_diagnostics(
25 r#"
26#[rustc_builtin_macro]
27macro_rules! include { () => {} }
28
29 include!("doesntexist");
30//^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist`
31 "#,
32 );
33 }
34
35 #[test]
36 fn include_macro_should_allow_empty_content() {
37 let mut config = DiagnosticsConfig::default();
38
39 // FIXME: This is a false-positive, the file is actually linked in via
40 // `include!` macro
41 config.disabled.insert("unlinked-file".to_string());
42
43 check_diagnostics_with_config(
44 config,
45 r#"
46//- /lib.rs
47#[rustc_builtin_macro]
48macro_rules! include { () => {} }
49
50include!("foo/bar.rs");
51//- /foo/bar.rs
52// empty
53"#,
54 );
55 }
56
57 #[test]
58 fn good_out_dir_diagnostic() {
59 check_diagnostics(
60 r#"
61#[rustc_builtin_macro]
62macro_rules! include { () => {} }
63#[rustc_builtin_macro]
64macro_rules! env { () => {} }
65#[rustc_builtin_macro]
66macro_rules! concat { () => {} }
67
68 include!(concat!(env!("OUT_DIR"), "/out.rs"));
69//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
70"#,
71 );
72 }
73
74 #[test]
75 fn register_attr_and_tool() {
76 cov_mark::check!(register_attr);
77 cov_mark::check!(register_tool);
78 check_diagnostics(
79 r#"
80#![register_tool(tool)]
81#![register_attr(attr)]
82
83#[tool::path]
84#[attr]
85struct S;
86"#,
87 );
88 // NB: we don't currently emit diagnostics here
89 }
90
91 #[test]
92 fn macro_diag_builtin() {
93 check_diagnostics(
94 r#"
95#[rustc_builtin_macro]
96macro_rules! env {}
97
98#[rustc_builtin_macro]
99macro_rules! include {}
100
101#[rustc_builtin_macro]
102macro_rules! compile_error {}
103
104#[rustc_builtin_macro]
105macro_rules! format_args { () => {} }
106
107fn main() {
108 // Test a handful of built-in (eager) macros:
109
110 include!(invalid);
111 //^^^^^^^^^^^^^^^^^ could not convert tokens
112 include!("does not exist");
113 //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist`
114
115 env!(invalid);
116 //^^^^^^^^^^^^^ could not convert tokens
117
118 env!("OUT_DIR");
119 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
120
121 compile_error!("compile_error works");
122 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
123
124 // Lazy:
125
126 format_args!();
127 //^^^^^^^^^^^^^^ no rule matches input tokens
128}
129"#,
130 );
131 }
132
133 #[test]
134 fn macro_rules_diag() {
135 check_diagnostics(
136 r#"
137macro_rules! m {
138 () => {};
139}
140fn f() {
141 m!();
142
143 m!(hi);
144 //^^^^^^ leftover tokens
145}
146 "#,
147 );
148 }
149 #[test]
150 fn dollar_crate_in_builtin_macro() {
151 check_diagnostics(
152 r#"
153#[macro_export]
154#[rustc_builtin_macro]
155macro_rules! format_args {}
156
157#[macro_export]
158macro_rules! arg { () => {} }
159
160#[macro_export]
161macro_rules! outer {
162 () => {
163 $crate::format_args!( "", $crate::arg!(1) )
164 };
165}
166
167fn f() {
168 outer!();
169} //^^^^^^^^ leftover tokens
170"#,
171 )
172 }
173}
diff --git a/crates/ide/src/diagnostics/mismatched_arg_count.rs b/crates/ide/src/diagnostics/mismatched_arg_count.rs
new file mode 100644
index 000000000..08e1cfa5f
--- /dev/null
+++ b/crates/ide/src/diagnostics/mismatched_arg_count.rs
@@ -0,0 +1,272 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: mismatched-arg-count
4//
5// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
6pub(super) fn mismatched_arg_count(
7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::MismatchedArgCount,
9) -> Diagnostic {
10 let s = if d.expected == 1 { "" } else { "s" };
11 let message = format!("expected {} argument{}, found {}", d.expected, s, d.found);
12 Diagnostic::new(
13 "mismatched-arg-count",
14 message,
15 ctx.sema.diagnostics_display_range(d.call_expr.clone().map(|it| it.into())).range,
16 )
17}
18
19#[cfg(test)]
20mod tests {
21 use crate::diagnostics::tests::check_diagnostics;
22
23 #[test]
24 fn simple_free_fn_zero() {
25 check_diagnostics(
26 r#"
27fn zero() {}
28fn f() { zero(1); }
29 //^^^^^^^ expected 0 arguments, found 1
30"#,
31 );
32
33 check_diagnostics(
34 r#"
35fn zero() {}
36fn f() { zero(); }
37"#,
38 );
39 }
40
41 #[test]
42 fn simple_free_fn_one() {
43 check_diagnostics(
44 r#"
45fn one(arg: u8) {}
46fn f() { one(); }
47 //^^^^^ expected 1 argument, found 0
48"#,
49 );
50
51 check_diagnostics(
52 r#"
53fn one(arg: u8) {}
54fn f() { one(1); }
55"#,
56 );
57 }
58
59 #[test]
60 fn method_as_fn() {
61 check_diagnostics(
62 r#"
63struct S;
64impl S { fn method(&self) {} }
65
66fn f() {
67 S::method();
68} //^^^^^^^^^^^ expected 1 argument, found 0
69"#,
70 );
71
72 check_diagnostics(
73 r#"
74struct S;
75impl S { fn method(&self) {} }
76
77fn f() {
78 S::method(&S);
79 S.method();
80}
81"#,
82 );
83 }
84
85 #[test]
86 fn method_with_arg() {
87 check_diagnostics(
88 r#"
89struct S;
90impl S { fn method(&self, arg: u8) {} }
91
92 fn f() {
93 S.method();
94 } //^^^^^^^^^^ expected 1 argument, found 0
95 "#,
96 );
97
98 check_diagnostics(
99 r#"
100struct S;
101impl S { fn method(&self, arg: u8) {} }
102
103fn f() {
104 S::method(&S, 0);
105 S.method(1);
106}
107"#,
108 );
109 }
110
111 #[test]
112 fn method_unknown_receiver() {
113 // note: this is incorrect code, so there might be errors on this in the
114 // future, but we shouldn't emit an argument count diagnostic here
115 check_diagnostics(
116 r#"
117trait Foo { fn method(&self, arg: usize) {} }
118
119fn f() {
120 let x;
121 x.method();
122}
123"#,
124 );
125 }
126
127 #[test]
128 fn tuple_struct() {
129 check_diagnostics(
130 r#"
131struct Tup(u8, u16);
132fn f() {
133 Tup(0);
134} //^^^^^^ expected 2 arguments, found 1
135"#,
136 )
137 }
138
139 #[test]
140 fn enum_variant() {
141 check_diagnostics(
142 r#"
143enum En { Variant(u8, u16), }
144fn f() {
145 En::Variant(0);
146} //^^^^^^^^^^^^^^ expected 2 arguments, found 1
147"#,
148 )
149 }
150
151 #[test]
152 fn enum_variant_type_macro() {
153 check_diagnostics(
154 r#"
155macro_rules! Type {
156 () => { u32 };
157}
158enum Foo {
159 Bar(Type![])
160}
161impl Foo {
162 fn new() {
163 Foo::Bar(0);
164 Foo::Bar(0, 1);
165 //^^^^^^^^^^^^^^ expected 1 argument, found 2
166 Foo::Bar();
167 //^^^^^^^^^^ expected 1 argument, found 0
168 }
169}
170 "#,
171 );
172 }
173
174 #[test]
175 fn varargs() {
176 check_diagnostics(
177 r#"
178extern "C" {
179 fn fixed(fixed: u8);
180 fn varargs(fixed: u8, ...);
181 fn varargs2(...);
182}
183
184fn f() {
185 unsafe {
186 fixed(0);
187 fixed(0, 1);
188 //^^^^^^^^^^^ expected 1 argument, found 2
189 varargs(0);
190 varargs(0, 1);
191 varargs2();
192 varargs2(0);
193 varargs2(0, 1);
194 }
195}
196 "#,
197 )
198 }
199
200 #[test]
201 fn arg_count_lambda() {
202 check_diagnostics(
203 r#"
204fn main() {
205 let f = |()| ();
206 f();
207 //^^^ expected 1 argument, found 0
208 f(());
209 f((), ());
210 //^^^^^^^^^ expected 1 argument, found 2
211}
212"#,
213 )
214 }
215
216 #[test]
217 fn cfgd_out_call_arguments() {
218 check_diagnostics(
219 r#"
220struct C(#[cfg(FALSE)] ());
221impl C {
222 fn new() -> Self {
223 Self(
224 #[cfg(FALSE)]
225 (),
226 )
227 }
228
229 fn method(&self) {}
230}
231
232fn main() {
233 C::new().method(#[cfg(FALSE)] 0);
234}
235 "#,
236 );
237 }
238
239 #[test]
240 fn cfgd_out_fn_params() {
241 check_diagnostics(
242 r#"
243fn foo(#[cfg(NEVER)] x: ()) {}
244
245struct S;
246
247impl S {
248 fn method(#[cfg(NEVER)] self) {}
249 fn method2(#[cfg(NEVER)] self, arg: u8) {}
250 fn method3(self, #[cfg(NEVER)] arg: u8) {}
251}
252
253extern "C" {
254 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
255 fn varargs(#[cfg(not(NEVER))] ...);
256}
257
258fn main() {
259 foo();
260 S::method();
261 S::method2(0);
262 S::method3(S);
263 S.method3();
264 unsafe {
265 fixed(0);
266 varargs(1, 2, 3);
267 }
268}
269 "#,
270 )
271 }
272}
diff --git a/crates/ide/src/diagnostics/missing_fields.rs b/crates/ide/src/diagnostics/missing_fields.rs
new file mode 100644
index 000000000..d01f05041
--- /dev/null
+++ b/crates/ide/src/diagnostics/missing_fields.rs
@@ -0,0 +1,327 @@
1use either::Either;
2use hir::{db::AstDatabase, InFile};
3use ide_assists::Assist;
4use ide_db::source_change::SourceChange;
5use stdx::format_to;
6use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
7use text_edit::TextEdit;
8
9use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
10
11// Diagnostic: missing-fields
12//
13// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
14//
15// Example:
16//
17// ```rust
18// struct A { a: u8, b: u8 }
19//
20// let a = A { a: 10 };
21// ```
22pub(super) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
23 let mut message = String::from("Missing structure fields:\n");
24 for field in &d.missed_fields {
25 format_to!(message, "- {}\n", field);
26 }
27
28 let ptr = InFile::new(
29 d.file,
30 d.field_list_parent_path
31 .clone()
32 .map(SyntaxNodePtr::from)
33 .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())),
34 );
35
36 Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range)
37 .with_fixes(fixes(ctx, d))
38}
39
40fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
41 // Note that although we could add a diagnostics to
42 // fill the missing tuple field, e.g :
43 // `struct A(usize);`
44 // `let a = A { 0: () }`
45 // but it is uncommon usage and it should not be encouraged.
46 if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
47 return None;
48 }
49
50 let root = ctx.sema.db.parse_or_expand(d.file)?;
51 let field_list_parent = match &d.field_list_parent {
52 Either::Left(record_expr) => record_expr.to_node(&root),
53 // FIXE: patterns should be fixable as well.
54 Either::Right(_) => return None,
55 };
56 let old_field_list = field_list_parent.record_expr_field_list()?;
57 let new_field_list = old_field_list.clone_for_update();
58 for f in d.missed_fields.iter() {
59 let field =
60 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
61 .clone_for_update();
62 new_field_list.add_field(field);
63 }
64
65 let edit = {
66 let mut builder = TextEdit::builder();
67 algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder);
68 builder.finish()
69 };
70 Some(vec![fix(
71 "fill_missing_fields",
72 "Fill struct fields",
73 SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit),
74 ctx.sema.original_range(field_list_parent.syntax()).range,
75 )])
76}
77
78#[cfg(test)]
79mod tests {
80 use crate::diagnostics::tests::{check_diagnostics, check_fix};
81
82 #[test]
83 fn missing_record_pat_field_diagnostic() {
84 check_diagnostics(
85 r#"
86struct S { foo: i32, bar: () }
87fn baz(s: S) {
88 let S { foo: _ } = s;
89 //^ Missing structure fields:
90 //| - bar
91}
92"#,
93 );
94 }
95
96 #[test]
97 fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
98 check_diagnostics(
99 r"
100struct S { foo: i32, bar: () }
101fn baz(s: S) -> i32 {
102 match s {
103 S { foo, .. } => foo,
104 }
105}
106",
107 )
108 }
109
110 #[test]
111 fn missing_record_pat_field_box() {
112 check_diagnostics(
113 r"
114struct S { s: Box<u32> }
115fn x(a: S) {
116 let S { box s } = a;
117}
118",
119 )
120 }
121
122 #[test]
123 fn missing_record_pat_field_ref() {
124 check_diagnostics(
125 r"
126struct S { s: u32 }
127fn x(a: S) {
128 let S { ref s } = a;
129}
130",
131 )
132 }
133
134 #[test]
135 fn range_mapping_out_of_macros() {
136 // FIXME: this is very wrong, but somewhat tricky to fix.
137 check_fix(
138 r#"
139fn some() {}
140fn items() {}
141fn here() {}
142
143macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
144
145fn main() {
146 let _x = id![Foo { a: $042 }];
147}
148
149pub struct Foo { pub a: i32, pub b: i32 }
150"#,
151 r#"
152fn some(, b: () ) {}
153fn items() {}
154fn here() {}
155
156macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
157
158fn main() {
159 let _x = id![Foo { a: 42 }];
160}
161
162pub struct Foo { pub a: i32, pub b: i32 }
163"#,
164 );
165 }
166
167 #[test]
168 fn test_fill_struct_fields_empty() {
169 check_fix(
170 r#"
171struct TestStruct { one: i32, two: i64 }
172
173fn test_fn() {
174 let s = TestStruct {$0};
175}
176"#,
177 r#"
178struct TestStruct { one: i32, two: i64 }
179
180fn test_fn() {
181 let s = TestStruct { one: (), two: () };
182}
183"#,
184 );
185 }
186
187 #[test]
188 fn test_fill_struct_fields_self() {
189 check_fix(
190 r#"
191struct TestStruct { one: i32 }
192
193impl TestStruct {
194 fn test_fn() { let s = Self {$0}; }
195}
196"#,
197 r#"
198struct TestStruct { one: i32 }
199
200impl TestStruct {
201 fn test_fn() { let s = Self { one: () }; }
202}
203"#,
204 );
205 }
206
207 #[test]
208 fn test_fill_struct_fields_enum() {
209 check_fix(
210 r#"
211enum Expr {
212 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
213}
214
215impl Expr {
216 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
217 Expr::Bin {$0 }
218 }
219}
220"#,
221 r#"
222enum Expr {
223 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
224}
225
226impl Expr {
227 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
228 Expr::Bin { lhs: (), rhs: () }
229 }
230}
231"#,
232 );
233 }
234
235 #[test]
236 fn test_fill_struct_fields_partial() {
237 check_fix(
238 r#"
239struct TestStruct { one: i32, two: i64 }
240
241fn test_fn() {
242 let s = TestStruct{ two: 2$0 };
243}
244"#,
245 r"
246struct TestStruct { one: i32, two: i64 }
247
248fn test_fn() {
249 let s = TestStruct{ two: 2, one: () };
250}
251",
252 );
253 }
254
255 #[test]
256 fn test_fill_struct_fields_raw_ident() {
257 check_fix(
258 r#"
259struct TestStruct { r#type: u8 }
260
261fn test_fn() {
262 TestStruct { $0 };
263}
264"#,
265 r"
266struct TestStruct { r#type: u8 }
267
268fn test_fn() {
269 TestStruct { r#type: () };
270}
271",
272 );
273 }
274
275 #[test]
276 fn test_fill_struct_fields_no_diagnostic() {
277 check_diagnostics(
278 r#"
279struct TestStruct { one: i32, two: i64 }
280
281fn test_fn() {
282 let one = 1;
283 let s = TestStruct{ one, two: 2 };
284}
285 "#,
286 );
287 }
288
289 #[test]
290 fn test_fill_struct_fields_no_diagnostic_on_spread() {
291 check_diagnostics(
292 r#"
293struct TestStruct { one: i32, two: i64 }
294
295fn test_fn() {
296 let one = 1;
297 let s = TestStruct{ ..a };
298}
299"#,
300 );
301 }
302
303 #[test]
304 fn test_fill_struct_fields_blank_line() {
305 check_fix(
306 r#"
307struct S { a: (), b: () }
308
309fn f() {
310 S {
311 $0
312 };
313}
314"#,
315 r#"
316struct S { a: (), b: () }
317
318fn f() {
319 S {
320 a: (),
321 b: (),
322 };
323}
324"#,
325 );
326 }
327}
diff --git a/crates/ide/src/diagnostics/missing_match_arms.rs b/crates/ide/src/diagnostics/missing_match_arms.rs
new file mode 100644
index 000000000..b636489b3
--- /dev/null
+++ b/crates/ide/src/diagnostics/missing_match_arms.rs
@@ -0,0 +1,929 @@
1use hir::InFile;
2
3use crate::diagnostics::{Diagnostic, DiagnosticsContext};
4
5// Diagnostic: missing-match-arm
6//
7// This diagnostic is triggered if `match` block is missing one or more match arms.
8pub(super) fn missing_match_arms(
9 ctx: &DiagnosticsContext<'_>,
10 d: &hir::MissingMatchArms,
11) -> Diagnostic {
12 Diagnostic::new(
13 "missing-match-arm",
14 "missing match arm",
15 ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range,
16 )
17}
18
19#[cfg(test)]
20pub(super) mod tests {
21 use crate::diagnostics::tests::check_diagnostics;
22
23 fn check_diagnostics_no_bails(ra_fixture: &str) {
24 cov_mark::check_count!(validate_match_bailed_out, 0);
25 crate::diagnostics::tests::check_diagnostics(ra_fixture)
26 }
27
28 #[test]
29 fn empty_tuple() {
30 check_diagnostics_no_bails(
31 r#"
32fn main() {
33 match () { }
34 //^^ missing match arm
35 match (()) { }
36 //^^^^ missing match arm
37
38 match () { _ => (), }
39 match () { () => (), }
40 match (()) { (()) => (), }
41}
42"#,
43 );
44 }
45
46 #[test]
47 fn tuple_of_two_empty_tuple() {
48 check_diagnostics_no_bails(
49 r#"
50fn main() {
51 match ((), ()) { }
52 //^^^^^^^^ missing match arm
53
54 match ((), ()) { ((), ()) => (), }
55}
56"#,
57 );
58 }
59
60 #[test]
61 fn boolean() {
62 check_diagnostics_no_bails(
63 r#"
64fn test_main() {
65 match false { }
66 //^^^^^ missing match arm
67 match false { true => (), }
68 //^^^^^ missing match arm
69 match (false, true) {}
70 //^^^^^^^^^^^^^ missing match arm
71 match (false, true) { (true, true) => (), }
72 //^^^^^^^^^^^^^ missing match arm
73 match (false, true) {
74 //^^^^^^^^^^^^^ missing match arm
75 (false, true) => (),
76 (false, false) => (),
77 (true, false) => (),
78 }
79 match (false, true) { (true, _x) => (), }
80 //^^^^^^^^^^^^^ missing match arm
81
82 match false { true => (), false => (), }
83 match (false, true) {
84 (false, _) => (),
85 (true, false) => (),
86 (_, true) => (),
87 }
88 match (false, true) {
89 (true, true) => (),
90 (true, false) => (),
91 (false, true) => (),
92 (false, false) => (),
93 }
94 match (false, true) {
95 (true, _x) => (),
96 (false, true) => (),
97 (false, false) => (),
98 }
99 match (false, true, false) {
100 (false, ..) => (),
101 (true, ..) => (),
102 }
103 match (false, true, false) {
104 (.., false) => (),
105 (.., true) => (),
106 }
107 match (false, true, false) { (..) => (), }
108}
109"#,
110 );
111 }
112
113 #[test]
114 fn tuple_of_tuple_and_bools() {
115 check_diagnostics_no_bails(
116 r#"
117fn main() {
118 match (false, ((), false)) {}
119 //^^^^^^^^^^^^^^^^^^^^ missing match arm
120 match (false, ((), false)) { (true, ((), true)) => (), }
121 //^^^^^^^^^^^^^^^^^^^^ missing match arm
122 match (false, ((), false)) { (true, _) => (), }
123 //^^^^^^^^^^^^^^^^^^^^ missing match arm
124
125 match (false, ((), false)) {
126 (true, ((), true)) => (),
127 (true, ((), false)) => (),
128 (false, ((), true)) => (),
129 (false, ((), false)) => (),
130 }
131 match (false, ((), false)) {
132 (true, ((), true)) => (),
133 (true, ((), false)) => (),
134 (false, _) => (),
135 }
136}
137"#,
138 );
139 }
140
141 #[test]
142 fn enums() {
143 check_diagnostics_no_bails(
144 r#"
145enum Either { A, B, }
146
147fn main() {
148 match Either::A { }
149 //^^^^^^^^^ missing match arm
150 match Either::B { Either::A => (), }
151 //^^^^^^^^^ missing match arm
152
153 match &Either::B {
154 //^^^^^^^^^^ missing match arm
155 Either::A => (),
156 }
157
158 match Either::B {
159 Either::A => (), Either::B => (),
160 }
161 match &Either::B {
162 Either::A => (), Either::B => (),
163 }
164}
165"#,
166 );
167 }
168
169 #[test]
170 fn enum_containing_bool() {
171 check_diagnostics_no_bails(
172 r#"
173enum Either { A(bool), B }
174
175fn main() {
176 match Either::B { }
177 //^^^^^^^^^ missing match arm
178 match Either::B {
179 //^^^^^^^^^ missing match arm
180 Either::A(true) => (), Either::B => ()
181 }
182
183 match Either::B {
184 Either::A(true) => (),
185 Either::A(false) => (),
186 Either::B => (),
187 }
188 match Either::B {
189 Either::B => (),
190 _ => (),
191 }
192 match Either::B {
193 Either::A(_) => (),
194 Either::B => (),
195 }
196
197}
198 "#,
199 );
200 }
201
202 #[test]
203 fn enum_different_sizes() {
204 check_diagnostics_no_bails(
205 r#"
206enum Either { A(bool), B(bool, bool) }
207
208fn main() {
209 match Either::A(false) {
210 //^^^^^^^^^^^^^^^^ missing match arm
211 Either::A(_) => (),
212 Either::B(false, _) => (),
213 }
214
215 match Either::A(false) {
216 Either::A(_) => (),
217 Either::B(true, _) => (),
218 Either::B(false, _) => (),
219 }
220 match Either::A(false) {
221 Either::A(true) | Either::A(false) => (),
222 Either::B(true, _) => (),
223 Either::B(false, _) => (),
224 }
225}
226"#,
227 );
228 }
229
230 #[test]
231 fn tuple_of_enum_no_diagnostic() {
232 check_diagnostics_no_bails(
233 r#"
234enum Either { A(bool), B(bool, bool) }
235enum Either2 { C, D }
236
237fn main() {
238 match (Either::A(false), Either2::C) {
239 (Either::A(true), _) | (Either::A(false), _) => (),
240 (Either::B(true, _), Either2::C) => (),
241 (Either::B(false, _), Either2::C) => (),
242 (Either::B(_, _), Either2::D) => (),
243 }
244}
245"#,
246 );
247 }
248
249 #[test]
250 fn or_pattern_no_diagnostic() {
251 check_diagnostics_no_bails(
252 r#"
253enum Either {A, B}
254
255fn main() {
256 match (Either::A, Either::B) {
257 (Either::A | Either::B, _) => (),
258 }
259}"#,
260 )
261 }
262
263 #[test]
264 fn mismatched_types() {
265 cov_mark::check_count!(validate_match_bailed_out, 4);
266 // Match statements with arms that don't match the
267 // expression pattern do not fire this diagnostic.
268 check_diagnostics(
269 r#"
270enum Either { A, B }
271enum Either2 { C, D }
272
273fn main() {
274 match Either::A {
275 Either2::C => (),
276 Either2::D => (),
277 }
278 match (true, false) {
279 (true, false, true) => (),
280 (true) => (),
281 }
282 match (true, false) { (true,) => {} }
283 match (0) { () => () }
284 match Unresolved::Bar { Unresolved::Baz => () }
285}
286 "#,
287 );
288 }
289
290 #[test]
291 fn mismatched_types_in_or_patterns() {
292 cov_mark::check_count!(validate_match_bailed_out, 2);
293 check_diagnostics(
294 r#"
295fn main() {
296 match false { true | () => {} }
297 match (false,) { (true | (),) => {} }
298}
299"#,
300 );
301 }
302
303 #[test]
304 fn malformed_match_arm_tuple_enum_missing_pattern() {
305 // We are testing to be sure we don't panic here when the match
306 // arm `Either::B` is missing its pattern.
307 check_diagnostics_no_bails(
308 r#"
309enum Either { A, B(u32) }
310
311fn main() {
312 match Either::A {
313 Either::A => (),
314 Either::B() => (),
315 }
316}
317"#,
318 );
319 }
320
321 #[test]
322 fn malformed_match_arm_extra_fields() {
323 cov_mark::check_count!(validate_match_bailed_out, 2);
324 check_diagnostics(
325 r#"
326enum A { B(isize, isize), C }
327fn main() {
328 match A::B(1, 2) {
329 A::B(_, _, _) => (),
330 }
331 match A::B(1, 2) {
332 A::C(_) => (),
333 }
334}
335"#,
336 );
337 }
338
339 #[test]
340 fn expr_diverges() {
341 cov_mark::check_count!(validate_match_bailed_out, 2);
342 check_diagnostics(
343 r#"
344enum Either { A, B }
345
346fn main() {
347 match loop {} {
348 Either::A => (),
349 Either::B => (),
350 }
351 match loop {} {
352 Either::A => (),
353 }
354 match loop { break Foo::A } {
355 //^^^^^^^^^^^^^^^^^^^^^ missing match arm
356 Either::A => (),
357 }
358 match loop { break Foo::A } {
359 Either::A => (),
360 Either::B => (),
361 }
362}
363"#,
364 );
365 }
366
367 #[test]
368 fn expr_partially_diverges() {
369 check_diagnostics_no_bails(
370 r#"
371enum Either<T> { A(T), B }
372
373fn foo() -> Either<!> { Either::B }
374fn main() -> u32 {
375 match foo() {
376 Either::A(val) => val,
377 Either::B => 0,
378 }
379}
380"#,
381 );
382 }
383
384 #[test]
385 fn enum_record() {
386 check_diagnostics_no_bails(
387 r#"
388enum Either { A { foo: bool }, B }
389
390fn main() {
391 let a = Either::A { foo: true };
392 match a { }
393 //^ missing match arm
394 match a { Either::A { foo: true } => () }
395 //^ missing match arm
396 match a {
397 Either::A { } => (),
398 //^^^^^^^^^ Missing structure fields:
399 // | - foo
400 Either::B => (),
401 }
402 match a {
403 //^ missing match arm
404 Either::A { } => (),
405 } //^^^^^^^^^ Missing structure fields:
406 // | - foo
407
408 match a {
409 Either::A { foo: true } => (),
410 Either::A { foo: false } => (),
411 Either::B => (),
412 }
413 match a {
414 Either::A { foo: _ } => (),
415 Either::B => (),
416 }
417}
418"#,
419 );
420 }
421
422 #[test]
423 fn enum_record_fields_out_of_order() {
424 check_diagnostics_no_bails(
425 r#"
426enum Either {
427 A { foo: bool, bar: () },
428 B,
429}
430
431fn main() {
432 let a = Either::A { foo: true, bar: () };
433 match a {
434 //^ missing match arm
435 Either::A { bar: (), foo: false } => (),
436 Either::A { foo: true, bar: () } => (),
437 }
438
439 match a {
440 Either::A { bar: (), foo: false } => (),
441 Either::A { foo: true, bar: () } => (),
442 Either::B => (),
443 }
444}
445"#,
446 );
447 }
448
449 #[test]
450 fn enum_record_ellipsis() {
451 check_diagnostics_no_bails(
452 r#"
453enum Either {
454 A { foo: bool, bar: bool },
455 B,
456}
457
458fn main() {
459 let a = Either::B;
460 match a {
461 //^ missing match arm
462 Either::A { foo: true, .. } => (),
463 Either::B => (),
464 }
465 match a {
466 //^ missing match arm
467 Either::A { .. } => (),
468 }
469
470 match a {
471 Either::A { foo: true, .. } => (),
472 Either::A { foo: false, .. } => (),
473 Either::B => (),
474 }
475
476 match a {
477 Either::A { .. } => (),
478 Either::B => (),
479 }
480}
481"#,
482 );
483 }
484
485 #[test]
486 fn enum_tuple_partial_ellipsis() {
487 check_diagnostics_no_bails(
488 r#"
489enum Either {
490 A(bool, bool, bool, bool),
491 B,
492}
493
494fn main() {
495 match Either::B {
496 //^^^^^^^^^ missing match arm
497 Either::A(true, .., true) => (),
498 Either::A(true, .., false) => (),
499 Either::A(false, .., false) => (),
500 Either::B => (),
501 }
502 match Either::B {
503 //^^^^^^^^^ missing match arm
504 Either::A(true, .., true) => (),
505 Either::A(true, .., false) => (),
506 Either::A(.., true) => (),
507 Either::B => (),
508 }
509
510 match Either::B {
511 Either::A(true, .., true) => (),
512 Either::A(true, .., false) => (),
513 Either::A(false, .., true) => (),
514 Either::A(false, .., false) => (),
515 Either::B => (),
516 }
517 match Either::B {
518 Either::A(true, .., true) => (),
519 Either::A(true, .., false) => (),
520 Either::A(.., true) => (),
521 Either::A(.., false) => (),
522 Either::B => (),
523 }
524}
525"#,
526 );
527 }
528
529 #[test]
530 fn never() {
531 check_diagnostics_no_bails(
532 r#"
533enum Never {}
534
535fn enum_(never: Never) {
536 match never {}
537}
538fn enum_ref(never: &Never) {
539 match never {}
540 //^^^^^ missing match arm
541}
542fn bang(never: !) {
543 match never {}
544}
545"#,
546 );
547 }
548
549 #[test]
550 fn unknown_type() {
551 cov_mark::check_count!(validate_match_bailed_out, 1);
552
553 check_diagnostics(
554 r#"
555enum Option<T> { Some(T), None }
556
557fn main() {
558 // `Never` is deliberately not defined so that it's an uninferred type.
559 match Option::<Never>::None {
560 None => (),
561 Some(never) => match never {},
562 }
563 match Option::<Never>::None {
564 //^^^^^^^^^^^^^^^^^^^^^ missing match arm
565 Option::Some(_never) => {},
566 }
567}
568"#,
569 );
570 }
571
572 #[test]
573 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
574 check_diagnostics_no_bails(
575 r#"
576fn main() {
577 match (false, true, false) {
578 //^^^^^^^^^^^^^^^^^^^^ missing match arm
579 (false, ..) => (),
580 }
581}"#,
582 );
583 }
584
585 #[test]
586 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
587 check_diagnostics_no_bails(
588 r#"
589fn main() {
590 match (false, true, false) {
591 //^^^^^^^^^^^^^^^^^^^^ missing match arm
592 (.., false) => (),
593 }
594}"#,
595 );
596 }
597
598 #[test]
599 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
600 check_diagnostics_no_bails(
601 r#"
602fn main() {
603 match (false, true, false) {
604 //^^^^^^^^^^^^^^^^^^^^ missing match arm
605 (true, .., false) => (),
606 }
607}"#,
608 );
609 }
610
611 #[test]
612 fn record_struct() {
613 check_diagnostics_no_bails(
614 r#"struct Foo { a: bool }
615fn main(f: Foo) {
616 match f {}
617 //^ missing match arm
618 match f { Foo { a: true } => () }
619 //^ missing match arm
620 match &f { Foo { a: true } => () }
621 //^^ missing match arm
622 match f { Foo { a: _ } => () }
623 match f {
624 Foo { a: true } => (),
625 Foo { a: false } => (),
626 }
627 match &f {
628 Foo { a: true } => (),
629 Foo { a: false } => (),
630 }
631}
632"#,
633 );
634 }
635
636 #[test]
637 fn tuple_struct() {
638 check_diagnostics_no_bails(
639 r#"struct Foo(bool);
640fn main(f: Foo) {
641 match f {}
642 //^ missing match arm
643 match f { Foo(true) => () }
644 //^ missing match arm
645 match f {
646 Foo(true) => (),
647 Foo(false) => (),
648 }
649}
650"#,
651 );
652 }
653
654 #[test]
655 fn unit_struct() {
656 check_diagnostics_no_bails(
657 r#"struct Foo;
658fn main(f: Foo) {
659 match f {}
660 //^ missing match arm
661 match f { Foo => () }
662}
663"#,
664 );
665 }
666
667 #[test]
668 fn record_struct_ellipsis() {
669 check_diagnostics_no_bails(
670 r#"struct Foo { foo: bool, bar: bool }
671fn main(f: Foo) {
672 match f { Foo { foo: true, .. } => () }
673 //^ missing match arm
674 match f {
675 //^ missing match arm
676 Foo { foo: true, .. } => (),
677 Foo { bar: false, .. } => ()
678 }
679 match f { Foo { .. } => () }
680 match f {
681 Foo { foo: true, .. } => (),
682 Foo { foo: false, .. } => ()
683 }
684}
685"#,
686 );
687 }
688
689 #[test]
690 fn internal_or() {
691 check_diagnostics_no_bails(
692 r#"
693fn main() {
694 enum Either { A(bool), B }
695 match Either::B {
696 //^^^^^^^^^ missing match arm
697 Either::A(true | false) => (),
698 }
699}
700"#,
701 );
702 }
703
704 #[test]
705 fn no_panic_at_unimplemented_subpattern_type() {
706 cov_mark::check_count!(validate_match_bailed_out, 1);
707
708 check_diagnostics(
709 r#"
710struct S { a: char}
711fn main(v: S) {
712 match v { S{ a } => {} }
713 match v { S{ a: _x } => {} }
714 match v { S{ a: 'a' } => {} }
715 match v { S{..} => {} }
716 match v { _ => {} }
717 match v { }
718 //^ missing match arm
719}
720"#,
721 );
722 }
723
724 #[test]
725 fn binding() {
726 check_diagnostics_no_bails(
727 r#"
728fn main() {
729 match true {
730 _x @ true => {}
731 false => {}
732 }
733 match true { _x @ true => {} }
734 //^^^^ missing match arm
735}
736"#,
737 );
738 }
739
740 #[test]
741 fn binding_ref_has_correct_type() {
742 cov_mark::check_count!(validate_match_bailed_out, 1);
743
744 // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
745 // If that's not true match checking will panic with "incompatible constructors"
746 // FIXME: make facilities to test this directly like `tests::check_infer(..)`
747 check_diagnostics(
748 r#"
749enum Foo { A }
750fn main() {
751 // FIXME: this should not bail out but current behavior is such as the old algorithm.
752 // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
753 match Foo::A {
754 ref _x => {}
755 Foo::A => {}
756 }
757 match (true,) {
758 (ref _x,) => {}
759 (true,) => {}
760 }
761}
762"#,
763 );
764 }
765
766 #[test]
767 fn enum_non_exhaustive() {
768 check_diagnostics_no_bails(
769 r#"
770//- /lib.rs crate:lib
771#[non_exhaustive]
772pub enum E { A, B }
773fn _local() {
774 match E::A { _ => {} }
775 match E::A {
776 E::A => {}
777 E::B => {}
778 }
779 match E::A {
780 E::A | E::B => {}
781 }
782}
783
784//- /main.rs crate:main deps:lib
785use lib::E;
786fn main() {
787 match E::A { _ => {} }
788 match E::A {
789 //^^^^ missing match arm
790 E::A => {}
791 E::B => {}
792 }
793 match E::A {
794 //^^^^ missing match arm
795 E::A | E::B => {}
796 }
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn match_guard() {
804 check_diagnostics_no_bails(
805 r#"
806fn main() {
807 match true {
808 true if false => {}
809 true => {}
810 false => {}
811 }
812 match true {
813 //^^^^ missing match arm
814 true if false => {}
815 false => {}
816 }
817}
818"#,
819 );
820 }
821
822 #[test]
823 fn pattern_type_is_of_substitution() {
824 cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
825 check_diagnostics_no_bails(
826 r#"
827struct Foo<T>(T);
828struct Bar;
829fn main() {
830 match Foo(Bar) {
831 _ | Foo(Bar) => {}
832 }
833}
834"#,
835 );
836 }
837
838 #[test]
839 fn record_struct_no_such_field() {
840 cov_mark::check_count!(validate_match_bailed_out, 1);
841
842 check_diagnostics(
843 r#"
844struct Foo { }
845fn main(f: Foo) {
846 match f { Foo { bar } => () }
847}
848"#,
849 );
850 }
851
852 #[test]
853 fn match_ergonomics_issue_9095() {
854 check_diagnostics_no_bails(
855 r#"
856enum Foo<T> { A(T) }
857fn main() {
858 match &Foo::A(true) {
859 _ => {}
860 Foo::A(_) => {}
861 }
862}
863"#,
864 );
865 }
866
867 mod false_negatives {
868 //! The implementation of match checking here is a work in progress. As we roll this out, we
869 //! prefer false negatives to false positives (ideally there would be no false positives). This
870 //! test module should document known false negatives. Eventually we will have a complete
871 //! implementation of match checking and this module will be empty.
872 //!
873 //! The reasons for documenting known false negatives:
874 //!
875 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
876 //! 2. It ensures the code doesn't panic when handling these cases.
877 use super::*;
878
879 #[test]
880 fn integers() {
881 cov_mark::check_count!(validate_match_bailed_out, 1);
882
883 // We don't currently check integer exhaustiveness.
884 check_diagnostics(
885 r#"
886fn main() {
887 match 5 {
888 10 => (),
889 11..20 => (),
890 }
891}
892"#,
893 );
894 }
895
896 #[test]
897 fn reference_patterns_at_top_level() {
898 cov_mark::check_count!(validate_match_bailed_out, 1);
899
900 check_diagnostics(
901 r#"
902fn main() {
903 match &false {
904 &true => {}
905 }
906}
907 "#,
908 );
909 }
910
911 #[test]
912 fn reference_patterns_in_fields() {
913 cov_mark::check_count!(validate_match_bailed_out, 2);
914
915 check_diagnostics(
916 r#"
917fn main() {
918 match (&false,) {
919 (true,) => {}
920 }
921 match (&false,) {
922 (&true,) => {}
923 }
924}
925 "#,
926 );
927 }
928 }
929}
diff --git a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs b/crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs
index 715a403b9..06005d156 100644
--- a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs
+++ b/crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs
@@ -1,31 +1,50 @@
1use hir::{db::AstDatabase, diagnostics::MissingOkOrSomeInTailExpr, Semantics}; 1use hir::db::AstDatabase;
2use ide_assists::{Assist, AssistResolveStrategy}; 2use ide_assists::Assist;
3use ide_db::{source_change::SourceChange, RootDatabase}; 3use ide_db::source_change::SourceChange;
4use syntax::AstNode; 4use syntax::AstNode;
5use text_edit::TextEdit; 5use text_edit::TextEdit;
6 6
7use crate::diagnostics::{fix, DiagnosticWithFixes}; 7use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
8 8
9impl DiagnosticWithFixes for MissingOkOrSomeInTailExpr { 9// Diagnostic: missing-ok-or-some-in-tail-expr
10 fn fixes( 10//
11 &self, 11// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
12 sema: &Semantics<RootDatabase>, 12// or if a block that should return `Option` returns a value not wrapped in `Some`.
13 _resolve: &AssistResolveStrategy, 13//
14 ) -> Option<Vec<Assist>> { 14// Example:
15 let root = sema.db.parse_or_expand(self.file)?; 15//
16 let tail_expr = self.expr.to_node(&root); 16// ```rust
17 let tail_expr_range = tail_expr.syntax().text_range(); 17// fn foo() -> Result<u8, ()> {
18 let replacement = format!("{}({})", self.required, tail_expr.syntax()); 18// 10
19 let edit = TextEdit::replace(tail_expr_range, replacement); 19// }
20 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); 20// ```
21 let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; 21pub(super) fn missing_ok_or_some_in_tail_expr(
22 Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)]) 22 ctx: &DiagnosticsContext<'_>,
23 } 23 d: &hir::MissingOkOrSomeInTailExpr,
24) -> Diagnostic {
25 Diagnostic::new(
26 "missing-ok-or-some-in-tail-expr",
27 format!("wrap return expression in {}", d.required),
28 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
29 )
30 .with_fixes(fixes(ctx, d))
31}
32
33fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Option<Vec<Assist>> {
34 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
35 let tail_expr = d.expr.value.to_node(&root);
36 let tail_expr_range = tail_expr.syntax().text_range();
37 let replacement = format!("{}({})", d.required, tail_expr.syntax());
38 let edit = TextEdit::replace(tail_expr_range, replacement);
39 let source_change =
40 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
41 let name = if d.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
42 Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)])
24} 43}
25 44
26#[cfg(test)] 45#[cfg(test)]
27mod tests { 46mod tests {
28 use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; 47 use crate::diagnostics::tests::{check_diagnostics, check_fix};
29 48
30 #[test] 49 #[test]
31 fn test_wrap_return_type_option() { 50 fn test_wrap_return_type_option() {
@@ -169,7 +188,7 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
169 188
170 #[test] 189 #[test]
171 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 190 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
172 check_no_diagnostics( 191 check_diagnostics(
173 r#" 192 r#"
174//- /main.rs crate:main deps:core 193//- /main.rs crate:main deps:core
175use core::result::Result::{self, Ok, Err}; 194use core::result::Result::{self, Ok, Err};
@@ -189,7 +208,7 @@ pub mod option {
189 208
190 #[test] 209 #[test]
191 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { 210 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
192 check_no_diagnostics( 211 check_diagnostics(
193 r#" 212 r#"
194//- /main.rs crate:main deps:core 213//- /main.rs crate:main deps:core
195use core::result::Result::{self, Ok, Err}; 214use core::result::Result::{self, Ok, Err};
diff --git a/crates/ide/src/diagnostics/missing_unsafe.rs b/crates/ide/src/diagnostics/missing_unsafe.rs
new file mode 100644
index 000000000..5c47e8d0a
--- /dev/null
+++ b/crates/ide/src/diagnostics/missing_unsafe.rs
@@ -0,0 +1,101 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: missing-unsafe
4//
5// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
6pub(super) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
7 Diagnostic::new(
8 "missing-unsafe",
9 "this operation is unsafe and requires an unsafe function or block",
10 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
11 )
12}
13
14#[cfg(test)]
15mod tests {
16 use crate::diagnostics::tests::check_diagnostics;
17
18 #[test]
19 fn missing_unsafe_diagnostic_with_raw_ptr() {
20 check_diagnostics(
21 r#"
22fn main() {
23 let x = &5 as *const usize;
24 unsafe { let y = *x; }
25 let z = *x;
26} //^^ this operation is unsafe and requires an unsafe function or block
27"#,
28 )
29 }
30
31 #[test]
32 fn missing_unsafe_diagnostic_with_unsafe_call() {
33 check_diagnostics(
34 r#"
35struct HasUnsafe;
36
37impl HasUnsafe {
38 unsafe fn unsafe_fn(&self) {
39 let x = &5 as *const usize;
40 let y = *x;
41 }
42}
43
44unsafe fn unsafe_fn() {
45 let x = &5 as *const usize;
46 let y = *x;
47}
48
49fn main() {
50 unsafe_fn();
51 //^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block
52 HasUnsafe.unsafe_fn();
53 //^^^^^^^^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block
54 unsafe {
55 unsafe_fn();
56 HasUnsafe.unsafe_fn();
57 }
58}
59"#,
60 );
61 }
62
63 #[test]
64 fn missing_unsafe_diagnostic_with_static_mut() {
65 check_diagnostics(
66 r#"
67struct Ty {
68 a: u8,
69}
70
71static mut STATIC_MUT: Ty = Ty { a: 0 };
72
73fn main() {
74 let x = STATIC_MUT.a;
75 //^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block
76 unsafe {
77 let x = STATIC_MUT.a;
78 }
79}
80"#,
81 );
82 }
83
84 #[test]
85 fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
86 check_diagnostics(
87 r#"
88extern "rust-intrinsic" {
89 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
90 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
91}
92
93fn main() {
94 let _ = bitreverse(12);
95 let _ = floorf32(12.0);
96 //^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block
97}
98"#,
99 );
100 }
101}
diff --git a/crates/ide/src/diagnostics/fixes/create_field.rs b/crates/ide/src/diagnostics/no_such_field.rs
index a5f457dce..edc63c246 100644
--- a/crates/ide/src/diagnostics/fixes/create_field.rs
+++ b/crates/ide/src/diagnostics/no_such_field.rs
@@ -1,4 +1,4 @@
1use hir::{db::AstDatabase, diagnostics::NoSuchField, HasSource, HirDisplay, Semantics}; 1use hir::{db::AstDatabase, HasSource, HirDisplay, Semantics};
2use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; 2use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase};
3use syntax::{ 3use syntax::{
4 ast::{self, edit::IndentLevel, make}, 4 ast::{self, edit::IndentLevel, make},
@@ -7,22 +7,29 @@ use syntax::{
7use text_edit::TextEdit; 7use text_edit::TextEdit;
8 8
9use crate::{ 9use crate::{
10 diagnostics::{fix, DiagnosticWithFixes}, 10 diagnostics::{fix, Diagnostic, DiagnosticsContext},
11 Assist, AssistResolveStrategy, 11 Assist,
12}; 12};
13impl DiagnosticWithFixes for NoSuchField { 13
14 fn fixes( 14// Diagnostic: no-such-field
15 &self, 15//
16 sema: &Semantics<RootDatabase>, 16// This diagnostic is triggered if created structure does not have field provided in record.
17 _resolve: &AssistResolveStrategy, 17pub(super) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
18 ) -> Option<Vec<Assist>> { 18 Diagnostic::new(
19 let root = sema.db.parse_or_expand(self.file)?; 19 "no-such-field",
20 missing_record_expr_field_fixes( 20 "no such field",
21 &sema, 21 ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range,
22 self.file.original_file(sema.db), 22 )
23 &self.field.to_node(&root), 23 .with_fixes(fixes(ctx, d))
24 ) 24}
25 } 25
26fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
27 let root = ctx.sema.db.parse_or_expand(d.field.file_id)?;
28 missing_record_expr_field_fixes(
29 &ctx.sema,
30 d.field.file_id.original_file(ctx.sema.db),
31 &d.field.value.to_node(&root),
32 )
26} 33}
27 34
28fn missing_record_expr_field_fixes( 35fn missing_record_expr_field_fixes(
@@ -105,7 +112,130 @@ fn missing_record_expr_field_fixes(
105 112
106#[cfg(test)] 113#[cfg(test)]
107mod tests { 114mod tests {
108 use crate::diagnostics::tests::check_fix; 115 use crate::diagnostics::tests::{check_diagnostics, check_fix};
116
117 #[test]
118 fn no_such_field_diagnostics() {
119 check_diagnostics(
120 r#"
121struct S { foo: i32, bar: () }
122impl S {
123 fn new() -> S {
124 S {
125 //^ Missing structure fields:
126 //| - bar
127 foo: 92,
128 baz: 62,
129 //^^^^^^^ no such field
130 }
131 }
132}
133"#,
134 );
135 }
136 #[test]
137 fn no_such_field_with_feature_flag_diagnostics() {
138 check_diagnostics(
139 r#"
140//- /lib.rs crate:foo cfg:feature=foo
141struct MyStruct {
142 my_val: usize,
143 #[cfg(feature = "foo")]
144 bar: bool,
145}
146
147impl MyStruct {
148 #[cfg(feature = "foo")]
149 pub(crate) fn new(my_val: usize, bar: bool) -> Self {
150 Self { my_val, bar }
151 }
152 #[cfg(not(feature = "foo"))]
153 pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
154 Self { my_val }
155 }
156}
157"#,
158 );
159 }
160
161 #[test]
162 fn no_such_field_enum_with_feature_flag_diagnostics() {
163 check_diagnostics(
164 r#"
165//- /lib.rs crate:foo cfg:feature=foo
166enum Foo {
167 #[cfg(not(feature = "foo"))]
168 Buz,
169 #[cfg(feature = "foo")]
170 Bar,
171 Baz
172}
173
174fn test_fn(f: Foo) {
175 match f {
176 Foo::Bar => {},
177 Foo::Baz => {},
178 }
179}
180"#,
181 );
182 }
183
184 #[test]
185 fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
186 check_diagnostics(
187 r#"
188//- /lib.rs crate:foo cfg:feature=foo
189struct S {
190 #[cfg(feature = "foo")]
191 foo: u32,
192 #[cfg(not(feature = "foo"))]
193 bar: u32,
194}
195
196impl S {
197 #[cfg(feature = "foo")]
198 fn new(foo: u32) -> Self {
199 Self { foo }
200 }
201 #[cfg(not(feature = "foo"))]
202 fn new(bar: u32) -> Self {
203 Self { bar }
204 }
205 fn new2(bar: u32) -> Self {
206 #[cfg(feature = "foo")]
207 { Self { foo: bar } }
208 #[cfg(not(feature = "foo"))]
209 { Self { bar } }
210 }
211 fn new2(val: u32) -> Self {
212 Self {
213 #[cfg(feature = "foo")]
214 foo: val,
215 #[cfg(not(feature = "foo"))]
216 bar: val,
217 }
218 }
219}
220"#,
221 );
222 }
223
224 #[test]
225 fn no_such_field_with_type_macro() {
226 check_diagnostics(
227 r#"
228macro_rules! Type { () => { u32 }; }
229struct Foo { bar: Type![] }
230
231impl Foo {
232 fn new() -> Self {
233 Foo { bar: 0 }
234 }
235}
236"#,
237 );
238 }
109 239
110 #[test] 240 #[test]
111 fn test_add_field_from_usage() { 241 fn test_add_field_from_usage() {
diff --git a/crates/ide/src/diagnostics/remove_this_semicolon.rs b/crates/ide/src/diagnostics/remove_this_semicolon.rs
new file mode 100644
index 000000000..814cb0f8c
--- /dev/null
+++ b/crates/ide/src/diagnostics/remove_this_semicolon.rs
@@ -0,0 +1,64 @@
1use hir::db::AstDatabase;
2use ide_db::source_change::SourceChange;
3use syntax::{ast, AstNode};
4use text_edit::TextEdit;
5
6use crate::{
7 diagnostics::{fix, Diagnostic, DiagnosticsContext},
8 Assist,
9};
10
11// Diagnostic: remove-this-semicolon
12//
13// This diagnostic is triggered when there's an erroneous `;` at the end of the block.
14pub(super) fn remove_this_semicolon(
15 ctx: &DiagnosticsContext<'_>,
16 d: &hir::RemoveThisSemicolon,
17) -> Diagnostic {
18 Diagnostic::new(
19 "remove-this-semicolon",
20 "remove this semicolon",
21 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
22 )
23 .with_fixes(fixes(ctx, d))
24}
25
26fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<Vec<Assist>> {
27 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
28
29 let semicolon = d
30 .expr
31 .value
32 .to_node(&root)
33 .syntax()
34 .parent()
35 .and_then(ast::ExprStmt::cast)
36 .and_then(|expr| expr.semicolon_token())?
37 .text_range();
38
39 let edit = TextEdit::delete(semicolon);
40 let source_change =
41 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
42
43 Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)])
44}
45
46#[cfg(test)]
47mod tests {
48 use crate::diagnostics::tests::{check_diagnostics, check_fix};
49
50 #[test]
51 fn missing_semicolon() {
52 check_diagnostics(
53 r#"
54fn test() -> i32 { 123; }
55 //^^^ remove this semicolon
56"#,
57 );
58 }
59
60 #[test]
61 fn remove_semicolon() {
62 check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
63 }
64}
diff --git a/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs b/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs
new file mode 100644
index 000000000..f3b011495
--- /dev/null
+++ b/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs
@@ -0,0 +1,182 @@
1use hir::{db::AstDatabase, InFile};
2use ide_db::source_change::SourceChange;
3use syntax::{
4 ast::{self, ArgListOwner},
5 AstNode, TextRange,
6};
7use text_edit::TextEdit;
8
9use crate::{
10 diagnostics::{fix, Diagnostic, DiagnosticsContext},
11 Assist, Severity,
12};
13
14// Diagnostic: replace-filter-map-next-with-find-map
15//
16// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
17pub(super) fn replace_filter_map_next_with_find_map(
18 ctx: &DiagnosticsContext<'_>,
19 d: &hir::ReplaceFilterMapNextWithFindMap,
20) -> Diagnostic {
21 Diagnostic::new(
22 "replace-filter-map-next-with-find-map",
23 "replace filter_map(..).next() with find_map(..)",
24 ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range,
25 )
26 .severity(Severity::WeakWarning)
27 .with_fixes(fixes(ctx, d))
28}
29
30fn fixes(
31 ctx: &DiagnosticsContext<'_>,
32 d: &hir::ReplaceFilterMapNextWithFindMap,
33) -> Option<Vec<Assist>> {
34 let root = ctx.sema.db.parse_or_expand(d.file)?;
35 let next_expr = d.next_expr.to_node(&root);
36 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
37
38 let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?;
39 let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range();
40 let filter_map_args = filter_map_call.arg_list()?;
41
42 let range_to_replace =
43 TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end());
44 let replacement = format!("find_map{}", filter_map_args.syntax().text());
45 let trigger_range = next_expr.syntax().text_range();
46
47 let edit = TextEdit::replace(range_to_replace, replacement);
48
49 let source_change = SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit);
50
51 Some(vec![fix(
52 "replace_with_find_map",
53 "Replace filter_map(..).next() with find_map()",
54 source_change,
55 trigger_range,
56 )])
57}
58
59#[cfg(test)]
60mod tests {
61 use crate::diagnostics::tests::check_fix;
62
63 // Register the required standard library types to make the tests work
64 #[track_caller]
65 fn check_diagnostics(ra_fixture: &str) {
66 let prefix = r#"
67//- /main.rs crate:main deps:core
68use core::iter::Iterator;
69use core::option::Option::{self, Some, None};
70"#;
71 let suffix = r#"
72//- /core/lib.rs crate:core
73pub mod option {
74 pub enum Option<T> { Some(T), None }
75}
76pub mod iter {
77 pub trait Iterator {
78 type Item;
79 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
80 fn next(&mut self) -> Option<Self::Item>;
81 }
82 pub struct FilterMap {}
83 impl Iterator for FilterMap {
84 type Item = i32;
85 fn next(&mut self) -> i32 { 7 }
86 }
87}
88"#;
89 crate::diagnostics::tests::check_diagnostics(&format!("{}{}{}", prefix, ra_fixture, suffix))
90 }
91
92 #[test]
93 fn replace_filter_map_next_with_find_map2() {
94 check_diagnostics(
95 r#"
96 fn foo() {
97 let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
98 } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..)
99"#,
100 );
101 }
102
103 #[test]
104 fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
105 check_diagnostics(
106 r#"
107fn foo() {
108 let m = [1, 2, 3]
109 .iter()
110 .filter_map(|x| if *x == 2 { Some (4) } else { None })
111 .len();
112}
113"#,
114 );
115 }
116
117 #[test]
118 fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
119 check_diagnostics(
120 r#"
121fn foo() {
122 let m = [1, 2, 3]
123 .iter()
124 .filter_map(|x| if *x == 2 { Some (4) } else { None })
125 .map(|x| x + 2)
126 .len();
127}
128"#,
129 );
130 }
131
132 #[test]
133 fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
134 check_diagnostics(
135 r#"
136fn foo() {
137 let m = [1, 2, 3]
138 .iter()
139 .filter_map(|x| if *x == 2 { Some (4) } else { None });
140 let n = m.next();
141}
142"#,
143 );
144 }
145
146 #[test]
147 fn replace_with_wind_map() {
148 check_fix(
149 r#"
150//- /main.rs crate:main deps:core
151use core::iter::Iterator;
152use core::option::Option::{self, Some, None};
153fn foo() {
154 let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
155}
156//- /core/lib.rs crate:core
157pub mod option {
158 pub enum Option<T> { Some(T), None }
159}
160pub mod iter {
161 pub trait Iterator {
162 type Item;
163 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
164 fn next(&mut self) -> Option<Self::Item>;
165 }
166 pub struct FilterMap {}
167 impl Iterator for FilterMap {
168 type Item = i32;
169 fn next(&mut self) -> i32 { 7 }
170 }
171}
172"#,
173 r#"
174use core::iter::Iterator;
175use core::option::Option::{self, Some, None};
176fn foo() {
177 let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None });
178}
179"#,
180 )
181 }
182}
diff --git a/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs b/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs
new file mode 100644
index 000000000..09faa3bbc
--- /dev/null
+++ b/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs
@@ -0,0 +1,19 @@
1use crate::{
2 diagnostics::{Diagnostic, DiagnosticsContext},
3 Severity,
4};
5
6// Diagnostic: unimplemented-builtin-macro
7//
8// This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer
9pub(super) fn unimplemented_builtin_macro(
10 ctx: &DiagnosticsContext<'_>,
11 d: &hir::UnimplementedBuiltinMacro,
12) -> Diagnostic {
13 Diagnostic::new(
14 "unimplemented-builtin-macro",
15 "unimplemented built-in macro".to_string(),
16 ctx.sema.diagnostics_display_range(d.node.clone()).range,
17 )
18 .severity(Severity::WeakWarning)
19}
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index 51fe0f360..a5b2e3399 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -1,11 +1,6 @@
1//! Diagnostic emitted for files that aren't part of any crate. 1//! Diagnostic emitted for files that aren't part of any crate.
2 2
3use hir::{ 3use hir::db::DefDatabase;
4 db::DefDatabase,
5 diagnostics::{Diagnostic, DiagnosticCode},
6 InFile,
7};
8use ide_assists::AssistResolveStrategy;
9use ide_db::{ 4use ide_db::{
10 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, 5 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
11 source_change::SourceChange, 6 source_change::SourceChange,
@@ -13,92 +8,77 @@ use ide_db::{
13}; 8};
14use syntax::{ 9use syntax::{
15 ast::{self, ModuleItemOwner, NameOwner}, 10 ast::{self, ModuleItemOwner, NameOwner},
16 AstNode, SyntaxNodePtr, 11 AstNode, TextRange, TextSize,
17}; 12};
18use text_edit::TextEdit; 13use text_edit::TextEdit;
19 14
20use crate::{ 15use crate::{
21 diagnostics::{fix, fixes::DiagnosticWithFixes}, 16 diagnostics::{fix, DiagnosticsContext},
22 Assist, 17 Assist, Diagnostic,
23}; 18};
24 19
20#[derive(Debug)]
21pub(crate) struct UnlinkedFile {
22 pub(crate) file: FileId,
23}
24
25// Diagnostic: unlinked-file 25// Diagnostic: unlinked-file
26// 26//
27// This diagnostic is shown for files that are not included in any crate, or files that are part of 27// This diagnostic is shown for files that are not included in any crate, or files that are part of
28// crates rust-analyzer failed to discover. The file will not have IDE features available. 28// crates rust-analyzer failed to discover. The file will not have IDE features available.
29#[derive(Debug)] 29pub(super) fn unlinked_file(ctx: &DiagnosticsContext, d: &UnlinkedFile) -> Diagnostic {
30pub(crate) struct UnlinkedFile { 30 // Limit diagnostic to the first few characters in the file. This matches how VS Code
31 pub(crate) file_id: FileId, 31 // renders it with the full span, but on other editors, and is less invasive.
32 pub(crate) node: SyntaxNodePtr, 32 let range = ctx.sema.db.parse(d.file).syntax_node().text_range();
33 // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`.
34 let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
35
36 Diagnostic::new("unlinked-file", "file not included in module tree", range)
37 .with_fixes(fixes(ctx, d))
33} 38}
34 39
35impl Diagnostic for UnlinkedFile { 40fn fixes(ctx: &DiagnosticsContext, d: &UnlinkedFile) -> Option<Vec<Assist>> {
36 fn code(&self) -> DiagnosticCode { 41 // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file,
37 DiagnosticCode("unlinked-file") 42 // suggest that as a fix.
38 }
39 43
40 fn message(&self) -> String { 44 let source_root = ctx.sema.db.source_root(ctx.sema.db.file_source_root(d.file));
41 "file not included in module tree".to_string() 45 let our_path = source_root.path_for_file(&d.file)?;
42 } 46 let module_name = our_path.name_and_extension()?.0;
43 47
44 fn display_source(&self) -> InFile<SyntaxNodePtr> { 48 // Candidates to look for:
45 InFile::new(self.file_id.into(), self.node.clone()) 49 // - `mod.rs` in the same folder
46 } 50 // - we also check `main.rs` and `lib.rs`
51 // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
52 let parent = our_path.parent()?;
53 let mut paths = vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?];
47 54
48 fn as_any(&self) -> &(dyn std::any::Any + Send + 'static) { 55 // `submod/bla.rs` -> `submod.rs`
49 self 56 if let Some(newmod) = (|| {
57 let name = parent.name_and_extension()?.0;
58 parent.parent()?.join(&format!("{}.rs", name))
59 })() {
60 paths.push(newmod);
50 } 61 }
51}
52 62
53impl DiagnosticWithFixes for UnlinkedFile { 63 for path in &paths {
54 fn fixes( 64 if let Some(parent_id) = source_root.file_for_path(path) {
55 &self, 65 for krate in ctx.sema.db.relevant_crates(*parent_id).iter() {
56 sema: &hir::Semantics<RootDatabase>, 66 let crate_def_map = ctx.sema.db.crate_def_map(*krate);
57 _resolve: &AssistResolveStrategy, 67 for (_, module) in crate_def_map.modules() {
58 ) -> Option<Vec<Assist>> { 68 if module.origin.is_inline() {
59 // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, 69 // We don't handle inline `mod parent {}`s, they use different paths.
60 // suggest that as a fix. 70 continue;
61 71 }
62 let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id));
63 let our_path = source_root.path_for_file(&self.file_id)?;
64 let module_name = our_path.name_and_extension()?.0;
65
66 // Candidates to look for:
67 // - `mod.rs` in the same folder
68 // - we also check `main.rs` and `lib.rs`
69 // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
70 let parent = our_path.parent()?;
71 let mut paths =
72 vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?];
73
74 // `submod/bla.rs` -> `submod.rs`
75 if let Some(newmod) = (|| {
76 let name = parent.name_and_extension()?.0;
77 parent.parent()?.join(&format!("{}.rs", name))
78 })() {
79 paths.push(newmod);
80 }
81 72
82 for path in &paths { 73 if module.origin.file_id() == Some(*parent_id) {
83 if let Some(parent_id) = source_root.file_for_path(path) { 74 return make_fixes(ctx.sema.db, *parent_id, module_name, d.file);
84 for krate in sema.db.relevant_crates(*parent_id).iter() {
85 let crate_def_map = sema.db.crate_def_map(*krate);
86 for (_, module) in crate_def_map.modules() {
87 if module.origin.is_inline() {
88 // We don't handle inline `mod parent {}`s, they use different paths.
89 continue;
90 }
91
92 if module.origin.file_id() == Some(*parent_id) {
93 return make_fixes(sema.db, *parent_id, module_name, self.file_id);
94 }
95 } 75 }
96 } 76 }
97 } 77 }
98 } 78 }
99
100 None
101 } 79 }
80
81 None
102} 82}
103 83
104fn make_fixes( 84fn make_fixes(
@@ -181,3 +161,144 @@ fn make_fixes(
181 ), 161 ),
182 ]) 162 ])
183} 163}
164
165#[cfg(test)]
166mod tests {
167 use crate::diagnostics::tests::{check_diagnostics, check_fix, check_fixes, check_no_fix};
168
169 #[test]
170 fn unlinked_file_prepend_first_item() {
171 cov_mark::check!(unlinked_file_prepend_before_first_item);
172 // Only tests the first one for `pub mod` since the rest are the same
173 check_fixes(
174 r#"
175//- /main.rs
176fn f() {}
177//- /foo.rs
178$0
179"#,
180 vec![
181 r#"
182mod foo;
183
184fn f() {}
185"#,
186 r#"
187pub mod foo;
188
189fn f() {}
190"#,
191 ],
192 );
193 }
194
195 #[test]
196 fn unlinked_file_append_mod() {
197 cov_mark::check!(unlinked_file_append_to_existing_mods);
198 check_fix(
199 r#"
200//- /main.rs
201//! Comment on top
202
203mod preexisting;
204
205mod preexisting2;
206
207struct S;
208
209mod preexisting_bottom;)
210//- /foo.rs
211$0
212"#,
213 r#"
214//! Comment on top
215
216mod preexisting;
217
218mod preexisting2;
219mod foo;
220
221struct S;
222
223mod preexisting_bottom;)
224"#,
225 );
226 }
227
228 #[test]
229 fn unlinked_file_insert_in_empty_file() {
230 cov_mark::check!(unlinked_file_empty_file);
231 check_fix(
232 r#"
233//- /main.rs
234//- /foo.rs
235$0
236"#,
237 r#"
238mod foo;
239"#,
240 );
241 }
242
243 #[test]
244 fn unlinked_file_old_style_modrs() {
245 check_fix(
246 r#"
247//- /main.rs
248mod submod;
249//- /submod/mod.rs
250// in mod.rs
251//- /submod/foo.rs
252$0
253"#,
254 r#"
255// in mod.rs
256mod foo;
257"#,
258 );
259 }
260
261 #[test]
262 fn unlinked_file_new_style_mod() {
263 check_fix(
264 r#"
265//- /main.rs
266mod submod;
267//- /submod.rs
268//- /submod/foo.rs
269$0
270"#,
271 r#"
272mod foo;
273"#,
274 );
275 }
276
277 #[test]
278 fn unlinked_file_with_cfg_off() {
279 cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists);
280 check_no_fix(
281 r#"
282//- /main.rs
283#[cfg(never)]
284mod foo;
285
286//- /foo.rs
287$0
288"#,
289 );
290 }
291
292 #[test]
293 fn unlinked_file_with_cfg_on() {
294 check_diagnostics(
295 r#"
296//- /main.rs
297#[cfg(not(never))]
298mod foo;
299
300//- /foo.rs
301"#,
302 );
303 }
304}
diff --git a/crates/ide/src/diagnostics/unresolved_extern_crate.rs b/crates/ide/src/diagnostics/unresolved_extern_crate.rs
new file mode 100644
index 000000000..2ea79c2ee
--- /dev/null
+++ b/crates/ide/src/diagnostics/unresolved_extern_crate.rs
@@ -0,0 +1,49 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: unresolved-extern-crate
4//
5// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
6pub(super) fn unresolved_extern_crate(
7 ctx: &DiagnosticsContext<'_>,
8 d: &hir::UnresolvedExternCrate,
9) -> Diagnostic {
10 Diagnostic::new(
11 "unresolved-extern-crate",
12 "unresolved extern crate",
13 ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
14 )
15}
16
17#[cfg(test)]
18mod tests {
19 use crate::diagnostics::tests::check_diagnostics;
20
21 #[test]
22 fn unresolved_extern_crate() {
23 check_diagnostics(
24 r#"
25//- /main.rs crate:main deps:core
26extern crate core;
27 extern crate doesnotexist;
28//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
29//- /lib.rs crate:core
30"#,
31 );
32 }
33
34 #[test]
35 fn extern_crate_self_as() {
36 cov_mark::check!(extern_crate_self_as);
37 check_diagnostics(
38 r#"
39//- /lib.rs
40 extern crate doesnotexist;
41//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
42// Should not error.
43extern crate self as foo;
44struct Foo;
45use foo::Foo as Bar;
46"#,
47 );
48 }
49}
diff --git a/crates/ide/src/diagnostics/unresolved_import.rs b/crates/ide/src/diagnostics/unresolved_import.rs
new file mode 100644
index 000000000..1cbf96ba1
--- /dev/null
+++ b/crates/ide/src/diagnostics/unresolved_import.rs
@@ -0,0 +1,90 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: unresolved-import
4//
5// This diagnostic is triggered if rust-analyzer is unable to resolve a path in
6// a `use` declaration.
7pub(super) fn unresolved_import(
8 ctx: &DiagnosticsContext<'_>,
9 d: &hir::UnresolvedImport,
10) -> Diagnostic {
11 Diagnostic::new(
12 "unresolved-import",
13 "unresolved import",
14 ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
15 )
16 // This currently results in false positives in the following cases:
17 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
18 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
19 // - proc macros and/or proc macro generated code
20 .experimental()
21}
22
23#[cfg(test)]
24mod tests {
25 use crate::diagnostics::tests::check_diagnostics;
26
27 #[test]
28 fn unresolved_import() {
29 check_diagnostics(
30 r#"
31use does_exist;
32use does_not_exist;
33 //^^^^^^^^^^^^^^ unresolved import
34
35mod does_exist {}
36"#,
37 );
38 }
39
40 #[test]
41 fn unresolved_import_in_use_tree() {
42 // Only the relevant part of a nested `use` item should be highlighted.
43 check_diagnostics(
44 r#"
45use does_exist::{Exists, DoesntExist};
46 //^^^^^^^^^^^ unresolved import
47
48use {does_not_exist::*, does_exist};
49 //^^^^^^^^^^^^^^^^^ unresolved import
50
51use does_not_exist::{
52 a,
53 //^ unresolved import
54 b,
55 //^ unresolved import
56 c,
57 //^ unresolved import
58};
59
60mod does_exist {
61 pub struct Exists;
62}
63"#,
64 );
65 }
66
67 #[test]
68 fn dedup_unresolved_import_from_unresolved_crate() {
69 check_diagnostics(
70 r#"
71//- /main.rs crate:main
72mod a {
73 extern crate doesnotexist;
74 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
75
76 // Should not error, since we already errored for the missing crate.
77 use doesnotexist::{self, bla, *};
78
79 use crate::doesnotexist;
80 //^^^^^^^^^^^^^^^^^^^ unresolved import
81}
82
83mod m {
84 use super::doesnotexist;
85 //^^^^^^^^^^^^^^^^^^^ unresolved import
86}
87"#,
88 );
89 }
90}
diff --git a/crates/ide/src/diagnostics/unresolved_macro_call.rs b/crates/ide/src/diagnostics/unresolved_macro_call.rs
new file mode 100644
index 000000000..15b6a2730
--- /dev/null
+++ b/crates/ide/src/diagnostics/unresolved_macro_call.rs
@@ -0,0 +1,84 @@
1use hir::{db::AstDatabase, InFile};
2use syntax::{AstNode, SyntaxNodePtr};
3
4use crate::diagnostics::{Diagnostic, DiagnosticsContext};
5
6// Diagnostic: unresolved-macro-call
7//
8// This diagnostic is triggered if rust-analyzer is unable to resolve the path
9// to a macro in a macro invocation.
10pub(super) fn unresolved_macro_call(
11 ctx: &DiagnosticsContext<'_>,
12 d: &hir::UnresolvedMacroCall,
13) -> Diagnostic {
14 let last_path_segment = ctx.sema.db.parse_or_expand(d.macro_call.file_id).and_then(|root| {
15 d.macro_call
16 .value
17 .to_node(&root)
18 .path()
19 .and_then(|it| it.segment())
20 .and_then(|it| it.name_ref())
21 .map(|it| InFile::new(d.macro_call.file_id, SyntaxNodePtr::new(it.syntax())))
22 });
23 let diagnostics = last_path_segment.unwrap_or_else(|| d.macro_call.clone().map(|it| it.into()));
24
25 Diagnostic::new(
26 "unresolved-macro-call",
27 format!("unresolved macro `{}!`", d.path),
28 ctx.sema.diagnostics_display_range(diagnostics).range,
29 )
30 .experimental()
31}
32
33#[cfg(test)]
34mod tests {
35 use crate::diagnostics::tests::check_diagnostics;
36
37 #[test]
38 fn unresolved_macro_diag() {
39 check_diagnostics(
40 r#"
41fn f() {
42 m!();
43} //^ unresolved macro `m!`
44
45"#,
46 );
47 }
48
49 #[test]
50 fn test_unresolved_macro_range() {
51 check_diagnostics(
52 r#"
53foo::bar!(92);
54 //^^^ unresolved macro `foo::bar!`
55"#,
56 );
57 }
58
59 #[test]
60 fn unresolved_legacy_scope_macro() {
61 check_diagnostics(
62 r#"
63macro_rules! m { () => {} }
64
65m!(); m2!();
66 //^^ unresolved macro `self::m2!`
67"#,
68 );
69 }
70
71 #[test]
72 fn unresolved_module_scope_macro() {
73 check_diagnostics(
74 r#"
75mod mac {
76#[macro_export]
77macro_rules! m { () => {} } }
78
79self::m!(); self::m2!();
80 //^^ unresolved macro `self::m2!`
81"#,
82 );
83 }
84}
diff --git a/crates/ide/src/diagnostics/fixes/unresolved_module.rs b/crates/ide/src/diagnostics/unresolved_module.rs
index b3d0283bb..977b46414 100644
--- a/crates/ide/src/diagnostics/fixes/unresolved_module.rs
+++ b/crates/ide/src/diagnostics/unresolved_module.rs
@@ -1,39 +1,62 @@
1use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics}; 1use hir::db::AstDatabase;
2use ide_assists::{Assist, AssistResolveStrategy}; 2use ide_assists::Assist;
3use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase}; 3use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit};
4use syntax::AstNode; 4use syntax::AstNode;
5 5
6use crate::diagnostics::{fix, DiagnosticWithFixes}; 6use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
7 7
8impl DiagnosticWithFixes for UnresolvedModule { 8// Diagnostic: unresolved-module
9 fn fixes( 9//
10 &self, 10// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
11 sema: &Semantics<RootDatabase>, 11pub(super) fn unresolved_module(
12 _resolve: &AssistResolveStrategy, 12 ctx: &DiagnosticsContext<'_>,
13 ) -> Option<Vec<Assist>> { 13 d: &hir::UnresolvedModule,
14 let root = sema.db.parse_or_expand(self.file)?; 14) -> Diagnostic {
15 let unresolved_module = self.decl.to_node(&root); 15 Diagnostic::new(
16 Some(vec![fix( 16 "unresolved-module",
17 "create_module", 17 "unresolved module",
18 "Create module", 18 ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
19 FileSystemEdit::CreateFile { 19 )
20 dst: AnchoredPathBuf { 20 .with_fixes(fixes(ctx, d))
21 anchor: self.file.original_file(sema.db), 21}
22 path: self.candidate.clone(), 22
23 }, 23fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
24 initial_contents: "".to_string(), 24 let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?;
25 } 25 let unresolved_module = d.decl.value.to_node(&root);
26 .into(), 26 Some(vec![fix(
27 unresolved_module.syntax().text_range(), 27 "create_module",
28 )]) 28 "Create module",
29 } 29 FileSystemEdit::CreateFile {
30 dst: AnchoredPathBuf {
31 anchor: d.decl.file_id.original_file(ctx.sema.db),
32 path: d.candidate.clone(),
33 },
34 initial_contents: "".to_string(),
35 }
36 .into(),
37 unresolved_module.syntax().text_range(),
38 )])
30} 39}
31 40
32#[cfg(test)] 41#[cfg(test)]
33mod tests { 42mod tests {
34 use expect_test::expect; 43 use expect_test::expect;
35 44
36 use crate::diagnostics::tests::check_expect; 45 use crate::diagnostics::tests::{check_diagnostics, check_expect};
46
47 #[test]
48 fn unresolved_module() {
49 check_diagnostics(
50 r#"
51//- /lib.rs
52mod foo;
53 mod bar;
54//^^^^^^^^ unresolved module
55mod baz {}
56//- /foo.rs
57"#,
58 );
59 }
37 60
38 #[test] 61 #[test]
39 fn test_unresolved_module_diagnostic() { 62 fn test_unresolved_module_diagnostic() {
@@ -42,9 +65,14 @@ mod tests {
42 expect![[r#" 65 expect![[r#"
43 [ 66 [
44 Diagnostic { 67 Diagnostic {
68 code: DiagnosticCode(
69 "unresolved-module",
70 ),
45 message: "unresolved module", 71 message: "unresolved module",
46 range: 0..8, 72 range: 0..8,
47 severity: Error, 73 severity: Error,
74 unused: false,
75 experimental: false,
48 fixes: Some( 76 fixes: Some(
49 [ 77 [
50 Assist { 78 Assist {
@@ -75,12 +103,6 @@ mod tests {
75 }, 103 },
76 ], 104 ],
77 ), 105 ),
78 unused: false,
79 code: Some(
80 DiagnosticCode(
81 "unresolved-module",
82 ),
83 ),
84 }, 106 },
85 ] 107 ]
86 "#]], 108 "#]],
diff --git a/crates/ide/src/diagnostics/unresolved_proc_macro.rs b/crates/ide/src/diagnostics/unresolved_proc_macro.rs
new file mode 100644
index 000000000..3dc6ab451
--- /dev/null
+++ b/crates/ide/src/diagnostics/unresolved_proc_macro.rs
@@ -0,0 +1,30 @@
1use crate::{
2 diagnostics::{Diagnostic, DiagnosticsContext},
3 Severity,
4};
5
6// Diagnostic: unresolved-proc-macro
7//
8// This diagnostic is shown when a procedural macro can not be found. This usually means that
9// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
10// but can also indicate project setup problems.
11//
12// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
13// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
14// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
15pub(super) fn unresolved_proc_macro(
16 ctx: &DiagnosticsContext<'_>,
17 d: &hir::UnresolvedProcMacro,
18) -> Diagnostic {
19 // Use more accurate position if available.
20 let display_range = d
21 .precise_location
22 .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range);
23 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
24 let message = match &d.macro_name {
25 Some(name) => format!("proc macro `{}` not expanded", name),
26 None => "proc macro not expanded".to_string(),
27 };
28
29 Diagnostic::new("unresolved-proc-macro", message, display_range).severity(Severity::WeakWarning)
30}
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index ec3828ab2..57ae9455b 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -151,18 +151,18 @@ pub(crate) fn resolve_doc_path_for_def(
151) -> Option<hir::ModuleDef> { 151) -> Option<hir::ModuleDef> {
152 match def { 152 match def {
153 Definition::ModuleDef(def) => match def { 153 Definition::ModuleDef(def) => match def {
154 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), 154 hir::ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns),
155 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), 155 hir::ModuleDef::Function(it) => it.resolve_doc_path(db, link, ns),
156 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), 156 hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, link, ns),
157 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), 157 hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, link, ns),
158 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), 158 hir::ModuleDef::Const(it) => it.resolve_doc_path(db, link, ns),
159 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), 159 hir::ModuleDef::Static(it) => it.resolve_doc_path(db, link, ns),
160 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), 160 hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, link, ns),
161 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), 161 hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
162 hir::ModuleDef::BuiltinType(_) => None, 162 hir::ModuleDef::BuiltinType(_) => None,
163 }, 163 },
164 Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), 164 Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
165 Definition::Field(it) => it.resolve_doc_path(db, &link, ns), 165 Definition::Field(it) => it.resolve_doc_path(db, link, ns),
166 Definition::SelfType(_) 166 Definition::SelfType(_)
167 | Definition::Local(_) 167 | Definition::Local(_)
168 | Definition::GenericParam(_) 168 | Definition::GenericParam(_)
@@ -192,7 +192,7 @@ pub(crate) fn doc_attributes(
192 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), 192 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
193 ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), 193 ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
194 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), 194 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
195 _ => return None 195 _ => None
196 } 196 }
197 } 197 }
198} 198}
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index eebae5ebe..cc43c5772 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -2,10 +2,7 @@ use std::iter;
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use syntax::{ 5use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T};
6 algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*,
7 SyntaxNode, WalkEvent, T,
8};
9 6
10use crate::FilePosition; 7use crate::FilePosition;
11 8
@@ -28,16 +25,33 @@ pub struct ExpandedMacro {
28pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { 25pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
29 let sema = Semantics::new(db); 26 let sema = Semantics::new(db);
30 let file = sema.parse(position.file_id); 27 let file = sema.parse(position.file_id);
31 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?;
32 let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?;
33 28
34 let expanded = expand_macro_recur(&sema, &mac)?; 29 let tok = file.syntax().token_at_offset(position.offset).left_biased()?;
30 let mut expanded = None;
31 let mut name = None;
32 for node in tok.ancestors() {
33 if let Some(item) = ast::Item::cast(node.clone()) {
34 expanded = sema.expand_attr_macro(&item);
35 if expanded.is_some() {
36 // FIXME: add the macro name
37 // FIXME: make this recursive too
38 name = Some("?".to_string());
39 break;
40 }
41 }
42
43 if let Some(mac) = ast::MacroCall::cast(node) {
44 name = Some(mac.path()?.segment()?.name_ref()?.to_string());
45 expanded = expand_macro_recur(&sema, &mac);
46 break;
47 }
48 }
35 49
36 // FIXME: 50 // FIXME:
37 // macro expansion may lose all white space information 51 // macro expansion may lose all white space information
38 // But we hope someday we can use ra_fmt for that 52 // But we hope someday we can use ra_fmt for that
39 let expansion = insert_whitespaces(expanded); 53 let expansion = insert_whitespaces(expanded?);
40 Some(ExpandedMacro { name: name_ref.text().to_string(), expansion }) 54 Some(ExpandedMacro { name: name?, expansion })
41} 55}
42 56
43fn expand_macro_recur( 57fn expand_macro_recur(
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 7032889ac..c7ec87edf 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -328,7 +328,7 @@ mod tests {
328 use super::*; 328 use super::*;
329 329
330 fn do_check(before: &str, afters: &[&str]) { 330 fn do_check(before: &str, afters: &[&str]) {
331 let (analysis, position) = fixture::position(&before); 331 let (analysis, position) = fixture::position(before);
332 let before = analysis.file_text(position.file_id).unwrap(); 332 let before = analysis.file_text(position.file_id).unwrap();
333 let range = TextRange::empty(position.offset); 333 let range = TextRange::empty(position.offset);
334 let mut frange = FileRange { file_id: position.file_id, range }; 334 let mut frange = FileRange { file_id: position.file_id, range };
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index 6780af617..38e2e866b 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -1,6 +1,5 @@
1//! Utilities for creating `Analysis` instances for tests. 1//! Utilities for creating `Analysis` instances for tests.
2use ide_db::base_db::fixture::ChangeFixture; 2use ide_db::base_db::fixture::ChangeFixture;
3use syntax::{TextRange, TextSize};
4use test_utils::extract_annotations; 3use test_utils::extract_annotations;
5 4
6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; 5use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
@@ -63,15 +62,8 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
63 62
64pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) { 63pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) {
65 let (analysis, position, mut annotations) = annotations(ra_fixture); 64 let (analysis, position, mut annotations) = annotations(ra_fixture);
66 let (mut expected, data) = annotations.pop().unwrap(); 65 let (expected, data) = annotations.pop().unwrap();
67 assert!(annotations.is_empty()); 66 assert!(annotations.is_empty());
68 match data.as_str() { 67 assert_eq!(data, "");
69 "" => (),
70 "file" => {
71 expected.range =
72 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
73 }
74 data => panic!("bad data: {}", data),
75 }
76 (analysis, position, expected) 68 (analysis, position, expected)
77} 69}
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index a04333e63..8dd643a0f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,10 +1,15 @@
1use std::convert::TryInto;
2
1use either::Either; 3use either::Either;
2use hir::{InFile, Semantics}; 4use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
3use ide_db::{ 5use ide_db::{
4 defs::{NameClass, NameRefClass}, 6 base_db::{AnchoredPath, FileId, FileLoader},
7 defs::{Definition, NameClass, NameRefClass},
5 RootDatabase, 8 RootDatabase,
6}; 9};
7use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 10use syntax::{
11 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TokenAtOffset, T,
12};
8 13
9use crate::{ 14use crate::{
10 display::TryToNav, 15 display::TryToNav,
@@ -32,20 +37,19 @@ pub(crate) fn goto_definition(
32 let original_token = pick_best(file.token_at_offset(position.offset))?; 37 let original_token = pick_best(file.token_at_offset(position.offset))?;
33 let token = sema.descend_into_macros(original_token.clone()); 38 let token = sema.descend_into_macros(original_token.clone());
34 let parent = token.parent()?; 39 let parent = token.parent()?;
35 if let Some(_) = ast::Comment::cast(token) { 40 if let Some(_) = ast::Comment::cast(token.clone()) {
36 let (attributes, def) = doc_attributes(&sema, &parent)?; 41 let (attributes, def) = doc_attributes(&sema, &parent)?;
37 42
38 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; 43 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
39 let (_, link, ns) = 44 let (_, link, ns) =
40 extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { 45 extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| {
41 doc_mapping.map(range.clone()).map_or(false, |InFile { file_id, value: range }| { 46 doc_mapping.map(*range).map_or(false, |InFile { file_id, value: range }| {
42 file_id == position.file_id.into() && range.contains(position.offset) 47 file_id == position.file_id.into() && range.contains(position.offset)
43 }) 48 })
44 })?; 49 })?;
45 let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; 50 let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?;
46 return Some(RangeInfo::new(original_token.text_range(), vec![nav])); 51 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
47 } 52 }
48
49 let nav = match_ast! { 53 let nav = match_ast! {
50 match parent { 54 match parent {
51 ast::NameRef(name_ref) => { 55 ast::NameRef(name_ref) => {
@@ -53,7 +57,8 @@ pub(crate) fn goto_definition(
53 }, 57 },
54 ast::Name(name) => { 58 ast::Name(name) => {
55 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); 59 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
56 def.try_to_nav(sema.db) 60 try_find_trait_item_definition(sema.db, &def)
61 .or_else(|| def.try_to_nav(sema.db))
57 }, 62 },
58 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) { 63 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
59 let def = name_class.referenced_or_defined(sema.db); 64 let def = name_class.referenced_or_defined(sema.db);
@@ -61,6 +66,7 @@ pub(crate) fn goto_definition(
61 } else { 66 } else {
62 reference_definition(&sema, Either::Left(&lt)) 67 reference_definition(&sema, Either::Left(&lt))
63 }, 68 },
69 ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id),
64 _ => return None, 70 _ => return None,
65 } 71 }
66 }; 72 };
@@ -68,6 +74,60 @@ pub(crate) fn goto_definition(
68 Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) 74 Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
69} 75}
70 76
77fn try_lookup_include_path(
78 db: &RootDatabase,
79 tt: ast::TokenTree,
80 token: SyntaxToken,
81 file_id: FileId,
82) -> Option<NavigationTarget> {
83 let path = ast::String::cast(token)?.value()?.into_owned();
84 let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
85 let name = macro_call.path()?.segment()?.name_ref()?;
86 if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
87 return None;
88 }
89 let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
90 let size = db.file_text(file_id).len().try_into().ok()?;
91 Some(NavigationTarget {
92 file_id,
93 full_range: TextRange::new(0.into(), size),
94 name: path.into(),
95 focus_range: None,
96 kind: None,
97 container_name: None,
98 description: None,
99 docs: None,
100 })
101}
102
103/// finds the trait definition of an impl'd item
104/// e.g.
105/// ```rust
106/// trait A { fn a(); }
107/// struct S;
108/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
109/// ```
110fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> {
111 let name = def.name(db)?;
112 let assoc = match def {
113 Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db),
114 Definition::ModuleDef(ModuleDef::Const(c)) => c.as_assoc_item(db),
115 Definition::ModuleDef(ModuleDef::TypeAlias(ty)) => ty.as_assoc_item(db),
116 _ => None,
117 }?;
118
119 let imp = match assoc.container(db) {
120 hir::AssocItemContainer::Impl(imp) => imp,
121 _ => return None,
122 };
123
124 let trait_ = imp.trait_(db)?;
125 trait_
126 .items(db)
127 .iter()
128 .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
129}
130
71fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 131fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
72 return tokens.max_by_key(priority); 132 return tokens.max_by_key(priority);
73 fn priority(n: &SyntaxToken) -> usize { 133 fn priority(n: &SyntaxToken) -> usize {
@@ -125,7 +185,7 @@ mod tests {
125extern crate std$0; 185extern crate std$0;
126//- /std/lib.rs crate:std 186//- /std/lib.rs crate:std
127// empty 187// empty
128//^ file 188//^file
129"#, 189"#,
130 ) 190 )
131 } 191 }
@@ -138,7 +198,7 @@ extern crate std$0;
138extern crate std as abc$0; 198extern crate std as abc$0;
139//- /std/lib.rs crate:std 199//- /std/lib.rs crate:std
140// empty 200// empty
141//^ file 201//^file
142"#, 202"#,
143 ) 203 )
144 } 204 }
@@ -193,7 +253,7 @@ mod $0foo;
193 253
194//- /foo.rs 254//- /foo.rs
195// empty 255// empty
196//^ file 256//^file
197"#, 257"#,
198 ); 258 );
199 259
@@ -204,7 +264,7 @@ mod $0foo;
204 264
205//- /foo/mod.rs 265//- /foo/mod.rs
206// empty 266// empty
207//^ file 267//^file
208"#, 268"#,
209 ); 269 );
210 } 270 }
@@ -335,7 +395,7 @@ use foo as bar$0;
335 395
336//- /foo/lib.rs crate:foo 396//- /foo/lib.rs crate:foo
337// empty 397// empty
338//^ file 398//^file
339"#, 399"#,
340 ); 400 );
341 } 401 }
@@ -1216,4 +1276,73 @@ fn f(e: Enum) {
1216"#, 1276"#,
1217 ); 1277 );
1218 } 1278 }
1279
1280 #[test]
1281 fn goto_include() {
1282 check(
1283 r#"
1284//- /main.rs
1285fn main() {
1286 let str = include_str!("foo.txt$0");
1287}
1288//- /foo.txt
1289// empty
1290//^file
1291"#,
1292 );
1293 }
1294
1295 #[test]
1296 fn goto_def_of_trait_impl_fn() {
1297 check(
1298 r#"
1299trait Twait {
1300 fn a();
1301 // ^
1302}
1303
1304struct Stwuct;
1305
1306impl Twait for Stwuct {
1307 fn a$0();
1308}
1309"#,
1310 );
1311 }
1312
1313 #[test]
1314 fn goto_def_of_trait_impl_const() {
1315 check(
1316 r#"
1317trait Twait {
1318 const NOMS: bool;
1319 // ^^^^
1320}
1321
1322struct Stwuct;
1323
1324impl Twait for Stwuct {
1325 const NOMS$0: bool = true;
1326}
1327"#,
1328 );
1329 }
1330
1331 #[test]
1332 fn goto_def_of_trait_impl_type_alias() {
1333 check(
1334 r#"
1335trait Twait {
1336 type IsBad;
1337 // ^^^^^
1338}
1339
1340struct Stwuct;
1341
1342impl Twait for Stwuct {
1343 type IsBad$0 = !;
1344}
1345"#,
1346 );
1347 }
1219} 1348}
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 43356a94e..0013820b4 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -52,13 +52,13 @@ pub(crate) fn goto_implementation(
52 hir::ModuleDef::Function(f) => { 52 hir::ModuleDef::Function(f) => {
53 let assoc = f.as_assoc_item(sema.db)?; 53 let assoc = f.as_assoc_item(sema.db)?;
54 let name = assoc.name(sema.db)?; 54 let name = assoc.name(sema.db)?;
55 let trait_ = assoc.containing_trait(sema.db)?; 55 let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
56 impls_for_trait_item(&sema, trait_, name) 56 impls_for_trait_item(&sema, trait_, name)
57 } 57 }
58 hir::ModuleDef::Const(c) => { 58 hir::ModuleDef::Const(c) => {
59 let assoc = c.as_assoc_item(sema.db)?; 59 let assoc = c.as_assoc_item(sema.db)?;
60 let name = assoc.name(sema.db)?; 60 let name = assoc.name(sema.db)?;
61 let trait_ = assoc.containing_trait(sema.db)?; 61 let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
62 impls_for_trait_item(&sema, trait_, name) 62 impls_for_trait_item(&sema, trait_, name)
63 } 63 }
64 _ => return None, 64 _ => return None,
@@ -87,7 +87,7 @@ fn impls_for_trait_item(
87 .filter_map(|imp| { 87 .filter_map(|imp| {
88 let item = imp.items(sema.db).iter().find_map(|itm| { 88 let item = imp.items(sema.db).iter().find_map(|itm| {
89 let itm_name = itm.name(sema.db)?; 89 let itm_name = itm.name(sema.db)?;
90 (itm_name == fun_name).then(|| itm.clone()) 90 (itm_name == fun_name).then(|| *itm)
91 })?; 91 })?;
92 item.try_to_nav(sema.db) 92 item.try_to_nav(sema.db)
93 }) 93 })
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 04598cd06..c08516805 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -6,12 +6,18 @@ use hir::{
6use ide_db::{ 6use ide_db::{
7 base_db::SourceDatabase, 7 base_db::SourceDatabase,
8 defs::{Definition, NameClass, NameRefClass}, 8 defs::{Definition, NameClass, NameRefClass},
9 helpers::FamousDefs, 9 helpers::{
10 generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
11 FamousDefs,
12 },
10 RootDatabase, 13 RootDatabase,
11}; 14};
12use itertools::Itertools; 15use itertools::Itertools;
13use stdx::format_to; 16use stdx::format_to;
14use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 17use syntax::{
18 algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, TokenAtOffset,
19 T,
20};
15 21
16use crate::{ 22use crate::{
17 display::{macro_label, TryToNav}, 23 display::{macro_label, TryToNav},
@@ -28,6 +34,7 @@ use crate::{
28#[derive(Clone, Debug, PartialEq, Eq)] 34#[derive(Clone, Debug, PartialEq, Eq)]
29pub struct HoverConfig { 35pub struct HoverConfig {
30 pub implementations: bool, 36 pub implementations: bool,
37 pub references: bool,
31 pub run: bool, 38 pub run: bool,
32 pub debug: bool, 39 pub debug: bool,
33 pub goto_type_def: bool, 40 pub goto_type_def: bool,
@@ -38,6 +45,7 @@ pub struct HoverConfig {
38impl HoverConfig { 45impl HoverConfig {
39 pub const NO_ACTIONS: Self = Self { 46 pub const NO_ACTIONS: Self = Self {
40 implementations: false, 47 implementations: false,
48 references: false,
41 run: false, 49 run: false,
42 debug: false, 50 debug: false,
43 goto_type_def: false, 51 goto_type_def: false,
@@ -46,7 +54,7 @@ impl HoverConfig {
46 }; 54 };
47 55
48 pub fn any(&self) -> bool { 56 pub fn any(&self) -> bool {
49 self.implementations || self.runnable() || self.goto_type_def 57 self.implementations || self.references || self.runnable() || self.goto_type_def
50 } 58 }
51 59
52 pub fn none(&self) -> bool { 60 pub fn none(&self) -> bool {
@@ -62,6 +70,7 @@ impl HoverConfig {
62pub enum HoverAction { 70pub enum HoverAction {
63 Runnable(Runnable), 71 Runnable(Runnable),
64 Implementation(FilePosition), 72 Implementation(FilePosition),
73 Reference(FilePosition),
65 GoToType(Vec<HoverGotoTypeData>), 74 GoToType(Vec<HoverGotoTypeData>),
66} 75}
67 76
@@ -115,13 +124,14 @@ pub(crate) fn hover(
115 |d| d.defined(db), 124 |d| d.defined(db),
116 ), 125 ),
117 126
118 _ => ast::Comment::cast(token.clone()) 127 _ => {
119 .and_then(|_| { 128 if ast::Comment::cast(token.clone()).is_some() {
129 cov_mark::hit!(no_highlight_on_comment_hover);
120 let (attributes, def) = doc_attributes(&sema, &node)?; 130 let (attributes, def) = doc_attributes(&sema, &node)?;
121 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; 131 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
122 let (idl_range, link, ns) = 132 let (idl_range, link, ns) =
123 extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { 133 extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| {
124 let InFile { file_id, value: range } = doc_mapping.map(range.clone())?; 134 let InFile { file_id, value: range } = doc_mapping.map(range)?;
125 if file_id == position.file_id.into() && range.contains(position.offset) { 135 if file_id == position.file_id.into() && range.contains(position.offset) {
126 Some((range, link, ns)) 136 Some((range, link, ns))
127 } else { 137 } else {
@@ -129,9 +139,13 @@ pub(crate) fn hover(
129 } 139 }
130 })?; 140 })?;
131 range = Some(idl_range); 141 range = Some(idl_range);
132 resolve_doc_path_for_def(db, def, &link, ns) 142 resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
133 }) 143 } else if let res@Some(_) = try_hover_for_attribute(&token) {
134 .map(Definition::ModuleDef), 144 return res;
145 } else {
146 None
147 }
148 },
135 } 149 }
136 }; 150 };
137 151
@@ -148,6 +162,10 @@ pub(crate) fn hover(
148 res.actions.push(action); 162 res.actions.push(action);
149 } 163 }
150 164
165 if let Some(action) = show_fn_references_action(db, definition) {
166 res.actions.push(action);
167 }
168
151 if let Some(action) = runnable_action(&sema, definition, position.file_id) { 169 if let Some(action) = runnable_action(&sema, definition, position.file_id) {
152 res.actions.push(action); 170 res.actions.push(action);
153 } 171 }
@@ -161,11 +179,6 @@ pub(crate) fn hover(
161 } 179 }
162 } 180 }
163 181
164 if token.kind() == syntax::SyntaxKind::COMMENT {
165 cov_mark::hit!(no_highlight_on_comment_hover);
166 return None;
167 }
168
169 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { 182 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
170 return res; 183 return res;
171 } 184 }
@@ -194,6 +207,51 @@ pub(crate) fn hover(
194 Some(RangeInfo::new(range, res)) 207 Some(RangeInfo::new(range, res))
195} 208}
196 209
210fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
211 let attr = token.ancestors().find_map(ast::Attr::cast)?;
212 let (path, tt) = attr.as_simple_call()?;
213 if !tt.syntax().text_range().contains(token.text_range().start()) {
214 return None;
215 }
216 let (is_clippy, lints) = match &*path {
217 "feature" => (false, FEATURES),
218 "allow" | "deny" | "forbid" | "warn" => {
219 let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
220 .filter(|t| t.kind() == T![:])
221 .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
222 .filter(|t| t.kind() == T![:])
223 .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
224 .map_or(false, |t| {
225 t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy")
226 });
227 if is_clippy {
228 (true, CLIPPY_LINTS)
229 } else {
230 (false, DEFAULT_LINTS)
231 }
232 }
233 _ => return None,
234 };
235
236 let tmp;
237 let needle = if is_clippy {
238 tmp = format!("clippy::{}", token.text());
239 &tmp
240 } else {
241 &*token.text()
242 };
243
244 let lint =
245 lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?;
246 Some(RangeInfo::new(
247 token.text_range(),
248 HoverResult {
249 markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)),
250 ..Default::default()
251 },
252 ))
253}
254
197fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 255fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
198 fn to_action(nav_target: NavigationTarget) -> HoverAction { 256 fn to_action(nav_target: NavigationTarget) -> HoverAction {
199 HoverAction::Implementation(FilePosition { 257 HoverAction::Implementation(FilePosition {
@@ -211,6 +269,18 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
211 adt.try_to_nav(db).map(to_action) 269 adt.try_to_nav(db).map(to_action)
212} 270}
213 271
272fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
273 match def {
274 Definition::ModuleDef(ModuleDef::Function(it)) => it.try_to_nav(db).map(|nav_target| {
275 HoverAction::Reference(FilePosition {
276 file_id: nav_target.file_id,
277 offset: nav_target.focus_or_full_range().start(),
278 })
279 }),
280 _ => None,
281 }
282}
283
214fn runnable_action( 284fn runnable_action(
215 sema: &Semantics<RootDatabase>, 285 sema: &Semantics<RootDatabase>,
216 def: Definition, 286 def: Definition,
@@ -218,7 +288,7 @@ fn runnable_action(
218) -> Option<HoverAction> { 288) -> Option<HoverAction> {
219 match def { 289 match def {
220 Definition::ModuleDef(it) => match it { 290 Definition::ModuleDef(it) => match it {
221 ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)), 291 ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
222 ModuleDef::Function(func) => { 292 ModuleDef::Function(func) => {
223 let src = func.source(sema.db)?; 293 let src = func.source(sema.db)?;
224 if src.file_id != file_id.into() { 294 if src.file_id != file_id.into() {
@@ -227,7 +297,7 @@ fn runnable_action(
227 return None; 297 return None;
228 } 298 }
229 299
230 runnable_fn(&sema, func).map(HoverAction::Runnable) 300 runnable_fn(sema, func).map(HoverAction::Runnable)
231 } 301 }
232 _ => None, 302 _ => None,
233 }, 303 },
@@ -362,7 +432,7 @@ fn hover_for_definition(
362 return match def { 432 return match def {
363 Definition::Macro(it) => match &it.source(db)?.value { 433 Definition::Macro(it) => match &it.source(db)?.value {
364 Either::Left(mac) => { 434 Either::Left(mac) => {
365 let label = macro_label(&mac); 435 let label = macro_label(mac);
366 from_def_source_labeled(db, it, Some(label), mod_path) 436 from_def_source_labeled(db, it, Some(label), mod_path)
367 } 437 }
368 Either::Right(_) => { 438 Either::Right(_) => {
@@ -446,7 +516,7 @@ fn hover_for_keyword(
446 if !token.kind().is_keyword() { 516 if !token.kind().is_keyword() {
447 return None; 517 return None;
448 } 518 }
449 let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()?).krate()); 519 let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate());
450 // std exposes {}_keyword modules with docstrings on the root to document keywords 520 // std exposes {}_keyword modules with docstrings on the root to document keywords
451 let keyword_mod = format!("{}_keyword", token.text()); 521 let keyword_mod = format!("{}_keyword", token.text());
452 let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; 522 let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
@@ -2377,6 +2447,14 @@ fn foo_$0test() {}
2377"#, 2447"#,
2378 expect![[r#" 2448 expect![[r#"
2379 [ 2449 [
2450 Reference(
2451 FilePosition {
2452 file_id: FileId(
2453 0,
2454 ),
2455 offset: 11,
2456 },
2457 ),
2380 Runnable( 2458 Runnable(
2381 Runnable { 2459 Runnable {
2382 nav: NavigationTarget { 2460 nav: NavigationTarget {
@@ -3977,4 +4055,74 @@ pub fn foo() {}
3977 "#]], 4055 "#]],
3978 ) 4056 )
3979 } 4057 }
4058
4059 #[test]
4060 fn hover_feature() {
4061 check(
4062 r#"#![feature(box_syntax$0)]"#,
4063 expect![[r##"
4064 *box_syntax*
4065 ```
4066 box_syntax
4067 ```
4068 ___
4069
4070 # `box_syntax`
4071
4072 The tracking issue for this feature is: [#49733]
4073
4074 [#49733]: https://github.com/rust-lang/rust/issues/49733
4075
4076 See also [`box_patterns`](box-patterns.md)
4077
4078 ------------------------
4079
4080 Currently the only stable way to create a `Box` is via the `Box::new` method.
4081 Also it is not possible in stable Rust to destructure a `Box` in a match
4082 pattern. The unstable `box` keyword can be used to create a `Box`. An example
4083 usage would be:
4084
4085 ```rust
4086 #![feature(box_syntax)]
4087
4088 fn main() {
4089 let b = box 5;
4090 }
4091 ```
4092
4093 "##]],
4094 )
4095 }
4096
4097 #[test]
4098 fn hover_lint() {
4099 check(
4100 r#"#![allow(arithmetic_overflow$0)]"#,
4101 expect![[r#"
4102 *arithmetic_overflow*
4103 ```
4104 arithmetic_overflow
4105 ```
4106 ___
4107
4108 arithmetic operation overflows
4109 "#]],
4110 )
4111 }
4112
4113 #[test]
4114 fn hover_clippy_lint() {
4115 check(
4116 r#"#![allow(clippy::almost_swapped$0)]"#,
4117 expect![[r#"
4118 *almost_swapped*
4119 ```
4120 clippy::almost_swapped
4121 ```
4122 ___
4123
4124 Checks for `foo = bar; bar = foo` sequences.
4125 "#]],
4126 )
4127 }
3980} 4128}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 85f887737..9cd33d0e4 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -96,7 +96,7 @@ fn get_chaining_hints(
96 } 96 }
97 97
98 let krate = sema.scope(expr.syntax()).module().map(|it| it.krate()); 98 let krate = sema.scope(expr.syntax()).module().map(|it| it.krate());
99 let famous_defs = FamousDefs(&sema, krate); 99 let famous_defs = FamousDefs(sema, krate);
100 100
101 let mut tokens = expr 101 let mut tokens = expr
102 .syntax() 102 .syntax()
@@ -150,17 +150,11 @@ fn get_param_name_hints(
150 return None; 150 return None;
151 } 151 }
152 152
153 let args = match &expr { 153 let (callable, arg_list) = get_callable(sema, &expr)?;
154 ast::Expr::CallExpr(expr) => expr.arg_list()?.args(),
155 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
156 _ => return None,
157 };
158
159 let callable = get_callable(sema, &expr)?;
160 let hints = callable 154 let hints = callable
161 .params(sema.db) 155 .params(sema.db)
162 .into_iter() 156 .into_iter()
163 .zip(args) 157 .zip(arg_list.args())
164 .filter_map(|((param, _ty), arg)| { 158 .filter_map(|((param, _ty), arg)| {
165 let param_name = match param? { 159 let param_name = match param? {
166 Either::Left(_) => "self".to_string(), 160 Either::Left(_) => "self".to_string(),
@@ -171,7 +165,7 @@ fn get_param_name_hints(
171 }; 165 };
172 Some((param_name, arg)) 166 Some((param_name, arg))
173 }) 167 })
174 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg)) 168 .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, arg))
175 .map(|(param_name, arg)| InlayHint { 169 .map(|(param_name, arg)| InlayHint {
176 range: arg.syntax().text_range(), 170 range: arg.syntax().text_range(),
177 kind: InlayKind::ParameterHint, 171 kind: InlayKind::ParameterHint,
@@ -193,7 +187,7 @@ fn get_bind_pat_hints(
193 } 187 }
194 188
195 let krate = sema.scope(pat.syntax()).module().map(|it| it.krate()); 189 let krate = sema.scope(pat.syntax()).module().map(|it| it.krate());
196 let famous_defs = FamousDefs(&sema, krate); 190 let famous_defs = FamousDefs(sema, krate);
197 191
198 let ty = sema.type_of_pat(&pat.clone().into())?; 192 let ty = sema.type_of_pat(&pat.clone().into())?;
199 193
@@ -289,15 +283,9 @@ fn should_not_display_type_hint(
289 for node in bind_pat.syntax().ancestors() { 283 for node in bind_pat.syntax().ancestors() {
290 match_ast! { 284 match_ast! {
291 match node { 285 match node {
292 ast::LetStmt(it) => { 286 ast::LetStmt(it) => return it.ty().is_some(),
293 return it.ty().is_some() 287 ast::Param(it) => return it.ty().is_some(),
294 }, 288 ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
295 ast::Param(it) => {
296 return it.ty().is_some()
297 },
298 ast::MatchArm(_it) => {
299 return pat_is_enum_variant(db, bind_pat, pat_ty);
300 },
301 ast::IfExpr(it) => { 289 ast::IfExpr(it) => {
302 return it.condition().and_then(|condition| condition.pat()).is_some() 290 return it.condition().and_then(|condition| condition.pat()).is_some()
303 && pat_is_enum_variant(db, bind_pat, pat_ty); 291 && pat_is_enum_variant(db, bind_pat, pat_ty);
@@ -311,9 +299,8 @@ fn should_not_display_type_hint(
311 // Type of expr should be iterable. 299 // Type of expr should be iterable.
312 return it.in_token().is_none() || 300 return it.in_token().is_none() ||
313 it.iterable() 301 it.iterable()
314 .and_then(|iterable_expr|sema.type_of_expr(&iterable_expr)) 302 .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
315 .map(|iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) 303 .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
316 .unwrap_or(true)
317 }, 304 },
318 _ => (), 305 _ => (),
319 } 306 }
@@ -322,76 +309,66 @@ fn should_not_display_type_hint(
322 false 309 false
323} 310}
324 311
325fn should_show_param_name_hint( 312fn should_hide_param_name_hint(
326 sema: &Semantics<RootDatabase>, 313 sema: &Semantics<RootDatabase>,
327 callable: &hir::Callable, 314 callable: &hir::Callable,
328 param_name: &str, 315 param_name: &str,
329 argument: &ast::Expr, 316 argument: &ast::Expr,
330) -> bool { 317) -> bool {
318 // These are to be tested in the `parameter_hint_heuristics` test
319 // hide when:
320 // - the parameter name is a suffix of the function's name
321 // - the argument is an enum whose name is equal to the parameter
322 // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
323 // of argument with _ splitting it off
324 // - param starts with `ra_fixture`
325 // - param is a well known name in an unary function
326
331 let param_name = param_name.trim_start_matches('_'); 327 let param_name = param_name.trim_start_matches('_');
328 if param_name.is_empty() {
329 return true;
330 }
331
332 let fn_name = match callable.kind() { 332 let fn_name = match callable.kind() {
333 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()), 333 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
334 hir::CallableKind::TupleStruct(_) 334 _ => None,
335 | hir::CallableKind::TupleEnumVariant(_)
336 | hir::CallableKind::Closure => None,
337 }; 335 };
338 336 let fn_name = fn_name.as_deref();
339 if param_name.is_empty() 337 is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
340 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_')) 338 || is_enum_name_similar_to_param_name(sema, argument, param_name)
341 || is_argument_similar_to_param_name(sema, argument, param_name) 339 || is_argument_similar_to_param_name(argument, param_name)
342 || is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref())
343 || param_name.starts_with("ra_fixture") 340 || param_name.starts_with("ra_fixture")
344 { 341 || (callable.n_params() == 1 && is_obvious_param(param_name))
345 return false;
346 }
347
348 // avoid displaying hints for common functions like map, filter, etc.
349 // or other obvious words used in std
350 !(callable.n_params() == 1 && is_obvious_param(param_name))
351} 342}
352 343
353fn is_argument_similar_to_param_name( 344fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
354 sema: &Semantics<RootDatabase>, 345 // check whether param_name and argument are the same or
355 argument: &ast::Expr, 346 // whether param_name is a prefix/suffix of argument(split at `_`)
356 param_name: &str, 347 let argument = match get_string_representation(argument) {
357) -> bool { 348 Some(argument) => argument,
358 if is_enum_name_similar_to_param_name(sema, argument, param_name) { 349 None => return false,
350 };
351
352 let param_name = param_name.trim_start_matches('_');
353 let argument = argument.trim_start_matches('_');
354 if argument.strip_prefix(param_name).map_or(false, |s| s.starts_with('_')) {
359 return true; 355 return true;
360 } 356 }
361 match get_string_representation(argument) { 357 if argument.strip_suffix(param_name).map_or(false, |s| s.ends_with('_')) {
362 None => false, 358 return true;
363 Some(argument_string) => {
364 let num_leading_underscores =
365 argument_string.bytes().take_while(|&c| c == b'_').count();
366
367 // Does the argument name begin with the parameter name? Ignore leading underscores.
368 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
369 let starts_with_pattern = param_name.bytes().all(
370 |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
371 );
372
373 if starts_with_pattern {
374 return true;
375 }
376
377 // Does the argument name end with the parameter name?
378 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
379 param_name.bytes().rev().all(
380 |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
381 )
382 }
383 } 359 }
360 argument == param_name
384} 361}
385 362
386fn is_param_name_similar_to_fn_name( 363/// Hide the parameter name of an unary function if it is a `_` - prefixed suffix of the function's name, or equal.
364///
365/// `fn strip_suffix(suffix)` will be hidden.
366/// `fn stripsuffix(suffix)` will not be hidden.
367fn is_param_name_suffix_of_fn_name(
387 param_name: &str, 368 param_name: &str,
388 callable: &Callable, 369 callable: &Callable,
389 fn_name: Option<&String>, 370 fn_name: Option<&str>,
390) -> bool { 371) -> bool {
391 // if it's the only parameter, don't show it if:
392 // - is the same as the function name, or
393 // - the function ends with '_' + param_name
394
395 match (callable.n_params(), fn_name) { 372 match (callable.n_params(), fn_name) {
396 (1, Some(function)) => { 373 (1, Some(function)) => {
397 function == param_name 374 function == param_name
@@ -419,12 +396,12 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
419 ast::Expr::MethodCallExpr(method_call_expr) => { 396 ast::Expr::MethodCallExpr(method_call_expr) => {
420 let name_ref = method_call_expr.name_ref()?; 397 let name_ref = method_call_expr.name_ref()?;
421 match name_ref.text().as_str() { 398 match name_ref.text().as_str() {
422 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), 399 "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()),
423 name_ref => Some(name_ref.to_owned()), 400 name_ref => Some(name_ref.to_owned()),
424 } 401 }
425 } 402 }
426 ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()), 403 ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
427 ast::Expr::PathExpr(path_expr) => Some(path_expr.to_string()), 404 ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
428 ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?), 405 ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
429 ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), 406 ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
430 _ => None, 407 _ => None,
@@ -432,15 +409,24 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
432} 409}
433 410
434fn is_obvious_param(param_name: &str) -> bool { 411fn is_obvious_param(param_name: &str) -> bool {
412 // avoid displaying hints for common functions like map, filter, etc.
413 // or other obvious words used in std
435 let is_obvious_param_name = 414 let is_obvious_param_name =
436 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other"); 415 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
437 param_name.len() == 1 || is_obvious_param_name 416 param_name.len() == 1 || is_obvious_param_name
438} 417}
439 418
440fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> { 419fn get_callable(
420 sema: &Semantics<RootDatabase>,
421 expr: &ast::Expr,
422) -> Option<(hir::Callable, ast::ArgList)> {
441 match expr { 423 match expr {
442 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), 424 ast::Expr::CallExpr(expr) => {
443 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), 425 sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list())
426 }
427 ast::Expr::MethodCallExpr(expr) => {
428 sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
429 }
444 _ => None, 430 _ => None,
445 } 431 }
446} 432}
@@ -464,6 +450,42 @@ mod tests {
464 check_with_config(TEST_CONFIG, ra_fixture); 450 check_with_config(TEST_CONFIG, ra_fixture);
465 } 451 }
466 452
453 fn check_params(ra_fixture: &str) {
454 check_with_config(
455 InlayHintsConfig {
456 parameter_hints: true,
457 type_hints: false,
458 chaining_hints: false,
459 max_length: None,
460 },
461 ra_fixture,
462 );
463 }
464
465 fn check_types(ra_fixture: &str) {
466 check_with_config(
467 InlayHintsConfig {
468 parameter_hints: false,
469 type_hints: true,
470 chaining_hints: false,
471 max_length: None,
472 },
473 ra_fixture,
474 );
475 }
476
477 fn check_chains(ra_fixture: &str) {
478 check_with_config(
479 InlayHintsConfig {
480 parameter_hints: false,
481 type_hints: false,
482 chaining_hints: true,
483 max_length: None,
484 },
485 ra_fixture,
486 );
487 }
488
467 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { 489 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
468 let ra_fixture = 490 let ra_fixture =
469 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); 491 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
@@ -484,17 +506,30 @@ mod tests {
484 } 506 }
485 507
486 #[test] 508 #[test]
487 fn param_hints_only() { 509 fn hints_disabled() {
488 check_with_config( 510 check_with_config(
489 InlayHintsConfig { 511 InlayHintsConfig {
490 parameter_hints: true,
491 type_hints: false, 512 type_hints: false,
513 parameter_hints: false,
492 chaining_hints: false, 514 chaining_hints: false,
493 max_length: None, 515 max_length: None,
494 }, 516 },
495 r#" 517 r#"
496fn foo(a: i32, b: i32) -> i32 { a + b } 518fn foo(a: i32, b: i32) -> i32 { a + b }
497fn main() { 519fn main() {
520 let _x = foo(4, 4);
521}"#,
522 );
523 }
524
525 // Parameter hint tests
526
527 #[test]
528 fn param_hints_only() {
529 check_params(
530 r#"
531fn foo(a: i32, b: i32) -> i32 { a + b }
532fn main() {
498 let _x = foo( 533 let _x = foo(
499 4, 534 4,
500 //^ a 535 //^ a
@@ -507,13 +542,7 @@ fn main() {
507 542
508 #[test] 543 #[test]
509 fn param_name_similar_to_fn_name_still_hints() { 544 fn param_name_similar_to_fn_name_still_hints() {
510 check_with_config( 545 check_params(
511 InlayHintsConfig {
512 parameter_hints: true,
513 type_hints: false,
514 chaining_hints: false,
515 max_length: None,
516 },
517 r#" 546 r#"
518fn max(x: i32, y: i32) -> i32 { x + y } 547fn max(x: i32, y: i32) -> i32 { x + y }
519fn main() { 548fn main() {
@@ -529,13 +558,7 @@ fn main() {
529 558
530 #[test] 559 #[test]
531 fn param_name_similar_to_fn_name() { 560 fn param_name_similar_to_fn_name() {
532 check_with_config( 561 check_params(
533 InlayHintsConfig {
534 parameter_hints: true,
535 type_hints: false,
536 chaining_hints: false,
537 max_length: None,
538 },
539 r#" 562 r#"
540fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore } 563fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
541fn main() { 564fn main() {
@@ -544,17 +567,20 @@ fn main() {
544 ); 567 );
545}"#, 568}"#,
546 ); 569 );
570 check_params(
571 r#"
572fn param_with_underscore(underscore: i32) -> i32 { underscore }
573fn main() {
574 let _x = param_with_underscore(
575 4,
576 );
577}"#,
578 );
547 } 579 }
548 580
549 #[test] 581 #[test]
550 fn param_name_same_as_fn_name() { 582 fn param_name_same_as_fn_name() {
551 check_with_config( 583 check_params(
552 InlayHintsConfig {
553 parameter_hints: true,
554 type_hints: false,
555 chaining_hints: false,
556 max_length: None,
557 },
558 r#" 584 r#"
559fn foo(foo: i32) -> i32 { foo } 585fn foo(foo: i32) -> i32 { foo }
560fn main() { 586fn main() {
@@ -567,52 +593,206 @@ fn main() {
567 593
568 #[test] 594 #[test]
569 fn never_hide_param_when_multiple_params() { 595 fn never_hide_param_when_multiple_params() {
570 check_with_config( 596 check_params(
571 InlayHintsConfig {
572 parameter_hints: true,
573 type_hints: false,
574 chaining_hints: false,
575 max_length: None,
576 },
577 r#" 597 r#"
578fn foo(bar: i32, baz: i32) -> i32 { bar + baz } 598fn foo(foo: i32, bar: i32) -> i32 { bar + baz }
579fn main() { 599fn main() {
580 let _x = foo( 600 let _x = foo(
581 4, 601 4,
582 //^ bar 602 //^ foo
583 8, 603 8,
584 //^ baz 604 //^ bar
585 ); 605 );
586}"#, 606}"#,
587 ); 607 );
588 } 608 }
589 609
590 #[test] 610 #[test]
591 fn hints_disabled() { 611 fn param_hints_look_through_as_ref_and_clone() {
592 check_with_config( 612 check_params(
593 InlayHintsConfig {
594 type_hints: false,
595 parameter_hints: false,
596 chaining_hints: false,
597 max_length: None,
598 },
599 r#" 613 r#"
600fn foo(a: i32, b: i32) -> i32 { a + b } 614fn foo(bar: i32, baz: f32) {}
615
601fn main() { 616fn main() {
602 let _x = foo(4, 4); 617 let bar = 3;
618 let baz = &"baz";
619 let fez = 1.0;
620 foo(bar.clone(), bar.clone());
621 //^^^^^^^^^^^ baz
622 foo(bar.as_ref(), bar.as_ref());
623 //^^^^^^^^^^^^ baz
624}
625"#,
626 );
627 }
628
629 #[test]
630 fn self_param_hints() {
631 check_params(
632 r#"
633struct Foo;
634
635impl Foo {
636 fn foo(self: Self) {}
637 fn bar(self: &Self) {}
638}
639
640fn main() {
641 Foo::foo(Foo);
642 //^^^ self
643 Foo::bar(&Foo);
644 //^^^^ self
645}
646"#,
647 )
648 }
649
650 #[test]
651 fn param_name_hints_show_for_literals() {
652 check_params(
653 r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
654fn main() {
655 test(
656 0xa_b,
657 //^^^^^ a
658 0xa_b,
659 //^^^^^ b
660 );
661}"#,
662 )
663 }
664
665 #[test]
666 fn function_call_parameter_hint() {
667 check_params(
668 r#"
669enum Option<T> { None, Some(T) }
670use Option::*;
671
672struct FileId {}
673struct SmolStr {}
674
675struct TextRange {}
676struct SyntaxKind {}
677struct NavigationTarget {}
678
679struct Test {}
680
681impl Test {
682 fn method(&self, mut param: i32) -> i32 { param * 2 }
683
684 fn from_syntax(
685 file_id: FileId,
686 name: SmolStr,
687 focus_range: Option<TextRange>,
688 full_range: TextRange,
689 kind: SyntaxKind,
690 docs: Option<String>,
691 ) -> NavigationTarget {
692 NavigationTarget {}
693 }
694}
695
696fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
697 foo + bar
698}
699
700fn main() {
701 let not_literal = 1;
702 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
703 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
704 let t: Test = Test {};
705 t.method(123);
706 //^^^ param
707 Test::method(&t, 3456);
708 //^^ self ^^^^ param
709 Test::from_syntax(
710 FileId {},
711 //^^^^^^^^^ file_id
712 "impl".into(),
713 //^^^^^^^^^^^^^ name
714 None,
715 //^^^^ focus_range
716 TextRange {},
717 //^^^^^^^^^^^^ full_range
718 SyntaxKind {},
719 //^^^^^^^^^^^^^ kind
720 None,
721 //^^^^ docs
722 );
723}"#,
724 );
725 }
726
727 #[test]
728 fn parameter_hint_heuristics() {
729 check_params(
730 r#"
731fn check(ra_fixture_thing: &str) {}
732
733fn map(f: i32) {}
734fn filter(predicate: i32) {}
735
736fn strip_suffix(suffix: &str) {}
737fn stripsuffix(suffix: &str) {}
738fn same(same: u32) {}
739fn same2(_same2: u32) {}
740
741fn enum_matches_param_name(completion_kind: CompletionKind) {}
742
743fn foo(param: u32) {}
744fn bar(param_eter: u32) {}
745
746enum CompletionKind {
747 Keyword,
748}
749
750fn non_ident_pat((a, b): (u32, u32)) {}
751
752fn main() {
753 check("");
754
755 map(0);
756 filter(0);
757
758 strip_suffix("");
759 stripsuffix("");
760 //^^ suffix
761 same(0);
762 same2(0);
763
764 enum_matches_param_name(CompletionKind::Keyword);
765
766 let param = 0;
767 foo(param);
768 let param_end = 0;
769 foo(param_end);
770 let start_param = 0;
771 foo(start_param);
772 let param2 = 0;
773 foo(param2);
774 //^^^^^^ param
775
776 let param_eter = 0;
777 bar(param_eter);
778 let param_eter_end = 0;
779 bar(param_eter_end);
780 let start_param_eter = 0;
781 bar(start_param_eter);
782 let param_eter2 = 0;
783 bar(param_eter2);
784 //^^^^^^^^^^^ param_eter
785
786 non_ident_pat((0, 0));
603}"#, 787}"#,
604 ); 788 );
605 } 789 }
606 790
791 // Type-Hint tests
792
607 #[test] 793 #[test]
608 fn type_hints_only() { 794 fn type_hints_only() {
609 check_with_config( 795 check_types(
610 InlayHintsConfig {
611 type_hints: true,
612 parameter_hints: false,
613 chaining_hints: false,
614 max_length: None,
615 },
616 r#" 796 r#"
617fn foo(a: i32, b: i32) -> i32 { a + b } 797fn foo(a: i32, b: i32) -> i32 { a + b }
618fn main() { 798fn main() {
@@ -640,8 +820,115 @@ fn main() {
640 } 820 }
641 821
642 #[test] 822 #[test]
823 fn shorten_iterators_in_associated_params() {
824 check_types(
825 r#"
826use core::iter;
827
828pub struct SomeIter<T> {}
829
830impl<T> SomeIter<T> {
831 pub fn new() -> Self { SomeIter {} }
832 pub fn push(&mut self, t: T) {}
833}
834
835impl<T> Iterator for SomeIter<T> {
836 type Item = T;
837 fn next(&mut self) -> Option<Self::Item> {
838 None
839 }
840}
841
842fn main() {
843 let mut some_iter = SomeIter::new();
844 //^^^^^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
845 some_iter.push(iter::repeat(2).take(2));
846 let iter_of_iters = some_iter.take(2);
847 //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
848}
849"#,
850 );
851 }
852
853 #[test]
854 fn infer_call_method_return_associated_types_with_generic() {
855 check_types(
856 r#"
857 pub trait Default {
858 fn default() -> Self;
859 }
860 pub trait Foo {
861 type Bar: Default;
862 }
863
864 pub fn quux<T: Foo>() -> T::Bar {
865 let y = Default::default();
866 //^ <T as Foo>::Bar
867
868 y
869 }
870 "#,
871 );
872 }
873
874 #[test]
875 fn fn_hints() {
876 check_types(
877 r#"
878trait Sized {}
879
880fn foo() -> impl Fn() { loop {} }
881fn foo1() -> impl Fn(f64) { loop {} }
882fn foo2() -> impl Fn(f64, f64) { loop {} }
883fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
884fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
885fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
886fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
887fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
888
889fn main() {
890 let foo = foo();
891 // ^^^ impl Fn()
892 let foo = foo1();
893 // ^^^ impl Fn(f64)
894 let foo = foo2();
895 // ^^^ impl Fn(f64, f64)
896 let foo = foo3();
897 // ^^^ impl Fn(f64, f64) -> u32
898 let foo = foo4();
899 // ^^^ &dyn Fn(f64, f64) -> u32
900 let foo = foo5();
901 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
902 let foo = foo6();
903 // ^^^ impl Fn(f64, f64) -> u32 + Sized
904 let foo = foo7();
905 // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized)
906}
907"#,
908 )
909 }
910
911 #[test]
912 fn unit_structs_have_no_type_hints() {
913 check_types(
914 r#"
915enum Result<T, E> { Ok(T), Err(E) }
916use Result::*;
917
918struct SyntheticSyntax;
919
920fn main() {
921 match Ok(()) {
922 Ok(_) => (),
923 Err(SyntheticSyntax) => (),
924 }
925}"#,
926 );
927 }
928
929 #[test]
643 fn let_statement() { 930 fn let_statement() {
644 check( 931 check_types(
645 r#" 932 r#"
646#[derive(PartialEq)] 933#[derive(PartialEq)]
647enum Option<T> { None, Some(T) } 934enum Option<T> { None, Some(T) }
@@ -676,34 +963,8 @@ fn main() {
676 } 963 }
677 964
678 #[test] 965 #[test]
679 fn closure_parameters() {
680 check(
681 r#"
682fn main() {
683 let mut start = 0;
684 //^^^^^^^^^ i32
685 (0..2).for_each(|increment| { start += increment; });
686 //^^^^^^^^^ i32
687
688 let multiply =
689 //^^^^^^^^ |…| -> i32
690 | a, b| a * b
691 //^ i32 ^ i32
692 ;
693
694 let _: i32 = multiply(1, 2);
695 let multiply_ref = &multiply;
696 //^^^^^^^^^^^^ &|…| -> i32
697
698 let return_42 = || 42;
699 //^^^^^^^^^ || -> i32
700}"#,
701 );
702 }
703
704 #[test]
705 fn if_expr() { 966 fn if_expr() {
706 check( 967 check_types(
707 r#" 968 r#"
708enum Option<T> { None, Some(T) } 969enum Option<T> { None, Some(T) }
709use Option::*; 970use Option::*;
@@ -735,7 +996,7 @@ fn main() {
735 996
736 #[test] 997 #[test]
737 fn while_expr() { 998 fn while_expr() {
738 check( 999 check_types(
739 r#" 1000 r#"
740enum Option<T> { None, Some(T) } 1001enum Option<T> { None, Some(T) }
741use Option::*; 1002use Option::*;
@@ -753,7 +1014,7 @@ fn main() {
753 1014
754 #[test] 1015 #[test]
755 fn match_arm_list() { 1016 fn match_arm_list() {
756 check( 1017 check_types(
757 r#" 1018 r#"
758enum Option<T> { None, Some(T) } 1019enum Option<T> { None, Some(T) }
759use Option::*; 1020use Option::*;
@@ -774,203 +1035,175 @@ fn main() {
774 } 1035 }
775 1036
776 #[test] 1037 #[test]
777 fn hint_truncation() { 1038 fn incomplete_for_no_hint() {
778 check_with_config( 1039 check_types(
779 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
780 r#" 1040 r#"
781struct Smol<T>(T);
782
783struct VeryLongOuterName<T>(T);
784
785fn main() { 1041fn main() {
786 let a = Smol(0u32); 1042 let data = &[1i32, 2, 3];
787 //^ Smol<u32> 1043 //^^^^ &[i32; 3]
788 let b = VeryLongOuterName(0usize); 1044 for i
789 //^ VeryLongOuterName<…>
790 let c = Smol(Smol(0u32))
791 //^ Smol<Smol<…>>
792}"#, 1045}"#,
793 ); 1046 );
794 }
795
796 #[test]
797 fn function_call_parameter_hint() {
798 check( 1047 check(
799 r#" 1048 r#"
800enum Option<T> { None, Some(T) } 1049pub struct Vec<T> {}
801use Option::*;
802
803struct FileId {}
804struct SmolStr {}
805
806struct TextRange {}
807struct SyntaxKind {}
808struct NavigationTarget {}
809
810struct Test {}
811
812impl Test {
813 fn method(&self, mut param: i32) -> i32 { param * 2 }
814 1050
815 fn from_syntax( 1051impl<T> Vec<T> {
816 file_id: FileId, 1052 pub fn new() -> Self { Vec {} }
817 name: SmolStr, 1053 pub fn push(&mut self, t: T) {}
818 focus_range: Option<TextRange>,
819 full_range: TextRange,
820 kind: SyntaxKind,
821 docs: Option<String>,
822 ) -> NavigationTarget {
823 NavigationTarget {}
824 }
825} 1054}
826 1055
827fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { 1056impl<T> IntoIterator for Vec<T> {
828 foo + bar 1057 type Item=T;
829} 1058}
830 1059
831fn main() { 1060fn main() {
832 let not_literal = 1; 1061 let mut data = Vec::new();
833 //^^^^^^^^^^^ i32 1062 //^^^^^^^^ Vec<&str>
834 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 1063 data.push("foo");
835 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last 1064 for i in
836 let t: Test = Test {}; 1065
837 t.method(123); 1066 println!("Unit expr");
838 //^^^ param 1067}
839 Test::method(&t, 3456); 1068"#,
840 //^^ self ^^^^ param
841 Test::from_syntax(
842 FileId {},
843 //^^^^^^^^^ file_id
844 "impl".into(),
845 //^^^^^^^^^^^^^ name
846 None,
847 //^^^^ focus_range
848 TextRange {},
849 //^^^^^^^^^^^^ full_range
850 SyntaxKind {},
851 //^^^^^^^^^^^^^ kind
852 None,
853 //^^^^ docs
854 );
855}"#,
856 ); 1069 );
857 } 1070 }
858 1071
859 #[test] 1072 #[test]
860 fn omitted_parameters_hints_heuristics() { 1073 fn complete_for_hint() {
861 check_with_config( 1074 check_types(
862 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
863 r#" 1075 r#"
864fn map(f: i32) {} 1076pub struct Vec<T> {}
865fn filter(predicate: i32) {}
866 1077
867struct TestVarContainer { 1078impl<T> Vec<T> {
868 test_var: i32, 1079 pub fn new() -> Self { Vec {} }
1080 pub fn push(&mut self, t: T) {}
869} 1081}
870 1082
871impl TestVarContainer { 1083impl<T> IntoIterator for Vec<T> {
872 fn test_var(&self) -> i32 { 1084 type Item=T;
873 self.test_var
874 }
875} 1085}
876 1086
877struct Test {} 1087fn main() {
878 1088 let mut data = Vec::new();
879impl Test { 1089 //^^^^^^^^ Vec<&str>
880 fn map(self, f: i32) -> Self { 1090 data.push("foo");
881 self 1091 for i in data {
882 } 1092 //^ &str
883 1093 let z = i;
884 fn filter(self, predicate: i32) -> Self { 1094 //^ &str
885 self
886 } 1095 }
887 1096}
888 fn field(self, value: i32) -> Self { 1097"#,
889 self 1098 );
890 } 1099 }
891 1100
892 fn no_hints_expected(&self, _: i32, test_var: i32) {} 1101 #[test]
1102 fn multi_dyn_trait_bounds() {
1103 check_types(
1104 r#"
1105pub struct Vec<T> {}
893 1106
894 fn frob(&self, frob: bool) {} 1107impl<T> Vec<T> {
1108 pub fn new() -> Self { Vec {} }
895} 1109}
896 1110
897struct Param {} 1111pub struct Box<T> {}
898
899fn different_order(param: &Param) {}
900fn different_order_mut(param: &mut Param) {}
901fn has_underscore(_param: bool) {}
902fn enum_matches_param_name(completion_kind: CompletionKind) {}
903fn param_destructuring_omitted_1((a, b): (u32, u32)) {}
904fn param_destructuring_omitted_2(TestVarContainer { test_var: _ }: TestVarContainer) {}
905
906fn twiddle(twiddle: bool) {}
907fn doo(_doo: bool) {}
908 1112
909enum CompletionKind { 1113trait Display {}
910 Keyword, 1114trait Sync {}
911}
912 1115
913fn main() { 1116fn main() {
914 let container: TestVarContainer = TestVarContainer { test_var: 42 }; 1117 let _v = Vec::<Box<&(dyn Display + Sync)>>::new();
915 let test: Test = Test {}; 1118 //^^ Vec<Box<&(dyn Display + Sync)>>
916 1119 let _v = Vec::<Box<*const (dyn Display + Sync)>>::new();
917 map(22); 1120 //^^ Vec<Box<*const (dyn Display + Sync)>>
918 filter(33); 1121 let _v = Vec::<Box<dyn Display + Sync>>::new();
919 1122 //^^ Vec<Box<dyn Display + Sync>>
920 let test_processed: Test = test.map(1).filter(2).field(3); 1123}
1124"#,
1125 );
1126 }
921 1127
922 let test_var: i32 = 55; 1128 #[test]
923 test_processed.no_hints_expected(22, test_var); 1129 fn shorten_iterator_hints() {
924 test_processed.no_hints_expected(33, container.test_var); 1130 check_types(
925 test_processed.no_hints_expected(44, container.test_var()); 1131 r#"
926 test_processed.frob(false); 1132use core::iter;
927 1133
928 twiddle(true); 1134struct MyIter;
929 doo(true);
930 1135
931 const TWIDDLE_UPPERCASE: bool = true; 1136impl Iterator for MyIter {
932 twiddle(TWIDDLE_UPPERCASE); 1137 type Item = ();
1138 fn next(&mut self) -> Option<Self::Item> {
1139 None
1140 }
1141}
933 1142
934 let mut param_begin: Param = Param {}; 1143fn main() {
935 different_order(&param_begin); 1144 let _x = MyIter;
936 different_order(&mut param_begin); 1145 //^^ MyIter
1146 let _x = iter::repeat(0);
1147 //^^ impl Iterator<Item = i32>
1148 fn generic<T: Clone>(t: T) {
1149 let _x = iter::repeat(t);
1150 //^^ impl Iterator<Item = T>
1151 let _chained = iter::repeat(t).take(10);
1152 //^^^^^^^^ impl Iterator<Item = T>
1153 }
1154}
1155"#,
1156 );
1157 }
937 1158
938 let param: bool = true; 1159 #[test]
939 has_underscore(param); 1160 fn closures() {
1161 check(
1162 r#"
1163fn main() {
1164 let mut start = 0;
1165 //^^^^^^^^^ i32
1166 (0..2).for_each(|increment| { start += increment; });
1167 //^^^^^^^^^ i32
940 1168
941 enum_matches_param_name(CompletionKind::Keyword); 1169 let multiply =
1170 //^^^^^^^^ |…| -> i32
1171 | a, b| a * b
1172 //^ i32 ^ i32
1173 ;
942 1174
943 let a: f64 = 7.0; 1175 let _: i32 = multiply(1, 2);
944 let b: f64 = 4.0; 1176 let multiply_ref = &multiply;
945 let _: f64 = a.div_euclid(b); 1177 //^^^^^^^^^^^^ &|…| -> i32
946 let _: f64 = a.abs_sub(b);
947 1178
948 let range: (u32, u32) = (3, 5); 1179 let return_42 = || 42;
949 param_destructuring_omitted_1(range); 1180 //^^^^^^^^^ || -> i32
950 param_destructuring_omitted_2(container);
951}"#, 1181}"#,
952 ); 1182 );
953 } 1183 }
954 1184
955 #[test] 1185 #[test]
956 fn unit_structs_have_no_type_hints() { 1186 fn hint_truncation() {
957 check_with_config( 1187 check_with_config(
958 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }, 1188 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
959 r#" 1189 r#"
960enum Result<T, E> { Ok(T), Err(E) } 1190struct Smol<T>(T);
961use Result::*;
962 1191
963struct SyntheticSyntax; 1192struct VeryLongOuterName<T>(T);
964 1193
965fn main() { 1194fn main() {
966 match Ok(()) { 1195 let a = Smol(0u32);
967 Ok(_) => (), 1196 //^ Smol<u32>
968 Err(SyntheticSyntax) => (), 1197 let b = VeryLongOuterName(0usize);
969 } 1198 //^ VeryLongOuterName<…>
1199 let c = Smol(Smol(0u32))
1200 //^ Smol<Smol<…>>
970}"#, 1201}"#,
971 ); 1202 );
972 } 1203 }
973 1204
1205 // Chaining hint tests
1206
974 #[test] 1207 #[test]
975 fn chaining_hints_ignore_comments() { 1208 fn chaining_hints_ignore_comments() {
976 check_expect( 1209 check_expect(
@@ -1013,13 +1246,7 @@ fn main() {
1013 1246
1014 #[test] 1247 #[test]
1015 fn chaining_hints_without_newlines() { 1248 fn chaining_hints_without_newlines() {
1016 check_with_config( 1249 check_chains(
1017 InlayHintsConfig {
1018 parameter_hints: false,
1019 type_hints: false,
1020 chaining_hints: true,
1021 max_length: None,
1022 },
1023 r#" 1250 r#"
1024struct A(B); 1251struct A(B);
1025impl A { fn into_b(self) -> B { self.0 } } 1252impl A { fn into_b(self) -> B { self.0 } }
@@ -1123,140 +1350,6 @@ fn main() {
1123 } 1350 }
1124 1351
1125 #[test] 1352 #[test]
1126 fn incomplete_for_no_hint() {
1127 check(
1128 r#"
1129fn main() {
1130 let data = &[1i32, 2, 3];
1131 //^^^^ &[i32; 3]
1132 for i
1133}"#,
1134 );
1135 check(
1136 r#"
1137pub struct Vec<T> {}
1138
1139impl<T> Vec<T> {
1140 pub fn new() -> Self { Vec {} }
1141 pub fn push(&mut self, t: T) {}
1142}
1143
1144impl<T> IntoIterator for Vec<T> {
1145 type Item=T;
1146}
1147
1148fn main() {
1149 let mut data = Vec::new();
1150 //^^^^^^^^ Vec<&str>
1151 data.push("foo");
1152 for i in
1153
1154 println!("Unit expr");
1155}
1156"#,
1157 );
1158 }
1159
1160 #[test]
1161 fn complete_for_hint() {
1162 check(
1163 r#"
1164pub struct Vec<T> {}
1165
1166impl<T> Vec<T> {
1167 pub fn new() -> Self { Vec {} }
1168 pub fn push(&mut self, t: T) {}
1169}
1170
1171impl<T> IntoIterator for Vec<T> {
1172 type Item=T;
1173}
1174
1175fn main() {
1176 let mut data = Vec::new();
1177 //^^^^^^^^ Vec<&str>
1178 data.push("foo");
1179 for i in data {
1180 //^ &str
1181 let z = i;
1182 //^ &str
1183 }
1184}
1185"#,
1186 );
1187 }
1188
1189 #[test]
1190 fn multi_dyn_trait_bounds() {
1191 check_with_config(
1192 InlayHintsConfig {
1193 type_hints: true,
1194 parameter_hints: false,
1195 chaining_hints: false,
1196 max_length: None,
1197 },
1198 r#"
1199pub struct Vec<T> {}
1200
1201impl<T> Vec<T> {
1202 pub fn new() -> Self { Vec {} }
1203}
1204
1205pub struct Box<T> {}
1206
1207trait Display {}
1208trait Sync {}
1209
1210fn main() {
1211 let _v = Vec::<Box<&(dyn Display + Sync)>>::new();
1212 //^^ Vec<Box<&(dyn Display + Sync)>>
1213 let _v = Vec::<Box<*const (dyn Display + Sync)>>::new();
1214 //^^ Vec<Box<*const (dyn Display + Sync)>>
1215 let _v = Vec::<Box<dyn Display + Sync>>::new();
1216 //^^ Vec<Box<dyn Display + Sync>>
1217}
1218"#,
1219 );
1220 }
1221
1222 #[test]
1223 fn shorten_iterator_hints() {
1224 check_with_config(
1225 InlayHintsConfig {
1226 parameter_hints: false,
1227 type_hints: true,
1228 chaining_hints: false,
1229 max_length: None,
1230 },
1231 r#"
1232use core::iter;
1233
1234struct MyIter;
1235
1236impl Iterator for MyIter {
1237 type Item = ();
1238 fn next(&mut self) -> Option<Self::Item> {
1239 None
1240 }
1241}
1242
1243fn main() {
1244 let _x = MyIter;
1245 //^^ MyIter
1246 let _x = iter::repeat(0);
1247 //^^ impl Iterator<Item = i32>
1248 fn generic<T: Clone>(t: T) {
1249 let _x = iter::repeat(t);
1250 //^^ impl Iterator<Item = T>
1251 let _chained = iter::repeat(t).take(10);
1252 //^^^^^^^^ impl Iterator<Item = T>
1253 }
1254}
1255"#,
1256 );
1257 }
1258
1259 #[test]
1260 fn shorten_iterator_chaining_hints() { 1353 fn shorten_iterator_chaining_hints() {
1261 check_expect( 1354 check_expect(
1262 InlayHintsConfig { 1355 InlayHintsConfig {
@@ -1311,158 +1404,4 @@ fn main() {
1311 "#]], 1404 "#]],
1312 ); 1405 );
1313 } 1406 }
1314
1315 #[test]
1316 fn shorten_iterators_in_associated_params() {
1317 check_with_config(
1318 InlayHintsConfig {
1319 parameter_hints: false,
1320 type_hints: true,
1321 chaining_hints: false,
1322 max_length: None,
1323 },
1324 r#"
1325use core::iter;
1326
1327pub struct SomeIter<T> {}
1328
1329impl<T> SomeIter<T> {
1330 pub fn new() -> Self { SomeIter {} }
1331 pub fn push(&mut self, t: T) {}
1332}
1333
1334impl<T> Iterator for SomeIter<T> {
1335 type Item = T;
1336 fn next(&mut self) -> Option<Self::Item> {
1337 None
1338 }
1339}
1340
1341fn main() {
1342 let mut some_iter = SomeIter::new();
1343 //^^^^^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
1344 some_iter.push(iter::repeat(2).take(2));
1345 let iter_of_iters = some_iter.take(2);
1346 //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
1347}
1348"#,
1349 );
1350 }
1351
1352 #[test]
1353 fn hide_param_hints_for_clones() {
1354 check_with_config(
1355 InlayHintsConfig {
1356 parameter_hints: true,
1357 type_hints: false,
1358 chaining_hints: false,
1359 max_length: None,
1360 },
1361 r#"
1362fn foo(bar: i32, baz: String, qux: f32) {}
1363
1364fn main() {
1365 let bar = 3;
1366 let baz = &"baz";
1367 let fez = 1.0;
1368 foo(bar.clone(), baz.clone(), fez.clone());
1369 //^^^^^^^^^^^ qux
1370}
1371"#,
1372 );
1373 }
1374
1375 #[test]
1376 fn infer_call_method_return_associated_types_with_generic() {
1377 check(
1378 r#"
1379 pub trait Default {
1380 fn default() -> Self;
1381 }
1382 pub trait Foo {
1383 type Bar: Default;
1384 }
1385
1386 pub fn quux<T: Foo>() -> T::Bar {
1387 let y = Default::default();
1388 //^ <T as Foo>::Bar
1389
1390 y
1391 }
1392 "#,
1393 );
1394 }
1395
1396 #[test]
1397 fn self_param_hints() {
1398 check(
1399 r#"
1400struct Foo;
1401
1402impl Foo {
1403 fn foo(self: Self) {}
1404 fn bar(self: &Self) {}
1405}
1406
1407fn main() {
1408 Foo::foo(Foo);
1409 //^^^ self
1410 Foo::bar(&Foo);
1411 //^^^^ self
1412}
1413"#,
1414 )
1415 }
1416
1417 #[test]
1418 fn fn_hints() {
1419 check(
1420 r#"
1421trait Sized {}
1422
1423fn foo() -> impl Fn() { loop {} }
1424fn foo1() -> impl Fn(f64) { loop {} }
1425fn foo2() -> impl Fn(f64, f64) { loop {} }
1426fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
1427fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
1428fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
1429fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
1430fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
1431
1432fn main() {
1433 let foo = foo();
1434 // ^^^ impl Fn()
1435 let foo = foo1();
1436 // ^^^ impl Fn(f64)
1437 let foo = foo2();
1438 // ^^^ impl Fn(f64, f64)
1439 let foo = foo3();
1440 // ^^^ impl Fn(f64, f64) -> u32
1441 let foo = foo4();
1442 // ^^^ &dyn Fn(f64, f64) -> u32
1443 let foo = foo5();
1444 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
1445 let foo = foo6();
1446 // ^^^ impl Fn(f64, f64) -> u32 + Sized
1447 let foo = foo7();
1448 // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized)
1449}
1450"#,
1451 )
1452 }
1453
1454 #[test]
1455 fn param_name_hints_show_for_literals() {
1456 check(
1457 r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
1458fn main() {
1459 test(
1460 0x0fab272b,
1461 //^^^^^^^^^^ a
1462 0x0fab272b
1463 //^^^^^^^^^^ b
1464 );
1465}"#,
1466 )
1467 }
1468} 1407}
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index c67ccd1a9..93d3760bf 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -60,7 +60,7 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR
60 let pos: TextSize = (pos as u32).into(); 60 let pos: TextSize = (pos as u32).into();
61 let offset = token.text_range().start() + range.start() + pos; 61 let offset = token.text_range().start() + range.start() + pos;
62 if !edit.invalidates_offset(offset) { 62 if !edit.invalidates_offset(offset) {
63 remove_newline(edit, &token, offset); 63 remove_newline(edit, token, offset);
64 } 64 }
65 } 65 }
66} 66}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 97c9e5d2b..0511efae3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -282,20 +282,20 @@ impl Analysis {
282 file_id: FileId, 282 file_id: FileId,
283 text_range: Option<TextRange>, 283 text_range: Option<TextRange>,
284 ) -> Cancellable<String> { 284 ) -> Cancellable<String> {
285 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) 285 self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range))
286 } 286 }
287 287
288 pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> { 288 pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
289 self.with_db(|db| view_hir::view_hir(&db, position)) 289 self.with_db(|db| view_hir::view_hir(db, position))
290 } 290 }
291 291
292 pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { 292 pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
293 self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) 293 self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
294 } 294 }
295 295
296 /// Renders the crate graph to GraphViz "dot" syntax. 296 /// Renders the crate graph to GraphViz "dot" syntax.
297 pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> { 297 pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> {
298 self.with_db(|db| view_crate_graph::view_crate_graph(&db)) 298 self.with_db(|db| view_crate_graph::view_crate_graph(db))
299 } 299 }
300 300
301 pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> { 301 pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
@@ -315,7 +315,7 @@ impl Analysis {
315 /// up minor stuff like continuing the comment. 315 /// up minor stuff like continuing the comment.
316 /// The edit will be a snippet (with `$0`). 316 /// The edit will be a snippet (with `$0`).
317 pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> { 317 pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
318 self.with_db(|db| typing::on_enter(&db, position)) 318 self.with_db(|db| typing::on_enter(db, position))
319 } 319 }
320 320
321 /// Returns an edit which should be applied after a character was typed. 321 /// Returns an edit which should be applied after a character was typed.
@@ -331,7 +331,7 @@ impl Analysis {
331 if !typing::TRIGGER_CHARS.contains(char_typed) { 331 if !typing::TRIGGER_CHARS.contains(char_typed) {
332 return Ok(None); 332 return Ok(None);
333 } 333 }
334 self.with_db(|db| typing::on_char_typed(&db, position, char_typed)) 334 self.with_db(|db| typing::on_char_typed(db, position, char_typed))
335 } 335 }
336 336
337 /// Returns a tree representation of symbols in the file. Useful to draw a 337 /// Returns a tree representation of symbols in the file. Useful to draw a
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index d912a01b8..36801c964 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -33,14 +33,15 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
33 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. 33 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.
34 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks 34 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks
35 // cancellation, so we cannot use rayon. 35 // cancellation, so we cannot use rayon.
36 for (i, krate) in topo.iter().enumerate() { 36 for (i, &crate_id) in topo.iter().enumerate() {
37 let crate_name = graph[*krate].display_name.as_deref().unwrap_or_default().to_string(); 37 let crate_name = graph[crate_id].display_name.as_deref().unwrap_or_default().to_string();
38 38
39 cb(PrimeCachesProgress::StartedOnCrate { 39 cb(PrimeCachesProgress::StartedOnCrate {
40 on_crate: crate_name, 40 on_crate: crate_name,
41 n_done: i, 41 n_done: i,
42 n_total: topo.len(), 42 n_total: topo.len(),
43 }); 43 });
44 db.crate_def_map(*krate); 44 db.crate_def_map(crate_id);
45 db.import_map(crate_id);
45 } 46 }
46} 47}
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index ae492a264..a0fdead2c 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -62,7 +62,7 @@ pub(crate) fn find_all_refs(
62 if let Some(name) = get_name_of_item_declaration(&syntax, position) { 62 if let Some(name) = get_name_of_item_declaration(&syntax, position) {
63 (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true) 63 (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true)
64 } else { 64 } else {
65 (find_def(&sema, &syntax, position)?, false) 65 (find_def(sema, &syntax, position)?, false)
66 }; 66 };
67 67
68 let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); 68 let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all();
@@ -1380,4 +1380,24 @@ lib::foo!();
1380 "#]], 1380 "#]],
1381 ); 1381 );
1382 } 1382 }
1383
1384 #[test]
1385 fn macro_doesnt_reference_attribute_on_call() {
1386 check(
1387 r#"
1388macro_rules! m {
1389 () => {};
1390}
1391
1392#[proc_macro_test::attr_noop]
1393m$0!();
1394
1395"#,
1396 expect![[r#"
1397 m Macro FileId(0) 0..32 13..14
1398
1399 FileId(0) 64..65
1400 "#]],
1401 );
1402 }
1383} 1403}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 01fe3a1a1..50cc1f963 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -64,7 +64,7 @@ pub(crate) fn prepare_rename(
64 } 64 }
65 }; 65 };
66 let name_like = sema 66 let name_like = sema
67 .find_node_at_offset_with_descend(&syntax, position.offset) 67 .find_node_at_offset_with_descend(syntax, position.offset)
68 .ok_or_else(|| format_err!("No references found at position"))?; 68 .ok_or_else(|| format_err!("No references found at position"))?;
69 let node = match &name_like { 69 let node = match &name_like {
70 ast::NameLike::Name(it) => it.syntax(), 70 ast::NameLike::Name(it) => it.syntax(),
@@ -104,7 +104,7 @@ pub(crate) fn rename_with_semantics(
104 104
105 let def = find_definition(sema, syntax, position)?; 105 let def = find_definition(sema, syntax, position)?;
106 match def { 106 match def {
107 Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name), 107 Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(sema, module, new_name),
108 Definition::SelfType(_) => bail!("Cannot rename `Self`"), 108 Definition::SelfType(_) => bail!("Cannot rename `Self`"),
109 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), 109 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"),
110 def => rename_reference(sema, def, new_name), 110 def => rename_reference(sema, def, new_name),
@@ -170,7 +170,17 @@ fn find_definition(
170 NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db)) 170 NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db))
171 } 171 }
172 ast::NameLike::NameRef(name_ref) => { 172 ast::NameLike::NameRef(name_ref) => {
173 NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db)) 173 if let Some(def) =
174 NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db))
175 {
176 // if the name differs from the definitions name it has to be an alias
177 if def.name(sema.db).map_or(false, |it| it.to_string() != name_ref.text()) {
178 bail!("Renaming aliases is currently unsupported");
179 }
180 Some(def)
181 } else {
182 None
183 }
174 } 184 }
175 ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime) 185 ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
176 .map(|class| NameRefClass::referenced(class, sema.db)) 186 .map(|class| NameRefClass::referenced(class, sema.db))
@@ -229,7 +239,7 @@ fn rename_mod(
229 239
230fn rename_reference( 240fn rename_reference(
231 sema: &Semantics<RootDatabase>, 241 sema: &Semantics<RootDatabase>,
232 def: Definition, 242 mut def: Definition,
233 new_name: &str, 243 new_name: &str,
234) -> RenameResult<SourceChange> { 244) -> RenameResult<SourceChange> {
235 let ident_kind = check_identifier(new_name)?; 245 let ident_kind = check_identifier(new_name)?;
@@ -275,14 +285,45 @@ fn rename_reference(
275 } 285 }
276 } 286 }
277 287
288 def = match def {
289 // HACK: resolve trait impl items to the item def of the trait definition
290 // so that we properly resolve all trait item references
291 Definition::ModuleDef(mod_def) => mod_def
292 .as_assoc_item(sema.db)
293 .and_then(|it| it.containing_trait_impl(sema.db))
294 .and_then(|it| {
295 it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) {
296 (hir::AssocItem::Function(trait_func), ModuleDef::Function(func))
297 if trait_func.name(sema.db) == func.name(sema.db) =>
298 {
299 Some(Definition::ModuleDef(ModuleDef::Function(trait_func)))
300 }
301 (hir::AssocItem::Const(trait_konst), ModuleDef::Const(konst))
302 if trait_konst.name(sema.db) == konst.name(sema.db) =>
303 {
304 Some(Definition::ModuleDef(ModuleDef::Const(trait_konst)))
305 }
306 (
307 hir::AssocItem::TypeAlias(trait_type_alias),
308 ModuleDef::TypeAlias(type_alias),
309 ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => {
310 Some(Definition::ModuleDef(ModuleDef::TypeAlias(trait_type_alias)))
311 }
312 _ => None,
313 })
314 })
315 .unwrap_or(def),
316 _ => def,
317 };
278 let usages = def.usages(sema).all(); 318 let usages = def.usages(sema).all();
319
279 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { 320 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
280 cov_mark::hit!(rename_underscore_multiple); 321 cov_mark::hit!(rename_underscore_multiple);
281 bail!("Cannot rename reference to `_` as it is being referenced multiple times"); 322 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
282 } 323 }
283 let mut source_change = SourceChange::default(); 324 let mut source_change = SourceChange::default();
284 source_change.extend(usages.iter().map(|(&file_id, references)| { 325 source_change.extend(usages.iter().map(|(&file_id, references)| {
285 (file_id, source_edit_from_references(&references, def, new_name)) 326 (file_id, source_edit_from_references(references, def, new_name))
286 })); 327 }));
287 328
288 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; 329 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
@@ -372,7 +413,7 @@ fn rename_self_to_param(
372 let mut source_change = SourceChange::default(); 413 let mut source_change = SourceChange::default();
373 source_change.insert_source_edit(file_id.original_file(sema.db), edit); 414 source_change.insert_source_edit(file_id.original_file(sema.db), edit);
374 source_change.extend(usages.iter().map(|(&file_id, references)| { 415 source_change.extend(usages.iter().map(|(&file_id, references)| {
375 (file_id, source_edit_from_references(&references, def, new_name)) 416 (file_id, source_edit_from_references(references, def, new_name))
376 })); 417 }));
377 Ok(source_change) 418 Ok(source_change)
378} 419}
@@ -385,7 +426,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
385 None 426 None
386 } 427 }
387 428
388 let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; 429 let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?;
389 let type_name = target_type_name(&impl_def)?; 430 let type_name = target_type_name(&impl_def)?;
390 431
391 let mut replacement_text = String::from(new_name); 432 let mut replacement_text = String::from(new_name);
@@ -1907,4 +1948,157 @@ impl Fo0 where Self: {}
1907"#, 1948"#,
1908 ); 1949 );
1909 } 1950 }
1951
1952 #[test]
1953 fn test_rename_fails_on_aliases() {
1954 check(
1955 "Baz",
1956 r#"
1957struct Foo;
1958use Foo as Bar$0;
1959"#,
1960 "error: Renaming aliases is currently unsupported",
1961 );
1962 check(
1963 "Baz",
1964 r#"
1965struct Foo;
1966use Foo as Bar;
1967use Bar$0;
1968"#,
1969 "error: Renaming aliases is currently unsupported",
1970 );
1971 }
1972
1973 #[test]
1974 fn test_rename_trait_method() {
1975 let res = r"
1976trait Foo {
1977 fn foo(&self) {
1978 self.foo();
1979 }
1980}
1981
1982impl Foo for () {
1983 fn foo(&self) {
1984 self.foo();
1985 }
1986}";
1987 check(
1988 "foo",
1989 r#"
1990trait Foo {
1991 fn bar$0(&self) {
1992 self.bar();
1993 }
1994}
1995
1996impl Foo for () {
1997 fn bar(&self) {
1998 self.bar();
1999 }
2000}"#,
2001 res,
2002 );
2003 check(
2004 "foo",
2005 r#"
2006trait Foo {
2007 fn bar(&self) {
2008 self.bar$0();
2009 }
2010}
2011
2012impl Foo for () {
2013 fn bar(&self) {
2014 self.bar();
2015 }
2016}"#,
2017 res,
2018 );
2019 check(
2020 "foo",
2021 r#"
2022trait Foo {
2023 fn bar(&self) {
2024 self.bar();
2025 }
2026}
2027
2028impl Foo for () {
2029 fn bar$0(&self) {
2030 self.bar();
2031 }
2032}"#,
2033 res,
2034 );
2035 check(
2036 "foo",
2037 r#"
2038trait Foo {
2039 fn bar(&self) {
2040 self.bar();
2041 }
2042}
2043
2044impl Foo for () {
2045 fn bar(&self) {
2046 self.bar$0();
2047 }
2048}"#,
2049 res,
2050 );
2051 }
2052
2053 #[test]
2054 fn test_rename_trait_const() {
2055 let res = r"
2056trait Foo {
2057 const FOO: ();
2058}
2059
2060impl Foo for () {
2061 const FOO: ();
2062}
2063fn f() { <()>::FOO; }";
2064 check(
2065 "FOO",
2066 r#"
2067trait Foo {
2068 const BAR$0: ();
2069}
2070
2071impl Foo for () {
2072 const BAR: ();
2073}
2074fn f() { <()>::BAR; }"#,
2075 res,
2076 );
2077 check(
2078 "FOO",
2079 r#"
2080trait Foo {
2081 const BAR: ();
2082}
2083
2084impl Foo for () {
2085 const BAR$0: ();
2086}
2087fn f() { <()>::BAR; }"#,
2088 res,
2089 );
2090 check(
2091 "FOO",
2092 r#"
2093trait Foo {
2094 const BAR: ();
2095}
2096
2097impl Foo for () {
2098 const BAR: ();
2099}
2100fn f() { <()>::BAR$0; }"#,
2101 res,
2102 );
2103 }
1910} 2104}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 552054951..03faabadc 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -158,7 +158,7 @@ fn find_related_tests(
158 search_scope: Option<SearchScope>, 158 search_scope: Option<SearchScope>,
159 tests: &mut FxHashSet<Runnable>, 159 tests: &mut FxHashSet<Runnable>,
160) { 160) {
161 if let Some(refs) = references::find_all_refs(&sema, position, search_scope) { 161 if let Some(refs) = references::find_all_refs(sema, position, search_scope) {
162 for (file_id, refs) in refs.references { 162 for (file_id, refs) in refs.references {
163 let file = sema.parse(file_id); 163 let file = sema.parse(file_id);
164 let file = file.syntax(); 164 let file = file.syntax();
@@ -169,10 +169,10 @@ fn find_related_tests(
169 }); 169 });
170 170
171 for fn_def in functions { 171 for fn_def in functions {
172 if let Some(runnable) = as_test_runnable(&sema, &fn_def) { 172 if let Some(runnable) = as_test_runnable(sema, &fn_def) {
173 // direct test 173 // direct test
174 tests.insert(runnable); 174 tests.insert(runnable);
175 } else if let Some(module) = parent_test_module(&sema, &fn_def) { 175 } else if let Some(module) = parent_test_module(sema, &fn_def) {
176 // indirect test 176 // indirect test
177 find_related_tests_in_module(sema, &fn_def, &module, tests); 177 find_related_tests_in_module(sema, &fn_def, &module, tests);
178 } 178 }
@@ -203,7 +203,7 @@ fn find_related_tests_in_module(
203} 203}
204 204
205fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> { 205fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
206 if test_related_attribute(&fn_def).is_some() { 206 if test_related_attribute(fn_def).is_some() {
207 let function = sema.to_def(fn_def)?; 207 let function = sema.to_def(fn_def)?;
208 runnable_fn(sema, function) 208 runnable_fn(sema, function)
209 } else { 209 } else {
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 79c2f4a1e..e186b82b7 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -192,6 +192,7 @@ fn traverse(
192 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); 192 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
193 193
194 let mut current_macro_call: Option<ast::MacroCall> = None; 194 let mut current_macro_call: Option<ast::MacroCall> = None;
195 let mut current_attr_macro_call = None;
195 let mut current_macro: Option<ast::Macro> = None; 196 let mut current_macro: Option<ast::Macro> = None;
196 let mut macro_highlighter = MacroHighlighter::default(); 197 let mut macro_highlighter = MacroHighlighter::default();
197 let mut inside_attribute = false; 198 let mut inside_attribute = false;
@@ -227,6 +228,19 @@ fn traverse(
227 } 228 }
228 _ => (), 229 _ => (),
229 } 230 }
231 match event.clone().map(|it| it.into_node().and_then(ast::Item::cast)) {
232 WalkEvent::Enter(Some(item)) => {
233 if sema.is_attr_macro_call(&item) {
234 current_attr_macro_call = Some(item);
235 }
236 }
237 WalkEvent::Leave(Some(item)) => {
238 if current_attr_macro_call == Some(item) {
239 current_attr_macro_call = None;
240 }
241 }
242 _ => (),
243 }
230 244
231 match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) { 245 match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) {
232 WalkEvent::Enter(Some(mac)) => { 246 WalkEvent::Enter(Some(mac)) => {
@@ -286,6 +300,22 @@ fn traverse(
286 } 300 }
287 None => token.into(), 301 None => token.into(),
288 } 302 }
303 } else if current_attr_macro_call.is_some() {
304 let token = match element.clone().into_token() {
305 Some(it) => it,
306 _ => continue,
307 };
308 let token = sema.descend_into_macros(token.clone());
309 match token.parent() {
310 Some(parent) => {
311 // We only care Name and Name_ref
312 match (token.kind(), parent.kind()) {
313 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
314 _ => token.into(),
315 }
316 }
317 None => token.into(),
318 }
289 } else { 319 } else {
290 element.clone() 320 element.clone()
291 }; 321 };
@@ -293,7 +323,7 @@ fn traverse(
293 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { 323 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) {
294 if token.is_raw() { 324 if token.is_raw() {
295 let expanded = element_to_highlight.as_token().unwrap().clone(); 325 let expanded = element_to_highlight.as_token().unwrap().clone();
296 if inject::ra_fixture(hl, &sema, token, expanded).is_some() { 326 if inject::ra_fixture(hl, sema, token, expanded).is_some() {
297 continue; 327 continue;
298 } 328 }
299 } 329 }
@@ -304,7 +334,7 @@ fn traverse(
304 } 334 }
305 335
306 if let Some((mut highlight, binding_hash)) = highlight::element( 336 if let Some((mut highlight, binding_hash)) = highlight::element(
307 &sema, 337 sema,
308 krate, 338 krate,
309 &mut bindings_shadow_count, 339 &mut bindings_shadow_count,
310 syntactic_name_ref_highlighting, 340 syntactic_name_ref_highlighting,
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 9503c936d..7a53268e8 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -131,6 +131,9 @@ pub(super) fn element(
131 } 131 }
132 STRING | BYTE_STRING => HlTag::StringLiteral.into(), 132 STRING | BYTE_STRING => HlTag::StringLiteral.into(),
133 ATTR => HlTag::Attribute.into(), 133 ATTR => HlTag::Attribute.into(),
134 INT_NUMBER if element.ancestors().nth(1).map_or(false, |it| it.kind() == FIELD_EXPR) => {
135 SymbolKind::Field.into()
136 }
134 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), 137 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
135 BYTE => HlTag::ByteLiteral.into(), 138 BYTE => HlTag::ByteLiteral.into(),
136 CHAR => HlTag::CharLiteral.into(), 139 CHAR => HlTag::CharLiteral.into(),
@@ -446,12 +449,12 @@ fn highlight_method_call(
446 krate: Option<hir::Crate>, 449 krate: Option<hir::Crate>,
447 method_call: &ast::MethodCallExpr, 450 method_call: &ast::MethodCallExpr,
448) -> Option<Highlight> { 451) -> Option<Highlight> {
449 let func = sema.resolve_method_call(&method_call)?; 452 let func = sema.resolve_method_call(method_call)?;
450 453
451 let mut h = SymbolKind::Function.into(); 454 let mut h = SymbolKind::Function.into();
452 h |= HlMod::Associated; 455 h |= HlMod::Associated;
453 456
454 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { 457 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(method_call) {
455 h |= HlMod::Unsafe; 458 h |= HlMod::Unsafe;
456 } 459 }
457 if func.is_async(sema.db) { 460 if func.is_async(sema.db) {
@@ -523,11 +526,9 @@ fn highlight_name_ref_by_syntax(
523 }; 526 };
524 527
525 match parent.kind() { 528 match parent.kind() {
526 METHOD_CALL_EXPR => { 529 METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent)
527 return ast::MethodCallExpr::cast(parent) 530 .and_then(|it| highlight_method_call(sema, krate, &it))
528 .and_then(|it| highlight_method_call(sema, krate, &it)) 531 .unwrap_or_else(|| SymbolKind::Function.into()),
529 .unwrap_or_else(|| SymbolKind::Function.into());
530 }
531 FIELD_EXPR => { 532 FIELD_EXPR => {
532 let h = HlTag::Symbol(SymbolKind::Field); 533 let h = HlTag::Symbol(SymbolKind::Field);
533 let is_union = ast::FieldExpr::cast(parent) 534 let is_union = ast::FieldExpr::cast(parent)
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 5327af845..478facfee 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -23,7 +23,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
23 let hl_ranges = highlight(db, file_id, None, false); 23 let hl_ranges = highlight(db, file_id, None, false);
24 let text = parse.tree().syntax().to_string(); 24 let text = parse.tree().syntax().to_string();
25 let mut buf = String::new(); 25 let mut buf = String::new();
26 buf.push_str(&STYLE); 26 buf.push_str(STYLE);
27 buf.push_str("<pre><code>"); 27 buf.push_str("<pre><code>");
28 for r in &hl_ranges { 28 for r in &hl_ranges {
29 let chunk = html_escape(&text[r.range]); 29 let chunk = html_escape(&text[r.range]);
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 4269d339e..ec43c8579 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -23,7 +23,7 @@ pub(super) fn ra_fixture(
23 literal: ast::String, 23 literal: ast::String,
24 expanded: SyntaxToken, 24 expanded: SyntaxToken,
25) -> Option<()> { 25) -> Option<()> {
26 let active_parameter = ActiveParameter::at_token(&sema, expanded)?; 26 let active_parameter = ActiveParameter::at_token(sema, expanded)?;
27 if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { 27 if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) {
28 return None; 28 return None;
29 } 29 }
@@ -124,7 +124,7 @@ pub(super) fn doc_comment(
124 } 124 }
125 125
126 for attr in attributes.by_key("doc").attrs() { 126 for attr in attributes.by_key("doc").attrs() {
127 let InFile { file_id, value: src } = attrs_source_map.source_of(&attr); 127 let InFile { file_id, value: src } = attrs_source_map.source_of(attr);
128 if file_id != node.file_id { 128 if file_id != node.file_id {
129 continue; 129 continue;
130 } 130 }
@@ -232,7 +232,7 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
232 string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) 232 string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text)
233 }) 233 })
234 } 234 }
235 _ => return None, 235 _ => None,
236 } 236 }
237} 237}
238 238
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 0264e39a3..59f1e8e4c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -148,6 +148,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
148 <span class="brace">}</span> 148 <span class="brace">}</span>
149<span class="brace">}</span> 149<span class="brace">}</span>
150 150
151<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span>
152 <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span>
153<span class="brace">}</span>
154
151<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> 155<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
152 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span> 156 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
153 <span class="punctuation">$</span>expr 157 <span class="punctuation">$</span>expr
@@ -171,6 +175,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
171<span class="comment">// comment</span> 175<span class="comment">// comment</span>
172<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 176<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
173 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> 177 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
178 <span class="macro">dont_color_me_braces!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
174 179
175 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 180 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
176 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="brace">{</span> 181 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="brace">{</span>
@@ -210,8 +215,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
210 <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> 215 <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span>
211 <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span> 216 <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span>
212 217
213 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> 218 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="comma">,</span><span class="parenthesis">)</span><span class="semicolon">;</span>
214 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> 219 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="operator">.</span><span class="field">0</span><span class="semicolon">;</span>
215 220
216 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> 221 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
217 222
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 662b53481..f7d8334a0 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -122,6 +122,10 @@ def_fn! {
122 } 122 }
123} 123}
124 124
125macro_rules! dont_color_me_braces {
126 () => {0}
127}
128
125macro_rules! noop { 129macro_rules! noop {
126 ($expr:expr) => { 130 ($expr:expr) => {
127 $expr 131 $expr
@@ -145,6 +149,7 @@ macro without_args {
145// comment 149// comment
146fn main() { 150fn main() {
147 println!("Hello, {}!", 92); 151 println!("Hello, {}!", 92);
152 dont_color_me_braces!();
148 153
149 let mut vec = Vec::new(); 154 let mut vec = Vec::new();
150 if true { 155 if true {
@@ -184,8 +189,8 @@ fn main() {
184 let a = |x| x; 189 let a = |x| x;
185 let bar = Foo::baz; 190 let bar = Foo::baz;
186 191
187 let baz = -42; 192 let baz = (-42,);
188 let baz = -baz; 193 let baz = -baz.0;
189 194
190 let _ = !true; 195 let _ = !true;
191 196
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 81c4d95b1..5cba9d11d 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -88,12 +88,12 @@ fn on_enter_in_comment(
88 if comment.text().ends_with(' ') { 88 if comment.text().ends_with(' ') {
89 cov_mark::hit!(continues_end_of_line_comment_with_space); 89 cov_mark::hit!(continues_end_of_line_comment_with_space);
90 remove_trailing_whitespace = true; 90 remove_trailing_whitespace = true;
91 } else if !followed_by_comment(&comment) { 91 } else if !followed_by_comment(comment) {
92 return None; 92 return None;
93 } 93 }
94 } 94 }
95 95
96 let indent = node_indent(&file, comment.syntax())?; 96 let indent = node_indent(file, comment.syntax())?;
97 let inserted = format!("\n{}{} $0", indent, prefix); 97 let inserted = format!("\n{}{} $0", indent, prefix);
98 let delete = if remove_trailing_whitespace { 98 let delete = if remove_trailing_whitespace {
99 let trimmed_len = comment.text().trim_end().len() as u32; 99 let trimmed_len = comment.text().trim_end().len() as u32;
@@ -188,7 +188,7 @@ mod tests {
188 use crate::fixture; 188 use crate::fixture;
189 189
190 fn apply_on_enter(before: &str) -> Option<String> { 190 fn apply_on_enter(before: &str) -> Option<String> {
191 let (analysis, position) = fixture::position(&before); 191 let (analysis, position) = fixture::position(before);
192 let result = analysis.on_enter(position).unwrap()?; 192 let result = analysis.on_enter(position).unwrap()?;
193 193
194 let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); 194 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml
index a83acb191..0d0d1605e 100644
--- a/crates/ide_assists/Cargo.toml
+++ b/crates/ide_assists/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15itertools = "0.10.0" 15itertools = "0.10.0"
16either = "1.6.1" 16either = "1.6.1"
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 8fc40f9bd..36a2bf89a 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -291,8 +291,7 @@ impl AssistBuilder {
291 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 291 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
292 } 292 }
293 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { 293 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
294 let file_system_edit = 294 let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() };
295 FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() };
296 self.source_change.push_file_system_edit(file_system_edit); 295 self.source_change.push_file_system_edit(file_system_edit);
297 } 296 }
298 297
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs
index 5c936a510..c93959e66 100644
--- a/crates/ide_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide_assists/src/handlers/apply_demorgan.rs
@@ -78,12 +78,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
78 terms.sort_by_key(|t| t.syntax().text_range().start()); 78 terms.sort_by_key(|t| t.syntax().text_range().start());
79 let mut terms = VecDeque::from(terms); 79 let mut terms = VecDeque::from(terms);
80 80
81 let paren_expr = expr.syntax().parent().and_then(|parent| ast::ParenExpr::cast(parent)); 81 let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast);
82 82
83 let neg_expr = paren_expr 83 let neg_expr = paren_expr
84 .clone() 84 .clone()
85 .and_then(|paren_expr| paren_expr.syntax().parent()) 85 .and_then(|paren_expr| paren_expr.syntax().parent())
86 .and_then(|parent| ast::PrefixExpr::cast(parent)) 86 .and_then(ast::PrefixExpr::cast)
87 .and_then(|prefix_expr| { 87 .and_then(|prefix_expr| {
88 if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not { 88 if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not {
89 Some(prefix_expr) 89 Some(prefix_expr)
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs
index d7e39b2ae..ed936667f 100644
--- a/crates/ide_assists/src/handlers/change_visibility.rs
+++ b/crates/ide_assists/src/handlers/change_visibility.rs
@@ -1,7 +1,9 @@
1use syntax::{ 1use syntax::{
2 ast::{self, NameOwner, VisibilityOwner}, 2 ast::{self, NameOwner, VisibilityOwner},
3 AstNode, 3 AstNode,
4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, 4 SyntaxKind::{
5 CONST, ENUM, FN, MACRO_DEF, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, USE, VISIBILITY,
6 },
5 T, 7 T,
6}; 8};
7 9
@@ -37,12 +39,15 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
37 | T![enum] 39 | T![enum]
38 | T![trait] 40 | T![trait]
39 | T![type] 41 | T![type]
42 | T![use]
43 | T![macro]
40 ) 44 )
41 }); 45 });
42 46
43 let (offset, target) = if let Some(keyword) = item_keyword { 47 let (offset, target) = if let Some(keyword) = item_keyword {
44 let parent = keyword.parent()?; 48 let parent = keyword.parent()?;
45 let def_kws = vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT]; 49 let def_kws =
50 vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT, USE, MACRO_DEF];
46 // Parent is not a definition, can't add visibility 51 // Parent is not a definition, can't add visibility
47 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { 52 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) {
48 return None; 53 return None;
@@ -122,6 +127,8 @@ mod tests {
122 check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}"); 127 check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}");
123 check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}"); 128 check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}");
124 check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}"); 129 check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}");
130 check_assist(change_visibility, "$0macro foo() {}", "pub(crate) macro foo() {}");
131 check_assist(change_visibility, "$0use foo;", "pub(crate) use foo;");
125 } 132 }
126 133
127 #[test] 134 #[test]
diff --git a/crates/ide_assists/src/handlers/convert_comment_block.rs b/crates/ide_assists/src/handlers/convert_comment_block.rs
index d202a85f9..749e8685b 100644
--- a/crates/ide_assists/src/handlers/convert_comment_block.rs
+++ b/crates/ide_assists/src/handlers/convert_comment_block.rs
@@ -88,7 +88,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
88 // We pick a single indentation level for the whole block comment based on the 88 // We pick a single indentation level for the whole block comment based on the
89 // comment where the assist was invoked. This will be prepended to the 89 // comment where the assist was invoked. This will be prepended to the
90 // contents of each line comment when they're put into the block comment. 90 // contents of each line comment when they're put into the block comment.
91 let indentation = IndentLevel::from_token(&comment.syntax()); 91 let indentation = IndentLevel::from_token(comment.syntax());
92 92
93 let block_comment_body = 93 let block_comment_body =
94 comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n"); 94 comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n");
@@ -167,7 +167,7 @@ fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String {
167 if contents.is_empty() { 167 if contents.is_empty() {
168 contents.to_owned() 168 contents.to_owned()
169 } else { 169 } else {
170 indentation.to_string() + &contents 170 indentation.to_string() + contents
171 } 171 }
172} 172}
173 173
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 70949ca35..fc5a17f05 100644
--- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use ide_db::defs::{Definition, NameRefClass}; 2use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ 3use syntax::{
3 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner}, 4 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner},
@@ -8,7 +9,7 @@ use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind,
8 9
9// Assist: convert_tuple_struct_to_named_struct 10// Assist: convert_tuple_struct_to_named_struct
10// 11//
11// Converts tuple struct to struct with named fields. 12// Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
12// 13//
13// ``` 14// ```
14// struct Point$0(f32, f32); 15// struct Point$0(f32, f32);
@@ -49,14 +50,21 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
49 acc: &mut Assists, 50 acc: &mut Assists,
50 ctx: &AssistContext, 51 ctx: &AssistContext,
51) -> Option<()> { 52) -> Option<()> {
52 let strukt = ctx.find_node_at_offset::<ast::Struct>()?; 53 let strukt = ctx
53 let tuple_fields = match strukt.field_list()? { 54 .find_node_at_offset::<ast::Struct>()
55 .map(Either::Left)
56 .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
57 let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
58 let tuple_fields = match field_list {
54 ast::FieldList::TupleFieldList(it) => it, 59 ast::FieldList::TupleFieldList(it) => it,
55 ast::FieldList::RecordFieldList(_) => return None, 60 ast::FieldList::RecordFieldList(_) => return None,
56 }; 61 };
57 let strukt_def = ctx.sema.to_def(&strukt)?; 62 let strukt_def = match &strukt {
63 Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
64 Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
65 };
66 let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
58 67
59 let target = strukt.syntax().text_range();
60 acc.add( 68 acc.add(
61 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite), 69 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite),
62 "Convert to named struct", 70 "Convert to named struct",
@@ -73,7 +81,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
73fn edit_struct_def( 81fn edit_struct_def(
74 ctx: &AssistContext, 82 ctx: &AssistContext,
75 edit: &mut AssistBuilder, 83 edit: &mut AssistBuilder,
76 strukt: &ast::Struct, 84 strukt: &Either<ast::Struct, ast::Variant>,
77 tuple_fields: ast::TupleFieldList, 85 tuple_fields: ast::TupleFieldList,
78 names: Vec<ast::Name>, 86 names: Vec<ast::Name>,
79) { 87) {
@@ -86,27 +94,40 @@ fn edit_struct_def(
86 94
87 edit.edit_file(ctx.frange.file_id); 95 edit.edit_file(ctx.frange.file_id);
88 96
89 if let Some(w) = strukt.where_clause() { 97 if let Either::Left(strukt) = strukt {
90 edit.delete(w.syntax().text_range()); 98 if let Some(w) = strukt.where_clause() {
91 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 99 edit.delete(w.syntax().text_range());
92 edit.insert(tuple_fields_text_range.start(), w.syntax().text()); 100 edit.insert(
93 edit.insert(tuple_fields_text_range.start(), ","); 101 tuple_fields_text_range.start(),
94 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 102 ast::make::tokens::single_newline().text(),
103 );
104 edit.insert(tuple_fields_text_range.start(), w.syntax().text());
105 edit.insert(tuple_fields_text_range.start(), ",");
106 edit.insert(
107 tuple_fields_text_range.start(),
108 ast::make::tokens::single_newline().text(),
109 );
110 } else {
111 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
112 }
113 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
95 } else { 114 } else {
96 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); 115 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
97 } 116 }
98 117
99 edit.replace(tuple_fields_text_range, record_fields.to_string()); 118 edit.replace(tuple_fields_text_range, record_fields.to_string());
100 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
101} 119}
102 120
103fn edit_struct_references( 121fn edit_struct_references(
104 ctx: &AssistContext, 122 ctx: &AssistContext,
105 edit: &mut AssistBuilder, 123 edit: &mut AssistBuilder,
106 strukt: hir::Struct, 124 strukt: Either<hir::Struct, hir::Variant>,
107 names: &[ast::Name], 125 names: &[ast::Name],
108) { 126) {
109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); 127 let strukt_def = match strukt {
128 Either::Left(s) => Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(s))),
129 Either::Right(v) => Definition::ModuleDef(hir::ModuleDef::Variant(v)),
130 };
110 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); 131 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
111 132
112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { 133 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
@@ -513,4 +534,305 @@ where
513"#, 534"#,
514 ); 535 );
515 } 536 }
537 #[test]
538 fn not_applicable_other_than_tuple_variant() {
539 check_assist_not_applicable(
540 convert_tuple_struct_to_named_struct,
541 r#"enum Enum { Variant$0 { value: usize } };"#,
542 );
543 check_assist_not_applicable(
544 convert_tuple_struct_to_named_struct,
545 r#"enum Enum { Variant$0 }"#,
546 );
547 }
548
549 #[test]
550 fn convert_simple_variant() {
551 check_assist(
552 convert_tuple_struct_to_named_struct,
553 r#"
554enum A {
555 $0Variant(usize),
556}
557
558impl A {
559 fn new(value: usize) -> A {
560 A::Variant(value)
561 }
562
563 fn new_with_default() -> A {
564 A::new(Default::default())
565 }
566
567 fn value(self) -> usize {
568 match self {
569 A::Variant(value) => value,
570 }
571 }
572}"#,
573 r#"
574enum A {
575 Variant { field1: usize },
576}
577
578impl A {
579 fn new(value: usize) -> A {
580 A::Variant { field1: value }
581 }
582
583 fn new_with_default() -> A {
584 A::new(Default::default())
585 }
586
587 fn value(self) -> usize {
588 match self {
589 A::Variant { field1: value } => value,
590 }
591 }
592}"#,
593 );
594 }
595
596 #[test]
597 fn convert_variant_referenced_via_self_kw() {
598 check_assist(
599 convert_tuple_struct_to_named_struct,
600 r#"
601enum A {
602 $0Variant(usize),
603}
604
605impl A {
606 fn new(value: usize) -> A {
607 Self::Variant(value)
608 }
609
610 fn new_with_default() -> A {
611 Self::new(Default::default())
612 }
613
614 fn value(self) -> usize {
615 match self {
616 Self::Variant(value) => value,
617 }
618 }
619}"#,
620 r#"
621enum A {
622 Variant { field1: usize },
623}
624
625impl A {
626 fn new(value: usize) -> A {
627 Self::Variant { field1: value }
628 }
629
630 fn new_with_default() -> A {
631 Self::new(Default::default())
632 }
633
634 fn value(self) -> usize {
635 match self {
636 Self::Variant { field1: value } => value,
637 }
638 }
639}"#,
640 );
641 }
642
643 #[test]
644 fn convert_destructured_variant() {
645 check_assist(
646 convert_tuple_struct_to_named_struct,
647 r#"
648enum A {
649 $0Variant(usize),
650}
651
652impl A {
653 fn into_inner(self) -> usize {
654 let A::Variant(first) = self;
655 first
656 }
657
658 fn into_inner_via_self(self) -> usize {
659 let Self::Variant(first) = self;
660 first
661 }
662}"#,
663 r#"
664enum A {
665 Variant { field1: usize },
666}
667
668impl A {
669 fn into_inner(self) -> usize {
670 let A::Variant { field1: first } = self;
671 first
672 }
673
674 fn into_inner_via_self(self) -> usize {
675 let Self::Variant { field1: first } = self;
676 first
677 }
678}"#,
679 );
680 }
681
682 #[test]
683 fn convert_variant_with_wrapped_references() {
684 check_assist(
685 convert_tuple_struct_to_named_struct,
686 r#"
687enum Inner {
688 $0Variant(usize),
689}
690enum Outer {
691 Variant(Inner),
692}
693
694impl Outer {
695 fn new() -> Self {
696 Self::Variant(Inner::Variant(42))
697 }
698
699 fn into_inner_destructed(self) -> u32 {
700 let Outer::Variant(Inner::Variant(x)) = self;
701 x
702 }
703}"#,
704 r#"
705enum Inner {
706 Variant { field1: usize },
707}
708enum Outer {
709 Variant(Inner),
710}
711
712impl Outer {
713 fn new() -> Self {
714 Self::Variant(Inner::Variant { field1: 42 })
715 }
716
717 fn into_inner_destructed(self) -> u32 {
718 let Outer::Variant(Inner::Variant { field1: x }) = self;
719 x
720 }
721}"#,
722 );
723
724 check_assist(
725 convert_tuple_struct_to_named_struct,
726 r#"
727enum Inner {
728 Variant(usize),
729}
730enum Outer {
731 $0Variant(Inner),
732}
733
734impl Outer {
735 fn new() -> Self {
736 Self::Variant(Inner::Variant(42))
737 }
738
739 fn into_inner_destructed(self) -> u32 {
740 let Outer::Variant(Inner::Variant(x)) = self;
741 x
742 }
743}"#,
744 r#"
745enum Inner {
746 Variant(usize),
747}
748enum Outer {
749 Variant { field1: Inner },
750}
751
752impl Outer {
753 fn new() -> Self {
754 Self::Variant { field1: Inner::Variant(42) }
755 }
756
757 fn into_inner_destructed(self) -> u32 {
758 let Outer::Variant { field1: Inner::Variant(x) } = self;
759 x
760 }
761}"#,
762 );
763 }
764
765 #[test]
766 fn convert_variant_with_multi_file_references() {
767 check_assist(
768 convert_tuple_struct_to_named_struct,
769 r#"
770//- /main.rs
771struct Inner;
772enum A {
773 $0Variant(Inner),
774}
775
776mod foo;
777
778//- /foo.rs
779use crate::{A, Inner};
780fn f() {
781 let a = A::Variant(Inner);
782}
783"#,
784 r#"
785//- /main.rs
786struct Inner;
787enum A {
788 Variant { field1: Inner },
789}
790
791mod foo;
792
793//- /foo.rs
794use crate::{A, Inner};
795fn f() {
796 let a = A::Variant { field1: Inner };
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn convert_directly_used_variant() {
804 check_assist(
805 convert_tuple_struct_to_named_struct,
806 r#"
807//- /main.rs
808struct Inner;
809enum A {
810 $0Variant(Inner),
811}
812
813mod foo;
814
815//- /foo.rs
816use crate::{A::Variant, Inner};
817fn f() {
818 let a = Variant(Inner);
819}
820"#,
821 r#"
822//- /main.rs
823struct Inner;
824enum A {
825 Variant { field1: Inner },
826}
827
828mod foo;
829
830//- /foo.rs
831use crate::{A::Variant, Inner};
832fn f() {
833 let a = Variant { field1: Inner };
834}
835"#,
836 );
837 }
516} 838}
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs
index 5eb6a57f0..ef4a7cb50 100644
--- a/crates/ide_assists/src/handlers/early_return.rs
+++ b/crates/ide_assists/src/handlers/early_return.rs
@@ -108,7 +108,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
108 "Convert to guarded return", 108 "Convert to guarded return",
109 target, 109 target,
110 |edit| { 110 |edit| {
111 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 111 let if_indent_level = IndentLevel::from_node(if_expr.syntax());
112 let new_block = match if_let_pat { 112 let new_block = match if_let_pat {
113 None => { 113 None => {
114 // If. 114 // If.
@@ -174,7 +174,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
174 .take_while(|i| *i != end_of_then), 174 .take_while(|i| *i != end_of_then),
175 ); 175 );
176 replace_children( 176 replace_children(
177 &parent_block.syntax(), 177 parent_block.syntax(),
178 RangeInclusive::new( 178 RangeInclusive::new(
179 if_expr.clone().syntax().clone().into(), 179 if_expr.clone().syntax().clone().into(),
180 if_expr.syntax().clone().into(), 180 if_expr.syntax().clone().into(),
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index a2dba915c..f2be091f4 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -76,7 +76,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
76 let module = ctx.sema.scope(&insert_after).module()?; 76 let module = ctx.sema.scope(&insert_after).module()?;
77 77
78 let vars_defined_in_body_and_outlive = 78 let vars_defined_in_body_and_outlive =
79 vars_defined_in_body_and_outlive(ctx, &body, &node.parent().as_ref().unwrap_or(&node)); 79 vars_defined_in_body_and_outlive(ctx, &body, node.parent().as_ref().unwrap_or(&node));
80 let ret_ty = body_return_ty(ctx, &body)?; 80 let ret_ty = body_return_ty(ctx, &body)?;
81 81
82 // FIXME: we compute variables that outlive here just to check `never!` condition 82 // FIXME: we compute variables that outlive here just to check `never!` condition
@@ -782,7 +782,7 @@ fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Optio
782 Some(false) 782 Some(false)
783} 783}
784 784
785/// Container of local varaible usages 785/// Container of local variable usages
786/// 786///
787/// Semanticall same as `UsageSearchResult`, but provides more convenient interface 787/// Semanticall same as `UsageSearchResult`, but provides more convenient interface
788struct LocalUsages(ide_db::search::UsageSearchResult); 788struct LocalUsages(ide_db::search::UsageSearchResult);
@@ -808,7 +808,7 @@ trait HasTokenAtOffset {
808 808
809impl HasTokenAtOffset for SyntaxNode { 809impl HasTokenAtOffset for SyntaxNode {
810 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { 810 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
811 SyntaxNode::token_at_offset(&self, offset) 811 SyntaxNode::token_at_offset(self, offset)
812 } 812 }
813} 813}
814 814
@@ -854,7 +854,7 @@ fn vars_defined_in_body_and_outlive(
854 body: &FunctionBody, 854 body: &FunctionBody,
855 parent: &SyntaxNode, 855 parent: &SyntaxNode,
856) -> Vec<OutlivedLocal> { 856) -> Vec<OutlivedLocal> {
857 let vars_defined_in_body = vars_defined_in_body(&body, ctx); 857 let vars_defined_in_body = vars_defined_in_body(body, ctx);
858 vars_defined_in_body 858 vars_defined_in_body
859 .into_iter() 859 .into_iter()
860 .filter_map(|var| var_outlives_body(ctx, body, var, parent)) 860 .filter_map(|var| var_outlives_body(ctx, body, var, parent))
@@ -868,7 +868,7 @@ fn is_defined_before(
868 src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>, 868 src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>,
869) -> bool { 869) -> bool {
870 src.file_id.original_file(ctx.db()) == ctx.frange.file_id 870 src.file_id.original_file(ctx.db()) == ctx.frange.file_id
871 && !body.contains_node(&either_syntax(&src.value)) 871 && !body.contains_node(either_syntax(&src.value))
872} 872}
873 873
874fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { 874fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 007aba23d..d3ff7b65c 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -11,14 +11,19 @@ use ide_db::{
11 search::FileReference, 11 search::FileReference,
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use itertools::Itertools;
14use rustc_hash::FxHashSet; 15use rustc_hash::FxHashSet;
15use syntax::{ 16use syntax::{
16 algo::find_node_at_offset, 17 ast::{
17 ast::{self, make, AstNode, NameOwner, VisibilityOwner}, 18 self, make, AstNode, AttrsOwner, GenericParamsOwner, NameOwner, TypeBoundsOwner,
18 ted, SyntaxNode, T, 19 VisibilityOwner,
20 },
21 match_ast,
22 ted::{self, Position},
23 SyntaxNode, T,
19}; 24};
20 25
21use crate::{AssistContext, AssistId, AssistKind, Assists}; 26use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
22 27
23// Assist: extract_struct_from_enum_variant 28// Assist: extract_struct_from_enum_variant
24// 29//
@@ -70,11 +75,10 @@ pub(crate) fn extract_struct_from_enum_variant(
70 continue; 75 continue;
71 } 76 }
72 builder.edit_file(file_id); 77 builder.edit_file(file_id);
73 let source_file = builder.make_mut(ctx.sema.parse(file_id));
74 let processed = process_references( 78 let processed = process_references(
75 ctx, 79 ctx,
80 builder,
76 &mut visited_modules_set, 81 &mut visited_modules_set,
77 source_file.syntax(),
78 &enum_module_def, 82 &enum_module_def,
79 &variant_hir_name, 83 &variant_hir_name,
80 references, 84 references,
@@ -84,13 +88,12 @@ pub(crate) fn extract_struct_from_enum_variant(
84 }); 88 });
85 } 89 }
86 builder.edit_file(ctx.frange.file_id); 90 builder.edit_file(ctx.frange.file_id);
87 let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id));
88 let variant = builder.make_mut(variant.clone()); 91 let variant = builder.make_mut(variant.clone());
89 if let Some(references) = def_file_references { 92 if let Some(references) = def_file_references {
90 let processed = process_references( 93 let processed = process_references(
91 ctx, 94 ctx,
95 builder,
92 &mut visited_modules_set, 96 &mut visited_modules_set,
93 source_file.syntax(),
94 &enum_module_def, 97 &enum_module_def,
95 &variant_hir_name, 98 &variant_hir_name,
96 references, 99 references,
@@ -100,12 +103,12 @@ pub(crate) fn extract_struct_from_enum_variant(
100 }); 103 });
101 } 104 }
102 105
103 let def = create_struct_def(variant_name.clone(), &field_list, enum_ast.visibility()); 106 let def = create_struct_def(variant_name.clone(), &field_list, &enum_ast);
104 let start_offset = &variant.parent_enum().syntax().clone(); 107 let start_offset = &variant.parent_enum().syntax().clone();
105 ted::insert_raw(ted::Position::before(start_offset), def.syntax()); 108 ted::insert_raw(ted::Position::before(start_offset), def.syntax());
106 ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line()); 109 ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line());
107 110
108 update_variant(&variant); 111 update_variant(&variant, enum_ast.generic_param_list());
109 }, 112 },
110 ) 113 )
111} 114}
@@ -149,7 +152,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
149fn create_struct_def( 152fn create_struct_def(
150 variant_name: ast::Name, 153 variant_name: ast::Name,
151 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, 154 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
152 visibility: Option<ast::Visibility>, 155 enum_: &ast::Enum,
153) -> ast::Struct { 156) -> ast::Struct {
154 let pub_vis = make::visibility_pub(); 157 let pub_vis = make::visibility_pub();
155 158
@@ -184,12 +187,38 @@ fn create_struct_def(
184 } 187 }
185 }; 188 };
186 189
187 make::struct_(visibility, variant_name, None, field_list).clone_for_update() 190 // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
191 let strukt =
192 make::struct_(enum_.visibility(), variant_name, enum_.generic_param_list(), field_list)
193 .clone_for_update();
194
195 // copy attributes
196 ted::insert_all(
197 Position::first_child_of(strukt.syntax()),
198 enum_.attrs().map(|it| it.syntax().clone_for_update().into()).collect(),
199 );
200 strukt
188} 201}
189 202
190fn update_variant(variant: &ast::Variant) -> Option<()> { 203fn update_variant(variant: &ast::Variant, generic: Option<ast::GenericParamList>) -> Option<()> {
191 let name = variant.name()?; 204 let name = variant.name()?;
192 let tuple_field = make::tuple_field(None, make::ty(&name.text())); 205 let ty = match generic {
206 // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
207 Some(gpl) => {
208 let gpl = gpl.clone_for_update();
209 gpl.generic_params().for_each(|gp| {
210 match gp {
211 ast::GenericParam::LifetimeParam(it) => it.type_bound_list(),
212 ast::GenericParam::TypeParam(it) => it.type_bound_list(),
213 ast::GenericParam::ConstParam(_) => return,
214 }
215 .map(|it| it.remove());
216 });
217 make::ty(&format!("{}<{}>", name.text(), gpl.generic_params().join(", ")))
218 }
219 None => make::ty(&name.text()),
220 };
221 let tuple_field = make::tuple_field(None, ty);
193 let replacement = make::variant( 222 let replacement = make::variant(
194 name, 223 name,
195 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), 224 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))),
@@ -208,18 +237,17 @@ fn apply_references(
208 if let Some((scope, path)) = import { 237 if let Some((scope, path)) = import {
209 insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); 238 insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg);
210 } 239 }
211 ted::insert_raw( 240 // deep clone to prevent cycle
212 ted::Position::before(segment.syntax()), 241 let path = make::path_from_segments(iter::once(segment.clone_subtree()), false);
213 make::path_from_text(&format!("{}", segment)).clone_for_update().syntax(), 242 ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax());
214 );
215 ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); 243 ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['(']));
216 ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); 244 ted::insert_raw(ted::Position::after(&node), make::token(T![')']));
217} 245}
218 246
219fn process_references( 247fn process_references(
220 ctx: &AssistContext, 248 ctx: &AssistContext,
249 builder: &mut AssistBuilder,
221 visited_modules: &mut FxHashSet<Module>, 250 visited_modules: &mut FxHashSet<Module>,
222 source_file: &SyntaxNode,
223 enum_module_def: &ModuleDef, 251 enum_module_def: &ModuleDef,
224 variant_hir_name: &Name, 252 variant_hir_name: &Name,
225 refs: Vec<FileReference>, 253 refs: Vec<FileReference>,
@@ -228,8 +256,9 @@ fn process_references(
228 // and corresponding nodes up front 256 // and corresponding nodes up front
229 refs.into_iter() 257 refs.into_iter()
230 .flat_map(|reference| { 258 .flat_map(|reference| {
231 let (segment, scope_node, module) = 259 let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?;
232 reference_to_node(&ctx.sema, source_file, reference)?; 260 let segment = builder.make_mut(segment);
261 let scope_node = builder.make_syntax_mut(scope_node);
233 if !visited_modules.contains(&module) { 262 if !visited_modules.contains(&module) {
234 let mod_path = module.find_use_path_prefixed( 263 let mod_path = module.find_use_path_prefixed(
235 ctx.sema.db, 264 ctx.sema.db,
@@ -251,23 +280,22 @@ fn process_references(
251 280
252fn reference_to_node( 281fn reference_to_node(
253 sema: &hir::Semantics<RootDatabase>, 282 sema: &hir::Semantics<RootDatabase>,
254 source_file: &SyntaxNode,
255 reference: FileReference, 283 reference: FileReference,
256) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> { 284) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> {
257 let offset = reference.range.start(); 285 let segment =
258 if let Some(path_expr) = find_node_at_offset::<ast::PathExpr>(source_file, offset) { 286 reference.name.as_name_ref()?.syntax().parent().and_then(ast::PathSegment::cast)?;
259 // tuple variant 287 let parent = segment.parent_path().syntax().parent()?;
260 Some((path_expr.path()?.segment()?, path_expr.syntax().parent()?)) 288 let expr_or_pat = match_ast! {
261 } else if let Some(record_expr) = find_node_at_offset::<ast::RecordExpr>(source_file, offset) { 289 match parent {
262 // record variant 290 ast::PathExpr(_it) => parent.parent()?,
263 Some((record_expr.path()?.segment()?, record_expr.syntax().clone())) 291 ast::RecordExpr(_it) => parent,
264 } else { 292 ast::TupleStructPat(_it) => parent,
265 None 293 ast::RecordPat(_it) => parent,
266 } 294 _ => return None,
267 .and_then(|(segment, expr)| { 295 }
268 let module = sema.scope(&expr).module()?; 296 };
269 Some((segment, expr, module)) 297 let module = sema.scope(&expr_or_pat).module()?;
270 }) 298 Some((segment, expr_or_pat, module))
271} 299}
272 300
273#[cfg(test)] 301#[cfg(test)]
@@ -278,6 +306,12 @@ mod tests {
278 306
279 use super::*; 307 use super::*;
280 308
309 fn check_not_applicable(ra_fixture: &str) {
310 let fixture =
311 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
312 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
313 }
314
281 #[test] 315 #[test]
282 fn test_extract_struct_several_fields_tuple() { 316 fn test_extract_struct_several_fields_tuple() {
283 check_assist( 317 check_assist(
@@ -312,6 +346,32 @@ enum A { One(One) }"#,
312 } 346 }
313 347
314 #[test] 348 #[test]
349 fn test_extract_struct_carries_over_generics() {
350 check_assist(
351 extract_struct_from_enum_variant,
352 r"enum En<T> { Var { a: T$0 } }",
353 r#"struct Var<T>{ pub a: T }
354
355enum En<T> { Var(Var<T>) }"#,
356 );
357 }
358
359 #[test]
360 fn test_extract_struct_carries_over_attributes() {
361 check_assist(
362 extract_struct_from_enum_variant,
363 r#"#[derive(Debug)]
364#[derive(Clone)]
365enum Enum { Variant{ field: u32$0 } }"#,
366 r#"#[derive(Debug)]#[derive(Clone)] struct Variant{ pub field: u32 }
367
368#[derive(Debug)]
369#[derive(Clone)]
370enum Enum { Variant(Variant) }"#,
371 );
372 }
373
374 #[test]
315 fn test_extract_struct_keep_comments_and_attrs_one_field_named() { 375 fn test_extract_struct_keep_comments_and_attrs_one_field_named() {
316 check_assist( 376 check_assist(
317 extract_struct_from_enum_variant, 377 extract_struct_from_enum_variant,
@@ -496,7 +556,7 @@ enum E {
496} 556}
497 557
498fn f() { 558fn f() {
499 let e = E::V { i: 9, j: 2 }; 559 let E::V { i, j } = E::V { i: 9, j: 2 };
500} 560}
501"#, 561"#,
502 r#" 562 r#"
@@ -507,7 +567,34 @@ enum E {
507} 567}
508 568
509fn f() { 569fn f() {
510 let e = E::V(V { i: 9, j: 2 }); 570 let E::V(V { i, j }) = E::V(V { i: 9, j: 2 });
571}
572"#,
573 )
574 }
575
576 #[test]
577 fn extract_record_fix_references2() {
578 check_assist(
579 extract_struct_from_enum_variant,
580 r#"
581enum E {
582 $0V(i32, i32)
583}
584
585fn f() {
586 let E::V(i, j) = E::V(9, 2);
587}
588"#,
589 r#"
590struct V(pub i32, pub i32);
591
592enum E {
593 V(V)
594}
595
596fn f() {
597 let E::V(V(i, j)) = E::V(V(9, 2));
511} 598}
512"#, 599"#,
513 ) 600 )
@@ -610,12 +697,6 @@ fn foo() {
610 ); 697 );
611 } 698 }
612 699
613 fn check_not_applicable(ra_fixture: &str) {
614 let fixture =
615 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
616 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
617 }
618
619 #[test] 700 #[test]
620 fn test_extract_enum_not_applicable_for_element_with_no_fields() { 701 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
621 check_not_applicable("enum A { $0One }"); 702 check_not_applicable("enum A { $0One }");
diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs
index 998e0de7b..eac8857c6 100644
--- a/crates/ide_assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide_assists/src/handlers/extract_type_alias.rs
@@ -1,4 +1,7 @@
1use syntax::ast::{self, AstNode}; 1use syntax::{
2 ast::{self, edit::IndentLevel, AstNode},
3 match_ast,
4};
2 5
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
4 7
@@ -25,12 +28,15 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
25 } 28 }
26 29
27 let node = ctx.find_node_at_range::<ast::Type>()?; 30 let node = ctx.find_node_at_range::<ast::Type>()?;
28 let insert = ctx 31 let item = ctx.find_node_at_offset::<ast::Item>()?;
29 .find_node_at_offset::<ast::Impl>() 32 let insert = match_ast! {
30 .map(|imp| imp.syntax().clone()) 33 match (item.syntax().parent()?) {
31 .or_else(|| ctx.find_node_at_offset::<ast::Item>().map(|item| item.syntax().clone()))? 34 ast::AssocItemList(it) => it.syntax().parent()?,
32 .text_range() 35 _ => item.syntax().clone(),
33 .start(); 36 }
37 };
38 let indent = IndentLevel::from_node(&insert);
39 let insert = insert.text_range().start();
34 let target = node.syntax().text_range(); 40 let target = node.syntax().text_range();
35 41
36 acc.add( 42 acc.add(
@@ -42,10 +48,14 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
42 builder.replace(target, "Type"); 48 builder.replace(target, "Type");
43 match ctx.config.snippet_cap { 49 match ctx.config.snippet_cap {
44 Some(cap) => { 50 Some(cap) => {
45 builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node)); 51 builder.insert_snippet(
52 cap,
53 insert,
54 format!("type $0Type = {};\n\n{}", node, indent),
55 );
46 } 56 }
47 None => { 57 None => {
48 builder.insert(insert, format!("type Type = {};\n\n", node)); 58 builder.insert(insert, format!("type Type = {};\n\n{}", node, indent));
49 } 59 }
50 } 60 }
51 }, 61 },
@@ -153,9 +163,9 @@ struct S {
153 } 163 }
154 164
155 #[test] 165 #[test]
156 fn extract_from_impl() { 166 fn extract_from_impl_or_trait() {
157 // When invoked in an impl, extracted type alias should be placed next to the impl, not 167 // When invoked in an impl/trait, extracted type alias should be placed next to the
158 // inside. 168 // impl/trait, not inside.
159 check_assist( 169 check_assist(
160 extract_type_alias, 170 extract_type_alias,
161 r#" 171 r#"
@@ -171,5 +181,39 @@ impl S {
171} 181}
172 "#, 182 "#,
173 ); 183 );
184 check_assist(
185 extract_type_alias,
186 r#"
187trait Tr {
188 fn f() -> $0(u8, u8)$0 {}
189}
190 "#,
191 r#"
192type $0Type = (u8, u8);
193
194trait Tr {
195 fn f() -> Type {}
196}
197 "#,
198 );
199 }
200
201 #[test]
202 fn indentation() {
203 check_assist(
204 extract_type_alias,
205 r#"
206mod m {
207 fn f() -> $0u8$0 {}
208}
209 "#,
210 r#"
211mod m {
212 type $0Type = u8;
213
214 fn f() -> Type {}
215}
216 "#,
217 );
174 } 218 }
175} 219}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index ae084c86c..46b54a5f5 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -36,6 +36,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
36 return None; 36 return None;
37 } 37 }
38 let to_extract = node.ancestors().find_map(valid_target_expr)?; 38 let to_extract = node.ancestors().find_map(valid_target_expr)?;
39 if let Some(ty) = ctx.sema.type_of_expr(&to_extract) {
40 if ty.is_unit() {
41 return None;
42 }
43 }
39 let anchor = Anchor::from(&to_extract)?; 44 let anchor = Anchor::from(&to_extract)?;
40 let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone(); 45 let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
41 let target = to_extract.syntax().text_range(); 46 let target = to_extract.syntax().text_range();
@@ -275,15 +280,23 @@ fn foo() {
275 check_assist( 280 check_assist(
276 extract_variable, 281 extract_variable,
277 r#" 282 r#"
278fn foo() { 283fn foo() -> i32 {
279 $0bar(1 + 1)$0 284 $0bar(1 + 1)$0
280} 285}
286
287fn bar(i: i32) -> i32 {
288 i
289}
281"#, 290"#,
282 r#" 291 r#"
283fn foo() { 292fn foo() -> i32 {
284 let $0bar = bar(1 + 1); 293 let $0bar = bar(1 + 1);
285 bar 294 bar
286} 295}
296
297fn bar(i: i32) -> i32 {
298 i
299}
287"#, 300"#,
288 ) 301 )
289 } 302 }
@@ -796,6 +809,22 @@ fn foo() {
796 check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }"); 809 check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }");
797 } 810 }
798 811
812 #[test]
813 fn test_extract_var_unit_expr_not_applicable() {
814 check_assist_not_applicable(
815 extract_variable,
816 r#"
817fn foo() {
818 let mut i = 3;
819 $0if i >= 0 {
820 i += 1;
821 } else {
822 i -= 1;
823 }$0
824}"#,
825 );
826 }
827
799 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic 828 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
800 #[test] 829 #[test]
801 fn extract_var_target() { 830 fn extract_var_target() {
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 3d2cd739a..5a43bdd6f 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -202,7 +202,7 @@ impl ExtendedEnum {
202 fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> { 202 fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
203 match self { 203 match self {
204 ExtendedEnum::Enum(e) => { 204 ExtendedEnum::Enum(e) => {
205 e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>() 205 e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
206 } 206 }
207 ExtendedEnum::Bool => { 207 ExtendedEnum::Bool => {
208 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) 208 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
@@ -212,7 +212,7 @@ impl ExtendedEnum {
212} 212}
213 213
214fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { 214fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
215 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 215 sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
216 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), 216 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
217 _ => { 217 _ => {
218 if ty.is_bool() { 218 if ty.is_bool() {
@@ -228,7 +228,7 @@ fn resolve_tuple_of_enum_def(
228 sema: &Semantics<RootDatabase>, 228 sema: &Semantics<RootDatabase>,
229 expr: &ast::Expr, 229 expr: &ast::Expr,
230) -> Option<Vec<ExtendedEnum>> { 230) -> Option<Vec<ExtendedEnum>> {
231 sema.type_of_expr(&expr)? 231 sema.type_of_expr(expr)?
232 .tuple_fields(sema.db) 232 .tuple_fields(sema.db)
233 .iter() 233 .iter()
234 .map(|ty| { 234 .map(|ty| {
diff --git a/crates/ide_assists/src/handlers/fix_visibility.rs b/crates/ide_assists/src/handlers/fix_visibility.rs
index 89f7b2c2c..9b432e92f 100644
--- a/crates/ide_assists/src/handlers/fix_visibility.rs
+++ b/crates/ide_assists/src/handlers/fix_visibility.rs
@@ -43,7 +43,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
43 _ => return None, 43 _ => return None,
44 }; 44 };
45 45
46 let current_module = ctx.sema.scope(&path.syntax()).module()?; 46 let current_module = ctx.sema.scope(path.syntax()).module()?;
47 let target_module = def.module(ctx.db())?; 47 let target_module = def.module(ctx.db())?;
48 48
49 let vis = target_module.visibility_of(ctx.db(), &def)?; 49 let vis = target_module.visibility_of(ctx.db(), &def)?;
diff --git a/crates/ide_assists/src/handlers/generate_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
index a9f71a703..24939f262 100644
--- a/crates/ide_assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
@@ -47,7 +47,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
47 let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text())); 47 let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text()));
48 48
49 // Return early if we've found an existing new fn 49 // Return early if we've found an existing new fn
50 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 50 let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
51 51
52 let target = variant.syntax().text_range(); 52 let target = variant.syntax().text_range();
53 acc.add( 53 acc.add(
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
index e2f572ba3..986fb2315 100644
--- a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
@@ -136,7 +136,7 @@ fn generate_enum_projection_method(
136 format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text())); 136 format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
137 137
138 // Return early if we've found an existing new fn 138 // Return early if we've found an existing new fn
139 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 139 let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
140 140
141 let target = variant.syntax().text_range(); 141 let target = variant.syntax().text_range();
142 acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { 142 acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| {
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index bc9fc524b..706c995ac 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -59,7 +59,7 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio
59 None => None, 59 None => None,
60 }; 60 };
61 61
62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(ctx, &call, &path, target_module)?;
63 63
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add( 65 acc.add(
@@ -128,12 +128,12 @@ impl FunctionBuilder {
128 file = in_file; 128 file = in_file;
129 target 129 target
130 } 130 }
131 None => next_space_for_fn_after_call_site(&call)?, 131 None => next_space_for_fn_after_call_site(call)?,
132 }; 132 };
133 let needs_pub = target_module.is_some(); 133 let needs_pub = target_module.is_some();
134 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; 134 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
135 let fn_name = fn_name(&path)?; 135 let fn_name = fn_name(path)?;
136 let (type_params, params) = fn_args(ctx, target_module, &call)?; 136 let (type_params, params) = fn_args(ctx, target_module, call)?;
137 137
138 // should_render_snippet intends to express a rough level of confidence about 138 // should_render_snippet intends to express a rough level of confidence about
139 // the correctness of the return type. 139 // the correctness of the return type.
diff --git a/crates/ide_assists/src/handlers/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs
index 09971226e..cc020c92c 100644
--- a/crates/ide_assists/src/handlers/generate_getter.rs
+++ b/crates/ide_assists/src/handlers/generate_getter.rs
@@ -75,7 +75,7 @@ pub(crate) fn generate_getter_impl(
75 if mutable { 75 if mutable {
76 format_to!(fn_name, "_mut"); 76 format_to!(fn_name, "_mut");
77 } 77 }
78 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; 78 let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
79 79
80 let (id, label) = if mutable { 80 let (id, label) = if mutable {
81 ("generate_getter_mut", "Generate a mut getter method") 81 ("generate_getter_mut", "Generate a mut getter method")
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs
index 959a1f86c..b65e8387b 100644
--- a/crates/ide_assists/src/handlers/generate_new.rs
+++ b/crates/ide_assists/src/handlers/generate_new.rs
@@ -36,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
36 }; 36 };
37 37
38 // Return early if we've found an existing new fn 38 // Return early if we've found an existing new fn
39 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; 39 let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
40 40
41 let target = strukt.syntax().text_range(); 41 let target = strukt.syntax().text_range();
42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { 42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
diff --git a/crates/ide_assists/src/handlers/generate_setter.rs b/crates/ide_assists/src/handlers/generate_setter.rs
index 288cf745d..5bdf6b3f4 100644
--- a/crates/ide_assists/src/handlers/generate_setter.rs
+++ b/crates/ide_assists/src/handlers/generate_setter.rs
@@ -39,7 +39,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<
39 // Return early if we've found an existing fn 39 // Return early if we've found an existing fn
40 let fn_name = to_lower_snake_case(&field_name.to_string()); 40 let fn_name = to_lower_snake_case(&field_name.to_string());
41 let impl_def = find_struct_impl( 41 let impl_def = find_struct_impl(
42 &ctx, 42 ctx,
43 &ast::Adt::Struct(strukt.clone()), 43 &ast::Adt::Struct(strukt.clone()),
44 format!("set_{}", fn_name).as_str(), 44 format!("set_{}", fn_name).as_str(),
45 )?; 45 )?;
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs
index f5dafc8cb..2441dbb8b 100644
--- a/crates/ide_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide_assists/src/handlers/inline_local_variable.rs
@@ -182,6 +182,10 @@ fn inline_usage(ctx: &AssistContext) -> Option<InlineData> {
182 PathResolution::Local(local) => local, 182 PathResolution::Local(local) => local,
183 _ => return None, 183 _ => return None,
184 }; 184 };
185 if local.is_mut(ctx.sema.db) {
186 cov_mark::hit!(test_not_inline_mut_variable_use);
187 return None;
188 }
185 189
186 let bind_pat = match local.source(ctx.db()).value { 190 let bind_pat = match local.source(ctx.db()).value {
187 Either::Left(ident) => ident, 191 Either::Left(ident) => ident,
@@ -427,6 +431,19 @@ fn foo() {
427 } 431 }
428 432
429 #[test] 433 #[test]
434 fn test_not_inline_mut_variable_use() {
435 cov_mark::check!(test_not_inline_mut_variable_use);
436 check_assist_not_applicable(
437 inline_local_variable,
438 r"
439fn foo() {
440 let mut a = 1 + 1;
441 a$0 + 1;
442}",
443 );
444 }
445
446 #[test]
430 fn test_call_expr() { 447 fn test_call_expr() {
431 check_assist( 448 check_assist(
432 inline_local_variable, 449 inline_local_variable,
diff --git a/crates/ide_assists/src/handlers/remove_dbg.rs b/crates/ide_assists/src/handlers/remove_dbg.rs
index c8226550f..b20fe992d 100644
--- a/crates/ide_assists/src/handlers/remove_dbg.rs
+++ b/crates/ide_assists/src/handlers/remove_dbg.rs
@@ -85,7 +85,7 @@ fn whitespace_start(it: SyntaxElement) -> Option<TextSize> {
85} 85}
86 86
87fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { 87fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
88 let contents = get_valid_macrocall_contents(&macro_call, "dbg")?; 88 let contents = get_valid_macrocall_contents(macro_call, "dbg")?;
89 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); 89 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
90 let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new( 90 let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
91 TextSize::of('('), 91 TextSize::of('('),
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs
index 2699d2861..fabfe7e93 100644
--- a/crates/ide_assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide_assists/src/handlers/remove_unused_param.rs
@@ -37,8 +37,20 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
37 _ => return None, 37 _ => return None,
38 }; 38 };
39 let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; 39 let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
40 let param_position = func.param_list()?.params().position(|it| it == param)?;
41 40
41 // check if fn is in impl Trait for ..
42 if func
43 .syntax()
44 .parent() // AssocItemList
45 .and_then(|x| x.parent())
46 .and_then(ast::Impl::cast)
47 .map_or(false, |imp| imp.trait_().is_some())
48 {
49 cov_mark::hit!(trait_impl);
50 return None;
51 }
52
53 let param_position = func.param_list()?.params().position(|it| it == param)?;
42 let fn_def = { 54 let fn_def = {
43 let func = ctx.sema.to_def(&func)?; 55 let func = ctx.sema.to_def(&func)?;
44 Definition::ModuleDef(func.into()) 56 Definition::ModuleDef(func.into())
@@ -254,6 +266,22 @@ fn main() { foo(9, 2) }
254 } 266 }
255 267
256 #[test] 268 #[test]
269 fn trait_impl() {
270 cov_mark::check!(trait_impl);
271 check_assist_not_applicable(
272 remove_unused_param,
273 r#"
274trait Trait {
275 fn foo(x: i32);
276}
277impl Trait for () {
278 fn foo($0x: i32) {}
279}
280"#,
281 );
282 }
283
284 #[test]
257 fn remove_across_files() { 285 fn remove_across_files() {
258 check_assist( 286 check_assist(
259 remove_unused_param, 287 remove_unused_param,
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index 933acead1..f6a926042 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -28,7 +28,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<(
28 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?; 28 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?;
29 29
30 let path = record.as_ref().either(|it| it.path(), |it| it.path())?; 30 let path = record.as_ref().either(|it| it.path(), |it| it.path())?;
31 let ranks = compute_fields_ranks(&path, &ctx)?; 31 let ranks = compute_fields_ranks(&path, ctx)?;
32 let get_rank_of_field = 32 let get_rank_of_field =
33 |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX); 33 |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
34 34
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index 10d9cec31..f9474c9f5 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -112,7 +112,7 @@ fn add_assist(
112 let insert_pos = adt.syntax().text_range().end(); 112 let insert_pos = adt.syntax().text_range().end();
113 let impl_def_with_items = 113 let impl_def_with_items =
114 impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path); 114 impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path);
115 update_attribute(builder, &input, &trait_name, &attr); 115 update_attribute(builder, input, &trait_name, attr);
116 let trait_path = format!("{}", trait_path); 116 let trait_path = format!("{}", trait_path);
117 match (ctx.config.snippet_cap, impl_def_with_items) { 117 match (ctx.config.snippet_cap, impl_def_with_items) {
118 (None, _) => { 118 (None, _) => {
diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
index aee880625..9404aa26d 100644
--- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
@@ -169,7 +169,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
169} 169}
170 170
171fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool { 171fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
172 sema.type_of_pat(&pat) 172 sema.type_of_pat(pat)
173 .and_then(|ty| TryEnum::from_ty(sema, &ty)) 173 .and_then(|ty| TryEnum::from_ty(sema, &ty))
174 .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text()) 174 .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text())
175 .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_))) 175 .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_)))
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
index 2f1da82c7..140e27356 100644
--- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
@@ -123,7 +123,7 @@ impl TailReturnCollector {
123 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { 123 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
124 match expr { 124 match expr {
125 Expr::BlockExpr(block_expr) => { 125 Expr::BlockExpr(block_expr) => {
126 self.collect_jump_exprs(&block_expr, collect_break); 126 self.collect_jump_exprs(block_expr, collect_break);
127 } 127 }
128 Expr::ReturnExpr(ret_expr) => { 128 Expr::ReturnExpr(ret_expr) => {
129 if let Some(ret_expr_arg) = &ret_expr.expr() { 129 if let Some(ret_expr_arg) = &ret_expr.expr() {
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 16af72927..331a6df2b 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -47,14 +47,14 @@ impl AssistKind {
47 } 47 }
48 48
49 match self { 49 match self {
50 AssistKind::None | AssistKind::Generate => return true, 50 AssistKind::None | AssistKind::Generate => true,
51 AssistKind::Refactor => match other { 51 AssistKind::Refactor => match other {
52 AssistKind::RefactorExtract 52 AssistKind::RefactorExtract
53 | AssistKind::RefactorInline 53 | AssistKind::RefactorInline
54 | AssistKind::RefactorRewrite => return true, 54 | AssistKind::RefactorRewrite => true,
55 _ => return false, 55 _ => false,
56 }, 56 },
57 _ => return false, 57 _ => false,
58 } 58 }
59 } 59 }
60 60
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 2b7c2d581..bdf9cb71c 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -74,7 +74,7 @@ pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) {
74#[track_caller] 74#[track_caller]
75fn check_doc_test(assist_id: &str, before: &str, after: &str) { 75fn check_doc_test(assist_id: &str, before: &str, after: &str) {
76 let after = trim_indent(after); 76 let after = trim_indent(after);
77 let (db, file_id, selection) = RootDatabase::with_range_or_offset(&before); 77 let (db, file_id, selection) = RootDatabase::with_range_or_offset(before);
78 let before = db.file_text(file_id).to_string(); 78 let before = db.file_text(file_id).to_string();
79 let frange = FileRange { file_id, range: selection.into() }; 79 let frange = FileRange { file_id, range: selection.into() };
80 80
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 30128a24a..068df005b 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -492,7 +492,7 @@ pub(crate) fn add_method_to_adt(
492 let start_offset = impl_def 492 let start_offset = impl_def
493 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) 493 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
494 .unwrap_or_else(|| { 494 .unwrap_or_else(|| {
495 buf = generate_impl_text(&adt, &buf); 495 buf = generate_impl_text(adt, &buf);
496 adt.syntax().text_range().end() 496 adt.syntax().text_range().end()
497 }); 497 });
498 498
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
index b3aabeab3..cb8bc8b2f 100644
--- a/crates/ide_assists/src/utils/suggest_name.rs
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -187,7 +187,7 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> {
187 } 187 }
188 } 188 }
189 189
190 normalize(&name) 190 normalize(name)
191} 191}
192 192
193fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { 193fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index ba81c9e04..3c45fe1cb 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14itertools = "0.10.0" 14itertools = "0.10.0"
15log = "0.4.8" 15log = "0.4.8"
16rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index ffdcdc930..bd90cefb2 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -6,7 +6,6 @@ pub(crate) mod flyimport;
6pub(crate) mod fn_param; 6pub(crate) mod fn_param;
7pub(crate) mod keyword; 7pub(crate) mod keyword;
8pub(crate) mod lifetime; 8pub(crate) mod lifetime;
9pub(crate) mod macro_in_item_position;
10pub(crate) mod mod_; 9pub(crate) mod mod_;
11pub(crate) mod pattern; 10pub(crate) mod pattern;
12pub(crate) mod postfix; 11pub(crate) mod postfix;
@@ -30,7 +29,7 @@ use crate::{
30 macro_::render_macro, 29 macro_::render_macro,
31 pattern::{render_struct_pat, render_variant_pat}, 30 pattern::{render_struct_pat, render_variant_pat},
32 render_field, render_resolution, render_tuple_field, 31 render_field, render_resolution, render_tuple_field,
33 type_alias::render_type_alias, 32 type_alias::{render_type_alias, render_type_alias_with_eq},
34 RenderContext, 33 RenderContext,
35 }, 34 },
36 CompletionContext, CompletionItem, CompletionItemKind, 35 CompletionContext, CompletionItem, CompletionItemKind,
@@ -57,10 +56,16 @@ impl Builder {
57} 56}
58 57
59impl Completions { 58impl Completions {
60 pub(crate) fn add(&mut self, item: CompletionItem) { 59 fn add(&mut self, item: CompletionItem) {
61 self.buf.push(item) 60 self.buf.push(item)
62 } 61 }
63 62
63 fn add_opt(&mut self, item: Option<CompletionItem>) {
64 if let Some(item) = item {
65 self.buf.push(item)
66 }
67 }
68
64 pub(crate) fn add_all<I>(&mut self, items: I) 69 pub(crate) fn add_all<I>(&mut self, items: I)
65 where 70 where
66 I: IntoIterator, 71 I: IntoIterator,
@@ -104,9 +109,10 @@ impl Completions {
104 local_name: hir::Name, 109 local_name: hir::Name,
105 resolution: &hir::ScopeDef, 110 resolution: &hir::ScopeDef,
106 ) { 111 ) {
107 if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) { 112 if ctx.expects_type() && resolution.is_value_def() {
108 self.add(item); 113 return;
109 } 114 }
115 self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution));
110 } 116 }
111 117
112 pub(crate) fn add_macro( 118 pub(crate) fn add_macro(
@@ -119,9 +125,7 @@ impl Completions {
119 Some(it) => it, 125 Some(it) => it,
120 None => return, 126 None => return,
121 }; 127 };
122 if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { 128 self.add_opt(render_macro(RenderContext::new(ctx), None, name, macro_));
123 self.add(item);
124 }
125 } 129 }
126 130
127 pub(crate) fn add_function( 131 pub(crate) fn add_function(
@@ -130,9 +134,10 @@ impl Completions {
130 func: hir::Function, 134 func: hir::Function,
131 local_name: Option<hir::Name>, 135 local_name: Option<hir::Name>,
132 ) { 136 ) {
133 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) { 137 if ctx.expects_type() {
134 self.add(item) 138 return;
135 } 139 }
140 self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func));
136 } 141 }
137 142
138 pub(crate) fn add_method( 143 pub(crate) fn add_method(
@@ -142,10 +147,7 @@ impl Completions {
142 receiver: Option<hir::Name>, 147 receiver: Option<hir::Name>,
143 local_name: Option<hir::Name>, 148 local_name: Option<hir::Name>,
144 ) { 149 ) {
145 if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func) 150 self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
146 {
147 self.add(item)
148 }
149 } 151 }
150 152
151 pub(crate) fn add_variant_pat( 153 pub(crate) fn add_variant_pat(
@@ -154,9 +156,7 @@ impl Completions {
154 variant: hir::Variant, 156 variant: hir::Variant,
155 local_name: Option<hir::Name>, 157 local_name: Option<hir::Name>,
156 ) { 158 ) {
157 if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name, None) { 159 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
158 self.add(item);
159 }
160 } 160 }
161 161
162 pub(crate) fn add_qualified_variant_pat( 162 pub(crate) fn add_qualified_variant_pat(
@@ -165,9 +165,7 @@ impl Completions {
165 variant: hir::Variant, 165 variant: hir::Variant,
166 path: hir::ModPath, 166 path: hir::ModPath,
167 ) { 167 ) {
168 if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) { 168 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
169 self.add(item);
170 }
171 } 169 }
172 170
173 pub(crate) fn add_struct_pat( 171 pub(crate) fn add_struct_pat(
@@ -176,21 +174,26 @@ impl Completions {
176 strukt: hir::Struct, 174 strukt: hir::Struct,
177 local_name: Option<hir::Name>, 175 local_name: Option<hir::Name>,
178 ) { 176 ) {
179 if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) { 177 self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
180 self.add(item);
181 }
182 } 178 }
183 179
184 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { 180 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
185 if let Some(item) = render_const(RenderContext::new(ctx), constant) { 181 if ctx.expects_type() {
186 self.add(item); 182 return;
187 } 183 }
184 self.add_opt(render_const(RenderContext::new(ctx), constant));
188 } 185 }
189 186
190 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { 187 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
191 if let Some(item) = render_type_alias(RenderContext::new(ctx), type_alias) { 188 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias));
192 self.add(item) 189 }
193 } 190
191 pub(crate) fn add_type_alias_with_eq(
192 &mut self,
193 ctx: &CompletionContext,
194 type_alias: hir::TypeAlias,
195 ) {
196 self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
194 } 197 }
195 198
196 pub(crate) fn add_qualified_enum_variant( 199 pub(crate) fn add_qualified_enum_variant(
@@ -209,6 +212,9 @@ impl Completions {
209 variant: hir::Variant, 212 variant: hir::Variant,
210 local_name: Option<hir::Name>, 213 local_name: Option<hir::Name>,
211 ) { 214 ) {
215 if ctx.expects_type() {
216 return;
217 }
212 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); 218 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
213 self.add(item); 219 self.add(item);
214 } 220 }
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index c48bb9e66..6df569c2a 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -3,20 +3,20 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use hir::HasAttrs;
7use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES};
6use once_cell::sync::Lazy; 8use once_cell::sync::Lazy;
7use rustc_hash::{FxHashMap, FxHashSet}; 9use rustc_hash::{FxHashMap, FxHashSet};
8use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T}; 10use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T};
9 11
10use crate::{ 12use crate::{
11 context::CompletionContext, 13 context::CompletionContext,
12 generated_lint_completions::{CLIPPY_LINTS, FEATURES},
13 item::{CompletionItem, CompletionItemKind, CompletionKind}, 14 item::{CompletionItem, CompletionItemKind, CompletionKind},
14 Completions, 15 Completions,
15}; 16};
16 17
17mod derive; 18mod derive;
18mod lint; 19mod lint;
19pub(crate) use self::lint::LintCompletion;
20 20
21pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 21pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
22 let attribute = ctx.attribute_under_caret.as_ref()?; 22 let attribute = ctx.attribute_under_caret.as_ref()?;
@@ -25,7 +25,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
25 "derive" => derive::complete_derive(acc, ctx, token_tree), 25 "derive" => derive::complete_derive(acc, ctx, token_tree),
26 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES), 26 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
27 "allow" | "warn" | "deny" | "forbid" => { 27 "allow" | "warn" | "deny" | "forbid" => {
28 lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS); 28 lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS);
29 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); 29 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30 } 30 }
31 _ => (), 31 _ => (),
@@ -69,7 +69,7 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib
69 } 69 }
70 70
71 if is_inner || !attr_completion.prefer_inner { 71 if is_inner || !attr_completion.prefer_inner {
72 acc.add(item.build()); 72 item.add_to(acc);
73 } 73 }
74 }; 74 };
75 75
@@ -82,6 +82,24 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib
82 None if is_inner => ATTRIBUTES.iter().for_each(add_completion), 82 None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
83 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion), 83 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
84 } 84 }
85
86 // FIXME: write a test for this when we can
87 ctx.scope.process_all_names(&mut |name, scope_def| {
88 if let hir::ScopeDef::MacroDef(mac) = scope_def {
89 if mac.kind() == hir::MacroKind::Attr {
90 let mut item = CompletionItem::new(
91 CompletionKind::Attribute,
92 ctx.source_range(),
93 name.to_string(),
94 );
95 item.kind(CompletionItemKind::Attribute);
96 if let Some(docs) = mac.docs(ctx.sema.db) {
97 item.documentation(docs);
98 }
99 item.add_to(acc);
100 }
101 }
102 });
85} 103}
86 104
87struct AttrCompletion { 105struct AttrCompletion {
@@ -201,7 +219,7 @@ static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
201}); 219});
202const EXPR_ATTRIBUTES: &[&str] = attrs!(); 220const EXPR_ATTRIBUTES: &[&str] = attrs!();
203 221
204/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 222/// <https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index>
205// Keep these sorted for the binary search! 223// Keep these sorted for the binary search!
206const ATTRIBUTES: &[AttrCompletion] = &[ 224const ATTRIBUTES: &[AttrCompletion] = &[
207 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 225 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index 0bc3eab98..d526824fb 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -1,6 +1,7 @@
1//! Completion for derives 1//! Completion for derives
2use hir::HasAttrs;
2use itertools::Itertools; 3use itertools::Itertools;
3use rustc_hash::FxHashSet; 4use rustc_hash::FxHashMap;
4use syntax::ast; 5use syntax::ast;
5 6
6use crate::{ 7use crate::{
@@ -15,66 +16,64 @@ pub(super) fn complete_derive(
15 derive_input: ast::TokenTree, 16 derive_input: ast::TokenTree,
16) { 17) {
17 if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { 18 if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
18 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 19 for (derive, docs) in get_derive_names_in_scope(ctx) {
19 .iter() 20 let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
20 .filter(|completion| !existing_derives.contains(completion.label)) 21 .iter()
21 { 22 .find(|derive_completion| derive_completion.label == derive)
22 let mut components = vec![derive_completion.label]; 23 {
23 components.extend( 24 let mut components = vec![derive_completion.label];
24 derive_completion 25 components.extend(
25 .dependencies 26 derive_completion
26 .iter() 27 .dependencies
27 .filter(|&&dependency| !existing_derives.contains(dependency)), 28 .iter()
28 ); 29 .filter(|&&dependency| !existing_derives.contains(dependency)),
29 let lookup = components.join(", "); 30 );
30 let label = components.iter().rev().join(", "); 31 let lookup = components.join(", ");
32 let label = components.iter().rev().join(", ");
33 (label, Some(lookup))
34 } else {
35 (derive, None)
36 };
31 let mut item = 37 let mut item =
32 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); 38 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
33 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
34 item.add_to(acc);
35 }
36
37 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
38 let mut item = CompletionItem::new(
39 CompletionKind::Attribute,
40 ctx.source_range(),
41 custom_derive_name,
42 );
43 item.kind(CompletionItemKind::Attribute); 39 item.kind(CompletionItemKind::Attribute);
40 if let Some(docs) = docs {
41 item.documentation(docs);
42 }
43 if let Some(lookup) = lookup {
44 item.lookup_by(lookup);
45 }
44 item.add_to(acc); 46 item.add_to(acc);
45 } 47 }
46 } 48 }
47} 49}
48 50
49fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { 51fn get_derive_names_in_scope(
50 let mut result = FxHashSet::default(); 52 ctx: &CompletionContext,
53) -> FxHashMap<String, Option<hir::Documentation>> {
54 let mut result = FxHashMap::default();
51 ctx.scope.process_all_names(&mut |name, scope_def| { 55 ctx.scope.process_all_names(&mut |name, scope_def| {
52 if let hir::ScopeDef::MacroDef(mac) = scope_def { 56 if let hir::ScopeDef::MacroDef(mac) = scope_def {
53 if mac.kind() == hir::MacroKind::Derive { 57 if mac.kind() == hir::MacroKind::Derive {
54 result.insert(name.to_string()); 58 result.insert(name.to_string(), mac.docs(ctx.db));
55 } 59 }
56 } 60 }
57 }); 61 });
58 result 62 result
59} 63}
60 64
61struct DeriveCompletion { 65struct DeriveDependencies {
62 label: &'static str, 66 label: &'static str,
63 dependencies: &'static [&'static str], 67 dependencies: &'static [&'static str],
64} 68}
65 69
66/// Standard Rust derives and the information about their dependencies 70/// Standard Rust derives that have dependencies
67/// (the dependencies are needed so that the main derive don't break the compilation when added) 71/// (the dependencies are needed so that the main derive don't break the compilation when added)
68const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ 72const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
69 DeriveCompletion { label: "Clone", dependencies: &[] }, 73 DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
70 DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, 74 DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
71 DeriveCompletion { label: "Debug", dependencies: &[] }, 75 DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
72 DeriveCompletion { label: "Default", dependencies: &[] }, 76 DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
73 DeriveCompletion { label: "Hash", dependencies: &[] },
74 DeriveCompletion { label: "PartialEq", dependencies: &[] },
75 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
76 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
77 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
78]; 77];
79 78
80#[cfg(test)] 79#[cfg(test)]
@@ -94,6 +93,7 @@ mod tests {
94 } 93 }
95 94
96 #[test] 95 #[test]
96 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
97 fn empty_derive() { 97 fn empty_derive() {
98 check( 98 check(
99 r#"#[derive($0)] struct Test;"#, 99 r#"#[derive($0)] struct Test;"#,
@@ -112,6 +112,7 @@ mod tests {
112 } 112 }
113 113
114 #[test] 114 #[test]
115 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
115 fn derive_with_input() { 116 fn derive_with_input() {
116 check( 117 check(
117 r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, 118 r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
@@ -129,6 +130,7 @@ mod tests {
129 } 130 }
130 131
131 #[test] 132 #[test]
133 #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
132 fn derive_with_input2() { 134 fn derive_with_input2() {
133 check( 135 check(
134 r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, 136 r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs
index 403630dce..ca99e9759 100644
--- a/crates/ide_completion/src/completions/attribute/lint.rs
+++ b/crates/ide_completion/src/completions/attribute/lint.rs
@@ -1,4 +1,5 @@
1//! Completion for lints 1//! Completion for lints
2use ide_db::helpers::generated_lints::Lint;
2use syntax::ast; 3use syntax::ast;
3 4
4use crate::{ 5use crate::{
@@ -11,7 +12,7 @@ pub(super) fn complete_lint(
11 acc: &mut Completions, 12 acc: &mut Completions,
12 ctx: &CompletionContext, 13 ctx: &CompletionContext,
13 derive_input: ast::TokenTree, 14 derive_input: ast::TokenTree,
14 lints_completions: &[LintCompletion], 15 lints_completions: &[Lint],
15) { 16) {
16 if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) { 17 if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) {
17 for lint_completion in lints_completions 18 for lint_completion in lints_completions
@@ -23,136 +24,13 @@ pub(super) fn complete_lint(
23 ctx.source_range(), 24 ctx.source_range(),
24 lint_completion.label, 25 lint_completion.label,
25 ); 26 );
26 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); 27 item.kind(CompletionItemKind::Attribute)
28 .documentation(hir::Documentation::new(lint_completion.description.to_owned()));
27 item.add_to(acc) 29 item.add_to(acc)
28 } 30 }
29 } 31 }
30} 32}
31 33
32pub(crate) struct LintCompletion {
33 pub(crate) label: &'static str,
34 pub(crate) description: &'static str,
35}
36
37#[rustfmt::skip]
38pub(super) const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
39 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
40 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
41 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
42 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
43 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
44 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
45 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
46 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
47 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
48 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
49 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
50 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
51 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
52 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
53 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
54 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
55 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
56 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
57 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
58 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
59 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
60 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
61 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
62 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
63 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
64 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
65 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
66 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
67 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
68 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
69 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
70 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
71 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
72 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
73 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
74 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
75 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
76 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
77 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
78 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
79 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
80 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
81 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
82 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
83 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
84 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
85 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
86 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
87 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
88 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
89 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
90 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
91 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
92 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
93 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
94 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
95 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
96 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
97 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
98 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
99 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
100 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
101 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
102 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
103 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
104 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
105 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
106 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
107 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
108 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
109 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
110 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
111 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
112 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
113 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
114 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
115 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
116 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
117 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
118 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
119 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
120 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
121 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
122 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
123 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
124 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
125 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
126 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
127 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
128 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
129 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
130 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
131 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
132 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
133 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
134 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
135 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
136 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
137 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
138 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
139 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
140 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
141 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
142 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
143 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
144 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
145 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
146 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
147 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
148 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
149 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
150 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
151 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
152 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
153 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
154];
155
156#[cfg(test)] 34#[cfg(test)]
157mod tests { 35mod tests {
158 36
@@ -184,4 +62,13 @@ mod tests {
184 r#"#[allow(keyword_idents, deprecated)] struct Test;"#, 62 r#"#[allow(keyword_idents, deprecated)] struct Test;"#,
185 ) 63 )
186 } 64 }
65
66 #[test]
67 fn check_feature() {
68 check_edit(
69 "box_syntax",
70 r#"#[feature(box_$0)] struct Test;"#,
71 r#"#[feature(box_syntax)] struct Test;"#,
72 )
73 }
187} 74}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index e0a7021fd..9552875c1 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -4,7 +4,7 @@ use either::Either;
4use hir::{HasVisibility, ScopeDef}; 4use hir::{HasVisibility, ScopeDef};
5use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
6 6
7use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions};
8 8
9/// Complete dot accesses, i.e. fields or methods. 9/// Complete dot accesses, i.e. fields or methods.
10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
@@ -13,12 +13,12 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
13 _ => return complete_undotted_self(acc, ctx), 13 _ => return complete_undotted_self(acc, ctx),
14 }; 14 };
15 15
16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 16 let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
17 Some(ty) => ty, 17 Some(ty) => ty,
18 _ => return, 18 _ => return,
19 }; 19 };
20 20
21 if ctx.is_call { 21 if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) {
22 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else { 23 } else {
24 complete_fields(ctx, &receiver_ty, |field, ty| match field { 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
@@ -33,7 +33,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.config.enable_self_on_the_fly { 33 if !ctx.config.enable_self_on_the_fly {
34 return; 34 return;
35 } 35 }
36 if !ctx.is_trivial_path || ctx.is_path_disallowed() { 36 if !ctx.is_trivial_path() || ctx.is_path_disallowed() {
37 return; 37 return;
38 } 38 }
39 ctx.scope.process_all_names(&mut |name, def| { 39 ctx.scope.process_all_names(&mut |name, def| {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index d72bf13d3..30b8d44bd 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -1,10 +1,10 @@
1//! Feature: completion with imports-on-the-fly 1//! Feature: completion with imports-on-the-fly
2//! 2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates, 3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input. 4//! if they can be qualified in the scope, and their name contains all symbols from the completion input.
5//! 5//!
6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent. 6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively. 7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the containing is checked case-insensitively.
8//! 8//!
9//! ``` 9//! ```
10//! fn main() { 10//! fn main() {
@@ -23,8 +23,8 @@
23//! ``` 23//! ```
24//! 24//!
25//! Also completes associated items, that require trait imports. 25//! Also completes associated items, that require trait imports.
26//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account. 26//! If any unresolved and/or partially-qualified path precedes the input, it will be taken into account.
27//! Currently, only the imports with their import path ending with the whole qialifier will be proposed 27//! Currently, only the imports with their import path ending with the whole qualifier will be proposed
28//! (no fuzzy matching for qualifier). 28//! (no fuzzy matching for qualifier).
29//! 29//!
30//! ``` 30//! ```
@@ -61,14 +61,14 @@
61//! } 61//! }
62//! ``` 62//! ```
63//! 63//!
64//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path, 64//! NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path,
65//! no imports will be proposed. 65//! no imports will be proposed.
66//! 66//!
67//! .Fuzzy search details 67//! .Fuzzy search details
68//! 68//!
69//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only 69//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
70//! (i.e. in `HashMap` in the `std::collections::HashMap` path). 70//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
71//! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols 71//! For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols
72//! (but shows all associated items for any input length). 72//! (but shows all associated items for any input length).
73//! 73//!
74//! .Import configuration 74//! .Import configuration
@@ -79,18 +79,17 @@
79//! .LSP and performance implications 79//! .LSP and performance implications
80//! 80//!
81//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` 81//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
82//! (case sensitive) resolve client capability in its client capabilities. 82//! (case-sensitive) resolve client capability in its client capabilities.
83//! This way the server is able to defer the costly computations, doing them for a selected completion item only. 83//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
84//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, 84//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
85//! which might be slow ergo the feature is automatically disabled. 85//! which might be slow ergo the feature is automatically disabled.
86//! 86//!
87//! .Feature toggle 87//! .Feature toggle
88//! 88//!
89//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag. 89//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
90//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 90//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
91//! capability enabled. 91//! capability enabled.
92 92
93use hir::ModPath;
94use ide_db::helpers::{ 93use ide_db::helpers::{
95 import_assets::{ImportAssets, ImportCandidate}, 94 import_assets::{ImportAssets, ImportCandidate},
96 insert_use::ImportScope, 95 insert_use::ImportScope,
@@ -161,13 +160,13 @@ pub(crate) fn position_for_import<'a>(
161) -> Option<&'a SyntaxNode> { 160) -> Option<&'a SyntaxNode> {
162 Some(match import_candidate { 161 Some(match import_candidate {
163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), 162 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), 163 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), 164 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
166 None => ctx 165 None => ctx
167 .name_ref_syntax 166 .name_ref_syntax
168 .as_ref() 167 .as_ref()
169 .map(|name_ref| name_ref.syntax()) 168 .map(|name_ref| name_ref.syntax())
170 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) 169 .or_else(|| ctx.path_qual().map(|path| path.syntax()))
171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, 170 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
172 }) 171 })
173} 172}
@@ -190,7 +189,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
190 }; 189 };
191 let assets_for_path = ImportAssets::for_fuzzy_path( 190 let assets_for_path = ImportAssets::for_fuzzy_path(
192 current_module, 191 current_module,
193 ctx.path_qual.clone(), 192 ctx.path_qual().cloned(),
194 fuzzy_name, 193 fuzzy_name,
195 &ctx.sema, 194 &ctx.sema,
196 approximate_node, 195 approximate_node,
@@ -208,7 +207,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
208} 207}
209 208
210fn compute_fuzzy_completion_order_key( 209fn compute_fuzzy_completion_order_key(
211 proposed_mod_path: &ModPath, 210 proposed_mod_path: &hir::ModPath,
212 user_input_lowercased: &str, 211 user_input_lowercased: &str,
213) -> usize { 212) -> usize {
214 cov_mark::hit!(certain_fuzzy_order_test); 213 cov_mark::hit!(certain_fuzzy_order_test);
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 1a7a484a4..ba13d3707 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -5,8 +5,8 @@ use std::iter;
5use syntax::{SyntaxKind, T}; 5use syntax::{SyntaxKind, T};
6 6
7use crate::{ 7use crate::{
8 patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind, 8 context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
9 CompletionKind, Completions, 9 CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { 12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
@@ -19,11 +19,12 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
19 }; 19 };
20 20
21 if ctx.use_item_syntax.is_some() { 21 if ctx.use_item_syntax.is_some() {
22 if ctx.path_qual.is_none() { 22 let qual = ctx.path_qual();
23 if qual.is_none() {
23 kw_completion("crate::").add_to(acc); 24 kw_completion("crate::").add_to(acc);
24 } 25 }
25 kw_completion("self").add_to(acc); 26 kw_completion("self").add_to(acc);
26 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 27 if iter::successors(qual.cloned(), |p| p.qualifier())
27 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 28 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
28 { 29 {
29 kw_completion("super::").add_to(acc); 30 kw_completion("super::").add_to(acc);
@@ -127,8 +128,15 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
127 add_keyword("mut", "mut "); 128 add_keyword("mut", "mut ");
128 } 129 }
129 130
130 if ctx.in_loop_body { 131 let (can_be_stmt, in_loop_body) = match ctx.path_context {
131 if ctx.can_be_stmt { 132 Some(PathCompletionContext {
133 is_trivial_path: true, can_be_stmt, in_loop_body, ..
134 }) => (can_be_stmt, in_loop_body),
135 _ => return,
136 };
137
138 if in_loop_body {
139 if can_be_stmt {
132 add_keyword("continue", "continue;"); 140 add_keyword("continue", "continue;");
133 add_keyword("break", "break;"); 141 add_keyword("break", "break;");
134 } else { 142 } else {
@@ -137,9 +145,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
137 } 145 }
138 } 146 }
139 147
140 if !ctx.is_trivial_path {
141 return;
142 }
143 let fn_def = match &ctx.function_def { 148 let fn_def = match &ctx.function_def {
144 Some(it) => it, 149 Some(it) => it,
145 None => return, 150 None => return,
@@ -147,7 +152,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
147 152
148 add_keyword( 153 add_keyword(
149 "return", 154 "return",
150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 155 match (can_be_stmt, fn_def.ret_type().is_some()) {
151 (true, true) => "return $0;", 156 (true, true) => "return $0;",
152 (true, false) => "return;", 157 (true, false) => "return;",
153 (false, true) => "return $0", 158 (false, true) => "return $0",
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
deleted file mode 100644
index 781b96ff1..000000000
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1//! Completes macro invocations used in item position.
2
3use crate::{CompletionContext, Completions};
4
5// Ideally this should be removed and moved into `(un)qualified_path` respectively
6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
7 // Show only macros in top level.
8 if !ctx.expects_item() {
9 return;
10 }
11
12 ctx.scope.process_all_names(&mut |name, res| {
13 if let hir::ScopeDef::MacroDef(mac) = res {
14 acc.add_macro(ctx, Some(name.clone()), mac);
15 }
16 // FIXME: This should be done in qualified_path/unqualified_path instead?
17 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
18 acc.add_resolution(ctx, name, &res);
19 }
20 })
21}
22
23#[cfg(test)]
24mod tests {
25 use expect_test::{expect, Expect};
26
27 use crate::{test_utils::completion_list, CompletionKind};
28
29 fn check(ra_fixture: &str, expect: Expect) {
30 let actual = completion_list(ra_fixture, CompletionKind::Reference);
31 expect.assert_eq(&actual)
32 }
33
34 #[test]
35 fn completes_macros_as_item() {
36 check(
37 r#"
38macro_rules! foo { () => {} }
39fn foo() {}
40
41$0
42"#,
43 expect![[r#"
44 ma foo!(…) macro_rules! foo
45 "#]],
46 )
47 }
48}
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 8a728c67e..1daa8595a 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -39,7 +39,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
39 | hir::ModuleDef::Module(..) => refutable, 39 | hir::ModuleDef::Module(..) => refutable,
40 _ => false, 40 _ => false,
41 }, 41 },
42 hir::ScopeDef::MacroDef(_) => true, 42 hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(),
43 hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { 43 hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
44 Some(hir::Adt::Struct(strukt)) => { 44 Some(hir::Adt::Struct(strukt)) => {
45 acc.add_struct_pat(ctx, strukt, Some(name.clone())); 45 acc.add_struct_pat(ctx, strukt, Some(name.clone()));
@@ -102,6 +102,28 @@ fn foo() {
102 } 102 }
103 103
104 #[test] 104 #[test]
105 fn does_not_complete_non_fn_macros() {
106 check(
107 r#"
108macro_rules! m { ($e:expr) => { $e } }
109enum E { X }
110
111#[rustc_builtin_macro]
112macro Clone {}
113
114fn foo() {
115 match E::X { $0 }
116}
117"#,
118 expect![[r#"
119 ev E::X ()
120 en E
121 ma m!(…) macro_rules! m
122 "#]],
123 );
124 }
125
126 #[test]
105 fn completes_in_simple_macro_call() { 127 fn completes_in_simple_macro_call() {
106 check( 128 check(
107 r#" 129 r#"
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 86bbb58e2..9f98b21be 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -24,7 +24,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
24 } 24 }
25 25
26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { 26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
27 Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false), 27 Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false),
28 Some(ImmediateLocation::FieldAccess { 28 Some(ImmediateLocation::FieldAccess {
29 receiver: Some(it), 29 receiver: Some(it),
30 receiver_is_ambiguous_float_literal, 30 receiver_is_ambiguous_float_literal,
@@ -34,7 +34,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
34 34
35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal); 35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
36 36
37 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 37 let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) {
38 Some(it) => it, 38 Some(it) => it,
39 None => return, 39 None => return,
40 }; 40 };
@@ -50,7 +50,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
50 postfix_snippet( 50 postfix_snippet(
51 ctx, 51 ctx,
52 cap, 52 cap,
53 &dot_receiver, 53 dot_receiver,
54 "ifl", 54 "ifl",
55 "if let Ok {}", 55 "if let Ok {}",
56 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text), 56 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
@@ -60,7 +60,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
60 postfix_snippet( 60 postfix_snippet(
61 ctx, 61 ctx,
62 cap, 62 cap,
63 &dot_receiver, 63 dot_receiver,
64 "while", 64 "while",
65 "while let Ok {}", 65 "while let Ok {}",
66 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text), 66 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
@@ -71,7 +71,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
71 postfix_snippet( 71 postfix_snippet(
72 ctx, 72 ctx,
73 cap, 73 cap,
74 &dot_receiver, 74 dot_receiver,
75 "ifl", 75 "ifl",
76 "if let Some {}", 76 "if let Some {}",
77 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text), 77 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
@@ -81,7 +81,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
81 postfix_snippet( 81 postfix_snippet(
82 ctx, 82 ctx,
83 cap, 83 cap,
84 &dot_receiver, 84 dot_receiver,
85 "while", 85 "while",
86 "while let Some {}", 86 "while let Some {}",
87 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text), 87 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
@@ -93,7 +93,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
93 postfix_snippet( 93 postfix_snippet(
94 ctx, 94 ctx,
95 cap, 95 cap,
96 &dot_receiver, 96 dot_receiver,
97 "if", 97 "if",
98 "if expr {}", 98 "if expr {}",
99 &format!("if {} {{\n $0\n}}", receiver_text), 99 &format!("if {} {{\n $0\n}}", receiver_text),
@@ -102,22 +102,22 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
102 postfix_snippet( 102 postfix_snippet(
103 ctx, 103 ctx,
104 cap, 104 cap,
105 &dot_receiver, 105 dot_receiver,
106 "while", 106 "while",
107 "while expr {}", 107 "while expr {}",
108 &format!("while {} {{\n $0\n}}", receiver_text), 108 &format!("while {} {{\n $0\n}}", receiver_text),
109 ) 109 )
110 .add_to(acc); 110 .add_to(acc);
111 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) 111 postfix_snippet(ctx, cap, dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
112 .add_to(acc); 112 .add_to(acc);
113 } 113 }
114 114
115 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) 115 postfix_snippet(ctx, cap, dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
116 .add_to(acc); 116 .add_to(acc);
117 postfix_snippet( 117 postfix_snippet(
118 ctx, 118 ctx,
119 cap, 119 cap,
120 &dot_receiver, 120 dot_receiver,
121 "refm", 121 "refm",
122 "&mut expr", 122 "&mut expr",
123 &format!("&mut {}", receiver_text), 123 &format!("&mut {}", receiver_text),
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 0dcb3e898..2dc13c293 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -4,15 +4,15 @@
4// 4//
5// The following postfix snippets are available: 5// The following postfix snippets are available:
6// 6//
7// - `format` -> `format!(...)` 7// * `format` -> `format!(...)`
8// - `panic` -> `panic!(...)` 8// * `panic` -> `panic!(...)`
9// - `println` -> `println!(...)` 9// * `println` -> `println!(...)`
10// - `log`: 10// * `log`:
11// + `logd` -> `log::debug!(...)` 11// ** `logd` -> `log::debug!(...)`
12// + `logt` -> `log::trace!(...)` 12// ** `logt` -> `log::trace!(...)`
13// + `logi` -> `log::info!(...)` 13// ** `logi` -> `log::info!(...)`
14// + `logw` -> `log::warn!(...)` 14// ** `logw` -> `log::warn!(...)`
15// + `loge` -> `log::error!(...)` 15// ** `loge` -> `log::error!(...)`
16// 16//
17// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] 17// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
18 18
@@ -53,7 +53,7 @@ pub(crate) fn add_format_like_completions(
53 for (label, macro_name) in KINDS { 53 for (label, macro_name) in KINDS {
54 let snippet = parser.into_suggestion(macro_name); 54 let snippet = parser.into_suggestion(macro_name);
55 55
56 postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc); 56 postfix_snippet(ctx, cap, dot_receiver, label, macro_name, &snippet).add_to(acc);
57 } 57 }
58 } 58 }
59} 59}
@@ -91,7 +91,7 @@ enum State {
91impl FormatStrParser { 91impl FormatStrParser {
92 pub(crate) fn new(input: String) -> Self { 92 pub(crate) fn new(input: String) -> Self {
93 Self { 93 Self {
94 input: input, 94 input,
95 output: String::new(), 95 output: String::new(),
96 extracted_expressions: Vec::new(), 96 extracted_expressions: Vec::new(),
97 state: State::NotExpr, 97 state: State::NotExpr,
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index de58ce1cd..6083537b7 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,25 +7,28 @@ use syntax::AstNode;
7use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
8 8
9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if ctx.is_path_disallowed() || ctx.expects_item() { 10 if ctx.is_path_disallowed() {
11 return; 11 return;
12 } 12 }
13 let path = match &ctx.path_qual { 13 let path = match ctx.path_qual() {
14 Some(path) => path.clone(), 14 Some(path) => path,
15 None => return, 15 None => return,
16 }; 16 };
17 17
18 let resolution = match ctx.sema.resolve_path(&path) { 18 let resolution = match ctx.sema.resolve_path(path) {
19 Some(res) => res, 19 Some(res) => res,
20 None => return, 20 None => return,
21 }; 21 };
22 let context_module = ctx.scope.module(); 22 let context_module = ctx.scope.module();
23 if ctx.expects_assoc_item() { 23
24 if ctx.expects_item() || ctx.expects_assoc_item() {
24 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { 25 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
25 let module_scope = module.scope(ctx.db, context_module); 26 let module_scope = module.scope(ctx.db, context_module);
26 for (name, def) in module_scope { 27 for (name, def) in module_scope {
27 if let hir::ScopeDef::MacroDef(macro_def) = def { 28 if let hir::ScopeDef::MacroDef(macro_def) = def {
28 acc.add_macro(ctx, Some(name.clone()), macro_def); 29 if macro_def.is_fn_like() {
30 acc.add_macro(ctx, Some(name.clone()), macro_def);
31 }
29 } 32 }
30 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { 33 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
31 acc.add_resolution(ctx, name, &def); 34 acc.add_resolution(ctx, name, &def);
@@ -57,6 +60,13 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
57 } 60 }
58 } 61 }
59 62
63 if let hir::ScopeDef::MacroDef(macro_def) = def {
64 if !macro_def.is_fn_like() {
65 // Don't suggest attribute macros and derives.
66 continue;
67 }
68 }
69
60 acc.add_resolution(ctx, name, &def); 70 acc.add_resolution(ctx, name, &def);
61 } 71 }
62 } 72 }
@@ -198,6 +208,36 @@ mod tests {
198 } 208 }
199 209
200 #[test] 210 #[test]
211 fn dont_complete_values_in_type_pos() {
212 check(
213 r#"
214const FOO: () = ();
215static BAR: () = ();
216struct Baz;
217fn foo() {
218 let _: self::$0;
219}
220"#,
221 expect![[r#"
222 st Baz
223 "#]],
224 );
225 }
226
227 #[test]
228 fn dont_complete_enum_variants_in_type_pos() {
229 check(
230 r#"
231enum Foo { Bar }
232fn foo() {
233 let _: Foo::$0;
234}
235"#,
236 expect![[r#""#]],
237 );
238 }
239
240 #[test]
201 fn dont_complete_current_use_in_braces_with_glob() { 241 fn dont_complete_current_use_in_braces_with_glob() {
202 check( 242 check(
203 r#" 243 r#"
@@ -617,6 +657,32 @@ fn main() { let _ = crate::$0 }
617 } 657 }
618 658
619 #[test] 659 #[test]
660 fn does_not_complete_non_fn_macros() {
661 check(
662 r#"
663mod m {
664 #[rustc_builtin_macro]
665 pub macro Clone {}
666}
667
668fn f() {m::$0}
669"#,
670 expect![[r#""#]],
671 );
672 check(
673 r#"
674mod m {
675 #[rustc_builtin_macro]
676 pub macro bench {}
677}
678
679fn f() {m::$0}
680"#,
681 expect![[r#""#]],
682 );
683 }
684
685 #[test]
620 fn completes_in_assoc_item_list() { 686 fn completes_in_assoc_item_list() {
621 check( 687 check(
622 r#" 688 r#"
@@ -631,17 +697,17 @@ impl MyStruct {
631"#, 697"#,
632 expect![[r##" 698 expect![[r##"
633 md bar 699 md bar
634 ma foo! #[macro_export] macro_rules! foo 700 ma foo!(…) #[macro_export] macro_rules! foo
635 "##]], 701 "##]],
636 ); 702 );
637 } 703 }
638 704
639 #[test] 705 #[test]
640 #[ignore] // FIXME doesn't complete anything atm
641 fn completes_in_item_list() { 706 fn completes_in_item_list() {
642 check( 707 check(
643 r#" 708 r#"
644struct MyStruct {} 709struct MyStruct {}
710#[macro_export]
645macro_rules! foo {} 711macro_rules! foo {}
646mod bar {} 712mod bar {}
647 713
@@ -649,7 +715,7 @@ crate::$0
649"#, 715"#,
650 expect![[r#" 716 expect![[r#"
651 md bar 717 md bar
652 ma foo! macro_rules! foo 718 ma foo!(…) #[macro_export] macro_rules! foo
653 "#]], 719 "#]],
654 ) 720 )
655 } 721 }
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 6e6a6eb92..b9862de67 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -3,8 +3,8 @@
3use ide_db::helpers::SnippetCap; 3use ide_db::helpers::SnippetCap;
4 4
5use crate::{ 5use crate::{
6 item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 6 context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem,
7 Completions, 7 CompletionItemKind, CompletionKind, Completions,
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
@@ -14,15 +14,21 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
17 if !(ctx.is_trivial_path && ctx.function_def.is_some()) { 17 if ctx.function_def.is_none() {
18 return; 18 return;
19 } 19 }
20
21 let can_be_stmt = match ctx.path_context {
22 Some(PathCompletionContext { is_trivial_path: true, can_be_stmt, .. }) => can_be_stmt,
23 _ => return,
24 };
25
20 let cap = match ctx.config.snippet_cap { 26 let cap = match ctx.config.snippet_cap {
21 Some(it) => it, 27 Some(it) => it,
22 None => return, 28 None => return,
23 }; 29 };
24 30
25 if ctx.can_be_stmt { 31 if can_be_stmt {
26 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 32 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
27 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 33 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
28 } 34 }
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index 968c0254d..a60e5f43c 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -34,20 +34,13 @@
34use hir::{self, HasAttrs, HasSource}; 34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind}; 35use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit, Impl}, 37 ast::{self, edit},
38 display::function_declaration, 38 display::function_declaration,
39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T, 39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, T,
40}; 40};
41use text_edit::TextEdit; 41use text_edit::TextEdit;
42 42
43use crate::{ 43use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
44 CompletionContext,
45 CompletionItem,
46 CompletionItemKind,
47 CompletionKind,
48 Completions,
49 // display::function_declaration,
50};
51 44
52#[derive(Debug, PartialEq, Eq)] 45#[derive(Debug, PartialEq, Eq)]
53enum ImplCompletionKind { 46enum ImplCompletionKind {
@@ -58,7 +51,7 @@ enum ImplCompletionKind {
58} 51}
59 52
60pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 53pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
61 if let Some((kind, trigger, impl_def)) = completion_match(ctx) { 54 if let Some((kind, trigger, impl_def)) = completion_match(ctx.token.clone()) {
62 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { 55 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
63 hir::AssocItem::Function(fn_item) 56 hir::AssocItem::Function(fn_item)
64 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => 57 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
@@ -80,8 +73,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
80 } 73 }
81} 74}
82 75
83fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { 76fn completion_match(mut token: SyntaxToken) -> Option<(ImplCompletionKind, SyntaxNode, ast::Impl)> {
84 let mut token = ctx.token.clone();
85 // For keyword without name like `impl .. { fn $0 }`, the current position is inside 77 // For keyword without name like `impl .. { fn $0 }`, the current position is inside
86 // the whitespace token, which is outside `FN` syntax node. 78 // the whitespace token, which is outside `FN` syntax node.
87 // We need to follow the previous token in this case. 79 // We need to follow the previous token in this case.
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index bd955aa85..952f052a1 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -1,30 +1,32 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::{ast, AstNode};
4 5
5use crate::{CompletionContext, Completions}; 6use crate::{patterns::ImmediateLocation, CompletionContext, Completions};
6 7
7pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 8pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
8 if !ctx.is_trivial_path { 9 if ctx.is_path_disallowed() || !ctx.is_trivial_path() {
9 return;
10 }
11 if ctx.is_path_disallowed() || ctx.expects_item() {
12 return; 10 return;
13 } 11 }
14 12
15 if ctx.expects_assoc_item() { 13 if ctx.expects_item() || ctx.expects_assoc_item() {
16 ctx.scope.process_all_names(&mut |name, def| { 14 // only show macros in {Assoc}ItemList
17 if let ScopeDef::MacroDef(macro_def) = def { 15 ctx.scope.process_all_names(&mut |name, res| {
18 acc.add_macro(ctx, Some(name.clone()), macro_def); 16 if let hir::ScopeDef::MacroDef(mac) = res {
17 if mac.is_fn_like() {
18 acc.add_macro(ctx, Some(name.clone()), mac);
19 }
19 } 20 }
20 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { 21 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
21 acc.add_resolution(ctx, name, &def); 22 acc.add_resolution(ctx, name, &res);
22 } 23 }
23 }); 24 });
24 return; 25 return;
25 } 26 }
26 27
27 if ctx.expects_use_tree() { 28 if ctx.expects_use_tree() {
29 // only show modules in a fresh UseTree
28 cov_mark::hit!(only_completes_modules_in_import); 30 cov_mark::hit!(only_completes_modules_in_import);
29 ctx.scope.process_all_names(&mut |name, res| { 31 ctx.scope.process_all_names(&mut |name, res| {
30 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { 32 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
@@ -42,12 +44,32 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
42 }); 44 });
43 } 45 }
44 46
47 if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
48 if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) {
49 if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) =
50 ctx.sema.resolve_path(&path_seg.parent_path())
51 {
52 trait_.items(ctx.sema.db).into_iter().for_each(|it| {
53 if let hir::AssocItem::TypeAlias(alias) = it {
54 acc.add_type_alias_with_eq(ctx, alias)
55 }
56 });
57 }
58 }
59 }
60
45 ctx.scope.process_all_names(&mut |name, res| { 61 ctx.scope.process_all_names(&mut |name, res| {
46 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 62 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
47 cov_mark::hit!(skip_lifetime_completion); 63 cov_mark::hit!(skip_lifetime_completion);
48 return; 64 return;
49 } 65 }
50 acc.add_resolution(ctx, name, &res); 66 let add_resolution = match res {
67 ScopeDef::MacroDef(mac) => mac.is_fn_like(),
68 _ => true,
69 };
70 if add_resolution {
71 acc.add_resolution(ctx, name, &res);
72 }
51 }); 73 });
52} 74}
53 75
@@ -70,6 +92,28 @@ mod tests {
70 } 92 }
71 93
72 #[test] 94 #[test]
95 fn dont_complete_values_in_type_pos() {
96 check(
97 r#"
98const FOO: () = ();
99static BAR: () = ();
100enum Foo {
101 Bar
102}
103struct Baz;
104fn foo() {
105 let local = ();
106 let _: $0;
107}
108"#,
109 expect![[r#"
110 en Foo
111 st Baz
112 "#]],
113 );
114 }
115
116 #[test]
73 fn only_completes_modules_in_import() { 117 fn only_completes_modules_in_import() {
74 cov_mark::check!(only_completes_modules_in_import); 118 cov_mark::check!(only_completes_modules_in_import);
75 check( 119 check(
@@ -340,7 +384,6 @@ fn x() -> $0
340"#, 384"#,
341 expect![[r#" 385 expect![[r#"
342 st Foo 386 st Foo
343 fn x() fn()
344 "#]], 387 "#]],
345 ); 388 );
346 } 389 }
@@ -392,7 +435,6 @@ pub mod prelude {
392} 435}
393"#, 436"#,
394 expect![[r#" 437 expect![[r#"
395 fn foo() fn()
396 md std 438 md std
397 st Option 439 st Option
398 "#]], 440 "#]],
@@ -428,6 +470,44 @@ mod macros {
428 } 470 }
429 471
430 #[test] 472 #[test]
473 fn does_not_complete_non_fn_macros() {
474 check(
475 r#"
476#[rustc_builtin_macro]
477pub macro Clone {}
478
479fn f() {$0}
480"#,
481 expect![[r#"
482 fn f() fn()
483 "#]],
484 );
485 check(
486 r#"
487#[rustc_builtin_macro]
488pub macro Clone {}
489
490struct S;
491impl S {
492 $0
493}
494"#,
495 expect![[r#""#]],
496 );
497 check(
498 r#"
499#[rustc_builtin_macro]
500pub macro bench {}
501
502fn f() {$0}
503"#,
504 expect![[r#"
505 fn f() fn()
506 "#]],
507 );
508 }
509
510 #[test]
431 fn completes_std_prelude_if_core_is_defined() { 511 fn completes_std_prelude_if_core_is_defined() {
432 check( 512 check(
433 r#" 513 r#"
@@ -449,7 +529,6 @@ pub mod prelude {
449} 529}
450"#, 530"#,
451 expect![[r#" 531 expect![[r#"
452 fn foo() fn()
453 md std 532 md std
454 md core 533 md core
455 st String 534 st String
@@ -510,7 +589,6 @@ macro_rules! foo { () => {} }
510fn main() { let x: $0 } 589fn main() { let x: $0 }
511"#, 590"#,
512 expect![[r#" 591 expect![[r#"
513 fn main() fn()
514 ma foo!(…) macro_rules! foo 592 ma foo!(…) macro_rules! foo
515 "#]], 593 "#]],
516 ); 594 );
@@ -693,12 +771,11 @@ impl MyStruct {
693"#, 771"#,
694 expect![[r#" 772 expect![[r#"
695 md bar 773 md bar
696 ma foo! macro_rules! foo 774 ma foo!(…) macro_rules! foo
697 "#]], 775 "#]],
698 ) 776 )
699 } 777 }
700 778
701 // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
702 #[test] 779 #[test]
703 fn completes_in_item_list() { 780 fn completes_in_item_list() {
704 check( 781 check(
@@ -715,4 +792,21 @@ $0
715 "#]], 792 "#]],
716 ) 793 )
717 } 794 }
795
796 #[test]
797 fn completes_assoc_types_in_dynimpl_trait() {
798 check(
799 r#"
800trait Foo {
801 type Bar;
802}
803
804fn foo(_: impl Foo<B$0>) {}
805"#,
806 expect![[r#"
807 ta Bar = type Bar;
808 tt Foo
809 "#]],
810 );
811 }
718} 812}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 6f685c02f..4c3929a26 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -29,6 +29,34 @@ pub(crate) enum PatternRefutability {
29 Irrefutable, 29 Irrefutable,
30} 30}
31 31
32#[derive(Debug)]
33pub(super) enum PathKind {
34 Expr,
35 Type,
36}
37
38#[derive(Debug)]
39pub(crate) struct PathCompletionContext {
40 /// If this is a call with () already there
41 call_kind: Option<CallKind>,
42 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
43 pub(super) is_trivial_path: bool,
44 /// If not a trivial path, the prefix (qualifier).
45 pub(super) qualifier: Option<ast::Path>,
46 pub(super) kind: Option<PathKind>,
47 /// Whether the path segment has type args or not.
48 pub(super) has_type_args: bool,
49 /// `true` if we are a statement or a last expr in the block.
50 pub(super) can_be_stmt: bool,
51 pub(super) in_loop_body: bool,
52}
53
54#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55pub(crate) enum CallKind {
56 Pat,
57 Mac,
58 Expr,
59}
32/// `CompletionContext` is created early during completion to figure out, where 60/// `CompletionContext` is created early during completion to figure out, where
33/// exactly is the cursor, syntax-wise. 61/// exactly is the cursor, syntax-wise.
34#[derive(Debug)] 62#[derive(Debug)]
@@ -45,14 +73,13 @@ pub(crate) struct CompletionContext<'a> {
45 pub(super) krate: Option<hir::Crate>, 73 pub(super) krate: Option<hir::Crate>,
46 pub(super) expected_name: Option<NameOrNameRef>, 74 pub(super) expected_name: Option<NameOrNameRef>,
47 pub(super) expected_type: Option<Type>, 75 pub(super) expected_type: Option<Type>,
48 pub(super) name_ref_syntax: Option<ast::NameRef>,
49
50 pub(super) use_item_syntax: Option<ast::Use>,
51 76
52 /// The parent function of the cursor position if it exists. 77 /// The parent function of the cursor position if it exists.
53 pub(super) function_def: Option<ast::Fn>, 78 pub(super) function_def: Option<ast::Fn>,
54 /// The parent impl of the cursor position if it exists. 79 /// The parent impl of the cursor position if it exists.
55 pub(super) impl_def: Option<ast::Impl>, 80 pub(super) impl_def: Option<ast::Impl>,
81 pub(super) name_ref_syntax: Option<ast::NameRef>,
82 pub(super) use_item_syntax: Option<ast::Use>,
56 83
57 // potentially set if we are completing a lifetime 84 // potentially set if we are completing a lifetime
58 pub(super) lifetime_syntax: Option<ast::Lifetime>, 85 pub(super) lifetime_syntax: Option<ast::Lifetime>,
@@ -67,29 +94,12 @@ pub(crate) struct CompletionContext<'a> {
67 pub(super) completion_location: Option<ImmediateLocation>, 94 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>, 95 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>, 96 pub(super) attribute_under_caret: Option<ast::Attr>,
97 pub(super) previous_token: Option<SyntaxToken>,
70 98
71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 99 pub(super) path_context: Option<PathCompletionContext>,
72 pub(super) active_parameter: Option<ActiveParameter>, 100 pub(super) active_parameter: Option<ActiveParameter>,
73 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
74 pub(super) is_trivial_path: bool,
75 /// If not a trivial path, the prefix (qualifier).
76 pub(super) path_qual: Option<ast::Path>,
77 /// `true` if we are a statement or a last expr in the block.
78 pub(super) can_be_stmt: bool,
79 /// `true` if we expect an expression at the cursor position.
80 pub(super) is_expr: bool,
81 /// If this is a call (method or function) in particular, i.e. the () are already there.
82 pub(super) is_call: bool,
83 /// Like `is_call`, but for tuple patterns.
84 pub(super) is_pattern_call: bool,
85 /// If this is a macro call, i.e. the () are already there.
86 pub(super) is_macro_call: bool,
87 pub(super) is_path_type: bool,
88 pub(super) has_type_args: bool,
89 pub(super) locals: Vec<(String, Local)>, 101 pub(super) locals: Vec<(String, Local)>,
90 102
91 pub(super) previous_token: Option<SyntaxToken>,
92 pub(super) in_loop_body: bool,
93 pub(super) incomplete_let: bool, 103 pub(super) incomplete_let: bool,
94 104
95 no_completion_required: bool, 105 no_completion_required: bool,
@@ -136,36 +146,27 @@ impl<'a> CompletionContext<'a> {
136 original_token, 146 original_token,
137 token, 147 token,
138 krate, 148 krate,
139 lifetime_allowed: false,
140 expected_name: None, 149 expected_name: None,
141 expected_type: None, 150 expected_type: None,
151 function_def: None,
152 impl_def: None,
142 name_ref_syntax: None, 153 name_ref_syntax: None,
154 use_item_syntax: None,
143 lifetime_syntax: None, 155 lifetime_syntax: None,
144 lifetime_param_syntax: None, 156 lifetime_param_syntax: None,
145 function_def: None, 157 lifetime_allowed: false,
146 use_item_syntax: None,
147 impl_def: None,
148 active_parameter: ActiveParameter::at(db, position),
149 is_label_ref: false, 158 is_label_ref: false,
150 is_param: false,
151 is_pat_or_const: None, 159 is_pat_or_const: None,
152 is_trivial_path: false, 160 is_param: false,
153 path_qual: None,
154 can_be_stmt: false,
155 is_expr: false,
156 is_call: false,
157 is_pattern_call: false,
158 is_macro_call: false,
159 is_path_type: false,
160 has_type_args: false,
161 previous_token: None,
162 in_loop_body: false,
163 completion_location: None, 161 completion_location: None,
164 prev_sibling: None, 162 prev_sibling: None,
165 no_completion_required: false,
166 incomplete_let: false,
167 attribute_under_caret: None, 163 attribute_under_caret: None,
164 previous_token: None,
165 path_context: None,
166 active_parameter: ActiveParameter::at(db, position),
168 locals, 167 locals,
168 incomplete_let: false,
169 no_completion_required: false,
169 }; 170 };
170 171
171 let mut original_file = original_file.syntax().clone(); 172 let mut original_file = original_file.syntax().clone();
@@ -250,14 +251,14 @@ impl<'a> CompletionContext<'a> {
250 pub(crate) fn has_dot_receiver(&self) -> bool { 251 pub(crate) fn has_dot_receiver(&self) -> bool {
251 matches!( 252 matches!(
252 &self.completion_location, 253 &self.completion_location,
253 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) 254 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. })
254 if receiver.is_some() 255 if receiver.is_some()
255 ) 256 )
256 } 257 }
257 258
258 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { 259 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259 match &self.completion_location { 260 match &self.completion_location {
260 Some(ImmediateLocation::MethodCall { receiver }) 261 Some(ImmediateLocation::MethodCall { receiver, .. })
261 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), 262 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262 _ => None, 263 _ => None,
263 } 264 }
@@ -275,11 +276,6 @@ impl<'a> CompletionContext<'a> {
275 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 276 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
276 } 277 }
277 278
278 // fn expects_value(&self) -> bool {
279 pub(crate) fn expects_expression(&self) -> bool {
280 self.is_expr
281 }
282
283 pub(crate) fn has_block_expr_parent(&self) -> bool { 279 pub(crate) fn has_block_expr_parent(&self) -> bool {
284 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 280 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
285 } 281 }
@@ -316,6 +312,26 @@ impl<'a> CompletionContext<'a> {
316 ) || self.attribute_under_caret.is_some() 312 ) || self.attribute_under_caret.is_some()
317 } 313 }
318 314
315 pub(crate) fn expects_expression(&self) -> bool {
316 matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Expr), .. }))
317 }
318
319 pub(crate) fn expects_type(&self) -> bool {
320 matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. }))
321 }
322
323 pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
324 self.path_context.as_ref().and_then(|it| it.call_kind)
325 }
326
327 pub(crate) fn is_trivial_path(&self) -> bool {
328 matches!(self.path_context, Some(PathCompletionContext { is_trivial_path: true, .. }))
329 }
330
331 pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
332 self.path_context.as_ref().and_then(|it| it.qualifier.as_ref())
333 }
334
319 fn fill_impl_def(&mut self) { 335 fn fill_impl_def(&mut self) {
320 self.impl_def = self 336 self.impl_def = self
321 .sema 337 .sema
@@ -364,7 +380,7 @@ impl<'a> CompletionContext<'a> {
364 (|| { 380 (|| {
365 let expr_field = self.token.prev_sibling_or_token()? 381 let expr_field = self.token.prev_sibling_or_token()?
366 .into_node() 382 .into_node()
367 .and_then(|node| ast::RecordExprField::cast(node))?; 383 .and_then(ast::RecordExprField::cast)?;
368 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; 384 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
369 Some(( 385 Some((
370 Some(ty), 386 Some(ty),
@@ -441,7 +457,6 @@ impl<'a> CompletionContext<'a> {
441 let for_is_prev2 = for_is_prev2(syntax_element.clone()); 457 let for_is_prev2 = for_is_prev2(syntax_element.clone());
442 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 458 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
443 }; 459 };
444 self.in_loop_body = is_in_loop_body(syntax_element.clone());
445 460
446 self.incomplete_let = 461 self.incomplete_let =
447 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { 462 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
@@ -452,7 +467,7 @@ impl<'a> CompletionContext<'a> {
452 self.expected_type = expected_type; 467 self.expected_type = expected_type;
453 self.expected_name = expected_name; 468 self.expected_name = expected_name;
454 469
455 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { 470 let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
456 Some(it) => it, 471 Some(it) => it,
457 None => return, 472 None => return,
458 }; 473 };
@@ -549,10 +564,6 @@ impl<'a> CompletionContext<'a> {
549 self.name_ref_syntax = 564 self.name_ref_syntax =
550 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 565 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
551 566
552 if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
553 return;
554 }
555
556 self.use_item_syntax = 567 self.use_item_syntax =
557 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); 568 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
558 569
@@ -567,23 +578,43 @@ impl<'a> CompletionContext<'a> {
567 None => return, 578 None => return,
568 }; 579 };
569 580
570 if let Some(segment) = ast::PathSegment::cast(parent.clone()) { 581 if let Some(segment) = ast::PathSegment::cast(parent) {
582 let path_ctx = self.path_context.get_or_insert(PathCompletionContext {
583 call_kind: None,
584 is_trivial_path: false,
585 qualifier: None,
586 has_type_args: false,
587 can_be_stmt: false,
588 in_loop_body: false,
589 kind: None,
590 });
591 path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
571 let path = segment.parent_path(); 592 let path = segment.parent_path();
572 self.is_call = path
573 .syntax()
574 .parent()
575 .and_then(ast::PathExpr::cast)
576 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
577 .is_some();
578 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
579 self.is_pattern_call =
580 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
581 593
582 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 594 if let Some(p) = path.syntax().parent() {
583 self.has_type_args = segment.generic_arg_list().is_some(); 595 path_ctx.call_kind = match_ast! {
596 match p {
597 ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
598 ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)),
599 ast::TupleStructPat(_it) => Some(CallKind::Pat),
600 _ => None
601 }
602 };
603 }
604
605 if let Some(parent) = path.syntax().parent() {
606 path_ctx.kind = match_ast! {
607 match parent {
608 ast::PathType(_it) => Some(PathKind::Type),
609 ast::PathExpr(_it) => Some(PathKind::Expr),
610 _ => None,
611 }
612 };
613 }
614 path_ctx.has_type_args = segment.generic_arg_list().is_some();
584 615
585 if let Some(path) = path_or_use_tree_qualifier(&path) { 616 if let Some(path) = path_or_use_tree_qualifier(&path) {
586 self.path_qual = path 617 path_ctx.qualifier = path
587 .segment() 618 .segment()
588 .and_then(|it| { 619 .and_then(|it| {
589 find_node_with_range::<ast::PathSegment>( 620 find_node_with_range::<ast::PathSegment>(
@@ -601,11 +632,11 @@ impl<'a> CompletionContext<'a> {
601 } 632 }
602 } 633 }
603 634
604 self.is_trivial_path = true; 635 path_ctx.is_trivial_path = true;
605 636
606 // Find either enclosing expr statement (thing with `;`) or a 637 // Find either enclosing expr statement (thing with `;`) or a
607 // block. If block, check that we are the last expr. 638 // block. If block, check that we are the last expr.
608 self.can_be_stmt = name_ref 639 path_ctx.can_be_stmt = name_ref
609 .syntax() 640 .syntax()
610 .ancestors() 641 .ancestors()
611 .find_map(|node| { 642 .find_map(|node| {
@@ -621,10 +652,7 @@ impl<'a> CompletionContext<'a> {
621 None 652 None
622 }) 653 })
623 .unwrap_or(false); 654 .unwrap_or(false);
624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625 } 655 }
626 self.is_call |=
627 matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628 } 656 }
629} 657}
630 658
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 1152a9850..18983aa01 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -4,7 +4,6 @@ mod config;
4mod item; 4mod item;
5mod context; 5mod context;
6mod patterns; 6mod patterns;
7mod generated_lint_completions;
8#[cfg(test)] 7#[cfg(test)]
9mod test_utils; 8mod test_utils;
10mod render; 9mod render;
@@ -159,7 +158,6 @@ pub fn completions(
159 completions::record::complete_record(&mut acc, &ctx); 158 completions::record::complete_record(&mut acc, &ctx);
160 completions::pattern::complete_pattern(&mut acc, &ctx); 159 completions::pattern::complete_pattern(&mut acc, &ctx);
161 completions::postfix::complete_postfix(&mut acc, &ctx); 160 completions::postfix::complete_postfix(&mut acc, &ctx);
162 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
163 completions::trait_impl::complete_trait_impl(&mut acc, &ctx); 161 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
164 completions::mod_::complete_mod(&mut acc, &ctx); 162 completions::mod_::complete_mod(&mut acc, &ctx);
165 completions::flyimport::import_on_the_fly(&mut acc, &ctx); 163 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 080898aef..72e67e3c4 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -4,7 +4,7 @@ use hir::Semantics;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use syntax::{ 5use syntax::{
6 algo::non_trivia_sibling, 6 algo::non_trivia_sibling,
7 ast::{self, LoopBodyOwner}, 7 ast::{self, ArgListOwner, LoopBodyOwner},
8 match_ast, AstNode, Direction, SyntaxElement, 8 match_ast, AstNode, Direction, SyntaxElement,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, T, 10 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
@@ -39,6 +39,7 @@ pub(crate) enum ImmediateLocation {
39 // Original file ast node 39 // Original file ast node
40 MethodCall { 40 MethodCall {
41 receiver: Option<ast::Expr>, 41 receiver: Option<ast::Expr>,
42 has_parens: bool,
42 }, 43 },
43 // Original file ast node 44 // Original file ast node
44 FieldAccess { 45 FieldAccess {
@@ -46,6 +47,9 @@ pub(crate) enum ImmediateLocation {
46 receiver_is_ambiguous_float_literal: bool, 47 receiver_is_ambiguous_float_literal: bool,
47 }, 48 },
48 // Original file ast node 49 // Original file ast node
50 // Only set from a type arg
51 GenericArgList(ast::GenericArgList),
52 // Original file ast node
49 /// The record expr of the field name we are completing 53 /// The record expr of the field name we are completing
50 RecordExpr(ast::RecordExpr), 54 RecordExpr(ast::RecordExpr),
51 // Original file ast node 55 // Original file ast node
@@ -111,12 +115,12 @@ pub(crate) fn determine_location(
111) -> Option<ImmediateLocation> { 115) -> Option<ImmediateLocation> {
112 let node = match name_like { 116 let node = match name_like {
113 ast::NameLike::NameRef(name_ref) => { 117 ast::NameLike::NameRef(name_ref) => {
114 if ast::RecordExprField::for_field_name(&name_ref).is_some() { 118 if ast::RecordExprField::for_field_name(name_ref).is_some() {
115 return sema 119 return sema
116 .find_node_at_offset_with_macros(original_file, offset) 120 .find_node_at_offset_with_macros(original_file, offset)
117 .map(ImmediateLocation::RecordExpr); 121 .map(ImmediateLocation::RecordExpr);
118 } 122 }
119 if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { 123 if ast::RecordPatField::for_field_name_ref(name_ref).is_some() {
120 return sema 124 return sema
121 .find_node_at_offset_with_macros(original_file, offset) 125 .find_node_at_offset_with_macros(original_file, offset)
122 .map(ImmediateLocation::RecordPat); 126 .map(ImmediateLocation::RecordPat);
@@ -124,7 +128,7 @@ pub(crate) fn determine_location(
124 maximize_name_ref(name_ref) 128 maximize_name_ref(name_ref)
125 } 129 }
126 ast::NameLike::Name(name) => { 130 ast::NameLike::Name(name) => {
127 if ast::RecordPatField::for_field_name(&name).is_some() { 131 if ast::RecordPatField::for_field_name(name).is_some() {
128 return sema 132 return sema
129 .find_node_at_offset_with_macros(original_file, offset) 133 .find_node_at_offset_with_macros(original_file, offset)
130 .map(ImmediateLocation::RecordPat); 134 .map(ImmediateLocation::RecordPat);
@@ -158,7 +162,6 @@ pub(crate) fn determine_location(
158 } 162 }
159 } 163 }
160 }; 164 };
161
162 let res = match_ast! { 165 let res = match_ast! {
163 match parent { 166 match parent {
164 ast::IdentPat(_it) => ImmediateLocation::IdentPat, 167 ast::IdentPat(_it) => ImmediateLocation::IdentPat,
@@ -173,6 +176,9 @@ pub(crate) fn determine_location(
173 Some(TRAIT) => ImmediateLocation::Trait, 176 Some(TRAIT) => ImmediateLocation::Trait,
174 _ => return None, 177 _ => return None,
175 }, 178 },
179 ast::GenericArgList(_it) => sema
180 .find_node_at_offset_with_macros(original_file, offset)
181 .map(ImmediateLocation::GenericArgList)?,
176 ast::Module(it) => { 182 ast::Module(it) => {
177 if it.item_list().is_none() { 183 if it.item_list().is_none() {
178 ImmediateLocation::ModDeclaration(it) 184 ImmediateLocation::ModDeclaration(it)
@@ -204,6 +210,7 @@ pub(crate) fn determine_location(
204 .receiver() 210 .receiver()
205 .map(|e| e.syntax().text_range()) 211 .map(|e| e.syntax().text_range())
206 .and_then(|r| find_node_with_range(original_file, r)), 212 .and_then(|r| find_node_with_range(original_file, r)),
213 has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
207 }, 214 },
208 _ => return None, 215 _ => return None,
209 } 216 }
@@ -252,7 +259,7 @@ fn test_inside_impl_trait_block() {
252} 259}
253 260
254pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { 261pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
255 element.into_token().and_then(|it| previous_non_trivia_token(it)) 262 element.into_token().and_then(previous_non_trivia_token)
256} 263}
257 264
258/// Check if the token previous to the previous one is `for`. 265/// Check if the token previous to the previous one is `for`.
@@ -260,8 +267,8 @@ pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
260pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { 267pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
261 element 268 element
262 .into_token() 269 .into_token()
263 .and_then(|it| previous_non_trivia_token(it)) 270 .and_then(previous_non_trivia_token)
264 .and_then(|it| previous_non_trivia_token(it)) 271 .and_then(previous_non_trivia_token)
265 .filter(|it| it.kind() == T![for]) 272 .filter(|it| it.kind() == T![for])
266 .is_some() 273 .is_some()
267} 274}
@@ -270,9 +277,8 @@ fn test_for_is_prev2() {
270 check_pattern_is_applicable(r"for i i$0", for_is_prev2); 277 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
271} 278}
272 279
273pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 280pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool {
274 element 281 node.ancestors()
275 .ancestors()
276 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR) 282 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR)
277 .find_map(|it| { 283 .find_map(|it| {
278 let loop_body = match_ast! { 284 let loop_body = match_ast! {
@@ -283,7 +289,7 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
283 _ => None, 289 _ => None,
284 } 290 }
285 }; 291 };
286 loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range())) 292 loop_body.filter(|it| it.syntax().text_range().contains_range(node.text_range()))
287 }) 293 })
288 .is_some() 294 .is_some()
289} 295}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index a49a60711..d8ca18c73 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -18,54 +18,56 @@ use ide_db::{
18use syntax::TextRange; 18use syntax::TextRange;
19 19
20use crate::{ 20use crate::{
21 context::{PathCompletionContext, PathKind},
21 item::{CompletionRelevanceTypeMatch, ImportEdit}, 22 item::{CompletionRelevanceTypeMatch, ImportEdit},
22 render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, 23 render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
23 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
24}; 25};
25 26
26pub(crate) fn render_field<'a>( 27pub(crate) fn render_field(
27 ctx: RenderContext<'a>, 28 ctx: RenderContext<'_>,
28 receiver: Option<hir::Name>, 29 receiver: Option<hir::Name>,
29 field: hir::Field, 30 field: hir::Field,
30 ty: &hir::Type, 31 ty: &hir::Type,
31) -> CompletionItem { 32) -> CompletionItem {
32 Render::new(ctx).render_field(receiver, field, ty) 33 render_field_(ctx, receiver, field, ty)
33} 34}
34 35
35pub(crate) fn render_tuple_field<'a>( 36pub(crate) fn render_tuple_field(
36 ctx: RenderContext<'a>, 37 ctx: RenderContext<'_>,
37 receiver: Option<hir::Name>, 38 receiver: Option<hir::Name>,
38 field: usize, 39 field: usize,
39 ty: &hir::Type, 40 ty: &hir::Type,
40) -> CompletionItem { 41) -> CompletionItem {
41 Render::new(ctx).render_tuple_field(receiver, field, ty) 42 render_tuple_field_(ctx, receiver, field, ty)
42} 43}
43 44
44pub(crate) fn render_resolution<'a>( 45pub(crate) fn render_resolution(
45 ctx: RenderContext<'a>, 46 ctx: RenderContext<'_>,
46 local_name: hir::Name, 47 local_name: hir::Name,
47 resolution: &hir::ScopeDef, 48 resolution: &hir::ScopeDef,
48) -> Option<CompletionItem> { 49) -> Option<CompletionItem> {
49 Render::new(ctx).render_resolution(local_name, None, resolution) 50 render_resolution_(ctx, local_name, None, resolution)
50} 51}
51 52
52pub(crate) fn render_resolution_with_import<'a>( 53pub(crate) fn render_resolution_with_import(
53 ctx: RenderContext<'a>, 54 ctx: RenderContext<'_>,
54 import_edit: ImportEdit, 55 import_edit: ImportEdit,
55) -> Option<CompletionItem> { 56) -> Option<CompletionItem> {
56 let resolution = hir::ScopeDef::from(import_edit.import.original_item); 57 let resolution = hir::ScopeDef::from(import_edit.import.original_item);
58 if ctx.completion.expects_type() && resolution.is_value_def() {
59 return None;
60 }
57 let local_name = match resolution { 61 let local_name = match resolution {
58 hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), 62 hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
59 hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, 63 hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
60 hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), 64 hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
61 _ => item_name(ctx.db(), import_edit.import.original_item)?, 65 _ => item_name(ctx.db(), import_edit.import.original_item)?,
62 }; 66 };
63 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map( 67 render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| {
64 |mut item| { 68 item.completion_kind = CompletionKind::Magic;
65 item.completion_kind = CompletionKind::Magic; 69 item
66 item 70 })
67 },
68 )
69} 71}
70 72
71/// Interface for data and methods required for items rendering. 73/// Interface for data and methods required for items rendering.
@@ -84,7 +86,7 @@ impl<'a> RenderContext<'a> {
84 } 86 }
85 87
86 fn db(&self) -> &'a RootDatabase { 88 fn db(&self) -> &'a RootDatabase {
87 &self.completion.db 89 self.completion.db
88 } 90 }
89 91
90 fn source_range(&self) -> TextRange { 92 fn source_range(&self) -> TextRange {
@@ -109,7 +111,10 @@ impl<'a> RenderContext<'a> {
109 hir::AssocItem::TypeAlias(it) => self.is_deprecated(it), 111 hir::AssocItem::TypeAlias(it) => self.is_deprecated(it),
110 }; 112 };
111 is_assoc_deprecated 113 is_assoc_deprecated
112 || assoc.containing_trait(db).map(|trait_| self.is_deprecated(trait_)).unwrap_or(false) 114 || assoc
115 .containing_trait_or_trait_impl(db)
116 .map(|trait_| self.is_deprecated(trait_))
117 .unwrap_or(false)
113 } 118 }
114 119
115 fn docs(&self, node: impl HasAttrs) -> Option<hir::Documentation> { 120 fn docs(&self, node: impl HasAttrs) -> Option<hir::Documentation> {
@@ -117,215 +122,191 @@ impl<'a> RenderContext<'a> {
117 } 122 }
118} 123}
119 124
120/// Generic renderer for completion items. 125fn render_field_(
121#[derive(Debug)] 126 ctx: RenderContext<'_>,
122struct Render<'a> { 127 receiver: Option<hir::Name>,
123 ctx: RenderContext<'a>, 128 field: hir::Field,
124} 129 ty: &hir::Type,
125 130) -> CompletionItem {
126impl<'a> Render<'a> { 131 let is_deprecated = ctx.is_deprecated(field);
127 fn new(ctx: RenderContext<'a>) -> Render<'a> { 132 let name = field.name(ctx.db()).to_string();
128 Render { ctx } 133 let mut item = CompletionItem::new(
134 CompletionKind::Reference,
135 ctx.source_range(),
136 receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)),
137 );
138
139 item.set_relevance(CompletionRelevance {
140 type_match: compute_type_match(ctx.completion, ty),
141 exact_name_match: compute_exact_name_match(ctx.completion, &name),
142 ..CompletionRelevance::default()
143 });
144 item.kind(SymbolKind::Field)
145 .detail(ty.display(ctx.db()).to_string())
146 .set_documentation(field.docs(ctx.db()))
147 .set_deprecated(is_deprecated)
148 .lookup_by(name);
149
150 if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) {
151 // FIXME
152 // For now we don't properly calculate the edits for ref match
153 // completions on struct fields, so we've disabled them. See #8058.
129 } 154 }
130 155
131 fn render_field( 156 item.build()
132 &self, 157}
133 receiver: Option<hir::Name>,
134 field: hir::Field,
135 ty: &hir::Type,
136 ) -> CompletionItem {
137 let is_deprecated = self.ctx.is_deprecated(field);
138 let name = field.name(self.ctx.db()).to_string();
139 let mut item = CompletionItem::new(
140 CompletionKind::Reference,
141 self.ctx.source_range(),
142 receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)),
143 );
144 item.kind(SymbolKind::Field)
145 .detail(ty.display(self.ctx.db()).to_string())
146 .set_documentation(field.docs(self.ctx.db()))
147 .set_deprecated(is_deprecated);
148
149 item.set_relevance(CompletionRelevance {
150 type_match: compute_type_match(self.ctx.completion, ty),
151 exact_name_match: compute_exact_name_match(self.ctx.completion, &name),
152 ..CompletionRelevance::default()
153 });
154
155 if let Some(_ref_match) = compute_ref_match(self.ctx.completion, ty) {
156 // FIXME
157 // For now we don't properly calculate the edits for ref match
158 // completions on struct fields, so we've disabled them. See #8058.
159 }
160 158
161 item.build() 159fn render_tuple_field_(
162 } 160 ctx: RenderContext<'_>,
161 receiver: Option<hir::Name>,
162 field: usize,
163 ty: &hir::Type,
164) -> CompletionItem {
165 let mut item = CompletionItem::new(
166 CompletionKind::Reference,
167 ctx.source_range(),
168 receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)),
169 );
163 170
164 fn render_tuple_field( 171 item.kind(SymbolKind::Field)
165 &self, 172 .detail(ty.display(ctx.db()).to_string())
166 receiver: Option<hir::Name>, 173 .lookup_by(field.to_string());
167 field: usize,
168 ty: &hir::Type,
169 ) -> CompletionItem {
170 let mut item = CompletionItem::new(
171 CompletionKind::Reference,
172 self.ctx.source_range(),
173 receiver
174 .map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)),
175 );
176 174
177 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); 175 item.build()
176}
178 177
179 item.build() 178fn render_resolution_(
180 } 179 ctx: RenderContext<'_>,
180 local_name: hir::Name,
181 import_to_add: Option<ImportEdit>,
182 resolution: &hir::ScopeDef,
183) -> Option<CompletionItem> {
184 let _p = profile::span("render_resolution");
185 use hir::ModuleDef::*;
181 186
182 fn render_resolution( 187 let completion_kind = match resolution {
183 self, 188 hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
184 local_name: hir::Name, 189 _ => CompletionKind::Reference,
185 import_to_add: Option<ImportEdit>, 190 };
186 resolution: &hir::ScopeDef,
187 ) -> Option<CompletionItem> {
188 let _p = profile::span("render_resolution");
189 use hir::ModuleDef::*;
190
191 let completion_kind = match resolution {
192 hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
193 _ => CompletionKind::Reference,
194 };
195 191
196 let kind = match resolution { 192 let kind = match resolution {
197 hir::ScopeDef::ModuleDef(Function(func)) => { 193 hir::ScopeDef::ModuleDef(Function(func)) => {
198 return render_fn(self.ctx, import_to_add, Some(local_name), *func); 194 return render_fn(ctx, import_to_add, Some(local_name), *func);
199 } 195 }
200 hir::ScopeDef::ModuleDef(Variant(_)) 196 hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.is_pat_or_const.is_some() => {
201 if self.ctx.completion.is_pat_or_const.is_some() => 197 CompletionItemKind::SymbolKind(SymbolKind::Variant)
202 { 198 }
203 CompletionItemKind::SymbolKind(SymbolKind::Variant) 199 hir::ScopeDef::ModuleDef(Variant(var)) => {
204 } 200 let item = render_variant(ctx, import_to_add, Some(local_name), *var, None);
205 hir::ScopeDef::ModuleDef(Variant(var)) => { 201 return Some(item);
206 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); 202 }
207 return Some(item); 203 hir::ScopeDef::MacroDef(mac) => {
208 } 204 let item = render_macro(ctx, import_to_add, local_name, *mac);
209 hir::ScopeDef::MacroDef(mac) => { 205 return item;
210 let item = render_macro(self.ctx, import_to_add, local_name, *mac); 206 }
211 return item;
212 }
213 207
214 hir::ScopeDef::ModuleDef(Module(..)) => { 208 hir::ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
215 CompletionItemKind::SymbolKind(SymbolKind::Module) 209 hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
216 } 210 hir::Adt::Struct(_) => SymbolKind::Struct,
217 hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { 211 hir::Adt::Union(_) => SymbolKind::Union,
218 hir::Adt::Struct(_) => SymbolKind::Struct, 212 hir::Adt::Enum(_) => SymbolKind::Enum,
219 hir::Adt::Union(_) => SymbolKind::Union, 213 }),
220 hir::Adt::Enum(_) => SymbolKind::Enum, 214 hir::ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
221 }), 215 hir::ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
222 hir::ScopeDef::ModuleDef(Const(..)) => { 216 hir::ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
223 CompletionItemKind::SymbolKind(SymbolKind::Const) 217 hir::ScopeDef::ModuleDef(TypeAlias(..)) => {
224 } 218 CompletionItemKind::SymbolKind(SymbolKind::TypeAlias)
225 hir::ScopeDef::ModuleDef(Static(..)) => { 219 }
226 CompletionItemKind::SymbolKind(SymbolKind::Static) 220 hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
227 } 221 hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
228 hir::ScopeDef::ModuleDef(Trait(..)) => { 222 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,
229 CompletionItemKind::SymbolKind(SymbolKind::Trait) 223 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,
230 } 224 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
231 hir::ScopeDef::ModuleDef(TypeAlias(..)) => { 225 }),
232 CompletionItemKind::SymbolKind(SymbolKind::TypeAlias) 226 hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
233 } 227 hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
234 hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, 228 hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => {
235 hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { 229 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
236 hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, 230 }
237 hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, 231 hir::ScopeDef::Unknown => {
238 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, 232 let mut item = CompletionItem::new(
239 }), 233 CompletionKind::Reference,
240 hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), 234 ctx.source_range(),
241 hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), 235 local_name.to_string(),
242 hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => { 236 );
243 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 237 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
244 } 238 return Some(item.build());
245 hir::ScopeDef::Unknown => { 239 }
246 let mut item = CompletionItem::new( 240 };
247 CompletionKind::Reference,
248 self.ctx.source_range(),
249 local_name.to_string(),
250 );
251 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
252 return Some(item.build());
253 }
254 };
255 241
256 let local_name = local_name.to_string(); 242 let local_name = local_name.to_string();
257 let mut item = 243 let mut item = CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
258 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); 244 if let hir::ScopeDef::Local(local) = resolution {
259 if let hir::ScopeDef::Local(local) = resolution { 245 let ty = local.ty(ctx.db());
260 let ty = local.ty(self.ctx.db()); 246 if !ty.is_unknown() {
261 if !ty.is_unknown() { 247 item.detail(ty.display(ctx.db()).to_string());
262 item.detail(ty.display(self.ctx.db()).to_string()); 248 }
263 }
264 249
265 item.set_relevance(CompletionRelevance { 250 item.set_relevance(CompletionRelevance {
266 type_match: compute_type_match(self.ctx.completion, &ty), 251 type_match: compute_type_match(ctx.completion, &ty),
267 exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name), 252 exact_name_match: compute_exact_name_match(ctx.completion, &local_name),
268 is_local: true, 253 is_local: true,
269 ..CompletionRelevance::default() 254 ..CompletionRelevance::default()
270 }); 255 });
271 256
272 if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { 257 if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
273 item.ref_match(ref_match); 258 item.ref_match(ref_match);
274 } 259 }
275 }; 260 };
276 261
277 // Add `<>` for generic types 262 // Add `<>` for generic types
278 if self.ctx.completion.is_path_type 263 if matches!(
279 && !self.ctx.completion.has_type_args 264 ctx.completion.path_context,
280 && self.ctx.completion.config.add_call_parenthesis 265 Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. })
281 { 266 ) && ctx.completion.config.add_call_parenthesis
282 if let Some(cap) = self.ctx.snippet_cap() { 267 {
283 let has_non_default_type_params = match resolution { 268 if let Some(cap) = ctx.snippet_cap() {
284 hir::ScopeDef::ModuleDef(Adt(it)) => { 269 let has_non_default_type_params = match resolution {
285 it.has_non_default_type_params(self.ctx.db()) 270 hir::ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db()),
286 } 271 hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db()),
287 hir::ScopeDef::ModuleDef(TypeAlias(it)) => { 272 _ => false,
288 it.has_non_default_type_params(self.ctx.db()) 273 };
289 } 274 if has_non_default_type_params {
290 _ => false, 275 cov_mark::hit!(inserts_angle_brackets_for_generics);
291 }; 276 item.lookup_by(local_name.clone())
292 if has_non_default_type_params { 277 .label(format!("{}<…>", local_name))
293 cov_mark::hit!(inserts_angle_brackets_for_generics); 278 .insert_snippet(cap, format!("{}<$0>", local_name));
294 item.lookup_by(local_name.clone())
295 .label(format!("{}<…>", local_name))
296 .insert_snippet(cap, format!("{}<$0>", local_name));
297 }
298 } 279 }
299 } 280 }
300 item.kind(kind)
301 .add_import(import_to_add)
302 .set_documentation(self.docs(resolution))
303 .set_deprecated(self.is_deprecated(resolution));
304 Some(item.build())
305 } 281 }
282 item.kind(kind)
283 .add_import(import_to_add)
284 .set_documentation(scope_def_docs(ctx.db(), resolution))
285 .set_deprecated(scope_def_is_deprecated(&ctx, resolution));
286 Some(item.build())
287}
306 288
307 fn docs(&self, resolution: &hir::ScopeDef) -> Option<hir::Documentation> { 289fn scope_def_docs(db: &RootDatabase, resolution: &hir::ScopeDef) -> Option<hir::Documentation> {
308 use hir::ModuleDef::*; 290 use hir::ModuleDef::*;
309 match resolution { 291 match resolution {
310 hir::ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()), 292 hir::ScopeDef::ModuleDef(Module(it)) => it.docs(db),
311 hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()), 293 hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(db),
312 hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(self.ctx.db()), 294 hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(db),
313 hir::ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()), 295 hir::ScopeDef::ModuleDef(Const(it)) => it.docs(db),
314 hir::ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()), 296 hir::ScopeDef::ModuleDef(Static(it)) => it.docs(db),
315 hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()), 297 hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(db),
316 hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()), 298 hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db),
317 _ => None, 299 _ => None,
318 }
319 } 300 }
301}
320 302
321 fn is_deprecated(&self, resolution: &hir::ScopeDef) -> bool { 303fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: &hir::ScopeDef) -> bool {
322 match resolution { 304 match resolution {
323 hir::ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it), 305 hir::ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(*it),
324 hir::ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it), 306 hir::ScopeDef::MacroDef(it) => ctx.is_deprecated(*it),
325 hir::ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it), 307 hir::ScopeDef::GenericParam(it) => ctx.is_deprecated(*it),
326 hir::ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it), 308 hir::ScopeDef::AdtSelfType(it) => ctx.is_deprecated(*it),
327 _ => false, 309 _ => false,
328 }
329 } 310 }
330} 311}
331 312
@@ -1026,6 +1007,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
1026 1007
1027 #[test] 1008 #[test]
1028 fn too_many_arguments() { 1009 fn too_many_arguments() {
1010 cov_mark::check!(too_many_arguments);
1029 check_relevance( 1011 check_relevance(
1030 r#" 1012 r#"
1031struct Foo; 1013struct Foo;
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index 6d062b3b9..c54752d30 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -2,7 +2,7 @@
2 2
3use itertools::Itertools; 3use itertools::Itertools;
4 4
5use crate::{item::Builder, CompletionContext}; 5use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
6 6
7#[derive(Debug)] 7#[derive(Debug)]
8pub(super) enum Params { 8pub(super) enum Params {
@@ -32,10 +32,12 @@ impl Builder {
32 cov_mark::hit!(no_parens_in_use_item); 32 cov_mark::hit!(no_parens_in_use_item);
33 return false; 33 return false;
34 } 34 }
35 if ctx.is_pattern_call { 35 if matches!(ctx.path_call_kind(), Some(CallKind::Expr) | Some(CallKind::Pat))
36 return false; 36 | matches!(
37 } 37 ctx.completion_location,
38 if ctx.is_call { 38 Some(ImmediateLocation::MethodCall { has_parens: true, .. })
39 )
40 {
39 return false; 41 return false;
40 } 42 }
41 43
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 1abeed96d..1357b9f4a 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -58,29 +58,29 @@ impl<'a> FunctionRender<'a> {
58 Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method }) 58 Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method })
59 } 59 }
60 60
61 fn render(mut self, import_to_add: Option<ImportEdit>) -> CompletionItem { 61 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
62 let params = self.params(); 62 let params = self.params();
63 if let Some(receiver) = &self.receiver { 63 let call = if let Some(receiver) = &self.receiver {
64 self.name = format!("{}.{}", receiver, &self.name) 64 format!("{}.{}", receiver, &self.name)
65 } 65 } else {
66 let mut item = CompletionItem::new( 66 self.name.clone()
67 CompletionKind::Reference, 67 };
68 self.ctx.source_range(), 68 let mut item =
69 self.name.clone(), 69 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), call.clone());
70 );
71 item.kind(self.kind()) 70 item.kind(self.kind())
72 .set_documentation(self.ctx.docs(self.func)) 71 .set_documentation(self.ctx.docs(self.func))
73 .set_deprecated( 72 .set_deprecated(
74 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), 73 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
75 ) 74 )
76 .detail(self.detail()) 75 .detail(self.detail())
77 .add_call_parens(self.ctx.completion, self.name.clone(), params) 76 .add_call_parens(self.ctx.completion, call.clone(), params)
78 .add_import(import_to_add); 77 .add_import(import_to_add)
78 .lookup_by(self.name);
79 79
80 let ret_type = self.func.ret_type(self.ctx.db()); 80 let ret_type = self.func.ret_type(self.ctx.db());
81 item.set_relevance(CompletionRelevance { 81 item.set_relevance(CompletionRelevance {
82 type_match: compute_type_match(self.ctx.completion, &ret_type), 82 type_match: compute_type_match(self.ctx.completion, &ret_type),
83 exact_name_match: compute_exact_name_match(self.ctx.completion, &self.name), 83 exact_name_match: compute_exact_name_match(self.ctx.completion, &call),
84 ..CompletionRelevance::default() 84 ..CompletionRelevance::default()
85 }); 85 });
86 86
@@ -263,7 +263,7 @@ fn bar(s: &S) {
263 ); 263 );
264 264
265 check_edit( 265 check_edit(
266 "self.foo", 266 "foo",
267 r#" 267 r#"
268struct S {} 268struct S {}
269impl S { 269impl S {
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 0dfba8acc..429d937c8 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -5,6 +5,7 @@ use ide_db::SymbolKind;
5use syntax::display::macro_label; 5use syntax::display::macro_label;
6 6
7use crate::{ 7use crate::{
8 context::CallKind,
8 item::{CompletionItem, CompletionKind, ImportEdit}, 9 item::{CompletionItem, CompletionKind, ImportEdit},
9 render::RenderContext, 10 render::RenderContext,
10}; 11};
@@ -68,7 +69,8 @@ impl<'a> MacroRender<'a> {
68 } 69 }
69 70
70 fn needs_bang(&self) -> bool { 71 fn needs_bang(&self) -> bool {
71 self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call 72 self.ctx.completion.use_item_syntax.is_none()
73 && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
72 } 74 }
73 75
74 fn label(&self) -> String { 76 fn label(&self) -> String {
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index b4e80f424..3717a0409 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -75,10 +75,10 @@ fn render_pat(
75) -> Option<String> { 75) -> Option<String> {
76 let mut pat = match kind { 76 let mut pat = match kind {
77 StructKind::Tuple if ctx.snippet_cap().is_some() => { 77 StructKind::Tuple if ctx.snippet_cap().is_some() => {
78 render_tuple_as_pat(&fields, &name, fields_omitted) 78 render_tuple_as_pat(fields, name, fields_omitted)
79 } 79 }
80 StructKind::Record => { 80 StructKind::Record => {
81 render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) 81 render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
82 } 82 }
83 _ => return None, 83 _ => return None,
84 }; 84 };
@@ -86,7 +86,7 @@ fn render_pat(
86 if ctx.completion.is_param { 86 if ctx.completion.is_param {
87 pat.push(':'); 87 pat.push(':');
88 pat.push(' '); 88 pat.push(' ');
89 pat.push_str(&name); 89 pat.push_str(name);
90 } 90 }
91 if ctx.snippet_cap().is_some() { 91 if ctx.snippet_cap().is_some() {
92 pat.push_str("$0"); 92 pat.push_str("$0");
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index e47b4c745..e0234171a 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -16,7 +16,14 @@ pub(crate) fn render_type_alias<'a>(
16 ctx: RenderContext<'a>, 16 ctx: RenderContext<'a>,
17 type_alias: hir::TypeAlias, 17 type_alias: hir::TypeAlias,
18) -> Option<CompletionItem> { 18) -> Option<CompletionItem> {
19 TypeAliasRender::new(ctx, type_alias)?.render() 19 TypeAliasRender::new(ctx, type_alias)?.render(false)
20}
21
22pub(crate) fn render_type_alias_with_eq<'a>(
23 ctx: RenderContext<'a>,
24 type_alias: hir::TypeAlias,
25) -> Option<CompletionItem> {
26 TypeAliasRender::new(ctx, type_alias)?.render(true)
20} 27}
21 28
22#[derive(Debug)] 29#[derive(Debug)]
@@ -32,8 +39,14 @@ impl<'a> TypeAliasRender<'a> {
32 Some(TypeAliasRender { ctx, type_alias, ast_node }) 39 Some(TypeAliasRender { ctx, type_alias, ast_node })
33 } 40 }
34 41
35 fn render(self) -> Option<CompletionItem> { 42 fn render(self, with_eq: bool) -> Option<CompletionItem> {
36 let name = self.name()?; 43 let name = self.ast_node.name().map(|name| {
44 if with_eq {
45 format!("{} = ", name.text())
46 } else {
47 name.text().to_string()
48 }
49 })?;
37 let detail = self.detail(); 50 let detail = self.detail();
38 51
39 let mut item = 52 let mut item =
@@ -49,10 +62,6 @@ impl<'a> TypeAliasRender<'a> {
49 Some(item.build()) 62 Some(item.build())
50 } 63 }
51 64
52 fn name(&self) -> Option<String> {
53 self.ast_node.name().map(|name| name.text().to_string())
54 }
55
56 fn detail(&self) -> String { 65 fn detail(&self) -> String {
57 type_label(&self.ast_node) 66 type_label(&self.ast_node)
58 } 67 }
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index 6229996ec..e219c577a 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14log = "0.4.8" 14log = "0.4.8"
15rayon = "1.5.0" 15rayon = "1.5.0"
16fst = { version = "0.4", default-features = false } 16fst = { version = "0.4", default-features = false }
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index bad277a95..4795e2565 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -162,7 +162,7 @@ impl ActiveParameter {
162 } 162 }
163 163
164 pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> { 164 pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
165 let (signature, active_parameter) = call_info_impl(&sema, token)?; 165 let (signature, active_parameter) = call_info_impl(sema, token)?;
166 166
167 let idx = active_parameter?; 167 let idx = active_parameter?;
168 let mut params = signature.params(sema.db); 168 let mut params = signature.params(sema.db);
@@ -223,9 +223,8 @@ impl FnCallNode {
223 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, 223 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
224 _ => return None, 224 _ => return None,
225 }), 225 }),
226
227 FnCallNode::MethodCallExpr(call_expr) => { 226 FnCallNode::MethodCallExpr(call_expr) => {
228 call_expr.syntax().children().filter_map(ast::NameRef::cast).next() 227 call_expr.syntax().children().find_map(ast::NameRef::cast)
229 } 228 }
230 } 229 }
231 } 230 }
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 1dcccbb8b..a54f2c323 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -357,9 +357,9 @@ impl NameRefClass {
357 } 357 }
358 } 358 }
359 359
360 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { 360 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
361 if let Some(path) = macro_call.path() { 361 if path.qualifier().is_none() {
362 if path.qualifier().is_none() { 362 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
363 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment 363 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
364 // paths are handled below (allowing `log$0::info!` to resolve to the log crate). 364 // paths are handled below (allowing `log$0::info!` to resolve to the log crate).
365 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) { 365 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
@@ -367,11 +367,9 @@ impl NameRefClass {
367 } 367 }
368 } 368 }
369 } 369 }
370 }
371 370
372 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
373 if let Some(resolved) = sema.resolve_path(&path) { 371 if let Some(resolved) = sema.resolve_path(&path) {
374 if path.syntax().parent().and_then(ast::Attr::cast).is_some() { 372 if path.syntax().ancestors().find_map(ast::Attr::cast).is_some() {
375 if let PathResolution::Def(ModuleDef::Function(func)) = resolved { 373 if let PathResolution::Def(ModuleDef::Function(func)) = resolved {
376 if func.attrs(sema.db).by_key("proc_macro_attribute").exists() { 374 if func.attrs(sema.db).by_key("proc_macro_attribute").exists() {
377 return Some(NameRefClass::Definition(resolved.into())); 375 return Some(NameRefClass::Definition(resolved.into()));
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 21b48237a..00900cdc2 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -3,6 +3,7 @@ pub mod import_assets;
3pub mod insert_use; 3pub mod insert_use;
4pub mod merge_imports; 4pub mod merge_imports;
5pub mod rust_doc; 5pub mod rust_doc;
6pub mod generated_lints;
6 7
7use std::collections::VecDeque; 8use std::collections::VecDeque;
8 9
diff --git a/crates/ide_completion/src/generated_lint_completions.rs b/crates/ide_db/src/helpers/generated_lints.rs
index 0d405350d..55438749b 100644
--- a/crates/ide_completion/src/generated_lint_completions.rs
+++ b/crates/ide_db/src/helpers/generated_lints.rs
@@ -1,320 +1,487 @@
1//! Generated file, do not edit by hand, see `xtask/src/codegen` 1//! Generated file, do not edit by hand, see `xtask/src/codegen`
2 2
3use crate::completions::attribute::LintCompletion; 3pub struct Lint {
4pub(super) const FEATURES: &[LintCompletion] = &[ 4 pub label: &'static str,
5 LintCompletion { 5 pub description: &'static str,
6 label: "plugin_registrar",
7 description: r##"# `plugin_registrar`
8
9The tracking issue for this feature is: [#29597]
10
11[#29597]: https://github.com/rust-lang/rust/issues/29597
12
13This feature is part of "compiler plugins." It will often be used with the
14[`plugin`] and `rustc_private` features as well. For more details, see
15their docs.
16
17[`plugin`]: plugin.md
18
19------------------------
20"##,
21 },
22 LintCompletion {
23 label: "inline_const",
24 description: r##"# `inline_const`
25
26The tracking issue for this feature is: [#76001]
27
28------
29
30This feature allows you to use inline constant expressions. For example, you can
31turn this code:
32
33```rust
34# fn add_one(x: i32) -> i32 { x + 1 }
35const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4;
36
37fn main() {
38 let x = add_one(MY_COMPUTATION);
39}
40```
41
42into this code:
43
44```rust
45#![feature(inline_const)]
46
47# fn add_one(x: i32) -> i32 { x + 1 }
48fn main() {
49 let x = add_one(const { 1 + 2 * 3 / 4 });
50}
51```
52
53You can also use inline constant expressions in patterns:
54
55```rust
56#![feature(inline_const)]
57
58const fn one() -> i32 { 1 }
59
60let some_int = 3;
61match some_int {
62 const { 1 + 2 } => println!("Matched 1 + 2"),
63 const { one() } => println!("Matched const fn returning 1"),
64 _ => println!("Didn't match anything :("),
65} 6}
66```
67
68[#76001]: https://github.com/rust-lang/rust/issues/76001
69"##,
70 },
71 LintCompletion {
72 label: "auto_traits",
73 description: r##"# `auto_traits`
74
75The tracking issue for this feature is [#13231]
76
77[#13231]: https://github.com/rust-lang/rust/issues/13231
78
79----
80
81The `auto_traits` feature gate allows you to define auto traits.
82
83Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits
84that are automatically implemented for every type, unless the type, or a type it contains,
85has explicitly opted out via a negative impl. (Negative impls are separately controlled
86by the `negative_impls` feature.)
87
88[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
89[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
90
91```rust,ignore (partial-example)
92impl !Trait for Type {}
93```
94
95Example:
96
97```rust
98#![feature(negative_impls)]
99#![feature(auto_traits)]
100
101auto trait Valid {}
102
103struct True;
104struct False;
105
106impl !Valid for False {}
107
108struct MaybeValid<T>(T);
109
110fn must_be_valid<T: Valid>(_t: T) { }
111
112fn main() {
113 // works
114 must_be_valid( MaybeValid(True) );
115
116 // compiler error - trait bound not satisfied
117 // must_be_valid( MaybeValid(False) );
118}
119```
120
121## Automatic trait implementations
122
123When a type is declared as an `auto trait`, we will automatically
124create impls for every struct/enum/union, unless an explicit impl is
125provided. These automatic impls contain a where clause for each field
126of the form `T: AutoTrait`, where `T` is the type of the field and
127`AutoTrait` is the auto trait in question. As an example, consider the
128struct `List` and the auto trait `Send`:
129
130```rust
131struct List<T> {
132 data: T,
133 next: Option<Box<List<T>>>,
134}
135```
136
137Presuming that there is no explicit impl of `Send` for `List`, the
138compiler will supply an automatic impl of the form:
139
140```rust
141struct List<T> {
142 data: T,
143 next: Option<Box<List<T>>>,
144}
145
146unsafe impl<T> Send for List<T>
147where
148 T: Send, // from the field `data`
149 Option<Box<List<T>>>: Send, // from the field `next`
150{ }
151```
152
153Explicit impls may be either positive or negative. They take the form:
154
155```rust,ignore (partial-example)
156impl<...> AutoTrait for StructName<..> { }
157impl<...> !AutoTrait for StructName<..> { }
158```
159
160## Coinduction: Auto traits permit cyclic matching
161
162Unlike ordinary trait matching, auto traits are **coinductive**. This
163means, in short, that cycles which occur in trait matching are
164considered ok. As an example, consider the recursive struct `List`
165introduced in the previous section. In attempting to determine whether
166`List: Send`, we would wind up in a cycle: to apply the impl, we must
167show that `Option<Box<List>>: Send`, which will in turn require
168`Box<List>: Send` and then finally `List: Send` again. Under ordinary
169trait matching, this cycle would be an error, but for an auto trait it
170is considered a successful match.
171
172## Items
173
174Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.
175
176## Supertraits
177
178Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.
179"##,
180 },
181 LintCompletion {
182 label: "ffi_const",
183 description: r##"# `ffi_const`
184
185The tracking issue for this feature is: [#58328]
186
187------
188
189The `#[ffi_const]` attribute applies clang's `const` attribute to foreign
190functions declarations.
191
192That is, `#[ffi_const]` functions shall have no effects except for its return
193value, which can only depend on the values of the function parameters, and is
194not affected by changes to the observable state of the program.
195
196Applying the `#[ffi_const]` attribute to a function that violates these
197requirements is undefined behaviour.
198
199This attribute enables Rust to perform common optimizations, like sub-expression
200elimination, and it can avoid emitting some calls in repeated invocations of the
201function with the same argument values regardless of other operations being
202performed in between these functions calls (as opposed to `#[ffi_pure]`
203functions).
204
205## Pitfalls
206
207A `#[ffi_const]` function can only read global memory that would not affect
208its return value for the whole execution of the program (e.g. immutable global
209memory). `#[ffi_const]` functions are referentially-transparent and therefore
210more strict than `#[ffi_pure]` functions.
211
212A common pitfall involves applying the `#[ffi_const]` attribute to a
213function that reads memory through pointer arguments which do not necessarily
214point to immutable global memory.
215
216A `#[ffi_const]` function that returns unit has no effect on the abstract
217machine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`.
218
219A `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a
220call to `abort`) nor by infinite loops.
221
222When translating C headers to Rust FFI, it is worth verifying for which targets
223the `const` attribute is enabled in those headers, and using the appropriate
224`cfg` macros in the Rust side to match those definitions. While the semantics of
225`const` are implemented identically by many C and C++ compilers, e.g., clang,
226[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
227implemented in this way on all of them. It is therefore also worth verifying
228that the semantics of the C toolchain used to compile the binary being linked
229against are compatible with those of the `#[ffi_const]`.
230 7
231[#58328]: https://github.com/rust-lang/rust/issues/58328 8pub const DEFAULT_LINTS: &[Lint] = &[
232[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html 9 Lint {
233[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute 10 label: "absolute_paths_not_starting_with_crate",
234[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm 11 description: r##"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"##,
235"##, 12 },
13 Lint { label: "ambiguous_associated_items", description: r##"ambiguous associated items"## },
14 Lint { label: "anonymous_parameters", description: r##"detects anonymous parameters"## },
15 Lint { label: "arithmetic_overflow", description: r##"arithmetic operation overflows"## },
16 Lint { label: "array_into_iter", description: r##"detects calling `into_iter` on arrays"## },
17 Lint {
18 label: "asm_sub_register",
19 description: r##"using only a subset of a register for inline asm inputs"##,
20 },
21 Lint { label: "bad_asm_style", description: r##"incorrect use of inline assembly"## },
22 Lint {
23 label: "bare_trait_objects",
24 description: r##"suggest using `dyn Trait` for trait objects"##,
25 },
26 Lint {
27 label: "bindings_with_variant_name",
28 description: r##"detects pattern bindings with the same name as one of the matched variants"##,
29 },
30 Lint { label: "box_pointers", description: r##"use of owned (Box type) heap memory"## },
31 Lint {
32 label: "cenum_impl_drop_cast",
33 description: r##"a C-like enum implementing Drop is cast"##,
34 },
35 Lint {
36 label: "clashing_extern_declarations",
37 description: r##"detects when an extern fn has been declared with the same name but different types"##,
38 },
39 Lint {
40 label: "coherence_leak_check",
41 description: r##"distinct impls distinguished only by the leak-check code"##,
42 },
43 Lint {
44 label: "conflicting_repr_hints",
45 description: r##"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"##,
46 },
47 Lint {
48 label: "confusable_idents",
49 description: r##"detects visually confusable pairs between identifiers"##,
50 },
51 Lint {
52 label: "const_err",
53 description: r##"constant evaluation encountered erroneous expression"##,
54 },
55 Lint {
56 label: "const_evaluatable_unchecked",
57 description: r##"detects a generic constant is used in a type without a emitting a warning"##,
58 },
59 Lint {
60 label: "const_item_mutation",
61 description: r##"detects attempts to mutate a `const` item"##,
62 },
63 Lint { label: "dead_code", description: r##"detect unused, unexported items"## },
64 Lint { label: "deprecated", description: r##"detects use of deprecated items"## },
65 Lint {
66 label: "deprecated_in_future",
67 description: r##"detects use of items that will be deprecated in a future version"##,
68 },
69 Lint {
70 label: "deref_nullptr",
71 description: r##"detects when an null pointer is dereferenced"##,
72 },
73 Lint {
74 label: "disjoint_capture_migration",
75 description: r##"Drop reorder and auto traits error because of `capture_disjoint_fields`"##,
76 },
77 Lint { label: "drop_bounds", description: r##"bounds of the form `T: Drop` are useless"## },
78 Lint {
79 label: "elided_lifetimes_in_paths",
80 description: r##"hidden lifetime parameters in types are deprecated"##,
81 },
82 Lint {
83 label: "ellipsis_inclusive_range_patterns",
84 description: r##"`...` range patterns are deprecated"##,
85 },
86 Lint {
87 label: "explicit_outlives_requirements",
88 description: r##"outlives requirements can be inferred"##,
89 },
90 Lint {
91 label: "exported_private_dependencies",
92 description: r##"public interface leaks type from a private dependency"##,
93 },
94 Lint { label: "forbidden_lint_groups", description: r##"applying forbid to lint-groups"## },
95 Lint {
96 label: "function_item_references",
97 description: r##"suggest casting to a function pointer when attempting to take references to function items"##,
98 },
99 Lint {
100 label: "future_incompatible",
101 description: r##"lint group for: keyword-idents, anonymous-parameters, ellipsis-inclusive-range-patterns, forbidden-lint-groups, illegal-floating-point-literal-pattern, private-in-public, pub-use-of-private-extern-crate, invalid-type-param-default, const-err, unaligned-references, patterns-in-fns-without-body, missing-fragment-specifier, late-bound-lifetime-arguments, order-dependent-trait-objects, coherence-leak-check, tyvar-behind-raw-pointer, bare-trait-objects, absolute-paths-not-starting-with-crate, unstable-name-collisions, where-clauses-object-safety, proc-macro-derive-resolution-fallback, macro-expanded-macro-exports-accessed-by-absolute-paths, ill-formed-attribute-input, conflicting-repr-hints, ambiguous-associated-items, mutable-borrow-reservation-conflict, indirect-structural-match, pointer-structural-match, nontrivial-structural-match, soft-unstable, cenum-impl-drop-cast, const-evaluatable-unchecked, uninhabited-static, unsupported-naked-functions, semicolon-in-expressions-from-macros, legacy-derive-helpers, proc-macro-back-compat, array-into-iter"##,
102 },
103 Lint {
104 label: "ill_formed_attribute_input",
105 description: r##"ill-formed attribute inputs that were previously accepted and used in practice"##,
106 },
107 Lint {
108 label: "illegal_floating_point_literal_pattern",
109 description: r##"floating-point literals cannot be used in patterns"##,
110 },
111 Lint {
112 label: "improper_ctypes",
113 description: r##"proper use of libc types in foreign modules"##,
114 },
115 Lint {
116 label: "improper_ctypes_definitions",
117 description: r##"proper use of libc types in foreign item definitions"##,
118 },
119 Lint {
120 label: "incomplete_features",
121 description: r##"incomplete features that may function improperly in some or all cases"##,
122 },
123 Lint { label: "incomplete_include", description: r##"trailing content in included file"## },
124 Lint {
125 label: "indirect_structural_match",
126 description: r##"constant used in pattern contains value of non-structural-match type in a field or a variant"##,
127 },
128 Lint {
129 label: "ineffective_unstable_trait_impl",
130 description: r##"detects `#[unstable]` on stable trait implementations for stable types"##,
131 },
132 Lint {
133 label: "inline_no_sanitize",
134 description: r##"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"##,
135 },
136 Lint {
137 label: "invalid_type_param_default",
138 description: r##"type parameter default erroneously allowed in invalid location"##,
139 },
140 Lint {
141 label: "invalid_value",
142 description: r##"an invalid value is being created (such as a null reference)"##,
143 },
144 Lint {
145 label: "irrefutable_let_patterns",
146 description: r##"detects irrefutable patterns in `if let` and `while let` statements"##,
147 },
148 Lint {
149 label: "keyword_idents",
150 description: r##"detects edition keywords being used as an identifier"##,
151 },
152 Lint { label: "large_assignments", description: r##"detects large moves or copies"## },
153 Lint {
154 label: "late_bound_lifetime_arguments",
155 description: r##"detects generic lifetime arguments in path segments with late bound lifetime parameters"##,
156 },
157 Lint {
158 label: "legacy_derive_helpers",
159 description: r##"detects derive helper attributes that are used before they are introduced"##,
160 },
161 Lint {
162 label: "macro_expanded_macro_exports_accessed_by_absolute_paths",
163 description: r##"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"##,
164 },
165 Lint {
166 label: "macro_use_extern_crate",
167 description: r##"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"##,
168 },
169 Lint {
170 label: "meta_variable_misuse",
171 description: r##"possible meta-variable misuse at macro definition"##,
172 },
173 Lint { label: "missing_abi", description: r##"No declared ABI for extern declaration"## },
174 Lint {
175 label: "missing_copy_implementations",
176 description: r##"detects potentially-forgotten implementations of `Copy`"##,
177 },
178 Lint {
179 label: "missing_debug_implementations",
180 description: r##"detects missing implementations of Debug"##,
181 },
182 Lint {
183 label: "missing_docs",
184 description: r##"detects missing documentation for public members"##,
185 },
186 Lint {
187 label: "missing_fragment_specifier",
188 description: r##"detects missing fragment specifiers in unused `macro_rules!` patterns"##,
189 },
190 Lint {
191 label: "mixed_script_confusables",
192 description: r##"detects Unicode scripts whose mixed script confusables codepoints are solely used"##,
193 },
194 Lint {
195 label: "mutable_borrow_reservation_conflict",
196 description: r##"reservation of a two-phased borrow conflicts with other shared borrows"##,
197 },
198 Lint {
199 label: "mutable_transmutes",
200 description: r##"mutating transmuted &mut T from &T may cause undefined behavior"##,
201 },
202 Lint {
203 label: "no_mangle_const_items",
204 description: r##"const items will not have their symbols exported"##,
205 },
206 Lint { label: "no_mangle_generic_items", description: r##"generic items must be mangled"## },
207 Lint { label: "non_ascii_idents", description: r##"detects non-ASCII identifiers"## },
208 Lint {
209 label: "non_camel_case_types",
210 description: r##"types, variants, traits and type parameters should have camel case names"##,
211 },
212 Lint {
213 label: "non_fmt_panic",
214 description: r##"detect single-argument panic!() invocations in which the argument is not a format string"##,
215 },
216 Lint {
217 label: "non_shorthand_field_patterns",
218 description: r##"using `Struct { x: x }` instead of `Struct { x }` in a pattern"##,
219 },
220 Lint {
221 label: "non_snake_case",
222 description: r##"variables, methods, functions, lifetime parameters and modules should have snake case names"##,
223 },
224 Lint {
225 label: "non_upper_case_globals",
226 description: r##"static constants should have uppercase identifiers"##,
227 },
228 Lint {
229 label: "nonstandard_style",
230 description: r##"lint group for: non-camel-case-types, non-snake-case, non-upper-case-globals"##,
231 },
232 Lint {
233 label: "nontrivial_structural_match",
234 description: r##"constant used in pattern of non-structural-match type and the constant's initializer expression contains values of non-structural-match types"##,
235 },
236 Lint {
237 label: "noop_method_call",
238 description: r##"detects the use of well-known noop methods"##,
239 },
240 Lint {
241 label: "or_patterns_back_compat",
242 description: r##"detects usage of old versions of or-patterns"##,
243 },
244 Lint {
245 label: "order_dependent_trait_objects",
246 description: r##"trait-object types were treated as different depending on marker-trait order"##,
247 },
248 Lint { label: "overflowing_literals", description: r##"literal out of range for its type"## },
249 Lint {
250 label: "overlapping_range_endpoints",
251 description: r##"detects range patterns with overlapping endpoints"##,
252 },
253 Lint { label: "path_statements", description: r##"path statements with no effect"## },
254 Lint {
255 label: "patterns_in_fns_without_body",
256 description: r##"patterns in functions without body were erroneously allowed"##,
257 },
258 Lint {
259 label: "pointer_structural_match",
260 description: r##"pointers are not structural-match"##,
261 },
262 Lint {
263 label: "private_in_public",
264 description: r##"detect private items in public interfaces not caught by the old implementation"##,
265 },
266 Lint {
267 label: "proc_macro_back_compat",
268 description: r##"detects usage of old versions of certain proc-macro crates"##,
269 },
270 Lint {
271 label: "proc_macro_derive_resolution_fallback",
272 description: r##"detects proc macro derives using inaccessible names from parent modules"##,
273 },
274 Lint {
275 label: "pub_use_of_private_extern_crate",
276 description: r##"detect public re-exports of private extern crates"##,
277 },
278 Lint {
279 label: "redundant_semicolons",
280 description: r##"detects unnecessary trailing semicolons"##,
281 },
282 Lint {
283 label: "renamed_and_removed_lints",
284 description: r##"lints that have been renamed or removed"##,
285 },
286 Lint {
287 label: "rust_2018_compatibility",
288 description: r##"lint group for: keyword-idents, anonymous-parameters, tyvar-behind-raw-pointer, absolute-paths-not-starting-with-crate"##,
289 },
290 Lint {
291 label: "rust_2018_idioms",
292 description: r##"lint group for: bare-trait-objects, unused-extern-crates, ellipsis-inclusive-range-patterns, elided-lifetimes-in-paths, explicit-outlives-requirements"##,
293 },
294 Lint {
295 label: "rust_2021_compatibility",
296 description: r##"lint group for: ellipsis-inclusive-range-patterns, bare-trait-objects"##,
297 },
298 Lint {
299 label: "semicolon_in_expressions_from_macros",
300 description: r##"trailing semicolon in macro body used as expression"##,
301 },
302 Lint {
303 label: "single_use_lifetimes",
304 description: r##"detects lifetime parameters that are only used once"##,
305 },
306 Lint {
307 label: "soft_unstable",
308 description: r##"a feature gate that doesn't break dependent crates"##,
309 },
310 Lint {
311 label: "stable_features",
312 description: r##"stable features found in `#[feature]` directive"##,
313 },
314 Lint {
315 label: "temporary_cstring_as_ptr",
316 description: r##"detects getting the inner pointer of a temporary `CString`"##,
317 },
318 Lint {
319 label: "trivial_bounds",
320 description: r##"these bounds don't depend on an type parameters"##,
321 },
322 Lint {
323 label: "trivial_casts",
324 description: r##"detects trivial casts which could be removed"##,
325 },
326 Lint {
327 label: "trivial_numeric_casts",
328 description: r##"detects trivial casts of numeric types which could be removed"##,
329 },
330 Lint {
331 label: "type_alias_bounds",
332 description: r##"bounds in type aliases are not enforced"##,
333 },
334 Lint {
335 label: "tyvar_behind_raw_pointer",
336 description: r##"raw pointer to an inference variable"##,
337 },
338 Lint {
339 label: "unaligned_references",
340 description: r##"detects unaligned references to fields of packed structs"##,
341 },
342 Lint {
343 label: "uncommon_codepoints",
344 description: r##"detects uncommon Unicode codepoints in identifiers"##,
345 },
346 Lint {
347 label: "unconditional_panic",
348 description: r##"operation will cause a panic at runtime"##,
349 },
350 Lint {
351 label: "unconditional_recursion",
352 description: r##"functions that cannot return without calling themselves"##,
353 },
354 Lint { label: "uninhabited_static", description: r##"uninhabited static"## },
355 Lint {
356 label: "unknown_crate_types",
357 description: r##"unknown crate type found in `#[crate_type]` directive"##,
358 },
359 Lint { label: "unknown_lints", description: r##"unrecognized lint attribute"## },
360 Lint {
361 label: "unnameable_test_items",
362 description: r##"detects an item that cannot be named being marked as `#[test_case]`"##,
363 },
364 Lint { label: "unreachable_code", description: r##"detects unreachable code paths"## },
365 Lint { label: "unreachable_patterns", description: r##"detects unreachable patterns"## },
366 Lint {
367 label: "unreachable_pub",
368 description: r##"`pub` items not reachable from crate root"##,
369 },
370 Lint { label: "unsafe_code", description: r##"usage of `unsafe` code"## },
371 Lint {
372 label: "unsafe_op_in_unsafe_fn",
373 description: r##"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"##,
374 },
375 Lint {
376 label: "unstable_features",
377 description: r##"enabling unstable features (deprecated. do not use)"##,
378 },
379 Lint {
380 label: "unstable_name_collisions",
381 description: r##"detects name collision with an existing but unstable method"##,
382 },
383 Lint {
384 label: "unsupported_naked_functions",
385 description: r##"unsupported naked function definitions"##,
386 },
387 Lint {
388 label: "unused",
389 description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons"##,
390 },
391 Lint {
392 label: "unused_allocation",
393 description: r##"detects unnecessary allocations that can be eliminated"##,
394 },
395 Lint {
396 label: "unused_assignments",
397 description: r##"detect assignments that will never be read"##,
398 },
399 Lint {
400 label: "unused_attributes",
401 description: r##"detects attributes that were not used by the compiler"##,
402 },
403 Lint { label: "unused_braces", description: r##"unnecessary braces around an expression"## },
404 Lint {
405 label: "unused_comparisons",
406 description: r##"comparisons made useless by limits of the types involved"##,
407 },
408 Lint {
409 label: "unused_crate_dependencies",
410 description: r##"crate dependencies that are never used"##,
411 },
412 Lint {
413 label: "unused_doc_comments",
414 description: r##"detects doc comments that aren't used by rustdoc"##,
415 },
416 Lint { label: "unused_extern_crates", description: r##"extern crates that are never used"## },
417 Lint {
418 label: "unused_features",
419 description: r##"unused features found in crate-level `#[feature]` directives"##,
420 },
421 Lint {
422 label: "unused_import_braces",
423 description: r##"unnecessary braces around an imported item"##,
424 },
425 Lint { label: "unused_imports", description: r##"imports that are never used"## },
426 Lint { label: "unused_labels", description: r##"detects labels that are never used"## },
427 Lint {
428 label: "unused_lifetimes",
429 description: r##"detects lifetime parameters that are never used"##,
430 },
431 Lint { label: "unused_macros", description: r##"detects macros that were not used"## },
432 Lint {
433 label: "unused_must_use",
434 description: r##"unused result of a type flagged as `#[must_use]`"##,
435 },
436 Lint {
437 label: "unused_mut",
438 description: r##"detect mut variables which don't need to be mutable"##,
439 },
440 Lint {
441 label: "unused_parens",
442 description: r##"`if`, `match`, `while` and `return` do not need parentheses"##,
443 },
444 Lint {
445 label: "unused_qualifications",
446 description: r##"detects unnecessarily qualified names"##,
447 },
448 Lint {
449 label: "unused_results",
450 description: r##"unused result of an expression in a statement"##,
451 },
452 Lint { label: "unused_unsafe", description: r##"unnecessary use of an `unsafe` block"## },
453 Lint {
454 label: "unused_variables",
455 description: r##"detect variables which are not used in any way"##,
456 },
457 Lint {
458 label: "useless_deprecated",
459 description: r##"detects deprecation attributes with no effect"##,
460 },
461 Lint {
462 label: "variant_size_differences",
463 description: r##"detects enums with widely varying variant sizes"##,
464 },
465 Lint {
466 label: "warnings",
467 description: r##"mass-change the level for lints which produce warnings"##,
468 },
469 Lint {
470 label: "warnings",
471 description: r##"lint group for: all lints that are set to issue warnings"##,
472 },
473 Lint {
474 label: "where_clauses_object_safety",
475 description: r##"checks the object safety of where clauses"##,
476 },
477 Lint {
478 label: "while_true",
479 description: r##"suggest using `loop { }` instead of `while true { }`"##,
236 }, 480 },
237 LintCompletion { 481];
238 label: "external_doc",
239 description: r##"# `external_doc`
240
241The tracking issue for this feature is: [#44732]
242
243The `external_doc` feature allows the use of the `include` parameter to the `#[doc]` attribute, to
244include external files in documentation. Use the attribute in place of, or in addition to, regular
245doc comments and `#[doc]` attributes, and `rustdoc` will load the given file when it renders
246documentation for your crate.
247
248With the following files in the same directory:
249
250`external-doc.md`:
251
252```markdown
253# My Awesome Type
254
255This is the documentation for this spectacular type.
256```
257
258`lib.rs`:
259
260```no_run (needs-external-files)
261#![feature(external_doc)]
262
263#[doc(include = "external-doc.md")]
264pub struct MyAwesomeType;
265```
266
267`rustdoc` will load the file `external-doc.md` and use it as the documentation for the `MyAwesomeType`
268struct.
269
270When locating files, `rustdoc` will base paths in the `src/` directory, as if they were alongside the
271`lib.rs` for your crate. So if you want a `docs/` folder to live alongside the `src/` directory,
272start your paths with `../docs/` for `rustdoc` to properly find the file.
273
274This feature was proposed in [RFC #1990] and initially implemented in PR [#44781].
275
276[#44732]: https://github.com/rust-lang/rust/issues/44732
277[RFC #1990]: https://github.com/rust-lang/rfcs/pull/1990
278[#44781]: https://github.com/rust-lang/rust/pull/44781
279"##,
280 },
281 LintCompletion {
282 label: "box_patterns",
283 description: r##"# `box_patterns`
284
285The tracking issue for this feature is: [#29641]
286
287[#29641]: https://github.com/rust-lang/rust/issues/29641
288
289See also [`box_syntax`](box-syntax.md)
290
291------------------------
292
293Box patterns let you match on `Box<T>`s:
294
295
296```rust
297#![feature(box_patterns)]
298 482
299fn main() { 483pub const FEATURES: &[Lint] = &[
300 let b = Some(Box::new(5)); 484 Lint {
301 match b {
302 Some(box n) if n < 0 => {
303 println!("Box contains negative number {}", n);
304 },
305 Some(box n) if n >= 0 => {
306 println!("Box contains non-negative number {}", n);
307 },
308 None => {
309 println!("No box");
310 },
311 _ => unreachable!()
312 }
313}
314```
315"##,
316 },
317 LintCompletion {
318 label: "abi_c_cmse_nonsecure_call", 485 label: "abi_c_cmse_nonsecure_call",
319 description: r##"# `abi_c_cmse_nonsecure_call` 486 description: r##"# `abi_c_cmse_nonsecure_call`
320 487
@@ -406,1682 +573,7 @@ call_nonsecure_function:
406``` 573```
407"##, 574"##,
408 }, 575 },
409 LintCompletion { 576 Lint {
410 label: "member_constraints",
411 description: r##"# `member_constraints`
412
413The tracking issue for this feature is: [#61997]
414
415[#61997]: https://github.com/rust-lang/rust/issues/61997
416
417------------------------
418
419The `member_constraints` feature gate lets you use `impl Trait` syntax with
420multiple unrelated lifetime parameters.
421
422A simple example is:
423
424```rust
425#![feature(member_constraints)]
426
427trait Trait<'a, 'b> { }
428impl<T> Trait<'_, '_> for T {}
429
430fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
431 (x, y)
432}
433
434fn main() { }
435```
436
437Without the `member_constraints` feature gate, the above example is an
438error because both `'a` and `'b` appear in the impl Trait bounds, but
439neither outlives the other.
440"##,
441 },
442 LintCompletion {
443 label: "allocator_internals",
444 description: r##"# `allocator_internals`
445
446This feature does not have a tracking issue, it is an unstable implementation
447detail of the `global_allocator` feature not intended for use outside the
448compiler.
449
450------------------------
451"##,
452 },
453 LintCompletion {
454 label: "cfg_sanitize",
455 description: r##"# `cfg_sanitize`
456
457The tracking issue for this feature is: [#39699]
458
459[#39699]: https://github.com/rust-lang/rust/issues/39699
460
461------------------------
462
463The `cfg_sanitize` feature makes it possible to execute different code
464depending on whether a particular sanitizer is enabled or not.
465
466## Examples
467
468```rust
469#![feature(cfg_sanitize)]
470
471#[cfg(sanitize = "thread")]
472fn a() {
473 // ...
474}
475
476#[cfg(not(sanitize = "thread"))]
477fn a() {
478 // ...
479}
480
481fn b() {
482 if cfg!(sanitize = "leak") {
483 // ...
484 } else {
485 // ...
486 }
487}
488```
489"##,
490 },
491 LintCompletion {
492 label: "cfg_panic",
493 description: r##"# `cfg_panic`
494
495The tracking issue for this feature is: [#77443]
496
497[#77443]: https://github.com/rust-lang/rust/issues/77443
498
499------------------------
500
501The `cfg_panic` feature makes it possible to execute different code
502depending on the panic strategy.
503
504Possible values at the moment are `"unwind"` or `"abort"`, although
505it is possible that new panic strategies may be added to Rust in the
506future.
507
508## Examples
509
510```rust
511#![feature(cfg_panic)]
512
513#[cfg(panic = "unwind")]
514fn a() {
515 // ...
516}
517
518#[cfg(not(panic = "unwind"))]
519fn a() {
520 // ...
521}
522
523fn b() {
524 if cfg!(panic = "abort") {
525 // ...
526 } else {
527 // ...
528 }
529}
530```
531"##,
532 },
533 LintCompletion {
534 label: "ffi_pure",
535 description: r##"# `ffi_pure`
536
537The tracking issue for this feature is: [#58329]
538
539------
540
541The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign
542functions declarations.
543
544That is, `#[ffi_pure]` functions shall have no effects except for its return
545value, which shall not change across two consecutive function calls with
546the same parameters.
547
548Applying the `#[ffi_pure]` attribute to a function that violates these
549requirements is undefined behavior.
550
551This attribute enables Rust to perform common optimizations, like sub-expression
552elimination and loop optimizations. Some common examples of pure functions are
553`strlen` or `memcmp`.
554
555These optimizations are only applicable when the compiler can prove that no
556program state observable by the `#[ffi_pure]` function has changed between calls
557of the function, which could alter the result. See also the `#[ffi_const]`
558attribute, which provides stronger guarantees regarding the allowable behavior
559of a function, enabling further optimization.
560
561## Pitfalls
562
563A `#[ffi_pure]` function can read global memory through the function
564parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not
565referentially-transparent, and are therefore more relaxed than `#[ffi_const]`
566functions.
567
568However, accessing global memory through volatile or atomic reads can violate the
569requirement that two consecutive function calls shall return the same value.
570
571A `pure` function that returns unit has no effect on the abstract machine's
572state.
573
574A `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a
575call to `abort`) nor by infinite loops.
576
577When translating C headers to Rust FFI, it is worth verifying for which targets
578the `pure` attribute is enabled in those headers, and using the appropriate
579`cfg` macros in the Rust side to match those definitions. While the semantics of
580`pure` are implemented identically by many C and C++ compilers, e.g., clang,
581[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
582implemented in this way on all of them. It is therefore also worth verifying
583that the semantics of the C toolchain used to compile the binary being linked
584against are compatible with those of the `#[ffi_pure]`.
585
586
587[#58329]: https://github.com/rust-lang/rust/issues/58329
588[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html
589[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute
590[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm
591"##,
592 },
593 LintCompletion {
594 label: "repr128",
595 description: r##"# `repr128`
596
597The tracking issue for this feature is: [#56071]
598
599[#56071]: https://github.com/rust-lang/rust/issues/56071
600
601------------------------
602
603The `repr128` feature adds support for `#[repr(u128)]` on `enum`s.
604
605```rust
606#![feature(repr128)]
607
608#[repr(u128)]
609enum Foo {
610 Bar(u64),
611}
612```
613"##,
614 },
615 LintCompletion {
616 label: "generators",
617 description: r##"# `generators`
618
619The tracking issue for this feature is: [#43122]
620
621[#43122]: https://github.com/rust-lang/rust/issues/43122
622
623------------------------
624
625The `generators` feature gate in Rust allows you to define generator or
626coroutine literals. A generator is a "resumable function" that syntactically
627resembles a closure but compiles to much different semantics in the compiler
628itself. The primary feature of a generator is that it can be suspended during
629execution to be resumed at a later date. Generators use the `yield` keyword to
630"return", and then the caller can `resume` a generator to resume execution just
631after the `yield` keyword.
632
633Generators are an extra-unstable feature in the compiler right now. Added in
634[RFC 2033] they're mostly intended right now as a information/constraint
635gathering phase. The intent is that experimentation can happen on the nightly
636compiler before actual stabilization. A further RFC will be required to
637stabilize generators/coroutines and will likely contain at least a few small
638tweaks to the overall design.
639
640[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
641
642A syntactical example of a generator is:
643
644```rust
645#![feature(generators, generator_trait)]
646
647use std::ops::{Generator, GeneratorState};
648use std::pin::Pin;
649
650fn main() {
651 let mut generator = || {
652 yield 1;
653 return "foo"
654 };
655
656 match Pin::new(&mut generator).resume(()) {
657 GeneratorState::Yielded(1) => {}
658 _ => panic!("unexpected value from resume"),
659 }
660 match Pin::new(&mut generator).resume(()) {
661 GeneratorState::Complete("foo") => {}
662 _ => panic!("unexpected value from resume"),
663 }
664}
665```
666
667Generators are closure-like literals which can contain a `yield` statement. The
668`yield` statement takes an optional expression of a value to yield out of the
669generator. All generator literals implement the `Generator` trait in the
670`std::ops` module. The `Generator` trait has one main method, `resume`, which
671resumes execution of the generator at the previous suspension point.
672
673An example of the control flow of generators is that the following example
674prints all numbers in order:
675
676```rust
677#![feature(generators, generator_trait)]
678
679use std::ops::Generator;
680use std::pin::Pin;
681
682fn main() {
683 let mut generator = || {
684 println!("2");
685 yield;
686 println!("4");
687 };
688
689 println!("1");
690 Pin::new(&mut generator).resume(());
691 println!("3");
692 Pin::new(&mut generator).resume(());
693 println!("5");
694}
695```
696
697At this time the main intended use case of generators is an implementation
698primitive for async/await syntax, but generators will likely be extended to
699ergonomic implementations of iterators and other primitives in the future.
700Feedback on the design and usage is always appreciated!
701
702### The `Generator` trait
703
704The `Generator` trait in `std::ops` currently looks like:
705
706```rust
707# #![feature(arbitrary_self_types, generator_trait)]
708# use std::ops::GeneratorState;
709# use std::pin::Pin;
710
711pub trait Generator<R = ()> {
712 type Yield;
713 type Return;
714 fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;
715}
716```
717
718The `Generator::Yield` type is the type of values that can be yielded with the
719`yield` statement. The `Generator::Return` type is the returned type of the
720generator. This is typically the last expression in a generator's definition or
721any value passed to `return` in a generator. The `resume` function is the entry
722point for executing the `Generator` itself.
723
724The return value of `resume`, `GeneratorState`, looks like:
725
726```rust
727pub enum GeneratorState<Y, R> {
728 Yielded(Y),
729 Complete(R),
730}
731```
732
733The `Yielded` variant indicates that the generator can later be resumed. This
734corresponds to a `yield` point in a generator. The `Complete` variant indicates
735that the generator is complete and cannot be resumed again. Calling `resume`
736after a generator has returned `Complete` will likely result in a panic of the
737program.
738
739### Closure-like semantics
740
741The closure-like syntax for generators alludes to the fact that they also have
742closure-like semantics. Namely:
743
744* When created, a generator executes no code. A closure literal does not
745 actually execute any of the closure's code on construction, and similarly a
746 generator literal does not execute any code inside the generator when
747 constructed.
748
749* Generators can capture outer variables by reference or by move, and this can
750 be tweaked with the `move` keyword at the beginning of the closure. Like
751 closures all generators will have an implicit environment which is inferred by
752 the compiler. Outer variables can be moved into a generator for use as the
753 generator progresses.
754
755* Generator literals produce a value with a unique type which implements the
756 `std::ops::Generator` trait. This allows actual execution of the generator
757 through the `Generator::resume` method as well as also naming it in return
758 types and such.
759
760* Traits like `Send` and `Sync` are automatically implemented for a `Generator`
761 depending on the captured variables of the environment. Unlike closures,
762 generators also depend on variables live across suspension points. This means
763 that although the ambient environment may be `Send` or `Sync`, the generator
764 itself may not be due to internal variables live across `yield` points being
765 not-`Send` or not-`Sync`. Note that generators do
766 not implement traits like `Copy` or `Clone` automatically.
767
768* Whenever a generator is dropped it will drop all captured environment
769 variables.
770
771### Generators as state machines
772
773In the compiler, generators are currently compiled as state machines. Each
774`yield` expression will correspond to a different state that stores all live
775variables over that suspension point. Resumption of a generator will dispatch on
776the current state and then execute internally until a `yield` is reached, at
777which point all state is saved off in the generator and a value is returned.
778
779Let's take a look at an example to see what's going on here:
780
781```rust
782#![feature(generators, generator_trait)]
783
784use std::ops::Generator;
785use std::pin::Pin;
786
787fn main() {
788 let ret = "foo";
789 let mut generator = move || {
790 yield 1;
791 return ret
792 };
793
794 Pin::new(&mut generator).resume(());
795 Pin::new(&mut generator).resume(());
796}
797```
798
799This generator literal will compile down to something similar to:
800
801```rust
802#![feature(arbitrary_self_types, generators, generator_trait)]
803
804use std::ops::{Generator, GeneratorState};
805use std::pin::Pin;
806
807fn main() {
808 let ret = "foo";
809 let mut generator = {
810 enum __Generator {
811 Start(&'static str),
812 Yield1(&'static str),
813 Done,
814 }
815
816 impl Generator for __Generator {
817 type Yield = i32;
818 type Return = &'static str;
819
820 fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {
821 use std::mem;
822 match mem::replace(&mut *self, __Generator::Done) {
823 __Generator::Start(s) => {
824 *self = __Generator::Yield1(s);
825 GeneratorState::Yielded(1)
826 }
827
828 __Generator::Yield1(s) => {
829 *self = __Generator::Done;
830 GeneratorState::Complete(s)
831 }
832
833 __Generator::Done => {
834 panic!("generator resumed after completion")
835 }
836 }
837 }
838 }
839
840 __Generator::Start(ret)
841 };
842
843 Pin::new(&mut generator).resume(());
844 Pin::new(&mut generator).resume(());
845}
846```
847
848Notably here we can see that the compiler is generating a fresh type,
849`__Generator` in this case. This type has a number of states (represented here
850as an `enum`) corresponding to each of the conceptual states of the generator.
851At the beginning we're closing over our outer variable `foo` and then that
852variable is also live over the `yield` point, so it's stored in both states.
853
854When the generator starts it'll immediately yield 1, but it saves off its state
855just before it does so indicating that it has reached the yield point. Upon
856resuming again we'll execute the `return ret` which returns the `Complete`
857state.
858
859Here we can also note that the `Done` state, if resumed, panics immediately as
860it's invalid to resume a completed generator. It's also worth noting that this
861is just a rough desugaring, not a normative specification for what the compiler
862does.
863"##,
864 },
865 LintCompletion {
866 label: "non_ascii_idents",
867 description: r##"# `non_ascii_idents`
868
869The tracking issue for this feature is: [#55467]
870
871[#55467]: https://github.com/rust-lang/rust/issues/55467
872
873------------------------
874
875The `non_ascii_idents` feature adds support for non-ASCII identifiers.
876
877## Examples
878
879```rust
880#![feature(non_ascii_idents)]
881
882const ε: f64 = 0.00001f64;
883const Π: f64 = 3.14f64;
884```
885
886## Changes to the language reference
887
888> **<sup>Lexer:<sup>**\
889> IDENTIFIER :\
890> &nbsp;&nbsp; &nbsp;&nbsp; XID_start XID_continue<sup>\*</sup>\
891> &nbsp;&nbsp; | `_` XID_continue<sup>+</sup>
892
893An identifier is any nonempty Unicode string of the following form:
894
895Either
896
897 * The first character has property [`XID_start`]
898 * The remaining characters have property [`XID_continue`]
899
900Or
901
902 * The first character is `_`
903 * The identifier is more than one character, `_` alone is not an identifier
904 * The remaining characters have property [`XID_continue`]
905
906that does _not_ occur in the set of [strict keywords].
907
908> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the
909> character ranges used to form the more familiar C and Java language-family
910> identifiers.
911
912[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=
913[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=
914[strict keywords]: ../../reference/keywords.md#strict-keywords
915"##,
916 },
917 LintCompletion {
918 label: "compiler_builtins",
919 description: r##"# `compiler_builtins`
920
921This feature is internal to the Rust compiler and is not intended for general use.
922
923------------------------
924"##,
925 },
926 LintCompletion {
927 label: "or_patterns",
928 description: r##"# `or_patterns`
929
930The tracking issue for this feature is: [#54883]
931
932[#54883]: https://github.com/rust-lang/rust/issues/54883
933
934------------------------
935
936The `or_pattern` language feature allows `|` to be arbitrarily nested within
937a pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern.
938
939## Examples
940
941```rust,no_run
942#![feature(or_patterns)]
943
944pub enum Foo {
945 Bar,
946 Baz,
947 Quux,
948}
949
950pub fn example(maybe_foo: Option<Foo>) {
951 match maybe_foo {
952 Some(Foo::Bar | Foo::Baz) => {
953 println!("The value contained `Bar` or `Baz`");
954 }
955 Some(_) => {
956 println!("The value did not contain `Bar` or `Baz`");
957 }
958 None => {
959 println!("The value was `None`");
960 }
961 }
962}
963```
964"##,
965 },
966 LintCompletion {
967 label: "negative_impls",
968 description: r##"# `negative_impls`
969
970The tracking issue for this feature is [#68318].
971
972[#68318]: https://github.com/rust-lang/rust/issues/68318
973
974----
975
976With the feature gate `negative_impls`, you can write negative impls as well as positive ones:
977
978```rust
979#![feature(negative_impls)]
980trait DerefMut { }
981impl<T: ?Sized> !DerefMut for &T { }
982```
983
984Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below.
985
986Negative impls have the following characteristics:
987
988* They do not have any items.
989* They must obey the orphan rules as if they were a positive impl.
990* They cannot "overlap" with any positive impls.
991
992## Semver interaction
993
994It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types.
995
996## Orphan and overlap rules
997
998Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth.
999
1000Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.)
1001
1002## Interaction with auto traits
1003
1004Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an
1005auto-trait serves two purposes:
1006
1007* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`;
1008* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated.
1009
1010Note that, at present, there is no way to indicate that a given type
1011does not implement an auto trait *but that it may do so in the
1012future*. For ordinary types, this is done by simply not declaring any
1013impl at all, but that is not an option for auto traits. A workaround
1014is that one could embed a marker type as one of the fields, where the
1015marker type is `!AutoTrait`.
1016
1017## Immediate uses
1018
1019Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544).
1020
1021This serves two purposes:
1022
1023* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists.
1024* It prevents downstream crates from creating such impls.
1025"##,
1026 },
1027 LintCompletion {
1028 label: "cmse_nonsecure_entry",
1029 description: r##"# `cmse_nonsecure_entry`
1030
1031The tracking issue for this feature is: [#75835]
1032
1033[#75835]: https://github.com/rust-lang/rust/issues/75835
1034
1035------------------------
1036
1037The [TrustZone-M
1038feature](https://developer.arm.com/documentation/100690/latest/) is available
1039for targets with the Armv8-M architecture profile (`thumbv8m` in their target
1040name).
1041LLVM, the Rust compiler and the linker are providing
1042[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
1043TrustZone-M feature.
1044
1045One of the things provided, with this unstable feature, is the
1046`cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an
1047entry function (see [section
10485.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
1049With this attribute, the compiler will do the following:
1050* add a special symbol on the function which is the `__acle_se_` prefix and the
1051 standard function name
1052* constrain the number of parameters to avoid using the Non-Secure stack
1053* before returning from the function, clear registers that might contain Secure
1054 information
1055* use the `BXNS` instruction to return
1056
1057Because the stack can not be used to pass parameters, there will be compilation
1058errors if:
1059* the total size of all parameters is too big (for example more than four 32
1060 bits integers)
1061* the entry function is not using a C ABI
1062
1063The special symbol `__acle_se_` will be used by the linker to generate a secure
1064gateway veneer.
1065
1066<!-- NOTE(ignore) this example is specific to thumbv8m targets -->
1067
1068``` rust,ignore
1069#![feature(cmse_nonsecure_entry)]
1070
1071#[no_mangle]
1072#[cmse_nonsecure_entry]
1073pub extern "C" fn entry_function(input: u32) -> u32 {
1074 input + 6
1075}
1076```
1077
1078``` text
1079$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs
1080$ arm-none-eabi-objdump -D function.o
1081
108200000000 <entry_function>:
1083 0: b580 push {r7, lr}
1084 2: 466f mov r7, sp
1085 4: b082 sub sp, #8
1086 6: 9001 str r0, [sp, #4]
1087 8: 1d81 adds r1, r0, #6
1088 a: 460a mov r2, r1
1089 c: 4281 cmp r1, r0
1090 e: 9200 str r2, [sp, #0]
1091 10: d30b bcc.n 2a <entry_function+0x2a>
1092 12: e7ff b.n 14 <entry_function+0x14>
1093 14: 9800 ldr r0, [sp, #0]
1094 16: b002 add sp, #8
1095 18: e8bd 4080 ldmia.w sp!, {r7, lr}
1096 1c: 4671 mov r1, lr
1097 1e: 4672 mov r2, lr
1098 20: 4673 mov r3, lr
1099 22: 46f4 mov ip, lr
1100 24: f38e 8800 msr CPSR_f, lr
1101 28: 4774 bxns lr
1102 2a: f240 0000 movw r0, #0
1103 2e: f2c0 0000 movt r0, #0
1104 32: f240 0200 movw r2, #0
1105 36: f2c0 0200 movt r2, #0
1106 3a: 211c movs r1, #28
1107 3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E>
1108 40: defe udf #254 ; 0xfe
1109```
1110"##,
1111 },
1112 LintCompletion {
1113 label: "plugin",
1114 description: r##"# `plugin`
1115
1116The tracking issue for this feature is: [#29597]
1117
1118[#29597]: https://github.com/rust-lang/rust/issues/29597
1119
1120
1121This feature is part of "compiler plugins." It will often be used with the
1122[`plugin_registrar`] and `rustc_private` features.
1123
1124[`plugin_registrar`]: plugin-registrar.md
1125
1126------------------------
1127
1128`rustc` can load compiler plugins, which are user-provided libraries that
1129extend the compiler's behavior with new lint checks, etc.
1130
1131A plugin is a dynamic library crate with a designated *registrar* function that
1132registers extensions with `rustc`. Other crates can load these extensions using
1133the crate attribute `#![plugin(...)]`. See the
1134`rustc_driver::plugin` documentation for more about the
1135mechanics of defining and loading a plugin.
1136
1137In the vast majority of cases, a plugin should *only* be used through
1138`#![plugin]` and not through an `extern crate` item. Linking a plugin would
1139pull in all of librustc_ast and librustc as dependencies of your crate. This is
1140generally unwanted unless you are building another plugin.
1141
1142The usual practice is to put compiler plugins in their own crate, separate from
1143any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
1144of a library.
1145
1146# Lint plugins
1147
1148Plugins can extend [Rust's lint
1149infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with
1150additional checks for code style, safety, etc. Now let's write a plugin
1151[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs)
1152that warns about any item named `lintme`.
1153
1154```rust,ignore (requires-stage-2)
1155#![feature(plugin_registrar)]
1156#![feature(box_syntax, rustc_private)]
1157
1158extern crate rustc_ast;
1159
1160// Load rustc as a plugin to get macros
1161extern crate rustc_driver;
1162#[macro_use]
1163extern crate rustc_lint;
1164#[macro_use]
1165extern crate rustc_session;
1166
1167use rustc_driver::plugin::Registry;
1168use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
1169use rustc_ast::ast;
1170declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
1171
1172declare_lint_pass!(Pass => [TEST_LINT]);
1173
1174impl EarlyLintPass for Pass {
1175 fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
1176 if it.ident.name.as_str() == "lintme" {
1177 cx.lint(TEST_LINT, |lint| {
1178 lint.build("item is named 'lintme'").set_span(it.span).emit()
1179 });
1180 }
1181 }
1182}
1183
1184#[plugin_registrar]
1185pub fn plugin_registrar(reg: &mut Registry) {
1186 reg.lint_store.register_lints(&[&TEST_LINT]);
1187 reg.lint_store.register_early_pass(|| box Pass);
1188}
1189```
1190
1191Then code like
1192
1193```rust,ignore (requires-plugin)
1194#![feature(plugin)]
1195#![plugin(lint_plugin_test)]
1196
1197fn lintme() { }
1198```
1199
1200will produce a compiler warning:
1201
1202```txt
1203foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
1204foo.rs:4 fn lintme() { }
1205 ^~~~~~~~~~~~~~~
1206```
1207
1208The components of a lint plugin are:
1209
1210* one or more `declare_lint!` invocations, which define static `Lint` structs;
1211
1212* a struct holding any state needed by the lint pass (here, none);
1213
1214* a `LintPass`
1215 implementation defining how to check each syntax element. A single
1216 `LintPass` may call `span_lint` for several different `Lint`s, but should
1217 register them all through the `get_lints` method.
1218
1219Lint passes are syntax traversals, but they run at a late stage of compilation
1220where type information is available. `rustc`'s [built-in
1221lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs)
1222mostly use the same infrastructure as lint plugins, and provide examples of how
1223to access type information.
1224
1225Lints defined by plugins are controlled by the usual [attributes and compiler
1226flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g.
1227`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the
1228first argument to `declare_lint!`, with appropriate case and punctuation
1229conversion.
1230
1231You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,
1232including those provided by plugins loaded by `foo.rs`.
1233"##,
1234 },
1235 LintCompletion {
1236 label: "intrinsics",
1237 description: r##"# `intrinsics`
1238
1239The tracking issue for this feature is: None.
1240
1241Intrinsics are never intended to be stable directly, but intrinsics are often
1242exported in some sort of stable manner. Prefer using the stable interfaces to
1243the intrinsic directly when you can.
1244
1245------------------------
1246
1247
1248These are imported as if they were FFI functions, with the special
1249`rust-intrinsic` ABI. For example, if one was in a freestanding
1250context, but wished to be able to `transmute` between types, and
1251perform efficient pointer arithmetic, one would import those functions
1252via a declaration like
1253
1254```rust
1255#![feature(intrinsics)]
1256# fn main() {}
1257
1258extern "rust-intrinsic" {
1259 fn transmute<T, U>(x: T) -> U;
1260
1261 fn offset<T>(dst: *const T, offset: isize) -> *const T;
1262}
1263```
1264
1265As with any other FFI functions, these are always `unsafe` to call.
1266"##,
1267 },
1268 LintCompletion {
1269 label: "rustc_attrs",
1270 description: r##"# `rustc_attrs`
1271
1272This feature has no tracking issue, and is therefore internal to
1273the compiler, not being intended for general use.
1274
1275Note: `rustc_attrs` enables many rustc-internal attributes and this page
1276only discuss a few of them.
1277
1278------------------------
1279
1280The `rustc_attrs` feature allows debugging rustc type layouts by using
1281`#[rustc_layout(...)]` to debug layout at compile time (it even works
1282with `cargo check`) as an alternative to `rustc -Z print-type-sizes`
1283that is way more verbose.
1284
1285Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`,
1286`abi`. Note that it only works on sized types without generics.
1287
1288## Examples
1289
1290```rust,compile_fail
1291#![feature(rustc_attrs)]
1292
1293#[rustc_layout(abi, size)]
1294pub enum X {
1295 Y(u8, u8, u8),
1296 Z(isize),
1297}
1298```
1299
1300When that is compiled, the compiler will error with something like
1301
1302```text
1303error: abi: Aggregate { sized: true }
1304 --> src/lib.rs:4:1
1305 |
13064 | / pub enum T {
13075 | | Y(u8, u8, u8),
13086 | | Z(isize),
13097 | | }
1310 | |_^
1311
1312error: size: Size { raw: 16 }
1313 --> src/lib.rs:4:1
1314 |
13154 | / pub enum T {
13165 | | Y(u8, u8, u8),
13176 | | Z(isize),
13187 | | }
1319 | |_^
1320
1321error: aborting due to 2 previous errors
1322```
1323"##,
1324 },
1325 LintCompletion {
1326 label: "const_fn",
1327 description: r##"# `const_fn`
1328
1329The tracking issue for this feature is: [#57563]
1330
1331[#57563]: https://github.com/rust-lang/rust/issues/57563
1332
1333------------------------
1334
1335The `const_fn` feature enables additional functionality not stabilized in the
1336[minimal subset of `const_fn`](https://github.com/rust-lang/rust/issues/53555)
1337"##,
1338 },
1339 LintCompletion {
1340 label: "abi_thiscall",
1341 description: r##"# `abi_thiscall`
1342
1343The tracking issue for this feature is: [#42202]
1344
1345[#42202]: https://github.com/rust-lang/rust/issues/42202
1346
1347------------------------
1348
1349The MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++
1350instance methods by default; it is identical to the usual (C) calling
1351convention on x86 Windows except that the first parameter of the method,
1352the `this` pointer, is passed in the ECX register.
1353"##,
1354 },
1355 LintCompletion {
1356 label: "trait_alias",
1357 description: r##"# `trait_alias`
1358
1359The tracking issue for this feature is: [#41517]
1360
1361[#41517]: https://github.com/rust-lang/rust/issues/41517
1362
1363------------------------
1364
1365The `trait_alias` feature adds support for trait aliases. These allow aliases
1366to be created for one or more traits (currently just a single regular trait plus
1367any number of auto-traits), and used wherever traits would normally be used as
1368either bounds or trait objects.
1369
1370```rust
1371#![feature(trait_alias)]
1372
1373trait Foo = std::fmt::Debug + Send;
1374trait Bar = Foo + Sync;
1375
1376// Use trait alias as bound on type parameter.
1377fn foo<T: Foo>(v: &T) {
1378 println!("{:?}", v);
1379}
1380
1381pub fn main() {
1382 foo(&1);
1383
1384 // Use trait alias for trait objects.
1385 let a: &Bar = &123;
1386 println!("{:?}", a);
1387 let b = Box::new(456) as Box<dyn Foo>;
1388 println!("{:?}", b);
1389}
1390```
1391"##,
1392 },
1393 LintCompletion {
1394 label: "lang_items",
1395 description: r##"# `lang_items`
1396
1397The tracking issue for this feature is: None.
1398
1399------------------------
1400
1401The `rustc` compiler has certain pluggable operations, that is,
1402functionality that isn't hard-coded into the language, but is
1403implemented in libraries, with a special marker to tell the compiler
1404it exists. The marker is the attribute `#[lang = "..."]` and there are
1405various different values of `...`, i.e. various different 'lang
1406items'.
1407
1408For example, `Box` pointers require two lang items, one for allocation
1409and one for deallocation. A freestanding program that uses the `Box`
1410sugar for dynamic allocations via `malloc` and `free`:
1411
1412```rust,ignore (libc-is-finicky)
1413#![feature(lang_items, box_syntax, start, libc, core_intrinsics, rustc_private)]
1414#![no_std]
1415use core::intrinsics;
1416use core::panic::PanicInfo;
1417
1418extern crate libc;
1419
1420#[lang = "owned_box"]
1421pub struct Box<T>(*mut T);
1422
1423#[lang = "exchange_malloc"]
1424unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
1425 let p = libc::malloc(size as libc::size_t) as *mut u8;
1426
1427 // Check if `malloc` failed:
1428 if p as usize == 0 {
1429 intrinsics::abort();
1430 }
1431
1432 p
1433}
1434
1435#[lang = "box_free"]
1436unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
1437 libc::free(ptr as *mut libc::c_void)
1438}
1439
1440#[start]
1441fn main(_argc: isize, _argv: *const *const u8) -> isize {
1442 let _x = box 1;
1443
1444 0
1445}
1446
1447#[lang = "eh_personality"] extern fn rust_eh_personality() {}
1448#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }
1449#[no_mangle] pub extern fn rust_eh_register_frames () {}
1450#[no_mangle] pub extern fn rust_eh_unregister_frames () {}
1451```
1452
1453Note the use of `abort`: the `exchange_malloc` lang item is assumed to
1454return a valid pointer, and so needs to do the check internally.
1455
1456Other features provided by lang items include:
1457
1458- overloadable operators via traits: the traits corresponding to the
1459 `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all
1460 marked with lang items; those specific four are `eq`, `ord`,
1461 `deref`, and `add` respectively.
1462- stack unwinding and general failure; the `eh_personality`,
1463 `panic` and `panic_bounds_check` lang items.
1464- the traits in `std::marker` used to indicate types of
1465 various kinds; lang items `send`, `sync` and `copy`.
1466- the marker types and variance indicators found in
1467 `std::marker`; lang items `covariant_type`,
1468 `contravariant_lifetime`, etc.
1469
1470Lang items are loaded lazily by the compiler; e.g. if one never uses
1471`Box` then there is no need to define functions for `exchange_malloc`
1472and `box_free`. `rustc` will emit an error when an item is needed
1473but not found in the current crate or any that it depends on.
1474
1475Most lang items are defined by `libcore`, but if you're trying to build
1476an executable without the standard library, you'll run into the need
1477for lang items. The rest of this page focuses on this use-case, even though
1478lang items are a bit broader than that.
1479
1480### Using libc
1481
1482In order to build a `#[no_std]` executable we will need libc as a dependency.
1483We can specify this using our `Cargo.toml` file:
1484
1485```toml
1486[dependencies]
1487libc = { version = "0.2.14", default-features = false }
1488```
1489
1490Note that the default features have been disabled. This is a critical step -
1491**the default features of libc include the standard library and so must be
1492disabled.**
1493
1494### Writing an executable without stdlib
1495
1496Controlling the entry point is possible in two ways: the `#[start]` attribute,
1497or overriding the default shim for the C `main` function with your own.
1498
1499The function marked `#[start]` is passed the command line parameters
1500in the same format as C:
1501
1502```rust,ignore (libc-is-finicky)
1503#![feature(lang_items, core_intrinsics, rustc_private)]
1504#![feature(start)]
1505#![no_std]
1506use core::intrinsics;
1507use core::panic::PanicInfo;
1508
1509// Pull in the system libc library for what crt0.o likely requires.
1510extern crate libc;
1511
1512// Entry point for this program.
1513#[start]
1514fn start(_argc: isize, _argv: *const *const u8) -> isize {
1515 0
1516}
1517
1518// These functions are used by the compiler, but not
1519// for a bare-bones hello world. These are normally
1520// provided by libstd.
1521#[lang = "eh_personality"]
1522#[no_mangle]
1523pub extern fn rust_eh_personality() {
1524}
1525
1526#[lang = "panic_impl"]
1527#[no_mangle]
1528pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
1529 unsafe { intrinsics::abort() }
1530}
1531```
1532
1533To override the compiler-inserted `main` shim, one has to disable it
1534with `#![no_main]` and then create the appropriate symbol with the
1535correct ABI and the correct name, which requires overriding the
1536compiler's name mangling too:
1537
1538```rust,ignore (libc-is-finicky)
1539#![feature(lang_items, core_intrinsics, rustc_private)]
1540#![feature(start)]
1541#![no_std]
1542#![no_main]
1543use core::intrinsics;
1544use core::panic::PanicInfo;
1545
1546// Pull in the system libc library for what crt0.o likely requires.
1547extern crate libc;
1548
1549// Entry point for this program.
1550#[no_mangle] // ensure that this symbol is called `main` in the output
1551pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {
1552 0
1553}
1554
1555// These functions are used by the compiler, but not
1556// for a bare-bones hello world. These are normally
1557// provided by libstd.
1558#[lang = "eh_personality"]
1559#[no_mangle]
1560pub extern fn rust_eh_personality() {
1561}
1562
1563#[lang = "panic_impl"]
1564#[no_mangle]
1565pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
1566 unsafe { intrinsics::abort() }
1567}
1568```
1569
1570In many cases, you may need to manually link to the `compiler_builtins` crate
1571when building a `no_std` binary. You may observe this via linker error messages
1572such as "```undefined reference to `__rust_probestack'```".
1573
1574## More about the language items
1575
1576The compiler currently makes a few assumptions about symbols which are
1577available in the executable to call. Normally these functions are provided by
1578the standard library, but without it you must define your own. These symbols
1579are called "language items", and they each have an internal name, and then a
1580signature that an implementation must conform to.
1581
1582The first of these functions, `rust_eh_personality`, is used by the failure
1583mechanisms of the compiler. This is often mapped to GCC's personality function
1584(see the [libstd implementation][unwind] for more information), but crates
1585which do not trigger a panic can be assured that this function is never
1586called. The language item's name is `eh_personality`.
1587
1588[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs
1589
1590The second function, `rust_begin_panic`, is also used by the failure mechanisms of the
1591compiler. When a panic happens, this controls the message that's displayed on
1592the screen. While the language item's name is `panic_impl`, the symbol name is
1593`rust_begin_panic`.
1594
1595Finally, a `eh_catch_typeinfo` static is needed for certain targets which
1596implement Rust panics on top of C++ exceptions.
1597
1598## List of all language items
1599
1600This is a list of all language items in Rust along with where they are located in
1601the source code.
1602
1603- Primitives
1604 - `i8`: `libcore/num/mod.rs`
1605 - `i16`: `libcore/num/mod.rs`
1606 - `i32`: `libcore/num/mod.rs`
1607 - `i64`: `libcore/num/mod.rs`
1608 - `i128`: `libcore/num/mod.rs`
1609 - `isize`: `libcore/num/mod.rs`
1610 - `u8`: `libcore/num/mod.rs`
1611 - `u16`: `libcore/num/mod.rs`
1612 - `u32`: `libcore/num/mod.rs`
1613 - `u64`: `libcore/num/mod.rs`
1614 - `u128`: `libcore/num/mod.rs`
1615 - `usize`: `libcore/num/mod.rs`
1616 - `f32`: `libstd/f32.rs`
1617 - `f64`: `libstd/f64.rs`
1618 - `char`: `libcore/char.rs`
1619 - `slice`: `liballoc/slice.rs`
1620 - `str`: `liballoc/str.rs`
1621 - `const_ptr`: `libcore/ptr.rs`
1622 - `mut_ptr`: `libcore/ptr.rs`
1623 - `unsafe_cell`: `libcore/cell.rs`
1624- Runtime
1625 - `start`: `libstd/rt.rs`
1626 - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC)
1627 - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)
1628 - `eh_personality`: `libpanic_unwind/seh.rs` (SEH)
1629 - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC)
1630 - `panic`: `libcore/panicking.rs`
1631 - `panic_bounds_check`: `libcore/panicking.rs`
1632 - `panic_impl`: `libcore/panicking.rs`
1633 - `panic_impl`: `libstd/panicking.rs`
1634- Allocations
1635 - `owned_box`: `liballoc/boxed.rs`
1636 - `exchange_malloc`: `liballoc/heap.rs`
1637 - `box_free`: `liballoc/heap.rs`
1638- Operands
1639 - `not`: `libcore/ops/bit.rs`
1640 - `bitand`: `libcore/ops/bit.rs`
1641 - `bitor`: `libcore/ops/bit.rs`
1642 - `bitxor`: `libcore/ops/bit.rs`
1643 - `shl`: `libcore/ops/bit.rs`
1644 - `shr`: `libcore/ops/bit.rs`
1645 - `bitand_assign`: `libcore/ops/bit.rs`
1646 - `bitor_assign`: `libcore/ops/bit.rs`
1647 - `bitxor_assign`: `libcore/ops/bit.rs`
1648 - `shl_assign`: `libcore/ops/bit.rs`
1649 - `shr_assign`: `libcore/ops/bit.rs`
1650 - `deref`: `libcore/ops/deref.rs`
1651 - `deref_mut`: `libcore/ops/deref.rs`
1652 - `index`: `libcore/ops/index.rs`
1653 - `index_mut`: `libcore/ops/index.rs`
1654 - `add`: `libcore/ops/arith.rs`
1655 - `sub`: `libcore/ops/arith.rs`
1656 - `mul`: `libcore/ops/arith.rs`
1657 - `div`: `libcore/ops/arith.rs`
1658 - `rem`: `libcore/ops/arith.rs`
1659 - `neg`: `libcore/ops/arith.rs`
1660 - `add_assign`: `libcore/ops/arith.rs`
1661 - `sub_assign`: `libcore/ops/arith.rs`
1662 - `mul_assign`: `libcore/ops/arith.rs`
1663 - `div_assign`: `libcore/ops/arith.rs`
1664 - `rem_assign`: `libcore/ops/arith.rs`
1665 - `eq`: `libcore/cmp.rs`
1666 - `ord`: `libcore/cmp.rs`
1667- Functions
1668 - `fn`: `libcore/ops/function.rs`
1669 - `fn_mut`: `libcore/ops/function.rs`
1670 - `fn_once`: `libcore/ops/function.rs`
1671 - `generator_state`: `libcore/ops/generator.rs`
1672 - `generator`: `libcore/ops/generator.rs`
1673- Other
1674 - `coerce_unsized`: `libcore/ops/unsize.rs`
1675 - `drop`: `libcore/ops/drop.rs`
1676 - `drop_in_place`: `libcore/ptr.rs`
1677 - `clone`: `libcore/clone.rs`
1678 - `copy`: `libcore/marker.rs`
1679 - `send`: `libcore/marker.rs`
1680 - `sized`: `libcore/marker.rs`
1681 - `unsize`: `libcore/marker.rs`
1682 - `sync`: `libcore/marker.rs`
1683 - `phantom_data`: `libcore/marker.rs`
1684 - `discriminant_kind`: `libcore/marker.rs`
1685 - `freeze`: `libcore/marker.rs`
1686 - `debug_trait`: `libcore/fmt/mod.rs`
1687 - `non_zero`: `libcore/nonzero.rs`
1688 - `arc`: `liballoc/sync.rs`
1689 - `rc`: `liballoc/rc.rs`
1690"##,
1691 },
1692 LintCompletion {
1693 label: "doc_spotlight",
1694 description: r##"# `doc_spotlight`
1695
1696The tracking issue for this feature is: [#45040]
1697
1698The `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute,
1699to "spotlight" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]`
1700attribute to a trait definition will make rustdoc print extra information for functions which return
1701a type that implements that trait. For example, this attribute is applied to the `Iterator`,
1702`io::Read`, `io::Write`, and `Future` traits in the standard library.
1703
1704You can do this on your own traits, like this:
1705
1706```
1707#![feature(doc_spotlight)]
1708
1709#[doc(spotlight)]
1710pub trait MyTrait {}
1711
1712pub struct MyStruct;
1713impl MyTrait for MyStruct {}
1714
1715/// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`,
1716/// without having to write that yourself!
1717pub fn my_fn() -> MyStruct { MyStruct }
1718```
1719
1720This feature was originally implemented in PR [#45039].
1721
1722[#45040]: https://github.com/rust-lang/rust/issues/45040
1723[#45039]: https://github.com/rust-lang/rust/pull/45039
1724"##,
1725 },
1726 LintCompletion {
1727 label: "c_variadic",
1728 description: r##"# `c_variadic`
1729
1730The tracking issue for this feature is: [#44930]
1731
1732[#44930]: https://github.com/rust-lang/rust/issues/44930
1733
1734------------------------
1735
1736The `c_variadic` language feature enables C-variadic functions to be
1737defined in Rust. The may be called both from within Rust and via FFI.
1738
1739## Examples
1740
1741```rust
1742#![feature(c_variadic)]
1743
1744pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize {
1745 let mut sum = 0;
1746 for _ in 0..n {
1747 sum += args.arg::<usize>();
1748 }
1749 sum
1750}
1751```
1752"##,
1753 },
1754 LintCompletion {
1755 label: "intra_doc_pointers",
1756 description: r##"# `intra-doc-pointers`
1757
1758The tracking issue for this feature is: [#80896]
1759
1760[#80896]: https://github.com/rust-lang/rust/issues/80896
1761
1762------------------------
1763
1764Rustdoc does not currently allow disambiguating between `*const` and `*mut`, and
1765raw pointers in intra-doc links are unstable until it does.
1766
1767```rust
1768#![feature(intra_doc_pointers)]
1769//! [pointer::add]
1770```
1771"##,
1772 },
1773 LintCompletion {
1774 label: "box_syntax",
1775 description: r##"# `box_syntax`
1776
1777The tracking issue for this feature is: [#49733]
1778
1779[#49733]: https://github.com/rust-lang/rust/issues/49733
1780
1781See also [`box_patterns`](box-patterns.md)
1782
1783------------------------
1784
1785Currently the only stable way to create a `Box` is via the `Box::new` method.
1786Also it is not possible in stable Rust to destructure a `Box` in a match
1787pattern. The unstable `box` keyword can be used to create a `Box`. An example
1788usage would be:
1789
1790```rust
1791#![feature(box_syntax)]
1792
1793fn main() {
1794 let b = box 5;
1795}
1796```
1797"##,
1798 },
1799 LintCompletion {
1800 label: "unsized_locals",
1801 description: r##"# `unsized_locals`
1802
1803The tracking issue for this feature is: [#48055]
1804
1805[#48055]: https://github.com/rust-lang/rust/issues/48055
1806
1807------------------------
1808
1809This implements [RFC1909]. When turned on, you can have unsized arguments and locals:
1810
1811[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md
1812
1813```rust
1814#![allow(incomplete_features)]
1815#![feature(unsized_locals, unsized_fn_params)]
1816
1817use std::any::Any;
1818
1819fn main() {
1820 let x: Box<dyn Any> = Box::new(42);
1821 let x: dyn Any = *x;
1822 // ^ unsized local variable
1823 // ^^ unsized temporary
1824 foo(x);
1825}
1826
1827fn foo(_: dyn Any) {}
1828// ^^^^^^ unsized argument
1829```
1830
1831The RFC still forbids the following unsized expressions:
1832
1833```rust,compile_fail
1834#![feature(unsized_locals)]
1835
1836use std::any::Any;
1837
1838struct MyStruct<T: ?Sized> {
1839 content: T,
1840}
1841
1842struct MyTupleStruct<T: ?Sized>(T);
1843
1844fn answer() -> Box<dyn Any> {
1845 Box::new(42)
1846}
1847
1848fn main() {
1849 // You CANNOT have unsized statics.
1850 static X: dyn Any = *answer(); // ERROR
1851 const Y: dyn Any = *answer(); // ERROR
1852
1853 // You CANNOT have struct initialized unsized.
1854 MyStruct { content: *answer() }; // ERROR
1855 MyTupleStruct(*answer()); // ERROR
1856 (42, *answer()); // ERROR
1857
1858 // You CANNOT have unsized return types.
1859 fn my_function() -> dyn Any { *answer() } // ERROR
1860
1861 // You CAN have unsized local variables...
1862 let mut x: dyn Any = *answer(); // OK
1863 // ...but you CANNOT reassign to them.
1864 x = *answer(); // ERROR
1865
1866 // You CANNOT even initialize them separately.
1867 let y: dyn Any; // OK
1868 y = *answer(); // ERROR
1869
1870 // Not mentioned in the RFC, but by-move captured variables are also Sized.
1871 let x: dyn Any = *answer();
1872 (move || { // ERROR
1873 let y = x;
1874 })();
1875
1876 // You CAN create a closure with unsized arguments,
1877 // but you CANNOT call it.
1878 // This is an implementation detail and may be changed in the future.
1879 let f = |x: dyn Any| {};
1880 f(*answer()); // ERROR
1881}
1882```
1883
1884## By-value trait objects
1885
1886With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
1887
1888```rust
1889#![feature(unsized_fn_params)]
1890
1891trait Foo {
1892 fn foo(self) {}
1893}
1894
1895impl<T: ?Sized> Foo for T {}
1896
1897fn main() {
1898 let slice: Box<[i32]> = Box::new([1, 2, 3]);
1899 <[i32] as Foo>::foo(*slice);
1900}
1901```
1902
1903And `Foo` will also be object-safe.
1904
1905```rust
1906#![feature(unsized_fn_params)]
1907
1908trait Foo {
1909 fn foo(self) {}
1910}
1911
1912impl<T: ?Sized> Foo for T {}
1913
1914fn main () {
1915 let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
1916 // doesn't compile yet
1917 <dyn Foo as Foo>::foo(*slice);
1918}
1919```
1920
1921One of the objectives of this feature is to allow `Box<dyn FnOnce>`.
1922
1923## Variable length arrays
1924
1925The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.
1926
1927```rust,ignore (not-yet-implemented)
1928#![feature(unsized_locals)]
1929
1930fn mergesort<T: Ord>(a: &mut [T]) {
1931 let mut tmp = [T; dyn a.len()];
1932 // ...
1933}
1934
1935fn main() {
1936 let mut a = [3, 1, 5, 6];
1937 mergesort(&mut a);
1938 assert_eq!(a, [1, 3, 5, 6]);
1939}
1940```
1941
1942VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.
1943
1944## Advisory on stack usage
1945
1946It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
1947
1948- When you need a by-value trait objects.
1949- When you really need a fast allocation of small temporary arrays.
1950
1951Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code
1952
1953```rust
1954#![feature(unsized_locals)]
1955
1956fn main() {
1957 let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
1958 let _x = {{{{{{{{{{*x}}}}}}}}}};
1959}
1960```
1961
1962and the code
1963
1964```rust
1965#![feature(unsized_locals)]
1966
1967fn main() {
1968 for _ in 0..10 {
1969 let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
1970 let _x = *x;
1971 }
1972}
1973```
1974
1975will unnecessarily extend the stack frame.
1976"##,
1977 },
1978 LintCompletion {
1979 label: "arbitrary_enum_discriminant",
1980 description: r##"# `arbitrary_enum_discriminant`
1981
1982The tracking issue for this feature is: [#60553]
1983
1984[#60553]: https://github.com/rust-lang/rust/issues/60553
1985
1986------------------------
1987
1988The `arbitrary_enum_discriminant` feature permits tuple-like and
1989struct-like enum variants with `#[repr(<int-type>)]` to have explicit discriminants.
1990
1991## Examples
1992
1993```rust
1994#![feature(arbitrary_enum_discriminant)]
1995
1996#[allow(dead_code)]
1997#[repr(u8)]
1998enum Enum {
1999 Unit = 3,
2000 Tuple(u16) = 2,
2001 Struct {
2002 a: u8,
2003 b: u16,
2004 } = 1,
2005}
2006
2007impl Enum {
2008 fn tag(&self) -> u8 {
2009 unsafe { *(self as *const Self as *const u8) }
2010 }
2011}
2012
2013assert_eq!(3, Enum::Unit.tag());
2014assert_eq!(2, Enum::Tuple(5).tag());
2015assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag());
2016```
2017"##,
2018 },
2019 LintCompletion {
2020 label: "unboxed_closures",
2021 description: r##"# `unboxed_closures`
2022
2023The tracking issue for this feature is [#29625]
2024
2025See Also: [`fn_traits`](../library-features/fn-traits.md)
2026
2027[#29625]: https://github.com/rust-lang/rust/issues/29625
2028
2029----
2030
2031The `unboxed_closures` feature allows you to write functions using the `"rust-call"` ABI,
2032required for implementing the [`Fn*`] family of traits. `"rust-call"` functions must have
2033exactly one (non self) argument, a tuple representing the argument list.
2034
2035[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
2036
2037```rust
2038#![feature(unboxed_closures)]
2039
2040extern "rust-call" fn add_args(args: (u32, u32)) -> u32 {
2041 args.0 + args.1
2042}
2043
2044fn main() {}
2045```
2046"##,
2047 },
2048 LintCompletion {
2049 label: "custom_test_frameworks",
2050 description: r##"# `custom_test_frameworks`
2051
2052The tracking issue for this feature is: [#50297]
2053
2054[#50297]: https://github.com/rust-lang/rust/issues/50297
2055
2056------------------------
2057
2058The `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`.
2059Any function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`)
2060and be passed to the test runner determined by the `#![test_runner]` crate attribute.
2061
2062```rust
2063#![feature(custom_test_frameworks)]
2064#![test_runner(my_runner)]
2065
2066fn my_runner(tests: &[&i32]) {
2067 for t in tests {
2068 if **t == 0 {
2069 println!("PASSED");
2070 } else {
2071 println!("FAILED");
2072 }
2073 }
2074}
2075
2076#[test_case]
2077const WILL_PASS: i32 = 0;
2078
2079#[test_case]
2080const WILL_FAIL: i32 = 4;
2081```
2082"##,
2083 },
2084 LintCompletion {
2085 label: "abi_msp430_interrupt", 577 label: "abi_msp430_interrupt",
2086 description: r##"# `abi_msp430_interrupt` 578 description: r##"# `abi_msp430_interrupt`
2087 579
@@ -2127,201 +619,7 @@ Disassembly of section .text:
2127``` 619```
2128"##, 620"##,
2129 }, 621 },
2130 LintCompletion { 622 Lint {
2131 label: "impl_trait_in_bindings",
2132 description: r##"# `impl_trait_in_bindings`
2133
2134The tracking issue for this feature is: [#63065]
2135
2136[#63065]: https://github.com/rust-lang/rust/issues/63065
2137
2138------------------------
2139
2140The `impl_trait_in_bindings` feature gate lets you use `impl Trait` syntax in
2141`let`, `static`, and `const` bindings.
2142
2143A simple example is:
2144
2145```rust
2146#![feature(impl_trait_in_bindings)]
2147
2148use std::fmt::Debug;
2149
2150fn main() {
2151 let a: impl Debug + Clone = 42;
2152 let b = a.clone();
2153 println!("{:?}", b); // prints `42`
2154}
2155```
2156
2157Note however that because the types of `a` and `b` are opaque in the above
2158example, calling inherent methods or methods outside of the specified traits
2159(e.g., `a.abs()` or `b.abs()`) is not allowed, and yields an error.
2160"##,
2161 },
2162 LintCompletion {
2163 label: "cfg_version",
2164 description: r##"# `cfg_version`
2165
2166The tracking issue for this feature is: [#64796]
2167
2168[#64796]: https://github.com/rust-lang/rust/issues/64796
2169
2170------------------------
2171
2172The `cfg_version` feature makes it possible to execute different code
2173depending on the compiler version.
2174
2175## Examples
2176
2177```rust
2178#![feature(cfg_version)]
2179
2180#[cfg(version("1.42"))]
2181fn a() {
2182 // ...
2183}
2184
2185#[cfg(not(version("1.42")))]
2186fn a() {
2187 // ...
2188}
2189
2190fn b() {
2191 if cfg!(version("1.42")) {
2192 // ...
2193 } else {
2194 // ...
2195 }
2196}
2197```
2198"##,
2199 },
2200 LintCompletion {
2201 label: "link_cfg",
2202 description: r##"# `link_cfg`
2203
2204This feature is internal to the Rust compiler and is not intended for general use.
2205
2206------------------------
2207"##,
2208 },
2209 LintCompletion {
2210 label: "infer_static_outlives_requirements",
2211 description: r##"# `infer_static_outlives_requirements`
2212
2213The tracking issue for this feature is: [#54185]
2214
2215[#54185]: https://github.com/rust-lang/rust/issues/54185
2216
2217------------------------
2218The `infer_static_outlives_requirements` feature indicates that certain
2219`'static` outlives requirements can be inferred by the compiler rather than
2220stating them explicitly.
2221
2222Note: It is an accompanying feature to `infer_outlives_requirements`,
2223which must be enabled to infer outlives requirements.
2224
2225For example, currently generic struct definitions that contain
2226references, require where-clauses of the form T: 'static. By using
2227this feature the outlives predicates will be inferred, although
2228they may still be written explicitly.
2229
2230```rust,ignore (pseudo-Rust)
2231struct Foo<U> where U: 'static { // <-- currently required
2232 bar: Bar<U>
2233}
2234struct Bar<T: 'static> {
2235 x: T,
2236}
2237```
2238
2239
2240## Examples:
2241
2242```rust,ignore (pseudo-Rust)
2243#![feature(infer_outlives_requirements)]
2244#![feature(infer_static_outlives_requirements)]
2245
2246#[rustc_outlives]
2247// Implicitly infer U: 'static
2248struct Foo<U> {
2249 bar: Bar<U>
2250}
2251struct Bar<T: 'static> {
2252 x: T,
2253}
2254```
2255"##,
2256 },
2257 LintCompletion {
2258 label: "marker_trait_attr",
2259 description: r##"# `marker_trait_attr`
2260
2261The tracking issue for this feature is: [#29864]
2262
2263[#29864]: https://github.com/rust-lang/rust/issues/29864
2264
2265------------------------
2266
2267Normally, Rust keeps you from adding trait implementations that could
2268overlap with each other, as it would be ambiguous which to use. This
2269feature, however, carves out an exception to that rule: a trait can
2270opt-in to having overlapping implementations, at the cost that those
2271implementations are not allowed to override anything (and thus the
2272trait itself cannot have any associated items, as they're pointless
2273when they'd need to do the same thing for every type anyway).
2274
2275```rust
2276#![feature(marker_trait_attr)]
2277
2278#[marker] trait CheapToClone: Clone {}
2279
2280impl<T: Copy> CheapToClone for T {}
2281
2282// These could potentially overlap with the blanket implementation above,
2283// so are only allowed because CheapToClone is a marker trait.
2284impl<T: CheapToClone, U: CheapToClone> CheapToClone for (T, U) {}
2285impl<T: CheapToClone> CheapToClone for std::ops::Range<T> {}
2286
2287fn cheap_clone<T: CheapToClone>(t: T) -> T {
2288 t.clone()
2289}
2290```
2291
2292This is expected to replace the unstable `overlapping_marker_traits`
2293feature, which applied to all empty traits (without needing an opt-in).
2294"##,
2295 },
2296 LintCompletion {
2297 label: "doc_masked",
2298 description: r##"# `doc_masked`
2299
2300The tracking issue for this feature is: [#44027]
2301
2302-----
2303
2304The `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists
2305of trait implementations. The specifics of the feature are as follows:
2306
23071. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute,
2308 it marks the crate as being masked.
2309
23102. When listing traits a given type implements, rustdoc ensures that traits from masked crates are
2311 not emitted into the documentation.
2312
23133. When listing types that implement a given trait, rustdoc ensures that types from masked crates
2314 are not emitted into the documentation.
2315
2316This feature was introduced in PR [#44026] to ensure that compiler-internal and
2317implementation-specific types and traits were not included in the standard library's documentation.
2318Such types would introduce broken links into the documentation.
2319
2320[#44026]: https://github.com/rust-lang/rust/pull/44026
2321[#44027]: https://github.com/rust-lang/rust/pull/44027
2322"##,
2323 },
2324 LintCompletion {
2325 label: "abi_ptx", 623 label: "abi_ptx",
2326 description: r##"# `abi_ptx` 624 description: r##"# `abi_ptx`
2327 625
@@ -2385,340 +683,94 @@ $ cat $(find -name '*.s')
2385``` 683```
2386"##, 684"##,
2387 }, 685 },
2388 LintCompletion { 686 Lint {
2389 label: "profiler_runtime", 687 label: "abi_thiscall",
2390 description: r##"# `profiler_runtime` 688 description: r##"# `abi_thiscall`
2391
2392The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524).
2393
2394------------------------
2395"##,
2396 },
2397 LintCompletion {
2398 label: "crate_visibility_modifier",
2399 description: r##"# `crate_visibility_modifier`
2400
2401The tracking issue for this feature is: [#53120]
2402
2403[#53120]: https://github.com/rust-lang/rust/issues/53120
2404
2405-----
2406
2407The `crate_visibility_modifier` feature allows the `crate` keyword to be used
2408as a visibility modifier synonymous to `pub(crate)`, indicating that a type
2409(function, _&c._) is to be visible to the entire enclosing crate, but not to
2410other crates.
2411
2412```rust
2413#![feature(crate_visibility_modifier)]
2414
2415crate struct Foo {
2416 bar: usize,
2417}
2418```
2419"##,
2420 },
2421 LintCompletion {
2422 label: "doc_cfg",
2423 description: r##"# `doc_cfg`
2424
2425The tracking issue for this feature is: [#43781]
2426
2427------
2428
2429The `doc_cfg` feature allows an API be documented as only available in some specific platforms.
2430This attribute has two effects:
2431
24321. In the annotated item's documentation, there will be a message saying "This is supported on
2433 (platform) only".
2434
24352. The item's doc-tests will only run on the specific platform.
2436
2437In addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a
2438special conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your
2439crate.
2440
2441This feature was introduced as part of PR [#43348] to allow the platform-specific parts of the
2442standard library be documented.
2443
2444```rust
2445#![feature(doc_cfg)]
2446
2447#[cfg(any(windows, doc))]
2448#[doc(cfg(windows))]
2449/// The application's icon in the notification area (a.k.a. system tray).
2450///
2451/// # Examples
2452///
2453/// ```no_run
2454/// extern crate my_awesome_ui_library;
2455/// use my_awesome_ui_library::current_app;
2456/// use my_awesome_ui_library::windows::notification;
2457///
2458/// let icon = current_app().get::<notification::Icon>();
2459/// icon.show();
2460/// icon.show_message("Hello");
2461/// ```
2462pub struct Icon {
2463 // ...
2464}
2465```
2466
2467[#43781]: https://github.com/rust-lang/rust/issues/43781
2468[#43348]: https://github.com/rust-lang/rust/issues/43348
2469"##,
2470 },
2471 LintCompletion {
2472 label: "unsized_tuple_coercion",
2473 description: r##"# `unsized_tuple_coercion`
2474 689
2475The tracking issue for this feature is: [#42877] 690The tracking issue for this feature is: [#42202]
2476 691
2477[#42877]: https://github.com/rust-lang/rust/issues/42877 692[#42202]: https://github.com/rust-lang/rust/issues/42202
2478 693
2479------------------------ 694------------------------
2480 695
2481This is a part of [RFC0401]. According to the RFC, there should be an implementation like this: 696The MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++
2482 697instance methods by default; it is identical to the usual (C) calling
2483```rust,ignore (partial-example) 698convention on x86 Windows except that the first parameter of the method,
2484impl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {} 699the `this` pointer, is passed in the ECX register.
2485```
2486
2487This implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this:
2488
2489```rust
2490#![feature(unsized_tuple_coercion)]
2491
2492fn main() {
2493 let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]);
2494 let y : &([i32; 3], [i32]) = &x;
2495 assert_eq!(y.1[0], 4);
2496}
2497```
2498
2499[RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
2500"##, 700"##,
2501 }, 701 },
2502 LintCompletion { 702 Lint {
2503 label: "no_sanitize", 703 label: "allocator_api",
2504 description: r##"# `no_sanitize` 704 description: r##"# `allocator_api`
2505 705
2506The tracking issue for this feature is: [#39699] 706The tracking issue for this feature is [#32838]
2507 707
2508[#39699]: https://github.com/rust-lang/rust/issues/39699 708[#32838]: https://github.com/rust-lang/rust/issues/32838
2509 709
2510------------------------ 710------------------------
2511 711
2512The `no_sanitize` attribute can be used to selectively disable sanitizer 712Sometimes you want the memory for one collection to use a different
2513instrumentation in an annotated function. This might be useful to: avoid 713allocator than the memory for another collection. In this case,
2514instrumentation overhead in a performance critical function, or avoid 714replacing the global allocator is not a workable option. Instead,
2515instrumenting code that contains constructs unsupported by given sanitizer. 715you need to pass in an instance of an `AllocRef` to each collection
2516 716for which you want a custom allocator.
2517The precise effect of this annotation depends on particular sanitizer in use.
2518For example, with `no_sanitize(thread)`, the thread sanitizer will no longer
2519instrument non-atomic store / load operations, but it will instrument atomic
2520operations to avoid reporting false positives and provide meaning full stack
2521traces.
2522
2523## Examples
2524
2525``` rust
2526#![feature(no_sanitize)]
2527 717
2528#[no_sanitize(address)] 718TBD
2529fn foo() {
2530 // ...
2531}
2532```
2533"##, 719"##,
2534 }, 720 },
2535 LintCompletion { 721 Lint {
2536 label: "try_blocks", 722 label: "allocator_internals",
2537 description: r##"# `try_blocks` 723 description: r##"# `allocator_internals`
2538
2539The tracking issue for this feature is: [#31436]
2540 724
2541[#31436]: https://github.com/rust-lang/rust/issues/31436 725This feature does not have a tracking issue, it is an unstable implementation
726detail of the `global_allocator` feature not intended for use outside the
727compiler.
2542 728
2543------------------------ 729------------------------
2544
2545The `try_blocks` feature adds support for `try` blocks. A `try`
2546block creates a new scope one can use the `?` operator in.
2547
2548```rust,edition2018
2549#![feature(try_blocks)]
2550
2551use std::num::ParseIntError;
2552
2553let result: Result<i32, ParseIntError> = try {
2554 "1".parse::<i32>()?
2555 + "2".parse::<i32>()?
2556 + "3".parse::<i32>()?
2557};
2558assert_eq!(result, Ok(6));
2559
2560let result: Result<i32, ParseIntError> = try {
2561 "1".parse::<i32>()?
2562 + "foo".parse::<i32>()?
2563 + "3".parse::<i32>()?
2564};
2565assert!(result.is_err());
2566```
2567"##, 730"##,
2568 }, 731 },
2569 LintCompletion { 732 Lint {
2570 label: "transparent_unions", 733 label: "arbitrary_enum_discriminant",
2571 description: r##"# `transparent_unions` 734 description: r##"# `arbitrary_enum_discriminant`
2572
2573The tracking issue for this feature is [#60405]
2574
2575[#60405]: https://github.com/rust-lang/rust/issues/60405
2576
2577----
2578 735
2579The `transparent_unions` feature allows you mark `union`s as 736The tracking issue for this feature is: [#60553]
2580`#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the
2581same conditions in which a `struct` may be `#[repr(transparent)]` (generally,
2582this means the `union` must have exactly one non-zero-sized field). Some
2583concrete illustrations follow.
2584 737
2585```rust 738[#60553]: https://github.com/rust-lang/rust/issues/60553
2586#![feature(transparent_unions)]
2587 739
2588// This union has the same representation as `f32`. 740------------------------
2589#[repr(transparent)]
2590union SingleFieldUnion {
2591 field: f32,
2592}
2593 741
2594// This union has the same representation as `usize`. 742The `arbitrary_enum_discriminant` feature permits tuple-like and
2595#[repr(transparent)] 743struct-like enum variants with `#[repr(<int-type>)]` to have explicit discriminants.
2596union MultiFieldUnion {
2597 field: usize,
2598 nothing: (),
2599}
2600```
2601 744
2602For consistency with transparent `struct`s, `union`s must have exactly one 745## Examples
2603non-zero-sized field. If all fields are zero-sized, the `union` must not be
2604`#[repr(transparent)]`:
2605 746
2606```rust 747```rust
2607#![feature(transparent_unions)] 748#![feature(arbitrary_enum_discriminant)]
2608 749
2609// This (non-transparent) union is already valid in stable Rust: 750#[allow(dead_code)]
2610pub union GoodUnion { 751#[repr(u8)]
2611 pub nothing: (), 752enum Enum {
753 Unit = 3,
754 Tuple(u16) = 2,
755 Struct {
756 a: u8,
757 b: u16,
758 } = 1,
2612} 759}
2613 760
2614// Error: transparent union needs exactly one non-zero-sized field, but has 0 761impl Enum {
2615// #[repr(transparent)] 762 fn tag(&self) -> u8 {
2616// pub union BadUnion { 763 unsafe { *(self as *const Self as *const u8) }
2617// pub nothing: (), 764 }
2618// }
2619```
2620
2621The one exception is if the `union` is generic over `T` and has a field of type
2622`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type:
2623
2624```rust
2625#![feature(transparent_unions)]
2626
2627// This union has the same representation as `T`.
2628#[repr(transparent)]
2629pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable.
2630 pub field: T,
2631 pub nothing: (),
2632} 765}
2633 766
2634// This is okay even though `()` is a zero-sized type. 767assert_eq!(3, Enum::Unit.tag());
2635pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () }; 768assert_eq!(2, Enum::Tuple(5).tag());
2636``` 769assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag());
2637
2638Like transarent `struct`s, a transparent `union` of type `U` has the same
2639layout, size, and ABI as its single non-ZST field. If it is generic over a type
2640`T`, and all its fields are ZSTs except for exactly one field of type `T`, then
2641it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).
2642
2643Like transparent `struct`s, transparent `union`s are FFI-safe if and only if
2644their underlying representation type is also FFI-safe.
2645
2646A `union` may not be eligible for the same nonnull-style optimizations that a
2647`struct` or `enum` (with the same fields) are eligible for. Adding
2648`#[repr(transparent)]` to `union` does not change this. To give a more concrete
2649example, it is unspecified whether `size_of::<T>()` is equal to
2650`size_of::<Option<T>>()`, where `T` is a `union` (regardless of whether or not
2651it is transparent). The Rust compiler is free to perform this optimization if
2652possible, but is not required to, and different compiler versions may differ in
2653their application of these optimizations.
2654"##,
2655 },
2656 LintCompletion {
2657 label: "const_eval_limit",
2658 description: r##"# `const_eval_limit`
2659
2660The tracking issue for this feature is: [#67217]
2661
2662[#67217]: https://github.com/rust-lang/rust/issues/67217
2663
2664The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`.
2665"##,
2666 },
2667 LintCompletion {
2668 label: "link_args",
2669 description: r##"# `link_args`
2670
2671The tracking issue for this feature is: [#29596]
2672
2673[#29596]: https://github.com/rust-lang/rust/issues/29596
2674
2675------------------------
2676
2677You can tell `rustc` how to customize linking, and that is via the `link_args`
2678attribute. This attribute is applied to `extern` blocks and specifies raw flags
2679which need to get passed to the linker when producing an artifact. An example
2680usage would be:
2681
2682```rust,no_run
2683#![feature(link_args)]
2684
2685#[link_args = "-foo -bar -baz"]
2686extern "C" {}
2687# fn main() {}
2688``` 770```
2689
2690Note that this feature is currently hidden behind the `feature(link_args)` gate
2691because this is not a sanctioned way of performing linking. Right now `rustc`
2692shells out to the system linker (`gcc` on most systems, `link.exe` on MSVC), so
2693it makes sense to provide extra command line arguments, but this will not
2694always be the case. In the future `rustc` may use LLVM directly to link native
2695libraries, in which case `link_args` will have no meaning. You can achieve the
2696same effect as the `link_args` attribute with the `-C link-args` argument to
2697`rustc`.
2698
2699It is highly recommended to *not* use this attribute, and rather use the more
2700formal `#[link(...)]` attribute on `extern` blocks instead.
2701"##,
2702 },
2703 LintCompletion {
2704 label: "internal_output_capture",
2705 description: r##"# `internal_output_capture`
2706
2707This feature is internal to the Rust compiler and is not intended for general use.
2708
2709------------------------
2710"##,
2711 },
2712 LintCompletion {
2713 label: "windows_handle",
2714 description: r##"# `windows_handle`
2715
2716This feature is internal to the Rust compiler and is not intended for general use.
2717
2718------------------------
2719"##, 771"##,
2720 }, 772 },
2721 LintCompletion { 773 Lint {
2722 label: "asm", 774 label: "asm",
2723 description: r##"# `asm` 775 description: r##"# `asm`
2724 776
@@ -2748,6 +800,7 @@ Inline assembly is currently supported on the following architectures:
2748- AArch64 800- AArch64
2749- RISC-V 801- RISC-V
2750- NVPTX 802- NVPTX
803- PowerPC
2751- Hexagon 804- Hexagon
2752- MIPS32r2 and MIPS64r2 805- MIPS32r2 and MIPS64r2
2753- wasm32 806- wasm32
@@ -2757,7 +810,7 @@ Inline assembly is currently supported on the following architectures:
2757Let us start with the simplest possible example: 810Let us start with the simplest possible example:
2758 811
2759```rust,allow_fail 812```rust,allow_fail
2760# #![feature(asm)] 813#![feature(asm)]
2761unsafe { 814unsafe {
2762 asm!("nop"); 815 asm!("nop");
2763} 816}
@@ -2774,7 +827,7 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet
2774actually acts on data: 827actually acts on data:
2775 828
2776```rust,allow_fail 829```rust,allow_fail
2777# #![feature(asm)] 830#![feature(asm)]
2778let x: u64; 831let x: u64;
2779unsafe { 832unsafe {
2780 asm!("mov {}, 5", out(reg) x); 833 asm!("mov {}, 5", out(reg) x);
@@ -2796,7 +849,7 @@ the template and will read the variable from there after the inline assembly fin
2796Let us see another example that also uses an input: 849Let us see another example that also uses an input:
2797 850
2798```rust,allow_fail 851```rust,allow_fail
2799# #![feature(asm)] 852#![feature(asm)]
2800let i: u64 = 3; 853let i: u64 = 3;
2801let o: u64; 854let o: u64;
2802unsafe { 855unsafe {
@@ -2836,7 +889,7 @@ readability, and allows reordering instructions without changing the argument or
2836We can further refine the above example to avoid the `mov` instruction: 889We can further refine the above example to avoid the `mov` instruction:
2837 890
2838```rust,allow_fail 891```rust,allow_fail
2839# #![feature(asm)] 892#![feature(asm)]
2840let mut x: u64 = 3; 893let mut x: u64 = 3;
2841unsafe { 894unsafe {
2842 asm!("add {0}, {number}", inout(reg) x, number = const 5); 895 asm!("add {0}, {number}", inout(reg) x, number = const 5);
@@ -2850,7 +903,7 @@ This is different from specifying an input and output separately in that it is g
2850It is also possible to specify different variables for the input and output parts of an `inout` operand: 903It is also possible to specify different variables for the input and output parts of an `inout` operand:
2851 904
2852```rust,allow_fail 905```rust,allow_fail
2853# #![feature(asm)] 906#![feature(asm)]
2854let x: u64 = 3; 907let x: u64 = 3;
2855let y: u64; 908let y: u64;
2856unsafe { 909unsafe {
@@ -2872,7 +925,7 @@ There is also a `inlateout` variant of this specifier.
2872Here is an example where `inlateout` *cannot* be used: 925Here is an example where `inlateout` *cannot* be used:
2873 926
2874```rust,allow_fail 927```rust,allow_fail
2875# #![feature(asm)] 928#![feature(asm)]
2876let mut a: u64 = 4; 929let mut a: u64 = 4;
2877let b: u64 = 4; 930let b: u64 = 4;
2878let c: u64 = 4; 931let c: u64 = 4;
@@ -2893,7 +946,7 @@ Here the compiler is free to allocate the same register for inputs `b` and `c` s
2893However the following example can use `inlateout` since the output is only modified after all input registers have been read: 946However the following example can use `inlateout` since the output is only modified after all input registers have been read:
2894 947
2895```rust,allow_fail 948```rust,allow_fail
2896# #![feature(asm)] 949#![feature(asm)]
2897let mut a: u64 = 4; 950let mut a: u64 = 4;
2898let b: u64 = 4; 951let b: u64 = 4;
2899unsafe { 952unsafe {
@@ -2912,7 +965,7 @@ While `reg` is generally available on any architecture, these are highly archite
2912among others can be addressed by their name. 965among others can be addressed by their name.
2913 966
2914```rust,allow_fail,no_run 967```rust,allow_fail,no_run
2915# #![feature(asm)] 968#![feature(asm)]
2916let cmd = 0xd1; 969let cmd = 0xd1;
2917unsafe { 970unsafe {
2918 asm!("out 0x64, eax", in("eax") cmd); 971 asm!("out 0x64, eax", in("eax") cmd);
@@ -2928,7 +981,7 @@ Note that unlike other operand types, explicit register operands cannot be used
2928Consider this example which uses the x86 `mul` instruction: 981Consider this example which uses the x86 `mul` instruction:
2929 982
2930```rust,allow_fail 983```rust,allow_fail
2931# #![feature(asm)] 984#![feature(asm)]
2932fn mul(a: u64, b: u64) -> u128 { 985fn mul(a: u64, b: u64) -> u128 {
2933 let lo: u64; 986 let lo: u64;
2934 let hi: u64; 987 let hi: u64;
@@ -2964,7 +1017,7 @@ We need to tell the compiler about this since it may need to save and restore th
2964around the inline assembly block. 1017around the inline assembly block.
2965 1018
2966```rust,allow_fail 1019```rust,allow_fail
2967# #![feature(asm)] 1020#![feature(asm)]
2968let ebx: u32; 1021let ebx: u32;
2969let ecx: u32; 1022let ecx: u32;
2970 1023
@@ -2994,7 +1047,7 @@ However we still need to tell the compiler that `eax` and `edx` have been modifi
2994This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code: 1047This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code:
2995 1048
2996```rust,allow_fail 1049```rust,allow_fail
2997# #![feature(asm)] 1050#![feature(asm)]
2998// Multiply x by 6 using shifts and adds 1051// Multiply x by 6 using shifts and adds
2999let mut x: u64 = 4; 1052let mut x: u64 = 4;
3000unsafe { 1053unsafe {
@@ -3016,7 +1069,7 @@ A special operand type, `sym`, allows you to use the symbol name of a `fn` or `s
3016This allows you to call a function or access a global variable without needing to keep its address in a register. 1069This allows you to call a function or access a global variable without needing to keep its address in a register.
3017 1070
3018```rust,allow_fail 1071```rust,allow_fail
3019# #![feature(asm)] 1072#![feature(asm)]
3020extern "C" fn foo(arg: i32) { 1073extern "C" fn foo(arg: i32) {
3021 println!("arg = {}", arg); 1074 println!("arg = {}", arg);
3022} 1075}
@@ -3028,13 +1081,19 @@ fn call_foo(arg: i32) {
3028 sym foo, 1081 sym foo,
3029 // 1st argument in rdi, which is caller-saved 1082 // 1st argument in rdi, which is caller-saved
3030 inout("rdi") arg => _, 1083 inout("rdi") arg => _,
3031 // All caller-saved registers must be marked as clobberred 1084 // All caller-saved registers must be marked as clobbered
3032 out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _, 1085 out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _,
3033 out("r8") _, out("r9") _, out("r10") _, out("r11") _, 1086 out("r8") _, out("r9") _, out("r10") _, out("r11") _,
3034 out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _, 1087 out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
3035 out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _, 1088 out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
3036 out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _, 1089 out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
3037 out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _, 1090 out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
1091 // Also mark AVX-512 registers as clobbered. This is accepted by the
1092 // compiler even if AVX-512 is not enabled on the current target.
1093 out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _,
1094 out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm23") _,
1095 out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _,
1096 out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _,
3038 ) 1097 )
3039 } 1098 }
3040} 1099}
@@ -3052,7 +1111,7 @@ By default the compiler will always choose the name that refers to the full regi
3052This default can be overriden by using modifiers on the template string operands, just like you would with format strings: 1111This default can be overriden by using modifiers on the template string operands, just like you would with format strings:
3053 1112
3054```rust,allow_fail 1113```rust,allow_fail
3055# #![feature(asm)] 1114#![feature(asm)]
3056let mut x: u16 = 0xab; 1115let mut x: u16 = 0xab;
3057 1116
3058unsafe { 1117unsafe {
@@ -3077,7 +1136,7 @@ For example, in x86/x86_64 and intel assembly syntax, you should wrap inputs/out
3077to indicate they are memory operands: 1136to indicate they are memory operands:
3078 1137
3079```rust,allow_fail 1138```rust,allow_fail
3080# #![feature(asm, llvm_asm)] 1139#![feature(asm, llvm_asm)]
3081# fn load_fpu_control_word(control: u16) { 1140# fn load_fpu_control_word(control: u16) {
3082unsafe { 1141unsafe {
3083 asm!("fldcw [{}]", in(reg) &control, options(nostack)); 1142 asm!("fldcw [{}]", in(reg) &control, options(nostack));
@@ -3088,6 +1147,43 @@ unsafe {
3088# } 1147# }
3089``` 1148```
3090 1149
1150## Labels
1151
1152The compiler is allowed to instantiate multiple copies an `asm!` block, for example when the function containing it is inlined in multiple places. As a consequence, you should only use GNU assembler [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions.
1153
1154Moreover, due to [an llvm bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values.
1155
1156```rust,allow_fail
1157#![feature(asm)]
1158
1159let mut a = 0;
1160unsafe {
1161 asm!(
1162 "mov {0}, 10",
1163 "2:",
1164 "sub {0}, 1",
1165 "cmp {0}, 3",
1166 "jle 2f",
1167 "jmp 2b",
1168 "2:",
1169 "add {0}, 2",
1170 out(reg) a
1171 );
1172}
1173assert_eq!(a, 5);
1174```
1175
1176This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`.
1177
1178This example show a few thing:
1179
1180First that the same number can be used as a label multiple times in the same inline block.
1181
1182Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes b (“backward”) or f (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction.
1183
1184[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels
1185[an llvm bug]: https://bugs.llvm.org/show_bug.cgi?id=36144
1186
3091## Options 1187## Options
3092 1188
3093By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. 1189By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better.
@@ -3095,7 +1191,7 @@ By default, an inline assembly block is treated the same way as an external FFI
3095Let's take our previous example of an `add` instruction: 1191Let's take our previous example of an `add` instruction:
3096 1192
3097```rust,allow_fail 1193```rust,allow_fail
3098# #![feature(asm)] 1194#![feature(asm)]
3099let mut a: u64 = 4; 1195let mut a: u64 = 4;
3100let b: u64 = 4; 1196let b: u64 = 4;
3101unsafe { 1197unsafe {
@@ -3138,7 +1234,7 @@ options := "options(" option *["," option] [","] ")"
3138asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")" 1234asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")"
3139``` 1235```
3140 1236
3141The macro will initially be supported only on ARM, AArch64, Hexagon, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target. 1237The macro will initially be supported only on ARM, AArch64, Hexagon, PowerPC, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
3142 1238
3143[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax 1239[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax
3144 1240
@@ -3187,7 +1283,7 @@ Several types of operands are supported:
3187 - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`). 1283 - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`).
3188 - You should only write to the register after all inputs are read, otherwise you may clobber an input. 1284 - You should only write to the register after all inputs are read, otherwise you may clobber an input.
3189* `const <expr>` 1285* `const <expr>`
3190 - `<expr>` must be an integer or floating-point constant expression. 1286 - `<expr>` must be an integer constant expression.
3191 - The value of the expression is formatted as a string and substituted directly into the asm template string. 1287 - The value of the expression is formatted as a string and substituted directly into the asm template string.
3192* `sym <path>` 1288* `sym <path>`
3193 - `<path>` must refer to a `fn` or `static`. 1289 - `<path>` must refer to a `fn` or `static`.
@@ -3214,20 +1310,20 @@ Here is the list of currently supported register classes:
3214 1310
3215| Architecture | Register class | Registers | LLVM constraint code | 1311| Architecture | Register class | Registers | LLVM constraint code |
3216| ------------ | -------------- | --------- | -------------------- | 1312| ------------ | -------------- | --------- | -------------------- |
3217| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` | 1313| x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` (x86-64 only) | `r` |
3218| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` | 1314| x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` |
3219| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` | 1315| x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` |
3220| x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\*, `bh`\*, `ch`\*, `dh`\* | `q` | 1316| x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | `q` |
3221| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` | 1317| x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` |
3222| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | 1318| x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` |
3223| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | 1319| x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` |
3224| x86 | `kreg` | `k[1-7]` | `Yk` | 1320| x86 | `kreg` | `k[1-7]` | `Yk` |
3225| AArch64 | `reg` | `x[0-28]`, `x30` | `r` | 1321| AArch64 | `reg` | `x[0-30]` | `r` |
3226| AArch64 | `vreg` | `v[0-31]` | `w` | 1322| AArch64 | `vreg` | `v[0-31]` | `w` |
3227| AArch64 | `vreg_low16` | `v[0-15]` | `x` | 1323| AArch64 | `vreg_low16` | `v[0-15]` | `x` |
3228| ARM | `reg` | `r[0-5]` `r7`\*, `r[8-10]`, `r11`\*, `r12`, `r14` | `r` | 1324| ARM | `reg` | `r[0-12]`, `r14` | `r` |
3229| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` | 1325| ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` |
3230| ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` | 1326| ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `l` |
3231| ARM | `sreg` | `s[0-31]` | `t` | 1327| ARM | `sreg` | `s[0-31]` | `t` |
3232| ARM | `sreg_low16` | `s[0-15]` | `x` | 1328| ARM | `sreg_low16` | `s[0-15]` | `x` |
3233| ARM | `dreg` | `d[0-31]` | `w` | 1329| ARM | `dreg` | `d[0-31]` | `w` |
@@ -3244,17 +1340,18 @@ Here is the list of currently supported register classes:
3244| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | 1340| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` |
3245| RISC-V | `freg` | `f[0-31]` | `f` | 1341| RISC-V | `freg` | `f[0-31]` | `f` |
3246| Hexagon | `reg` | `r[0-28]` | `r` | 1342| Hexagon | `reg` | `r[0-28]` | `r` |
1343| PowerPC | `reg` | `r[0-31]` | `r` |
1344| PowerPC | `reg_nonzero` | | `r[1-31]` | `b` |
1345| PowerPC | `freg` | `f[0-31]` | `f` |
3247| wasm32 | `local` | None\* | `r` | 1346| wasm32 | `local` | None\* | `r` |
3248 1347
3249> **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. 1348> **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register.
3250> 1349>
3251> Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register. 1350> Note #2: On x86-64 the high byte registers (e.g. `ah`) are not available in the `reg_byte` register class.
3252> 1351>
3253> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. 1352> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported.
3254> 1353>
3255> Note #4: On ARM the frame pointer is either `r7` or `r11` depending on the platform. 1354> Note #4: WebAssembly doesn't have registers, so named registers are not supported.
3256>
3257> Note #5: WebAssembly doesn't have registers, so named registers are not supported.
3258 1355
3259Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc). 1356Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc).
3260 1357
@@ -3288,6 +1385,9 @@ Each register class has constraints on which value types they can be used with.
3288| RISC-V | `freg` | `f` | `f32` | 1385| RISC-V | `freg` | `f` | `f32` |
3289| RISC-V | `freg` | `d` | `f64` | 1386| RISC-V | `freg` | `d` | `f64` |
3290| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | 1387| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` |
1388| PowerPC | `reg` | None | `i8`, `i16`, `i32` |
1389| PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32` |
1390| PowerPC | `freg` | None | `f32`, `f64` |
3291| wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | 1391| wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` |
3292 1392
3293> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). 1393> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target).
@@ -3356,13 +1456,14 @@ Some registers cannot be used for input or output operands:
3356| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | 1456| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. |
3357| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. | 1457| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. |
3358| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. | 1458| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. |
3359| ARM | `r6` | `r6` is used internally by LLVM as a base pointer and therefore cannot be used as an input or output. | 1459| All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64), `r19` (Hexagon), `x9` (RISC-V) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. |
3360| x86 | `k0` | This is a constant zero register which can't be modified. | 1460| x86 | `k0` | This is a constant zero register which can't be modified. |
3361| x86 | `ip` | This is the program counter, not a real register. | 1461| x86 | `ip` | This is the program counter, not a real register. |
3362| x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). | 1462| x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). |
3363| x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). | 1463| x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). |
3364| AArch64 | `xzr` | This is a constant zero register which can't be modified. | 1464| AArch64 | `xzr` | This is a constant zero register which can't be modified. |
3365| ARM | `pc` | This is the program counter, not a real register. | 1465| ARM | `pc` | This is the program counter, not a real register. |
1466| ARM | `r9` | This is a reserved register on some ARM targets. |
3366| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | 1467| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |
3367| MIPS | `$1` or `$at` | Reserved for assembler. | 1468| MIPS | `$1` or `$at` | Reserved for assembler. |
3368| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | 1469| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. |
@@ -3372,9 +1473,10 @@ Some registers cannot be used for input or output operands:
3372| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | 1473| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. |
3373| Hexagon | `lr` | This is the link register which cannot be used as an input or output. | 1474| Hexagon | `lr` | This is the link register which cannot be used as an input or output. |
3374 1475
3375In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are: 1476In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are the frame pointer and base pointer
3376- The frame pointer on all architectures. 1477- The frame pointer and LLVM base pointer on all architectures.
3377- `r6` on ARM. 1478- `r9` on ARM.
1479- `x18` on AArch64.
3378 1480
3379## Template modifiers 1481## Template modifiers
3380 1482
@@ -3423,6 +1525,9 @@ The supported modifiers are a subset of LLVM's (and GCC's) [asm template argumen
3423| RISC-V | `reg` | None | `x1` | None | 1525| RISC-V | `reg` | None | `x1` | None |
3424| RISC-V | `freg` | None | `f0` | None | 1526| RISC-V | `freg` | None | `f0` | None |
3425| Hexagon | `reg` | None | `r0` | None | 1527| Hexagon | `reg` | None | `r0` | None |
1528| PowerPC | `reg` | None | `0` | None |
1529| PowerPC | `reg_nonzero` | None | `3` | `b` |
1530| PowerPC | `freg` | None | `0` | None |
3426 1531
3427> Notes: 1532> Notes:
3428> - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register. 1533> - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register.
@@ -3503,116 +1608,633 @@ The compiler performs some additional checks on options:
3503 - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds). 1608 - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds).
3504 - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited. 1609 - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited.
3505- You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places. 1610- You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places.
3506 - As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions.
3507 1611
3508> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. 1612> **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call.
1613"##,
1614 },
1615 Lint {
1616 label: "auto_traits",
1617 description: r##"# `auto_traits`
3509 1618
3510[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels 1619The tracking issue for this feature is [#13231]
1620
1621[#13231]: https://github.com/rust-lang/rust/issues/13231
1622
1623----
1624
1625The `auto_traits` feature gate allows you to define auto traits.
1626
1627Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits
1628that are automatically implemented for every type, unless the type, or a type it contains,
1629has explicitly opted out via a negative impl. (Negative impls are separately controlled
1630by the `negative_impls` feature.)
1631
1632[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
1633[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
1634
1635```rust,ignore (partial-example)
1636impl !Trait for Type {}
1637```
1638
1639Example:
1640
1641```rust
1642#![feature(negative_impls)]
1643#![feature(auto_traits)]
1644
1645auto trait Valid {}
1646
1647struct True;
1648struct False;
1649
1650impl !Valid for False {}
1651
1652struct MaybeValid<T>(T);
1653
1654fn must_be_valid<T: Valid>(_t: T) { }
1655
1656fn main() {
1657 // works
1658 must_be_valid( MaybeValid(True) );
1659
1660 // compiler error - trait bound not satisfied
1661 // must_be_valid( MaybeValid(False) );
1662}
1663```
1664
1665## Automatic trait implementations
1666
1667When a type is declared as an `auto trait`, we will automatically
1668create impls for every struct/enum/union, unless an explicit impl is
1669provided. These automatic impls contain a where clause for each field
1670of the form `T: AutoTrait`, where `T` is the type of the field and
1671`AutoTrait` is the auto trait in question. As an example, consider the
1672struct `List` and the auto trait `Send`:
1673
1674```rust
1675struct List<T> {
1676 data: T,
1677 next: Option<Box<List<T>>>,
1678}
1679```
1680
1681Presuming that there is no explicit impl of `Send` for `List`, the
1682compiler will supply an automatic impl of the form:
1683
1684```rust
1685struct List<T> {
1686 data: T,
1687 next: Option<Box<List<T>>>,
1688}
1689
1690unsafe impl<T> Send for List<T>
1691where
1692 T: Send, // from the field `data`
1693 Option<Box<List<T>>>: Send, // from the field `next`
1694{ }
1695```
1696
1697Explicit impls may be either positive or negative. They take the form:
1698
1699```rust,ignore (partial-example)
1700impl<...> AutoTrait for StructName<..> { }
1701impl<...> !AutoTrait for StructName<..> { }
1702```
1703
1704## Coinduction: Auto traits permit cyclic matching
1705
1706Unlike ordinary trait matching, auto traits are **coinductive**. This
1707means, in short, that cycles which occur in trait matching are
1708considered ok. As an example, consider the recursive struct `List`
1709introduced in the previous section. In attempting to determine whether
1710`List: Send`, we would wind up in a cycle: to apply the impl, we must
1711show that `Option<Box<List>>: Send`, which will in turn require
1712`Box<List>: Send` and then finally `List: Send` again. Under ordinary
1713trait matching, this cycle would be an error, but for an auto trait it
1714is considered a successful match.
1715
1716## Items
1717
1718Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.
1719
1720## Supertraits
1721
1722Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.
3511"##, 1723"##,
3512 }, 1724 },
3513 LintCompletion { 1725 Lint {
3514 label: "flt2dec", 1726 label: "box_patterns",
3515 description: r##"# `flt2dec` 1727 description: r##"# `box_patterns`
1728
1729The tracking issue for this feature is: [#29641]
1730
1731[#29641]: https://github.com/rust-lang/rust/issues/29641
1732
1733See also [`box_syntax`](box-syntax.md)
1734
1735------------------------
1736
1737Box patterns let you match on `Box<T>`s:
1738
1739
1740```rust
1741#![feature(box_patterns)]
1742
1743fn main() {
1744 let b = Some(Box::new(5));
1745 match b {
1746 Some(box n) if n < 0 => {
1747 println!("Box contains negative number {}", n);
1748 },
1749 Some(box n) if n >= 0 => {
1750 println!("Box contains non-negative number {}", n);
1751 },
1752 None => {
1753 println!("No box");
1754 },
1755 _ => unreachable!()
1756 }
1757}
1758```
1759"##,
1760 },
1761 Lint {
1762 label: "box_syntax",
1763 description: r##"# `box_syntax`
1764
1765The tracking issue for this feature is: [#49733]
1766
1767[#49733]: https://github.com/rust-lang/rust/issues/49733
1768
1769See also [`box_patterns`](box-patterns.md)
1770
1771------------------------
1772
1773Currently the only stable way to create a `Box` is via the `Box::new` method.
1774Also it is not possible in stable Rust to destructure a `Box` in a match
1775pattern. The unstable `box` keyword can be used to create a `Box`. An example
1776usage would be:
1777
1778```rust
1779#![feature(box_syntax)]
1780
1781fn main() {
1782 let b = box 5;
1783}
1784```
1785"##,
1786 },
1787 Lint {
1788 label: "c_unwind",
1789 description: r##"# `c_unwind`
1790
1791The tracking issue for this feature is: [#74990]
1792
1793[#74990]: https://github.com/rust-lang/rust/issues/74990
1794
1795------------------------
1796
1797Introduces four new ABI strings: "C-unwind", "stdcall-unwind",
1798"thiscall-unwind", and "system-unwind". These enable unwinding from other
1799languages (such as C++) into Rust frames and from Rust into other languages.
1800
1801See [RFC 2945] for more information.
1802
1803[RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
1804"##,
1805 },
1806 Lint {
1807 label: "c_variadic",
1808 description: r##"# `c_variadic`
1809
1810The tracking issue for this feature is: [#44930]
1811
1812[#44930]: https://github.com/rust-lang/rust/issues/44930
1813
1814------------------------
1815
1816The `c_variadic` language feature enables C-variadic functions to be
1817defined in Rust. The may be called both from within Rust and via FFI.
1818
1819## Examples
1820
1821```rust
1822#![feature(c_variadic)]
1823
1824pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize {
1825 let mut sum = 0;
1826 for _ in 0..n {
1827 sum += args.arg::<usize>();
1828 }
1829 sum
1830}
1831```
1832"##,
1833 },
1834 Lint {
1835 label: "c_variadic",
1836 description: r##"# `c_variadic`
1837
1838The tracking issue for this feature is: [#44930]
1839
1840[#44930]: https://github.com/rust-lang/rust/issues/44930
1841
1842------------------------
1843
1844The `c_variadic` library feature exposes the `VaList` structure,
1845Rust's analogue of C's `va_list` type.
1846
1847## Examples
1848
1849```rust
1850#![feature(c_variadic)]
1851
1852use std::ffi::VaList;
1853
1854pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize {
1855 let mut sum = 0;
1856 for _ in 0..n {
1857 sum += args.arg::<usize>();
1858 }
1859 sum
1860}
1861```
1862"##,
1863 },
1864 Lint {
1865 label: "c_void_variant",
1866 description: r##"# `c_void_variant`
3516 1867
3517This feature is internal to the Rust compiler and is not intended for general use. 1868This feature is internal to the Rust compiler and is not intended for general use.
3518 1869
3519------------------------ 1870------------------------
3520"##, 1871"##,
3521 }, 1872 },
3522 LintCompletion { 1873 Lint {
3523 label: "global_asm", 1874 label: "cfg_panic",
3524 description: r##"# `global_asm` 1875 description: r##"# `cfg_panic`
3525 1876
3526The tracking issue for this feature is: [#35119] 1877The tracking issue for this feature is: [#77443]
3527 1878
3528[#35119]: https://github.com/rust-lang/rust/issues/35119 1879[#77443]: https://github.com/rust-lang/rust/issues/77443
3529 1880
3530------------------------ 1881------------------------
3531 1882
3532The `global_asm!` macro allows the programmer to write arbitrary 1883The `cfg_panic` feature makes it possible to execute different code
3533assembly outside the scope of a function body, passing it through 1884depending on the panic strategy.
3534`rustc` and `llvm` to the assembler. The macro is a no-frills
3535interface to LLVM's concept of [module-level inline assembly]. That is,
3536all caveats applicable to LLVM's module-level inline assembly apply
3537to `global_asm!`.
3538 1885
3539[module-level inline assembly]: http://llvm.org/docs/LangRef.html#module-level-inline-assembly 1886Possible values at the moment are `"unwind"` or `"abort"`, although
1887it is possible that new panic strategies may be added to Rust in the
1888future.
3540 1889
3541`global_asm!` fills a role not currently satisfied by either `asm!` 1890## Examples
3542or `#[naked]` functions. The programmer has _all_ features of the
3543assembler at their disposal. The linker will expect to resolve any
3544symbols defined in the inline assembly, modulo any symbols marked as
3545external. It also means syntax for directives and assembly follow the
3546conventions of the assembler in your toolchain.
3547 1891
3548A simple usage looks like this: 1892```rust
1893#![feature(cfg_panic)]
3549 1894
3550```rust,ignore (requires-external-file) 1895#[cfg(panic = "unwind")]
3551#![feature(global_asm)] 1896fn a() {
3552# // you also need relevant target_arch cfgs 1897 // ...
3553global_asm!(include_str!("something_neato.s")); 1898}
1899
1900#[cfg(not(panic = "unwind"))]
1901fn a() {
1902 // ...
1903}
1904
1905fn b() {
1906 if cfg!(panic = "abort") {
1907 // ...
1908 } else {
1909 // ...
1910 }
1911}
3554``` 1912```
1913"##,
1914 },
1915 Lint {
1916 label: "cfg_sanitize",
1917 description: r##"# `cfg_sanitize`
3555 1918
3556And a more complicated usage looks like this: 1919The tracking issue for this feature is: [#39699]
3557 1920
3558```rust,no_run 1921[#39699]: https://github.com/rust-lang/rust/issues/39699
3559#![feature(global_asm)]
3560# #[cfg(any(target_arch="x86", target_arch="x86_64"))]
3561# mod x86 {
3562 1922
3563pub mod sally { 1923------------------------
3564 global_asm!(r#"
3565 .global foo
3566 foo:
3567 jmp baz
3568 "#);
3569 1924
3570 #[no_mangle] 1925The `cfg_sanitize` feature makes it possible to execute different code
3571 pub unsafe extern "C" fn baz() {} 1926depending on whether a particular sanitizer is enabled or not.
1927
1928## Examples
1929
1930```rust
1931#![feature(cfg_sanitize)]
1932
1933#[cfg(sanitize = "thread")]
1934fn a() {
1935 // ...
3572} 1936}
3573 1937
3574// the symbols `foo` and `bar` are global, no matter where 1938#[cfg(not(sanitize = "thread"))]
3575// `global_asm!` was used. 1939fn a() {
3576extern "C" { 1940 // ...
3577 fn foo();
3578 fn bar();
3579} 1941}
3580 1942
3581pub mod harry { 1943fn b() {
3582 global_asm!(r#" 1944 if cfg!(sanitize = "leak") {
3583 .global bar 1945 // ...
3584 bar: 1946 } else {
3585 jmp quux 1947 // ...
3586 "#); 1948 }
1949}
1950```
1951"##,
1952 },
1953 Lint {
1954 label: "cfg_version",
1955 description: r##"# `cfg_version`
3587 1956
3588 #[no_mangle] 1957The tracking issue for this feature is: [#64796]
3589 pub unsafe extern "C" fn quux() {} 1958
1959[#64796]: https://github.com/rust-lang/rust/issues/64796
1960
1961------------------------
1962
1963The `cfg_version` feature makes it possible to execute different code
1964depending on the compiler version. It will return true if the compiler
1965version is greater than or equal to the specified version.
1966
1967## Examples
1968
1969```rust
1970#![feature(cfg_version)]
1971
1972#[cfg(version("1.42"))] // 1.42 and above
1973fn a() {
1974 // ...
1975}
1976
1977#[cfg(not(version("1.42")))] // 1.41 and below
1978fn a() {
1979 // ...
1980}
1981
1982fn b() {
1983 if cfg!(version("1.42")) {
1984 // ...
1985 } else {
1986 // ...
1987 }
3590} 1988}
3591# }
3592``` 1989```
1990"##,
1991 },
1992 Lint {
1993 label: "char_error_internals",
1994 description: r##"# `char_error_internals`
3593 1995
3594You may use `global_asm!` multiple times, anywhere in your crate, in 1996This feature is internal to the Rust compiler and is not intended for general use.
3595whatever way suits you. The effect is as if you concatenated all
3596usages and placed the larger, single usage in the crate root.
3597 1997
3598------------------------ 1998------------------------
1999"##,
2000 },
2001 Lint {
2002 label: "cmse_nonsecure_entry",
2003 description: r##"# `cmse_nonsecure_entry`
3599 2004
3600If you don't need quite as much power and flexibility as 2005The tracking issue for this feature is: [#75835]
3601`global_asm!` provides, and you don't mind restricting your inline 2006
3602assembly to `fn` bodies only, you might try the 2007[#75835]: https://github.com/rust-lang/rust/issues/75835
3603[asm](asm.md) feature instead. 2008
2009------------------------
2010
2011The [TrustZone-M
2012feature](https://developer.arm.com/documentation/100690/latest/) is available
2013for targets with the Armv8-M architecture profile (`thumbv8m` in their target
2014name).
2015LLVM, the Rust compiler and the linker are providing
2016[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
2017TrustZone-M feature.
2018
2019One of the things provided, with this unstable feature, is the
2020`cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an
2021entry function (see [section
20225.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
2023With this attribute, the compiler will do the following:
2024* add a special symbol on the function which is the `__acle_se_` prefix and the
2025 standard function name
2026* constrain the number of parameters to avoid using the Non-Secure stack
2027* before returning from the function, clear registers that might contain Secure
2028 information
2029* use the `BXNS` instruction to return
2030
2031Because the stack can not be used to pass parameters, there will be compilation
2032errors if:
2033* the total size of all parameters is too big (for example more than four 32
2034 bits integers)
2035* the entry function is not using a C ABI
2036
2037The special symbol `__acle_se_` will be used by the linker to generate a secure
2038gateway veneer.
2039
2040<!-- NOTE(ignore) this example is specific to thumbv8m targets -->
2041
2042``` rust,ignore
2043#![feature(cmse_nonsecure_entry)]
2044
2045#[no_mangle]
2046#[cmse_nonsecure_entry]
2047pub extern "C" fn entry_function(input: u32) -> u32 {
2048 input + 6
2049}
2050```
2051
2052``` text
2053$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs
2054$ arm-none-eabi-objdump -D function.o
2055
205600000000 <entry_function>:
2057 0: b580 push {r7, lr}
2058 2: 466f mov r7, sp
2059 4: b082 sub sp, #8
2060 6: 9001 str r0, [sp, #4]
2061 8: 1d81 adds r1, r0, #6
2062 a: 460a mov r2, r1
2063 c: 4281 cmp r1, r0
2064 e: 9200 str r2, [sp, #0]
2065 10: d30b bcc.n 2a <entry_function+0x2a>
2066 12: e7ff b.n 14 <entry_function+0x14>
2067 14: 9800 ldr r0, [sp, #0]
2068 16: b002 add sp, #8
2069 18: e8bd 4080 ldmia.w sp!, {r7, lr}
2070 1c: 4671 mov r1, lr
2071 1e: 4672 mov r2, lr
2072 20: 4673 mov r3, lr
2073 22: 46f4 mov ip, lr
2074 24: f38e 8800 msr CPSR_f, lr
2075 28: 4774 bxns lr
2076 2a: f240 0000 movw r0, #0
2077 2e: f2c0 0000 movt r0, #0
2078 32: f240 0200 movw r2, #0
2079 36: f2c0 0200 movt r2, #0
2080 3a: 211c movs r1, #28
2081 3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E>
2082 40: defe udf #254 ; 0xfe
2083```
3604"##, 2084"##,
3605 }, 2085 },
3606 LintCompletion { 2086 Lint {
3607 label: "derive_eq", 2087 label: "compiler_builtins",
3608 description: r##"# `derive_eq` 2088 description: r##"# `compiler_builtins`
2089
2090This feature is internal to the Rust compiler and is not intended for general use.
2091
2092------------------------
2093"##,
2094 },
2095 Lint {
2096 label: "concat_idents",
2097 description: r##"# `concat_idents`
2098
2099The tracking issue for this feature is: [#29599]
2100
2101[#29599]: https://github.com/rust-lang/rust/issues/29599
2102
2103------------------------
2104
2105The `concat_idents` feature adds a macro for concatenating multiple identifiers
2106into one identifier.
2107
2108## Examples
2109
2110```rust
2111#![feature(concat_idents)]
2112
2113fn main() {
2114 fn foobar() -> u32 { 23 }
2115 let f = concat_idents!(foo, bar);
2116 assert_eq!(f(), 23);
2117}
2118```
2119"##,
2120 },
2121 Lint {
2122 label: "const_eval_limit",
2123 description: r##"# `const_eval_limit`
2124
2125The tracking issue for this feature is: [#67217]
2126
2127[#67217]: https://github.com/rust-lang/rust/issues/67217
2128
2129The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`.
2130"##,
2131 },
2132 Lint {
2133 label: "core_intrinsics",
2134 description: r##"# `core_intrinsics`
2135
2136This feature is internal to the Rust compiler and is not intended for general use.
2137
2138------------------------
2139"##,
2140 },
2141 Lint {
2142 label: "core_panic",
2143 description: r##"# `core_panic`
2144
2145This feature is internal to the Rust compiler and is not intended for general use.
2146
2147------------------------
2148"##,
2149 },
2150 Lint {
2151 label: "core_private_bignum",
2152 description: r##"# `core_private_bignum`
3609 2153
3610This feature is internal to the Rust compiler and is not intended for general use. 2154This feature is internal to the Rust compiler and is not intended for general use.
3611 2155
3612------------------------ 2156------------------------
3613"##, 2157"##,
3614 }, 2158 },
3615 LintCompletion { 2159 Lint {
2160 label: "core_private_diy_float",
2161 description: r##"# `core_private_diy_float`
2162
2163This feature is internal to the Rust compiler and is not intended for general use.
2164
2165------------------------
2166"##,
2167 },
2168 Lint {
2169 label: "crate_visibility_modifier",
2170 description: r##"# `crate_visibility_modifier`
2171
2172The tracking issue for this feature is: [#53120]
2173
2174[#53120]: https://github.com/rust-lang/rust/issues/53120
2175
2176-----
2177
2178The `crate_visibility_modifier` feature allows the `crate` keyword to be used
2179as a visibility modifier synonymous to `pub(crate)`, indicating that a type
2180(function, _&c._) is to be visible to the entire enclosing crate, but not to
2181other crates.
2182
2183```rust
2184#![feature(crate_visibility_modifier)]
2185
2186crate struct Foo {
2187 bar: usize,
2188}
2189```
2190"##,
2191 },
2192 Lint {
2193 label: "custom_test_frameworks",
2194 description: r##"# `custom_test_frameworks`
2195
2196The tracking issue for this feature is: [#50297]
2197
2198[#50297]: https://github.com/rust-lang/rust/issues/50297
2199
2200------------------------
2201
2202The `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`.
2203Any function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`)
2204and be passed to the test runner determined by the `#![test_runner]` crate attribute.
2205
2206```rust
2207#![feature(custom_test_frameworks)]
2208#![test_runner(my_runner)]
2209
2210fn my_runner(tests: &[&i32]) {
2211 for t in tests {
2212 if **t == 0 {
2213 println!("PASSED");
2214 } else {
2215 println!("FAILED");
2216 }
2217 }
2218}
2219
2220#[test_case]
2221const WILL_PASS: i32 = 0;
2222
2223#[test_case]
2224const WILL_FAIL: i32 = 4;
2225```
2226"##,
2227 },
2228 Lint {
2229 label: "dec2flt",
2230 description: r##"# `dec2flt`
2231
2232This feature is internal to the Rust compiler and is not intended for general use.
2233
2234------------------------
2235"##,
2236 },
2237 Lint {
3616 label: "default_free_fn", 2238 label: "default_free_fn",
3617 description: r##"# `default_free_fn` 2239 description: r##"# `default_free_fn`
3618 2240
@@ -3663,75 +2285,331 @@ fn main() {
3663``` 2285```
3664"##, 2286"##,
3665 }, 2287 },
3666 LintCompletion { 2288 Lint {
3667 label: "char_error_internals", 2289 label: "derive_clone_copy",
3668 description: r##"# `char_error_internals` 2290 description: r##"# `derive_clone_copy`
3669 2291
3670This feature is internal to the Rust compiler and is not intended for general use. 2292This feature is internal to the Rust compiler and is not intended for general use.
3671 2293
3672------------------------ 2294------------------------
3673"##, 2295"##,
3674 }, 2296 },
3675 LintCompletion { 2297 Lint {
3676 label: "libstd_sys_internals", 2298 label: "derive_eq",
3677 description: r##"# `libstd_sys_internals` 2299 description: r##"# `derive_eq`
3678 2300
3679This feature is internal to the Rust compiler and is not intended for general use. 2301This feature is internal to the Rust compiler and is not intended for general use.
3680 2302
3681------------------------ 2303------------------------
3682"##, 2304"##,
3683 }, 2305 },
3684 LintCompletion { 2306 Lint {
3685 label: "is_sorted", 2307 label: "doc_cfg",
3686 description: r##"# `is_sorted` 2308 description: r##"# `doc_cfg`
3687 2309
3688The tracking issue for this feature is: [#53485] 2310The tracking issue for this feature is: [#43781]
3689 2311
3690[#53485]: https://github.com/rust-lang/rust/issues/53485 2312------
3691 2313
3692------------------------ 2314The `doc_cfg` feature allows an API be documented as only available in some specific platforms.
2315This attribute has two effects:
3693 2316
3694Add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to `[T]`; 23171. In the annotated item's documentation, there will be a message saying "This is supported on
3695add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to 2318 (platform) only".
3696`Iterator`. 2319
23202. The item's doc-tests will only run on the specific platform.
2321
2322In addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a
2323special conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your
2324crate.
2325
2326This feature was introduced as part of PR [#43348] to allow the platform-specific parts of the
2327standard library be documented.
2328
2329```rust
2330#![feature(doc_cfg)]
2331
2332#[cfg(any(windows, doc))]
2333#[doc(cfg(windows))]
2334/// The application's icon in the notification area (a.k.a. system tray).
2335///
2336/// # Examples
2337///
2338/// ```no_run
2339/// extern crate my_awesome_ui_library;
2340/// use my_awesome_ui_library::current_app;
2341/// use my_awesome_ui_library::windows::notification;
2342///
2343/// let icon = current_app().get::<notification::Icon>();
2344/// icon.show();
2345/// icon.show_message("Hello");
2346/// ```
2347pub struct Icon {
2348 // ...
2349}
2350```
2351
2352[#43781]: https://github.com/rust-lang/rust/issues/43781
2353[#43348]: https://github.com/rust-lang/rust/issues/43348
3697"##, 2354"##,
3698 }, 2355 },
3699 LintCompletion { 2356 Lint {
3700 label: "c_void_variant", 2357 label: "doc_masked",
3701 description: r##"# `c_void_variant` 2358 description: r##"# `doc_masked`
2359
2360The tracking issue for this feature is: [#44027]
2361
2362-----
2363
2364The `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists
2365of trait implementations. The specifics of the feature are as follows:
2366
23671. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute,
2368 it marks the crate as being masked.
2369
23702. When listing traits a given type implements, rustdoc ensures that traits from masked crates are
2371 not emitted into the documentation.
2372
23733. When listing types that implement a given trait, rustdoc ensures that types from masked crates
2374 are not emitted into the documentation.
2375
2376This feature was introduced in PR [#44026] to ensure that compiler-internal and
2377implementation-specific types and traits were not included in the standard library's documentation.
2378Such types would introduce broken links into the documentation.
2379
2380[#44026]: https://github.com/rust-lang/rust/pull/44026
2381[#44027]: https://github.com/rust-lang/rust/pull/44027
2382"##,
2383 },
2384 Lint {
2385 label: "doc_notable_trait",
2386 description: r##"# `doc_notable_trait`
2387
2388The tracking issue for this feature is: [#45040]
2389
2390The `doc_notable_trait` feature allows the use of the `#[doc(notable_trait)]`
2391attribute, which will display the trait in a "Notable traits" dialog for
2392functions returning types that implement the trait. For example, this attribute
2393is applied to the `Iterator`, `Future`, `io::Read`, and `io::Write` traits in
2394the standard library.
2395
2396You can do this on your own traits like so:
2397
2398```
2399#![feature(doc_notable_trait)]
2400
2401#[doc(notable_trait)]
2402pub trait MyTrait {}
2403
2404pub struct MyStruct;
2405impl MyTrait for MyStruct {}
2406
2407/// The docs for this function will have a button that displays a dialog about
2408/// `MyStruct` implementing `MyTrait`.
2409pub fn my_fn() -> MyStruct { MyStruct }
2410```
2411
2412This feature was originally implemented in PR [#45039].
2413
2414See also its documentation in [the rustdoc book][rustdoc-book-notable_trait].
2415
2416[#45040]: https://github.com/rust-lang/rust/issues/45040
2417[#45039]: https://github.com/rust-lang/rust/pull/45039
2418[rustdoc-book-notable_trait]: ../../rustdoc/unstable-features.html#adding-your-trait-to-the-notable-traits-dialog
2419"##,
2420 },
2421 Lint {
2422 label: "fd",
2423 description: r##"# `fd`
3702 2424
3703This feature is internal to the Rust compiler and is not intended for general use. 2425This feature is internal to the Rust compiler and is not intended for general use.
3704 2426
3705------------------------ 2427------------------------
3706"##, 2428"##,
3707 }, 2429 },
3708 LintCompletion { 2430 Lint {
3709 label: "concat_idents", 2431 label: "fd_read",
3710 description: r##"# `concat_idents` 2432 description: r##"# `fd_read`
3711 2433
3712The tracking issue for this feature is: [#29599] 2434This feature is internal to the Rust compiler and is not intended for general use.
3713 2435
3714[#29599]: https://github.com/rust-lang/rust/issues/29599 2436------------------------
2437"##,
2438 },
2439 Lint {
2440 label: "ffi_const",
2441 description: r##"# `ffi_const`
2442
2443The tracking issue for this feature is: [#58328]
2444
2445------
2446
2447The `#[ffi_const]` attribute applies clang's `const` attribute to foreign
2448functions declarations.
2449
2450That is, `#[ffi_const]` functions shall have no effects except for its return
2451value, which can only depend on the values of the function parameters, and is
2452not affected by changes to the observable state of the program.
2453
2454Applying the `#[ffi_const]` attribute to a function that violates these
2455requirements is undefined behaviour.
2456
2457This attribute enables Rust to perform common optimizations, like sub-expression
2458elimination, and it can avoid emitting some calls in repeated invocations of the
2459function with the same argument values regardless of other operations being
2460performed in between these functions calls (as opposed to `#[ffi_pure]`
2461functions).
2462
2463## Pitfalls
2464
2465A `#[ffi_const]` function can only read global memory that would not affect
2466its return value for the whole execution of the program (e.g. immutable global
2467memory). `#[ffi_const]` functions are referentially-transparent and therefore
2468more strict than `#[ffi_pure]` functions.
2469
2470A common pitfall involves applying the `#[ffi_const]` attribute to a
2471function that reads memory through pointer arguments which do not necessarily
2472point to immutable global memory.
2473
2474A `#[ffi_const]` function that returns unit has no effect on the abstract
2475machine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`.
2476
2477A `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a
2478call to `abort`) nor by infinite loops.
2479
2480When translating C headers to Rust FFI, it is worth verifying for which targets
2481the `const` attribute is enabled in those headers, and using the appropriate
2482`cfg` macros in the Rust side to match those definitions. While the semantics of
2483`const` are implemented identically by many C and C++ compilers, e.g., clang,
2484[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
2485implemented in this way on all of them. It is therefore also worth verifying
2486that the semantics of the C toolchain used to compile the binary being linked
2487against are compatible with those of the `#[ffi_const]`.
2488
2489[#58328]: https://github.com/rust-lang/rust/issues/58328
2490[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html
2491[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute
2492[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm
2493"##,
2494 },
2495 Lint {
2496 label: "ffi_pure",
2497 description: r##"# `ffi_pure`
2498
2499The tracking issue for this feature is: [#58329]
2500
2501------
2502
2503The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign
2504functions declarations.
2505
2506That is, `#[ffi_pure]` functions shall have no effects except for its return
2507value, which shall not change across two consecutive function calls with
2508the same parameters.
2509
2510Applying the `#[ffi_pure]` attribute to a function that violates these
2511requirements is undefined behavior.
2512
2513This attribute enables Rust to perform common optimizations, like sub-expression
2514elimination and loop optimizations. Some common examples of pure functions are
2515`strlen` or `memcmp`.
2516
2517These optimizations are only applicable when the compiler can prove that no
2518program state observable by the `#[ffi_pure]` function has changed between calls
2519of the function, which could alter the result. See also the `#[ffi_const]`
2520attribute, which provides stronger guarantees regarding the allowable behavior
2521of a function, enabling further optimization.
2522
2523## Pitfalls
2524
2525A `#[ffi_pure]` function can read global memory through the function
2526parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not
2527referentially-transparent, and are therefore more relaxed than `#[ffi_const]`
2528functions.
2529
2530However, accessing global memory through volatile or atomic reads can violate the
2531requirement that two consecutive function calls shall return the same value.
2532
2533A `pure` function that returns unit has no effect on the abstract machine's
2534state.
2535
2536A `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a
2537call to `abort`) nor by infinite loops.
2538
2539When translating C headers to Rust FFI, it is worth verifying for which targets
2540the `pure` attribute is enabled in those headers, and using the appropriate
2541`cfg` macros in the Rust side to match those definitions. While the semantics of
2542`pure` are implemented identically by many C and C++ compilers, e.g., clang,
2543[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily
2544implemented in this way on all of them. It is therefore also worth verifying
2545that the semantics of the C toolchain used to compile the binary being linked
2546against are compatible with those of the `#[ffi_pure]`.
2547
2548
2549[#58329]: https://github.com/rust-lang/rust/issues/58329
2550[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html
2551[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute
2552[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm
2553"##,
2554 },
2555 Lint {
2556 label: "flt2dec",
2557 description: r##"# `flt2dec`
2558
2559This feature is internal to the Rust compiler and is not intended for general use.
2560
2561------------------------
2562"##,
2563 },
2564 Lint {
2565 label: "fmt_internals",
2566 description: r##"# `fmt_internals`
2567
2568This feature is internal to the Rust compiler and is not intended for general use.
3715 2569
3716------------------------ 2570------------------------
2571"##,
2572 },
2573 Lint {
2574 label: "fn_traits",
2575 description: r##"# `fn_traits`
3717 2576
3718The `concat_idents` feature adds a macro for concatenating multiple identifiers 2577The tracking issue for this feature is [#29625]
3719into one identifier.
3720 2578
3721## Examples 2579See Also: [`unboxed_closures`](../language-features/unboxed-closures.md)
2580
2581[#29625]: https://github.com/rust-lang/rust/issues/29625
2582
2583----
2584
2585The `fn_traits` feature allows for implementation of the [`Fn*`] traits
2586for creating custom closure-like types.
2587
2588[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
3722 2589
3723```rust 2590```rust
3724#![feature(concat_idents)] 2591#![feature(unboxed_closures)]
2592#![feature(fn_traits)]
2593
2594struct Adder {
2595 a: u32
2596}
2597
2598impl FnOnce<(u32, )> for Adder {
2599 type Output = u32;
2600 extern "rust-call" fn call_once(self, b: (u32, )) -> Self::Output {
2601 self.a + b.0
2602 }
2603}
3725 2604
3726fn main() { 2605fn main() {
3727 fn foobar() -> u32 { 23 } 2606 let adder = Adder { a: 3 };
3728 let f = concat_idents!(foo, bar); 2607 assert_eq!(adder(2), 5);
3729 assert_eq!(f(), 23);
3730} 2608}
3731``` 2609```
3732"##, 2610"##,
3733 }, 2611 },
3734 LintCompletion { 2612 Lint {
3735 label: "format_args_capture", 2613 label: "format_args_capture",
3736 description: r##"# `format_args_capture` 2614 description: r##"# `format_args_capture`
3737 2615
@@ -3782,16 +2660,914 @@ A non-exhaustive list of macros which benefit from this functionality include:
3782- macros in many thirdparty crates, such as `log` 2660- macros in many thirdparty crates, such as `log`
3783"##, 2661"##,
3784 }, 2662 },
3785 LintCompletion { 2663 Lint {
3786 label: "print_internals", 2664 label: "generators",
3787 description: r##"# `print_internals` 2665 description: r##"# `generators`
2666
2667The tracking issue for this feature is: [#43122]
2668
2669[#43122]: https://github.com/rust-lang/rust/issues/43122
2670
2671------------------------
2672
2673The `generators` feature gate in Rust allows you to define generator or
2674coroutine literals. A generator is a "resumable function" that syntactically
2675resembles a closure but compiles to much different semantics in the compiler
2676itself. The primary feature of a generator is that it can be suspended during
2677execution to be resumed at a later date. Generators use the `yield` keyword to
2678"return", and then the caller can `resume` a generator to resume execution just
2679after the `yield` keyword.
2680
2681Generators are an extra-unstable feature in the compiler right now. Added in
2682[RFC 2033] they're mostly intended right now as a information/constraint
2683gathering phase. The intent is that experimentation can happen on the nightly
2684compiler before actual stabilization. A further RFC will be required to
2685stabilize generators/coroutines and will likely contain at least a few small
2686tweaks to the overall design.
2687
2688[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
2689
2690A syntactical example of a generator is:
2691
2692```rust
2693#![feature(generators, generator_trait)]
2694
2695use std::ops::{Generator, GeneratorState};
2696use std::pin::Pin;
2697
2698fn main() {
2699 let mut generator = || {
2700 yield 1;
2701 return "foo"
2702 };
2703
2704 match Pin::new(&mut generator).resume(()) {
2705 GeneratorState::Yielded(1) => {}
2706 _ => panic!("unexpected value from resume"),
2707 }
2708 match Pin::new(&mut generator).resume(()) {
2709 GeneratorState::Complete("foo") => {}
2710 _ => panic!("unexpected value from resume"),
2711 }
2712}
2713```
2714
2715Generators are closure-like literals which can contain a `yield` statement. The
2716`yield` statement takes an optional expression of a value to yield out of the
2717generator. All generator literals implement the `Generator` trait in the
2718`std::ops` module. The `Generator` trait has one main method, `resume`, which
2719resumes execution of the generator at the previous suspension point.
2720
2721An example of the control flow of generators is that the following example
2722prints all numbers in order:
2723
2724```rust
2725#![feature(generators, generator_trait)]
2726
2727use std::ops::Generator;
2728use std::pin::Pin;
2729
2730fn main() {
2731 let mut generator = || {
2732 println!("2");
2733 yield;
2734 println!("4");
2735 };
2736
2737 println!("1");
2738 Pin::new(&mut generator).resume(());
2739 println!("3");
2740 Pin::new(&mut generator).resume(());
2741 println!("5");
2742}
2743```
2744
2745At this time the main intended use case of generators is an implementation
2746primitive for async/await syntax, but generators will likely be extended to
2747ergonomic implementations of iterators and other primitives in the future.
2748Feedback on the design and usage is always appreciated!
2749
2750### The `Generator` trait
2751
2752The `Generator` trait in `std::ops` currently looks like:
2753
2754```rust
2755# #![feature(arbitrary_self_types, generator_trait)]
2756# use std::ops::GeneratorState;
2757# use std::pin::Pin;
2758
2759pub trait Generator<R = ()> {
2760 type Yield;
2761 type Return;
2762 fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>;
2763}
2764```
2765
2766The `Generator::Yield` type is the type of values that can be yielded with the
2767`yield` statement. The `Generator::Return` type is the returned type of the
2768generator. This is typically the last expression in a generator's definition or
2769any value passed to `return` in a generator. The `resume` function is the entry
2770point for executing the `Generator` itself.
2771
2772The return value of `resume`, `GeneratorState`, looks like:
2773
2774```rust
2775pub enum GeneratorState<Y, R> {
2776 Yielded(Y),
2777 Complete(R),
2778}
2779```
2780
2781The `Yielded` variant indicates that the generator can later be resumed. This
2782corresponds to a `yield` point in a generator. The `Complete` variant indicates
2783that the generator is complete and cannot be resumed again. Calling `resume`
2784after a generator has returned `Complete` will likely result in a panic of the
2785program.
2786
2787### Closure-like semantics
2788
2789The closure-like syntax for generators alludes to the fact that they also have
2790closure-like semantics. Namely:
2791
2792* When created, a generator executes no code. A closure literal does not
2793 actually execute any of the closure's code on construction, and similarly a
2794 generator literal does not execute any code inside the generator when
2795 constructed.
2796
2797* Generators can capture outer variables by reference or by move, and this can
2798 be tweaked with the `move` keyword at the beginning of the closure. Like
2799 closures all generators will have an implicit environment which is inferred by
2800 the compiler. Outer variables can be moved into a generator for use as the
2801 generator progresses.
2802
2803* Generator literals produce a value with a unique type which implements the
2804 `std::ops::Generator` trait. This allows actual execution of the generator
2805 through the `Generator::resume` method as well as also naming it in return
2806 types and such.
2807
2808* Traits like `Send` and `Sync` are automatically implemented for a `Generator`
2809 depending on the captured variables of the environment. Unlike closures,
2810 generators also depend on variables live across suspension points. This means
2811 that although the ambient environment may be `Send` or `Sync`, the generator
2812 itself may not be due to internal variables live across `yield` points being
2813 not-`Send` or not-`Sync`. Note that generators do
2814 not implement traits like `Copy` or `Clone` automatically.
2815
2816* Whenever a generator is dropped it will drop all captured environment
2817 variables.
2818
2819### Generators as state machines
2820
2821In the compiler, generators are currently compiled as state machines. Each
2822`yield` expression will correspond to a different state that stores all live
2823variables over that suspension point. Resumption of a generator will dispatch on
2824the current state and then execute internally until a `yield` is reached, at
2825which point all state is saved off in the generator and a value is returned.
2826
2827Let's take a look at an example to see what's going on here:
2828
2829```rust
2830#![feature(generators, generator_trait)]
2831
2832use std::ops::Generator;
2833use std::pin::Pin;
2834
2835fn main() {
2836 let ret = "foo";
2837 let mut generator = move || {
2838 yield 1;
2839 return ret
2840 };
2841
2842 Pin::new(&mut generator).resume(());
2843 Pin::new(&mut generator).resume(());
2844}
2845```
2846
2847This generator literal will compile down to something similar to:
2848
2849```rust
2850#![feature(arbitrary_self_types, generators, generator_trait)]
2851
2852use std::ops::{Generator, GeneratorState};
2853use std::pin::Pin;
2854
2855fn main() {
2856 let ret = "foo";
2857 let mut generator = {
2858 enum __Generator {
2859 Start(&'static str),
2860 Yield1(&'static str),
2861 Done,
2862 }
2863
2864 impl Generator for __Generator {
2865 type Yield = i32;
2866 type Return = &'static str;
2867
2868 fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> {
2869 use std::mem;
2870 match mem::replace(&mut *self, __Generator::Done) {
2871 __Generator::Start(s) => {
2872 *self = __Generator::Yield1(s);
2873 GeneratorState::Yielded(1)
2874 }
2875
2876 __Generator::Yield1(s) => {
2877 *self = __Generator::Done;
2878 GeneratorState::Complete(s)
2879 }
2880
2881 __Generator::Done => {
2882 panic!("generator resumed after completion")
2883 }
2884 }
2885 }
2886 }
2887
2888 __Generator::Start(ret)
2889 };
2890
2891 Pin::new(&mut generator).resume(());
2892 Pin::new(&mut generator).resume(());
2893}
2894```
2895
2896Notably here we can see that the compiler is generating a fresh type,
2897`__Generator` in this case. This type has a number of states (represented here
2898as an `enum`) corresponding to each of the conceptual states of the generator.
2899At the beginning we're closing over our outer variable `foo` and then that
2900variable is also live over the `yield` point, so it's stored in both states.
2901
2902When the generator starts it'll immediately yield 1, but it saves off its state
2903just before it does so indicating that it has reached the yield point. Upon
2904resuming again we'll execute the `return ret` which returns the `Complete`
2905state.
2906
2907Here we can also note that the `Done` state, if resumed, panics immediately as
2908it's invalid to resume a completed generator. It's also worth noting that this
2909is just a rough desugaring, not a normative specification for what the compiler
2910does.
2911"##,
2912 },
2913 Lint {
2914 label: "global_asm",
2915 description: r##"# `global_asm`
2916
2917The tracking issue for this feature is: [#35119]
2918
2919[#35119]: https://github.com/rust-lang/rust/issues/35119
2920
2921------------------------
2922
2923The `global_asm!` macro allows the programmer to write arbitrary
2924assembly outside the scope of a function body, passing it through
2925`rustc` and `llvm` to the assembler. That is to say, `global_asm!` is
2926equivalent to assembling the asm with an external assembler and then
2927linking the resulting object file with the current crate.
2928
2929`global_asm!` fills a role not currently satisfied by either `asm!`
2930or `#[naked]` functions. The programmer has _all_ features of the
2931assembler at their disposal. The linker will expect to resolve any
2932symbols defined in the inline assembly, modulo any symbols marked as
2933external. It also means syntax for directives and assembly follow the
2934conventions of the assembler in your toolchain.
2935
2936A simple usage looks like this:
2937
2938```rust,ignore (requires-external-file)
2939#![feature(global_asm)]
2940# // you also need relevant target_arch cfgs
2941global_asm!(include_str!("something_neato.s"));
2942```
2943
2944And a more complicated usage looks like this:
2945
2946```rust,no_run
2947#![feature(global_asm)]
2948# #[cfg(any(target_arch="x86", target_arch="x86_64"))]
2949# mod x86 {
2950
2951pub mod sally {
2952 global_asm!(
2953 ".global foo",
2954 "foo:",
2955 "jmp baz",
2956 );
2957
2958 #[no_mangle]
2959 pub unsafe extern "C" fn baz() {}
2960}
2961
2962// the symbols `foo` and `bar` are global, no matter where
2963// `global_asm!` was used.
2964extern "C" {
2965 fn foo();
2966 fn bar();
2967}
2968
2969pub mod harry {
2970 global_asm!(
2971 ".global bar",
2972 "bar:",
2973 "jmp quux",
2974 );
2975
2976 #[no_mangle]
2977 pub unsafe extern "C" fn quux() {}
2978}
2979# }
2980```
2981
2982You may use `global_asm!` multiple times, anywhere in your crate, in
2983whatever way suits you. However, you should not rely on assembler state
2984(e.g. assembler macros) defined in one `global_asm!` to be available in
2985another one. It is implementation-defined whether the multiple usages
2986are concatenated into one or assembled separately.
2987
2988`global_asm!` also supports `const` operands like `asm!`, which allows
2989constants defined in Rust to be used in assembly code:
2990
2991```rust,no_run
2992#![feature(global_asm)]
2993# #[cfg(any(target_arch="x86", target_arch="x86_64"))]
2994# mod x86 {
2995const C: i32 = 1234;
2996global_asm!(
2997 ".global bar",
2998 "bar: .word {c}",
2999 c = const C,
3000);
3001# }
3002```
3003
3004The syntax for passing operands is the same as `asm!` except that only
3005`const` operands are allowed. Refer to the [asm](asm.md) documentation
3006for more details.
3007
3008On x86, the assembly code will use intel syntax by default. You can
3009override this by adding `options(att_syntax)` at the end of the macro
3010arguments list:
3011
3012```rust,no_run
3013#![feature(global_asm)]
3014# #[cfg(any(target_arch="x86", target_arch="x86_64"))]
3015# mod x86 {
3016global_asm!("movl ${}, %ecx", const 5, options(att_syntax));
3017// is equivalent to
3018global_asm!("mov ecx, {}", const 5);
3019# }
3020```
3021
3022------------------------
3023
3024If you don't need quite as much power and flexibility as
3025`global_asm!` provides, and you don't mind restricting your inline
3026assembly to `fn` bodies only, you might try the
3027[asm](asm.md) feature instead.
3028"##,
3029 },
3030 Lint {
3031 label: "impl_trait_in_bindings",
3032 description: r##"# `impl_trait_in_bindings`
3033
3034The tracking issue for this feature is: [#63065]
3035
3036[#63065]: https://github.com/rust-lang/rust/issues/63065
3037
3038------------------------
3039
3040The `impl_trait_in_bindings` feature gate lets you use `impl Trait` syntax in
3041`let`, `static`, and `const` bindings.
3042
3043A simple example is:
3044
3045```rust
3046#![feature(impl_trait_in_bindings)]
3047
3048use std::fmt::Debug;
3049
3050fn main() {
3051 let a: impl Debug + Clone = 42;
3052 let b = a.clone();
3053 println!("{:?}", b); // prints `42`
3054}
3055```
3056
3057Note however that because the types of `a` and `b` are opaque in the above
3058example, calling inherent methods or methods outside of the specified traits
3059(e.g., `a.abs()` or `b.abs()`) is not allowed, and yields an error.
3060"##,
3061 },
3062 Lint {
3063 label: "infer_static_outlives_requirements",
3064 description: r##"# `infer_static_outlives_requirements`
3065
3066The tracking issue for this feature is: [#54185]
3067
3068[#54185]: https://github.com/rust-lang/rust/issues/54185
3069
3070------------------------
3071The `infer_static_outlives_requirements` feature indicates that certain
3072`'static` outlives requirements can be inferred by the compiler rather than
3073stating them explicitly.
3074
3075Note: It is an accompanying feature to `infer_outlives_requirements`,
3076which must be enabled to infer outlives requirements.
3077
3078For example, currently generic struct definitions that contain
3079references, require where-clauses of the form T: 'static. By using
3080this feature the outlives predicates will be inferred, although
3081they may still be written explicitly.
3082
3083```rust,ignore (pseudo-Rust)
3084struct Foo<U> where U: 'static { // <-- currently required
3085 bar: Bar<U>
3086}
3087struct Bar<T: 'static> {
3088 x: T,
3089}
3090```
3091
3092
3093## Examples:
3094
3095```rust,ignore (pseudo-Rust)
3096#![feature(infer_outlives_requirements)]
3097#![feature(infer_static_outlives_requirements)]
3098
3099#[rustc_outlives]
3100// Implicitly infer U: 'static
3101struct Foo<U> {
3102 bar: Bar<U>
3103}
3104struct Bar<T: 'static> {
3105 x: T,
3106}
3107```
3108"##,
3109 },
3110 Lint {
3111 label: "inline_const",
3112 description: r##"# `inline_const`
3113
3114The tracking issue for this feature is: [#76001]
3115
3116------
3117
3118This feature allows you to use inline constant expressions. For example, you can
3119turn this code:
3120
3121```rust
3122# fn add_one(x: i32) -> i32 { x + 1 }
3123const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4;
3124
3125fn main() {
3126 let x = add_one(MY_COMPUTATION);
3127}
3128```
3129
3130into this code:
3131
3132```rust
3133#![feature(inline_const)]
3134
3135# fn add_one(x: i32) -> i32 { x + 1 }
3136fn main() {
3137 let x = add_one(const { 1 + 2 * 3 / 4 });
3138}
3139```
3140
3141You can also use inline constant expressions in patterns:
3142
3143```rust
3144#![feature(inline_const)]
3145
3146const fn one() -> i32 { 1 }
3147
3148let some_int = 3;
3149match some_int {
3150 const { 1 + 2 } => println!("Matched 1 + 2"),
3151 const { one() } => println!("Matched const fn returning 1"),
3152 _ => println!("Didn't match anything :("),
3153}
3154```
3155
3156[#76001]: https://github.com/rust-lang/rust/issues/76001
3157"##,
3158 },
3159 Lint {
3160 label: "int_error_internals",
3161 description: r##"# `int_error_internals`
3162
3163This feature is internal to the Rust compiler and is not intended for general use.
3164
3165------------------------
3166"##,
3167 },
3168 Lint {
3169 label: "internal_output_capture",
3170 description: r##"# `internal_output_capture`
3788 3171
3789This feature is internal to the Rust compiler and is not intended for general use. 3172This feature is internal to the Rust compiler and is not intended for general use.
3790 3173
3791------------------------ 3174------------------------
3792"##, 3175"##,
3793 }, 3176 },
3794 LintCompletion { 3177 Lint {
3178 label: "intra_doc_pointers",
3179 description: r##"# `intra-doc-pointers`
3180
3181The tracking issue for this feature is: [#80896]
3182
3183[#80896]: https://github.com/rust-lang/rust/issues/80896
3184
3185------------------------
3186
3187Rustdoc does not currently allow disambiguating between `*const` and `*mut`, and
3188raw pointers in intra-doc links are unstable until it does.
3189
3190```rust
3191#![feature(intra_doc_pointers)]
3192//! [pointer::add]
3193```
3194"##,
3195 },
3196 Lint {
3197 label: "intrinsics",
3198 description: r##"# `intrinsics`
3199
3200The tracking issue for this feature is: None.
3201
3202Intrinsics are never intended to be stable directly, but intrinsics are often
3203exported in some sort of stable manner. Prefer using the stable interfaces to
3204the intrinsic directly when you can.
3205
3206------------------------
3207
3208
3209These are imported as if they were FFI functions, with the special
3210`rust-intrinsic` ABI. For example, if one was in a freestanding
3211context, but wished to be able to `transmute` between types, and
3212perform efficient pointer arithmetic, one would import those functions
3213via a declaration like
3214
3215```rust
3216#![feature(intrinsics)]
3217# fn main() {}
3218
3219extern "rust-intrinsic" {
3220 fn transmute<T, U>(x: T) -> U;
3221
3222 fn offset<T>(dst: *const T, offset: isize) -> *const T;
3223}
3224```
3225
3226As with any other FFI functions, these are always `unsafe` to call.
3227"##,
3228 },
3229 Lint {
3230 label: "is_sorted",
3231 description: r##"# `is_sorted`
3232
3233The tracking issue for this feature is: [#53485]
3234
3235[#53485]: https://github.com/rust-lang/rust/issues/53485
3236
3237------------------------
3238
3239Add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to `[T]`;
3240add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to
3241`Iterator`.
3242"##,
3243 },
3244 Lint {
3245 label: "lang_items",
3246 description: r##"# `lang_items`
3247
3248The tracking issue for this feature is: None.
3249
3250------------------------
3251
3252The `rustc` compiler has certain pluggable operations, that is,
3253functionality that isn't hard-coded into the language, but is
3254implemented in libraries, with a special marker to tell the compiler
3255it exists. The marker is the attribute `#[lang = "..."]` and there are
3256various different values of `...`, i.e. various different 'lang
3257items'.
3258
3259For example, `Box` pointers require two lang items, one for allocation
3260and one for deallocation. A freestanding program that uses the `Box`
3261sugar for dynamic allocations via `malloc` and `free`:
3262
3263```rust,ignore (libc-is-finicky)
3264#![feature(lang_items, box_syntax, start, libc, core_intrinsics, rustc_private)]
3265#![no_std]
3266use core::intrinsics;
3267use core::panic::PanicInfo;
3268
3269extern crate libc;
3270
3271#[lang = "owned_box"]
3272pub struct Box<T>(*mut T);
3273
3274#[lang = "exchange_malloc"]
3275unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
3276 let p = libc::malloc(size as libc::size_t) as *mut u8;
3277
3278 // Check if `malloc` failed:
3279 if p as usize == 0 {
3280 intrinsics::abort();
3281 }
3282
3283 p
3284}
3285
3286#[lang = "box_free"]
3287unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
3288 libc::free(ptr as *mut libc::c_void)
3289}
3290
3291#[start]
3292fn main(_argc: isize, _argv: *const *const u8) -> isize {
3293 let _x = box 1;
3294
3295 0
3296}
3297
3298#[lang = "eh_personality"] extern fn rust_eh_personality() {}
3299#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } }
3300#[no_mangle] pub extern fn rust_eh_register_frames () {}
3301#[no_mangle] pub extern fn rust_eh_unregister_frames () {}
3302```
3303
3304Note the use of `abort`: the `exchange_malloc` lang item is assumed to
3305return a valid pointer, and so needs to do the check internally.
3306
3307Other features provided by lang items include:
3308
3309- overloadable operators via traits: the traits corresponding to the
3310 `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all
3311 marked with lang items; those specific four are `eq`, `ord`,
3312 `deref`, and `add` respectively.
3313- stack unwinding and general failure; the `eh_personality`,
3314 `panic` and `panic_bounds_check` lang items.
3315- the traits in `std::marker` used to indicate types of
3316 various kinds; lang items `send`, `sync` and `copy`.
3317- the marker types and variance indicators found in
3318 `std::marker`; lang items `covariant_type`,
3319 `contravariant_lifetime`, etc.
3320
3321Lang items are loaded lazily by the compiler; e.g. if one never uses
3322`Box` then there is no need to define functions for `exchange_malloc`
3323and `box_free`. `rustc` will emit an error when an item is needed
3324but not found in the current crate or any that it depends on.
3325
3326Most lang items are defined by `libcore`, but if you're trying to build
3327an executable without the standard library, you'll run into the need
3328for lang items. The rest of this page focuses on this use-case, even though
3329lang items are a bit broader than that.
3330
3331### Using libc
3332
3333In order to build a `#[no_std]` executable we will need libc as a dependency.
3334We can specify this using our `Cargo.toml` file:
3335
3336```toml
3337[dependencies]
3338libc = { version = "0.2.14", default-features = false }
3339```
3340
3341Note that the default features have been disabled. This is a critical step -
3342**the default features of libc include the standard library and so must be
3343disabled.**
3344
3345### Writing an executable without stdlib
3346
3347Controlling the entry point is possible in two ways: the `#[start]` attribute,
3348or overriding the default shim for the C `main` function with your own.
3349
3350The function marked `#[start]` is passed the command line parameters
3351in the same format as C:
3352
3353```rust,ignore (libc-is-finicky)
3354#![feature(lang_items, core_intrinsics, rustc_private)]
3355#![feature(start)]
3356#![no_std]
3357use core::intrinsics;
3358use core::panic::PanicInfo;
3359
3360// Pull in the system libc library for what crt0.o likely requires.
3361extern crate libc;
3362
3363// Entry point for this program.
3364#[start]
3365fn start(_argc: isize, _argv: *const *const u8) -> isize {
3366 0
3367}
3368
3369// These functions are used by the compiler, but not
3370// for a bare-bones hello world. These are normally
3371// provided by libstd.
3372#[lang = "eh_personality"]
3373#[no_mangle]
3374pub extern fn rust_eh_personality() {
3375}
3376
3377#[lang = "panic_impl"]
3378#[no_mangle]
3379pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
3380 unsafe { intrinsics::abort() }
3381}
3382```
3383
3384To override the compiler-inserted `main` shim, one has to disable it
3385with `#![no_main]` and then create the appropriate symbol with the
3386correct ABI and the correct name, which requires overriding the
3387compiler's name mangling too:
3388
3389```rust,ignore (libc-is-finicky)
3390#![feature(lang_items, core_intrinsics, rustc_private)]
3391#![feature(start)]
3392#![no_std]
3393#![no_main]
3394use core::intrinsics;
3395use core::panic::PanicInfo;
3396
3397// Pull in the system libc library for what crt0.o likely requires.
3398extern crate libc;
3399
3400// Entry point for this program.
3401#[no_mangle] // ensure that this symbol is called `main` in the output
3402pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {
3403 0
3404}
3405
3406// These functions are used by the compiler, but not
3407// for a bare-bones hello world. These are normally
3408// provided by libstd.
3409#[lang = "eh_personality"]
3410#[no_mangle]
3411pub extern fn rust_eh_personality() {
3412}
3413
3414#[lang = "panic_impl"]
3415#[no_mangle]
3416pub extern fn rust_begin_panic(info: &PanicInfo) -> ! {
3417 unsafe { intrinsics::abort() }
3418}
3419```
3420
3421In many cases, you may need to manually link to the `compiler_builtins` crate
3422when building a `no_std` binary. You may observe this via linker error messages
3423such as "```undefined reference to `__rust_probestack'```".
3424
3425## More about the language items
3426
3427The compiler currently makes a few assumptions about symbols which are
3428available in the executable to call. Normally these functions are provided by
3429the standard library, but without it you must define your own. These symbols
3430are called "language items", and they each have an internal name, and then a
3431signature that an implementation must conform to.
3432
3433The first of these functions, `rust_eh_personality`, is used by the failure
3434mechanisms of the compiler. This is often mapped to GCC's personality function
3435(see the [libstd implementation][unwind] for more information), but crates
3436which do not trigger a panic can be assured that this function is never
3437called. The language item's name is `eh_personality`.
3438
3439[unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs
3440
3441The second function, `rust_begin_panic`, is also used by the failure mechanisms of the
3442compiler. When a panic happens, this controls the message that's displayed on
3443the screen. While the language item's name is `panic_impl`, the symbol name is
3444`rust_begin_panic`.
3445
3446Finally, a `eh_catch_typeinfo` static is needed for certain targets which
3447implement Rust panics on top of C++ exceptions.
3448
3449## List of all language items
3450
3451This is a list of all language items in Rust along with where they are located in
3452the source code.
3453
3454- Primitives
3455 - `i8`: `libcore/num/mod.rs`
3456 - `i16`: `libcore/num/mod.rs`
3457 - `i32`: `libcore/num/mod.rs`
3458 - `i64`: `libcore/num/mod.rs`
3459 - `i128`: `libcore/num/mod.rs`
3460 - `isize`: `libcore/num/mod.rs`
3461 - `u8`: `libcore/num/mod.rs`
3462 - `u16`: `libcore/num/mod.rs`
3463 - `u32`: `libcore/num/mod.rs`
3464 - `u64`: `libcore/num/mod.rs`
3465 - `u128`: `libcore/num/mod.rs`
3466 - `usize`: `libcore/num/mod.rs`
3467 - `f32`: `libstd/f32.rs`
3468 - `f64`: `libstd/f64.rs`
3469 - `char`: `libcore/char.rs`
3470 - `slice`: `liballoc/slice.rs`
3471 - `str`: `liballoc/str.rs`
3472 - `const_ptr`: `libcore/ptr.rs`
3473 - `mut_ptr`: `libcore/ptr.rs`
3474 - `unsafe_cell`: `libcore/cell.rs`
3475- Runtime
3476 - `start`: `libstd/rt.rs`
3477 - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC)
3478 - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU)
3479 - `eh_personality`: `libpanic_unwind/seh.rs` (SEH)
3480 - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC)
3481 - `panic`: `libcore/panicking.rs`
3482 - `panic_bounds_check`: `libcore/panicking.rs`
3483 - `panic_impl`: `libcore/panicking.rs`
3484 - `panic_impl`: `libstd/panicking.rs`
3485- Allocations
3486 - `owned_box`: `liballoc/boxed.rs`
3487 - `exchange_malloc`: `liballoc/heap.rs`
3488 - `box_free`: `liballoc/heap.rs`
3489- Operands
3490 - `not`: `libcore/ops/bit.rs`
3491 - `bitand`: `libcore/ops/bit.rs`
3492 - `bitor`: `libcore/ops/bit.rs`
3493 - `bitxor`: `libcore/ops/bit.rs`
3494 - `shl`: `libcore/ops/bit.rs`
3495 - `shr`: `libcore/ops/bit.rs`
3496 - `bitand_assign`: `libcore/ops/bit.rs`
3497 - `bitor_assign`: `libcore/ops/bit.rs`
3498 - `bitxor_assign`: `libcore/ops/bit.rs`
3499 - `shl_assign`: `libcore/ops/bit.rs`
3500 - `shr_assign`: `libcore/ops/bit.rs`
3501 - `deref`: `libcore/ops/deref.rs`
3502 - `deref_mut`: `libcore/ops/deref.rs`
3503 - `index`: `libcore/ops/index.rs`
3504 - `index_mut`: `libcore/ops/index.rs`
3505 - `add`: `libcore/ops/arith.rs`
3506 - `sub`: `libcore/ops/arith.rs`
3507 - `mul`: `libcore/ops/arith.rs`
3508 - `div`: `libcore/ops/arith.rs`
3509 - `rem`: `libcore/ops/arith.rs`
3510 - `neg`: `libcore/ops/arith.rs`
3511 - `add_assign`: `libcore/ops/arith.rs`
3512 - `sub_assign`: `libcore/ops/arith.rs`
3513 - `mul_assign`: `libcore/ops/arith.rs`
3514 - `div_assign`: `libcore/ops/arith.rs`
3515 - `rem_assign`: `libcore/ops/arith.rs`
3516 - `eq`: `libcore/cmp.rs`
3517 - `ord`: `libcore/cmp.rs`
3518- Functions
3519 - `fn`: `libcore/ops/function.rs`
3520 - `fn_mut`: `libcore/ops/function.rs`
3521 - `fn_once`: `libcore/ops/function.rs`
3522 - `generator_state`: `libcore/ops/generator.rs`
3523 - `generator`: `libcore/ops/generator.rs`
3524- Other
3525 - `coerce_unsized`: `libcore/ops/unsize.rs`
3526 - `drop`: `libcore/ops/drop.rs`
3527 - `drop_in_place`: `libcore/ptr.rs`
3528 - `clone`: `libcore/clone.rs`
3529 - `copy`: `libcore/marker.rs`
3530 - `send`: `libcore/marker.rs`
3531 - `sized`: `libcore/marker.rs`
3532 - `unsize`: `libcore/marker.rs`
3533 - `sync`: `libcore/marker.rs`
3534 - `phantom_data`: `libcore/marker.rs`
3535 - `discriminant_kind`: `libcore/marker.rs`
3536 - `freeze`: `libcore/marker.rs`
3537 - `debug_trait`: `libcore/fmt/mod.rs`
3538 - `non_zero`: `libcore/nonzero.rs`
3539 - `arc`: `liballoc/sync.rs`
3540 - `rc`: `liballoc/rc.rs`
3541"##,
3542 },
3543 Lint {
3544 label: "libstd_sys_internals",
3545 description: r##"# `libstd_sys_internals`
3546
3547This feature is internal to the Rust compiler and is not intended for general use.
3548
3549------------------------
3550"##,
3551 },
3552 Lint {
3553 label: "libstd_thread_internals",
3554 description: r##"# `libstd_thread_internals`
3555
3556This feature is internal to the Rust compiler and is not intended for general use.
3557
3558------------------------
3559"##,
3560 },
3561 Lint {
3562 label: "link_cfg",
3563 description: r##"# `link_cfg`
3564
3565This feature is internal to the Rust compiler and is not intended for general use.
3566
3567------------------------
3568"##,
3569 },
3570 Lint {
3795 label: "llvm_asm", 3571 label: "llvm_asm",
3796 description: r##"# `llvm_asm` 3572 description: r##"# `llvm_asm`
3797 3573
@@ -3988,188 +3764,544 @@ If you need more power and don't mind losing some of the niceties of
3988`llvm_asm!`, check out [global_asm](global-asm.md). 3764`llvm_asm!`, check out [global_asm](global-asm.md).
3989"##, 3765"##,
3990 }, 3766 },
3991 LintCompletion { 3767 Lint {
3992 label: "core_intrinsics", 3768 label: "marker_trait_attr",
3993 description: r##"# `core_intrinsics` 3769 description: r##"# `marker_trait_attr`
3994 3770
3995This feature is internal to the Rust compiler and is not intended for general use. 3771The tracking issue for this feature is: [#29864]
3772
3773[#29864]: https://github.com/rust-lang/rust/issues/29864
3996 3774
3997------------------------ 3775------------------------
3776
3777Normally, Rust keeps you from adding trait implementations that could
3778overlap with each other, as it would be ambiguous which to use. This
3779feature, however, carves out an exception to that rule: a trait can
3780opt-in to having overlapping implementations, at the cost that those
3781implementations are not allowed to override anything (and thus the
3782trait itself cannot have any associated items, as they're pointless
3783when they'd need to do the same thing for every type anyway).
3784
3785```rust
3786#![feature(marker_trait_attr)]
3787
3788#[marker] trait CheapToClone: Clone {}
3789
3790impl<T: Copy> CheapToClone for T {}
3791
3792// These could potentially overlap with the blanket implementation above,
3793// so are only allowed because CheapToClone is a marker trait.
3794impl<T: CheapToClone, U: CheapToClone> CheapToClone for (T, U) {}
3795impl<T: CheapToClone> CheapToClone for std::ops::Range<T> {}
3796
3797fn cheap_clone<T: CheapToClone>(t: T) -> T {
3798 t.clone()
3799}
3800```
3801
3802This is expected to replace the unstable `overlapping_marker_traits`
3803feature, which applied to all empty traits (without needing an opt-in).
3998"##, 3804"##,
3999 }, 3805 },
4000 LintCompletion { 3806 Lint {
4001 label: "trace_macros", 3807 label: "native_link_modifiers",
4002 description: r##"# `trace_macros` 3808 description: r##"# `native_link_modifiers`
4003 3809
4004The tracking issue for this feature is [#29598]. 3810The tracking issue for this feature is: [#81490]
4005 3811
4006[#29598]: https://github.com/rust-lang/rust/issues/29598 3812[#81490]: https://github.com/rust-lang/rust/issues/81490
4007 3813
4008------------------------ 3814------------------------
4009 3815
4010With `trace_macros` you can trace the expansion of macros in your code. 3816The `native_link_modifiers` feature allows you to use the `modifiers` syntax with the `#[link(..)]` attribute.
4011 3817
4012## Examples 3818Modifiers are specified as a comma-delimited string with each modifier prefixed with either a `+` or `-` to indicate that the modifier is enabled or disabled, respectively. The last boolean value specified for a given modifier wins.
3819"##,
3820 },
3821 Lint {
3822 label: "native_link_modifiers_as_needed",
3823 description: r##"# `native_link_modifiers_as_needed`
4013 3824
4014```rust 3825The tracking issue for this feature is: [#81490]
4015#![feature(trace_macros)]
4016 3826
4017fn main() { 3827[#81490]: https://github.com/rust-lang/rust/issues/81490
4018 trace_macros!(true);
4019 println!("Hello, Rust!");
4020 trace_macros!(false);
4021}
4022```
4023 3828
4024The `cargo build` output: 3829------------------------
4025 3830
4026```txt 3831The `native_link_modifiers_as_needed` feature allows you to use the `as-needed` modifier.
4027note: trace_macro
4028 --> src/main.rs:5:5
4029 |
40305 | println!("Hello, Rust!");
4031 | ^^^^^^^^^^^^^^^^^^^^^^^^^
4032 |
4033 = note: expanding `println! { "Hello, Rust!" }`
4034 = note: to `print ! ( concat ! ( "Hello, Rust!" , "\n" ) )`
4035 = note: expanding `print! { concat ! ( "Hello, Rust!" , "\n" ) }`
4036 = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( "Hello, Rust!" , "\n" ) )
4037 )`
4038 3832
4039 Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs 3833`as-needed` is only compatible with the `dynamic` and `framework` linking kinds. Using any other kind will result in a compiler error.
4040``` 3834
3835`+as-needed` means that the library will be actually linked only if it satisfies some undefined symbols at the point at which it is specified on the command line, making it similar to static libraries in this regard.
3836
3837This modifier translates to `--as-needed` for ld-like linkers, and to `-dead_strip_dylibs` / `-needed_library` / `-needed_framework` for ld64.
3838The modifier does nothing for linkers that don't support it (e.g. `link.exe`).
3839
3840The default for this modifier is unclear, some targets currently specify it as `+as-needed`, some do not. We may want to try making `+as-needed` a default for all targets.
4041"##, 3841"##,
4042 }, 3842 },
4043 LintCompletion { 3843 Lint {
4044 label: "update_panic_count", 3844 label: "native_link_modifiers_bundle",
4045 description: r##"# `update_panic_count` 3845 description: r##"# `native_link_modifiers_bundle`
4046 3846
4047This feature is internal to the Rust compiler and is not intended for general use. 3847The tracking issue for this feature is: [#81490]
3848
3849[#81490]: https://github.com/rust-lang/rust/issues/81490
4048 3850
4049------------------------ 3851------------------------
3852
3853The `native_link_modifiers_bundle` feature allows you to use the `bundle` modifier.
3854
3855Only compatible with the `static` linking kind. Using any other kind will result in a compiler error.
3856
3857`+bundle` means objects from the static library are bundled into the produced crate (a rlib, for example) and are used from this crate later during linking of the final binary.
3858
3859`-bundle` means the static library is included into the produced rlib "by name" and object files from it are included only during linking of the final binary, the file search by that name is also performed during final linking.
3860
3861This modifier is supposed to supersede the `static-nobundle` linking kind defined by [RFC 1717](https://github.com/rust-lang/rfcs/pull/1717).
3862
3863The default for this modifier is currently `+bundle`, but it could be changed later on some future edition boundary.
4050"##, 3864"##,
4051 }, 3865 },
4052 LintCompletion { 3866 Lint {
4053 label: "core_private_bignum", 3867 label: "native_link_modifiers_verbatim",
4054 description: r##"# `core_private_bignum` 3868 description: r##"# `native_link_modifiers_verbatim`
4055 3869
4056This feature is internal to the Rust compiler and is not intended for general use. 3870The tracking issue for this feature is: [#81490]
3871
3872[#81490]: https://github.com/rust-lang/rust/issues/81490
4057 3873
4058------------------------ 3874------------------------
3875
3876The `native_link_modifiers_verbatim` feature allows you to use the `verbatim` modifier.
3877
3878`+verbatim` means that rustc itself won't add any target-specified library prefixes or suffixes (like `lib` or `.a`) to the library name, and will try its best to ask for the same thing from the linker.
3879
3880For `ld`-like linkers rustc will use the `-l:filename` syntax (note the colon) when passing the library, so the linker won't add any prefixes or suffixes as well.
3881See [`-l namespec`](https://sourceware.org/binutils/docs/ld/Options.html) in ld documentation for more details.
3882For linkers not supporting any verbatim modifiers (e.g. `link.exe` or `ld64`) the library name will be passed as is.
3883
3884The default for this modifier is `-verbatim`.
3885
3886This RFC changes the behavior of `raw-dylib` linking kind specified by [RFC 2627](https://github.com/rust-lang/rfcs/pull/2627). The `.dll` suffix (or other target-specified suffixes for other targets) is now added automatically.
3887If your DLL doesn't have the `.dll` suffix, it can be specified with `+verbatim`.
4059"##, 3888"##,
4060 }, 3889 },
4061 LintCompletion { 3890 Lint {
4062 label: "sort_internals", 3891 label: "native_link_modifiers_whole_archive",
4063 description: r##"# `sort_internals` 3892 description: r##"# `native_link_modifiers_whole_archive`
4064 3893
4065This feature is internal to the Rust compiler and is not intended for general use. 3894The tracking issue for this feature is: [#81490]
3895
3896[#81490]: https://github.com/rust-lang/rust/issues/81490
4066 3897
4067------------------------ 3898------------------------
3899
3900The `native_link_modifiers_whole_archive` feature allows you to use the `whole-archive` modifier.
3901
3902Only compatible with the `static` linking kind. Using any other kind will result in a compiler error.
3903
3904`+whole-archive` means that the static library is linked as a whole archive without throwing any object files away.
3905
3906This modifier translates to `--whole-archive` for `ld`-like linkers, to `/WHOLEARCHIVE` for `link.exe`, and to `-force_load` for `ld64`.
3907The modifier does nothing for linkers that don't support it.
3908
3909The default for this modifier is `-whole-archive`.
4068"##, 3910"##,
4069 }, 3911 },
4070 LintCompletion { 3912 Lint {
4071 label: "windows_net", 3913 label: "negative_impls",
4072 description: r##"# `windows_net` 3914 description: r##"# `negative_impls`
4073 3915
4074This feature is internal to the Rust compiler and is not intended for general use. 3916The tracking issue for this feature is [#68318].
4075 3917
4076------------------------ 3918[#68318]: https://github.com/rust-lang/rust/issues/68318
3919
3920----
3921
3922With the feature gate `negative_impls`, you can write negative impls as well as positive ones:
3923
3924```rust
3925#![feature(negative_impls)]
3926trait DerefMut { }
3927impl<T: ?Sized> !DerefMut for &T { }
3928```
3929
3930Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below.
3931
3932Negative impls have the following characteristics:
3933
3934* They do not have any items.
3935* They must obey the orphan rules as if they were a positive impl.
3936* They cannot "overlap" with any positive impls.
3937
3938## Semver interaction
3939
3940It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types.
3941
3942## Orphan and overlap rules
3943
3944Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth.
3945
3946Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.)
3947
3948## Interaction with auto traits
3949
3950Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an
3951auto-trait serves two purposes:
3952
3953* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`;
3954* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated.
3955
3956Note that, at present, there is no way to indicate that a given type
3957does not implement an auto trait *but that it may do so in the
3958future*. For ordinary types, this is done by simply not declaring any
3959impl at all, but that is not an option for auto traits. A workaround
3960is that one could embed a marker type as one of the fields, where the
3961marker type is `!AutoTrait`.
3962
3963## Immediate uses
3964
3965Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544).
3966
3967This serves two purposes:
3968
3969* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists.
3970* It prevents downstream crates from creating such impls.
4077"##, 3971"##,
4078 }, 3972 },
4079 LintCompletion { 3973 Lint {
4080 label: "c_variadic", 3974 label: "no_coverage",
4081 description: r##"# `c_variadic` 3975 description: r##"# `no_coverage`
4082 3976
4083The tracking issue for this feature is: [#44930] 3977The tracking issue for this feature is: [#84605]
4084 3978
4085[#44930]: https://github.com/rust-lang/rust/issues/44930 3979[#84605]: https://github.com/rust-lang/rust/issues/84605
3980
3981---
3982
3983The `no_coverage` attribute can be used to selectively disable coverage
3984instrumentation in an annotated function. This might be useful to:
3985
3986- Avoid instrumentation overhead in a performance critical function
3987- Avoid generating coverage for a function that is not meant to be executed,
3988 but still target 100% coverage for the rest of the program.
3989
3990## Example
3991
3992```rust
3993#![feature(no_coverage)]
3994
3995// `foo()` will get coverage instrumentation (by default)
3996fn foo() {
3997 // ...
3998}
3999
4000#[no_coverage]
4001fn bar() {
4002 // ...
4003}
4004```
4005"##,
4006 },
4007 Lint {
4008 label: "no_sanitize",
4009 description: r##"# `no_sanitize`
4010
4011The tracking issue for this feature is: [#39699]
4012
4013[#39699]: https://github.com/rust-lang/rust/issues/39699
4086 4014
4087------------------------ 4015------------------------
4088 4016
4089The `c_variadic` library feature exposes the `VaList` structure, 4017The `no_sanitize` attribute can be used to selectively disable sanitizer
4090Rust's analogue of C's `va_list` type. 4018instrumentation in an annotated function. This might be useful to: avoid
4019instrumentation overhead in a performance critical function, or avoid
4020instrumenting code that contains constructs unsupported by given sanitizer.
4021
4022The precise effect of this annotation depends on particular sanitizer in use.
4023For example, with `no_sanitize(thread)`, the thread sanitizer will no longer
4024instrument non-atomic store / load operations, but it will instrument atomic
4025operations to avoid reporting false positives and provide meaning full stack
4026traces.
4091 4027
4092## Examples 4028## Examples
4093 4029
4094```rust 4030``` rust
4095#![feature(c_variadic)] 4031#![feature(no_sanitize)]
4096 4032
4097use std::ffi::VaList; 4033#[no_sanitize(address)]
4034fn foo() {
4035 // ...
4036}
4037```
4038"##,
4039 },
4040 Lint {
4041 label: "plugin",
4042 description: r##"# `plugin`
4098 4043
4099pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize { 4044The tracking issue for this feature is: [#29597]
4100 let mut sum = 0; 4045
4101 for _ in 0..n { 4046[#29597]: https://github.com/rust-lang/rust/issues/29597
4102 sum += args.arg::<usize>(); 4047
4048
4049This feature is part of "compiler plugins." It will often be used with the
4050[`plugin_registrar`] and `rustc_private` features.
4051
4052[`plugin_registrar`]: plugin-registrar.md
4053
4054------------------------
4055
4056`rustc` can load compiler plugins, which are user-provided libraries that
4057extend the compiler's behavior with new lint checks, etc.
4058
4059A plugin is a dynamic library crate with a designated *registrar* function that
4060registers extensions with `rustc`. Other crates can load these extensions using
4061the crate attribute `#![plugin(...)]`. See the
4062`rustc_driver::plugin` documentation for more about the
4063mechanics of defining and loading a plugin.
4064
4065In the vast majority of cases, a plugin should *only* be used through
4066`#![plugin]` and not through an `extern crate` item. Linking a plugin would
4067pull in all of librustc_ast and librustc as dependencies of your crate. This is
4068generally unwanted unless you are building another plugin.
4069
4070The usual practice is to put compiler plugins in their own crate, separate from
4071any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
4072of a library.
4073
4074# Lint plugins
4075
4076Plugins can extend [Rust's lint
4077infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with
4078additional checks for code style, safety, etc. Now let's write a plugin
4079[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs)
4080that warns about any item named `lintme`.
4081
4082```rust,ignore (requires-stage-2)
4083#![feature(plugin_registrar)]
4084#![feature(box_syntax, rustc_private)]
4085
4086extern crate rustc_ast;
4087
4088// Load rustc as a plugin to get macros
4089extern crate rustc_driver;
4090#[macro_use]
4091extern crate rustc_lint;
4092#[macro_use]
4093extern crate rustc_session;
4094
4095use rustc_driver::plugin::Registry;
4096use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
4097use rustc_ast::ast;
4098declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
4099
4100declare_lint_pass!(Pass => [TEST_LINT]);
4101
4102impl EarlyLintPass for Pass {
4103 fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
4104 if it.ident.name.as_str() == "lintme" {
4105 cx.lint(TEST_LINT, |lint| {
4106 lint.build("item is named 'lintme'").set_span(it.span).emit()
4107 });
4108 }
4103 } 4109 }
4104 sum
4105} 4110}
4111
4112#[plugin_registrar]
4113pub fn plugin_registrar(reg: &mut Registry) {
4114 reg.lint_store.register_lints(&[&TEST_LINT]);
4115 reg.lint_store.register_early_pass(|| box Pass);
4116}
4117```
4118
4119Then code like
4120
4121```rust,ignore (requires-plugin)
4122#![feature(plugin)]
4123#![plugin(lint_plugin_test)]
4124
4125fn lintme() { }
4126```
4127
4128will produce a compiler warning:
4129
4130```txt
4131foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
4132foo.rs:4 fn lintme() { }
4133 ^~~~~~~~~~~~~~~
4106``` 4134```
4135
4136The components of a lint plugin are:
4137
4138* one or more `declare_lint!` invocations, which define static `Lint` structs;
4139
4140* a struct holding any state needed by the lint pass (here, none);
4141
4142* a `LintPass`
4143 implementation defining how to check each syntax element. A single
4144 `LintPass` may call `span_lint` for several different `Lint`s, but should
4145 register them all through the `get_lints` method.
4146
4147Lint passes are syntax traversals, but they run at a late stage of compilation
4148where type information is available. `rustc`'s [built-in
4149lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs)
4150mostly use the same infrastructure as lint plugins, and provide examples of how
4151to access type information.
4152
4153Lints defined by plugins are controlled by the usual [attributes and compiler
4154flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g.
4155`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the
4156first argument to `declare_lint!`, with appropriate case and punctuation
4157conversion.
4158
4159You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`,
4160including those provided by plugins loaded by `foo.rs`.
4107"##, 4161"##,
4108 }, 4162 },
4109 LintCompletion { 4163 Lint {
4110 label: "core_private_diy_float", 4164 label: "plugin_registrar",
4111 description: r##"# `core_private_diy_float` 4165 description: r##"# `plugin_registrar`
4112 4166
4113This feature is internal to the Rust compiler and is not intended for general use. 4167The tracking issue for this feature is: [#29597]
4168
4169[#29597]: https://github.com/rust-lang/rust/issues/29597
4170
4171This feature is part of "compiler plugins." It will often be used with the
4172[`plugin`] and `rustc_private` features as well. For more details, see
4173their docs.
4174
4175[`plugin`]: plugin.md
4114 4176
4115------------------------ 4177------------------------
4116"##, 4178"##,
4117 }, 4179 },
4118 LintCompletion { 4180 Lint {
4119 label: "profiler_runtime_lib", 4181 label: "print_internals",
4120 description: r##"# `profiler_runtime_lib` 4182 description: r##"# `print_internals`
4121 4183
4122This feature is internal to the Rust compiler and is not intended for general use. 4184This feature is internal to the Rust compiler and is not intended for general use.
4123 4185
4124------------------------ 4186------------------------
4125"##, 4187"##,
4126 }, 4188 },
4127 LintCompletion { 4189 Lint {
4128 label: "thread_local_internals", 4190 label: "profiler_runtime",
4129 description: r##"# `thread_local_internals` 4191 description: r##"# `profiler_runtime`
4130 4192
4131This feature is internal to the Rust compiler and is not intended for general use. 4193The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524).
4132 4194
4133------------------------ 4195------------------------
4134"##, 4196"##,
4135 }, 4197 },
4136 LintCompletion { 4198 Lint {
4137 label: "int_error_internals", 4199 label: "profiler_runtime_lib",
4138 description: r##"# `int_error_internals` 4200 description: r##"# `profiler_runtime_lib`
4139 4201
4140This feature is internal to the Rust compiler and is not intended for general use. 4202This feature is internal to the Rust compiler and is not intended for general use.
4141 4203
4142------------------------ 4204------------------------
4143"##, 4205"##,
4144 }, 4206 },
4145 LintCompletion { 4207 Lint {
4146 label: "windows_stdio", 4208 label: "repr128",
4147 description: r##"# `windows_stdio` 4209 description: r##"# `repr128`
4148 4210
4149This feature is internal to the Rust compiler and is not intended for general use. 4211The tracking issue for this feature is: [#56071]
4212
4213[#56071]: https://github.com/rust-lang/rust/issues/56071
4150 4214
4151------------------------ 4215------------------------
4216
4217The `repr128` feature adds support for `#[repr(u128)]` on `enum`s.
4218
4219```rust
4220#![feature(repr128)]
4221
4222#[repr(u128)]
4223enum Foo {
4224 Bar(u64),
4225}
4226```
4152"##, 4227"##,
4153 }, 4228 },
4154 LintCompletion { 4229 Lint {
4155 label: "fmt_internals", 4230 label: "rt",
4156 description: r##"# `fmt_internals` 4231 description: r##"# `rt`
4157 4232
4158This feature is internal to the Rust compiler and is not intended for general use. 4233This feature is internal to the Rust compiler and is not intended for general use.
4159 4234
4160------------------------ 4235------------------------
4161"##, 4236"##,
4162 }, 4237 },
4163 LintCompletion { 4238 Lint {
4164 label: "fd_read", 4239 label: "rustc_attrs",
4165 description: r##"# `fd_read` 4240 description: r##"# `rustc_attrs`
4241
4242This feature has no tracking issue, and is therefore internal to
4243the compiler, not being intended for general use.
4244
4245Note: `rustc_attrs` enables many rustc-internal attributes and this page
4246only discuss a few of them.
4247
4248------------------------
4249
4250The `rustc_attrs` feature allows debugging rustc type layouts by using
4251`#[rustc_layout(...)]` to debug layout at compile time (it even works
4252with `cargo check`) as an alternative to `rustc -Z print-type-sizes`
4253that is way more verbose.
4254
4255Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`,
4256`abi`. Note that it only works on sized types without generics.
4257
4258## Examples
4259
4260```rust,compile_fail
4261#![feature(rustc_attrs)]
4262
4263#[rustc_layout(abi, size)]
4264pub enum X {
4265 Y(u8, u8, u8),
4266 Z(isize),
4267}
4268```
4269
4270When that is compiled, the compiler will error with something like
4271
4272```text
4273error: abi: Aggregate { sized: true }
4274 --> src/lib.rs:4:1
4275 |
42764 | / pub enum T {
42775 | | Y(u8, u8, u8),
42786 | | Z(isize),
42797 | | }
4280 | |_^
4281
4282error: size: Size { raw: 16 }
4283 --> src/lib.rs:4:1
4284 |
42854 | / pub enum T {
42865 | | Y(u8, u8, u8),
42876 | | Z(isize),
42887 | | }
4289 | |_^
4290
4291error: aborting due to 2 previous errors
4292```
4293"##,
4294 },
4295 Lint {
4296 label: "sort_internals",
4297 description: r##"# `sort_internals`
4166 4298
4167This feature is internal to the Rust compiler and is not intended for general use. 4299This feature is internal to the Rust compiler and is not intended for general use.
4168 4300
4169------------------------ 4301------------------------
4170"##, 4302"##,
4171 }, 4303 },
4172 LintCompletion { 4304 Lint {
4173 label: "str_internals", 4305 label: "str_internals",
4174 description: r##"# `str_internals` 4306 description: r##"# `str_internals`
4175 4307
@@ -4178,7 +4310,7 @@ This feature is internal to the Rust compiler and is not intended for general us
4178------------------------ 4310------------------------
4179"##, 4311"##,
4180 }, 4312 },
4181 LintCompletion { 4313 Lint {
4182 label: "test", 4314 label: "test",
4183 description: r##"# `test` 4315 description: r##"# `test`
4184 4316
@@ -4340,101 +4472,218 @@ However, the optimizer can still modify a testcase in an undesirable manner
4340even when using either of the above. 4472even when using either of the above.
4341"##, 4473"##,
4342 }, 4474 },
4343 LintCompletion { 4475 Lint {
4344 label: "windows_c", 4476 label: "thread_local_internals",
4345 description: r##"# `windows_c` 4477 description: r##"# `thread_local_internals`
4346 4478
4347This feature is internal to the Rust compiler and is not intended for general use. 4479This feature is internal to the Rust compiler and is not intended for general use.
4348 4480
4349------------------------ 4481------------------------
4350"##, 4482"##,
4351 }, 4483 },
4352 LintCompletion { 4484 Lint {
4353 label: "dec2flt", 4485 label: "trace_macros",
4354 description: r##"# `dec2flt` 4486 description: r##"# `trace_macros`
4355 4487
4356This feature is internal to the Rust compiler and is not intended for general use. 4488The tracking issue for this feature is [#29598].
4489
4490[#29598]: https://github.com/rust-lang/rust/issues/29598
4357 4491
4358------------------------ 4492------------------------
4359"##,
4360 },
4361 LintCompletion {
4362 label: "derive_clone_copy",
4363 description: r##"# `derive_clone_copy`
4364 4493
4365This feature is internal to the Rust compiler and is not intended for general use. 4494With `trace_macros` you can trace the expansion of macros in your code.
4366 4495
4367------------------------ 4496## Examples
4368"##,
4369 },
4370 LintCompletion {
4371 label: "allocator_api",
4372 description: r##"# `allocator_api`
4373 4497
4374The tracking issue for this feature is [#32838] 4498```rust
4499#![feature(trace_macros)]
4375 4500
4376[#32838]: https://github.com/rust-lang/rust/issues/32838 4501fn main() {
4502 trace_macros!(true);
4503 println!("Hello, Rust!");
4504 trace_macros!(false);
4505}
4506```
4377 4507
4378------------------------ 4508The `cargo build` output:
4379 4509
4380Sometimes you want the memory for one collection to use a different 4510```txt
4381allocator than the memory for another collection. In this case, 4511note: trace_macro
4382replacing the global allocator is not a workable option. Instead, 4512 --> src/main.rs:5:5
4383you need to pass in an instance of an `AllocRef` to each collection 4513 |
4384for which you want a custom allocator. 45145 | println!("Hello, Rust!");
4515 | ^^^^^^^^^^^^^^^^^^^^^^^^^
4516 |
4517 = note: expanding `println! { "Hello, Rust!" }`
4518 = note: to `print ! ( concat ! ( "Hello, Rust!" , "\n" ) )`
4519 = note: expanding `print! { concat ! ( "Hello, Rust!" , "\n" ) }`
4520 = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( "Hello, Rust!" , "\n" ) )
4521 )`
4385 4522
4386TBD 4523 Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs
4524```
4387"##, 4525"##,
4388 }, 4526 },
4389 LintCompletion { 4527 Lint {
4390 label: "core_panic", 4528 label: "trait_alias",
4391 description: r##"# `core_panic` 4529 description: r##"# `trait_alias`
4392 4530
4393This feature is internal to the Rust compiler and is not intended for general use. 4531The tracking issue for this feature is: [#41517]
4532
4533[#41517]: https://github.com/rust-lang/rust/issues/41517
4394 4534
4395------------------------ 4535------------------------
4536
4537The `trait_alias` feature adds support for trait aliases. These allow aliases
4538to be created for one or more traits (currently just a single regular trait plus
4539any number of auto-traits), and used wherever traits would normally be used as
4540either bounds or trait objects.
4541
4542```rust
4543#![feature(trait_alias)]
4544
4545trait Foo = std::fmt::Debug + Send;
4546trait Bar = Foo + Sync;
4547
4548// Use trait alias as bound on type parameter.
4549fn foo<T: Foo>(v: &T) {
4550 println!("{:?}", v);
4551}
4552
4553pub fn main() {
4554 foo(&1);
4555
4556 // Use trait alias for trait objects.
4557 let a: &Bar = &123;
4558 println!("{:?}", a);
4559 let b = Box::new(456) as Box<dyn Foo>;
4560 println!("{:?}", b);
4561}
4562```
4396"##, 4563"##,
4397 }, 4564 },
4398 LintCompletion { 4565 Lint {
4399 label: "fn_traits", 4566 label: "transparent_unions",
4400 description: r##"# `fn_traits` 4567 description: r##"# `transparent_unions`
4401
4402The tracking issue for this feature is [#29625]
4403 4568
4404See Also: [`unboxed_closures`](../language-features/unboxed-closures.md) 4569The tracking issue for this feature is [#60405]
4405 4570
4406[#29625]: https://github.com/rust-lang/rust/issues/29625 4571[#60405]: https://github.com/rust-lang/rust/issues/60405
4407 4572
4408---- 4573----
4409 4574
4410The `fn_traits` feature allows for implementation of the [`Fn*`] traits 4575The `transparent_unions` feature allows you mark `union`s as
4411for creating custom closure-like types. 4576`#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the
4412 4577same conditions in which a `struct` may be `#[repr(transparent)]` (generally,
4413[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html 4578this means the `union` must have exactly one non-zero-sized field). Some
4579concrete illustrations follow.
4414 4580
4415```rust 4581```rust
4416#![feature(unboxed_closures)] 4582#![feature(transparent_unions)]
4417#![feature(fn_traits)]
4418 4583
4419struct Adder { 4584// This union has the same representation as `f32`.
4420 a: u32 4585#[repr(transparent)]
4586union SingleFieldUnion {
4587 field: f32,
4421} 4588}
4422 4589
4423impl FnOnce<(u32, )> for Adder { 4590// This union has the same representation as `usize`.
4424 type Output = u32; 4591#[repr(transparent)]
4425 extern "rust-call" fn call_once(self, b: (u32, )) -> Self::Output { 4592union MultiFieldUnion {
4426 self.a + b.0 4593 field: usize,
4427 } 4594 nothing: (),
4428} 4595}
4596```
4429 4597
4430fn main() { 4598For consistency with transparent `struct`s, `union`s must have exactly one
4431 let adder = Adder { a: 3 }; 4599non-zero-sized field. If all fields are zero-sized, the `union` must not be
4432 assert_eq!(adder(2), 5); 4600`#[repr(transparent)]`:
4601
4602```rust
4603#![feature(transparent_unions)]
4604
4605// This (non-transparent) union is already valid in stable Rust:
4606pub union GoodUnion {
4607 pub nothing: (),
4433} 4608}
4609
4610// Error: transparent union needs exactly one non-zero-sized field, but has 0
4611// #[repr(transparent)]
4612// pub union BadUnion {
4613// pub nothing: (),
4614// }
4434``` 4615```
4616
4617The one exception is if the `union` is generic over `T` and has a field of type
4618`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type:
4619
4620```rust
4621#![feature(transparent_unions)]
4622
4623// This union has the same representation as `T`.
4624#[repr(transparent)]
4625pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable.
4626 pub field: T,
4627 pub nothing: (),
4628}
4629
4630// This is okay even though `()` is a zero-sized type.
4631pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () };
4632```
4633
4634Like transarent `struct`s, a transparent `union` of type `U` has the same
4635layout, size, and ABI as its single non-ZST field. If it is generic over a type
4636`T`, and all its fields are ZSTs except for exactly one field of type `T`, then
4637it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).
4638
4639Like transparent `struct`s, transparent `union`s are FFI-safe if and only if
4640their underlying representation type is also FFI-safe.
4641
4642A `union` may not be eligible for the same nonnull-style optimizations that a
4643`struct` or `enum` (with the same fields) are eligible for. Adding
4644`#[repr(transparent)]` to `union` does not change this. To give a more concrete
4645example, it is unspecified whether `size_of::<T>()` is equal to
4646`size_of::<Option<T>>()`, where `T` is a `union` (regardless of whether or not
4647it is transparent). The Rust compiler is free to perform this optimization if
4648possible, but is not required to, and different compiler versions may differ in
4649their application of these optimizations.
4435"##, 4650"##,
4436 }, 4651 },
4437 LintCompletion { 4652 Lint {
4653 label: "try_blocks",
4654 description: r##"# `try_blocks`
4655
4656The tracking issue for this feature is: [#31436]
4657
4658[#31436]: https://github.com/rust-lang/rust/issues/31436
4659
4660------------------------
4661
4662The `try_blocks` feature adds support for `try` blocks. A `try`
4663block creates a new scope one can use the `?` operator in.
4664
4665```rust,edition2018
4666#![feature(try_blocks)]
4667
4668use std::num::ParseIntError;
4669
4670let result: Result<i32, ParseIntError> = try {
4671 "1".parse::<i32>()?
4672 + "2".parse::<i32>()?
4673 + "3".parse::<i32>()?
4674};
4675assert_eq!(result, Ok(6));
4676
4677let result: Result<i32, ParseIntError> = try {
4678 "1".parse::<i32>()?
4679 + "foo".parse::<i32>()?
4680 + "3".parse::<i32>()?
4681};
4682assert!(result.is_err());
4683```
4684"##,
4685 },
4686 Lint {
4438 label: "try_trait", 4687 label: "try_trait",
4439 description: r##"# `try_trait` 4688 description: r##"# `try_trait`
4440 4689
@@ -4488,27 +4737,284 @@ function (or catch block). Having a distinct error type (as opposed to
4488just `()`, or similar) restricts this to where it's semantically meaningful. 4737just `()`, or similar) restricts this to where it's semantically meaningful.
4489"##, 4738"##,
4490 }, 4739 },
4491 LintCompletion { 4740 Lint {
4492 label: "rt", 4741 label: "unboxed_closures",
4493 description: r##"# `rt` 4742 description: r##"# `unboxed_closures`
4743
4744The tracking issue for this feature is [#29625]
4745
4746See Also: [`fn_traits`](../library-features/fn-traits.md)
4747
4748[#29625]: https://github.com/rust-lang/rust/issues/29625
4749
4750----
4751
4752The `unboxed_closures` feature allows you to write functions using the `"rust-call"` ABI,
4753required for implementing the [`Fn*`] family of traits. `"rust-call"` functions must have
4754exactly one (non self) argument, a tuple representing the argument list.
4755
4756[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html
4757
4758```rust
4759#![feature(unboxed_closures)]
4760
4761extern "rust-call" fn add_args(args: (u32, u32)) -> u32 {
4762 args.0 + args.1
4763}
4764
4765fn main() {}
4766```
4767"##,
4768 },
4769 Lint {
4770 label: "unsized_locals",
4771 description: r##"# `unsized_locals`
4772
4773The tracking issue for this feature is: [#48055]
4774
4775[#48055]: https://github.com/rust-lang/rust/issues/48055
4776
4777------------------------
4778
4779This implements [RFC1909]. When turned on, you can have unsized arguments and locals:
4780
4781[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md
4782
4783```rust
4784#![allow(incomplete_features)]
4785#![feature(unsized_locals, unsized_fn_params)]
4786
4787use std::any::Any;
4788
4789fn main() {
4790 let x: Box<dyn Any> = Box::new(42);
4791 let x: dyn Any = *x;
4792 // ^ unsized local variable
4793 // ^^ unsized temporary
4794 foo(x);
4795}
4796
4797fn foo(_: dyn Any) {}
4798// ^^^^^^ unsized argument
4799```
4800
4801The RFC still forbids the following unsized expressions:
4802
4803```rust,compile_fail
4804#![feature(unsized_locals)]
4805
4806use std::any::Any;
4807
4808struct MyStruct<T: ?Sized> {
4809 content: T,
4810}
4811
4812struct MyTupleStruct<T: ?Sized>(T);
4813
4814fn answer() -> Box<dyn Any> {
4815 Box::new(42)
4816}
4817
4818fn main() {
4819 // You CANNOT have unsized statics.
4820 static X: dyn Any = *answer(); // ERROR
4821 const Y: dyn Any = *answer(); // ERROR
4822
4823 // You CANNOT have struct initialized unsized.
4824 MyStruct { content: *answer() }; // ERROR
4825 MyTupleStruct(*answer()); // ERROR
4826 (42, *answer()); // ERROR
4827
4828 // You CANNOT have unsized return types.
4829 fn my_function() -> dyn Any { *answer() } // ERROR
4830
4831 // You CAN have unsized local variables...
4832 let mut x: dyn Any = *answer(); // OK
4833 // ...but you CANNOT reassign to them.
4834 x = *answer(); // ERROR
4835
4836 // You CANNOT even initialize them separately.
4837 let y: dyn Any; // OK
4838 y = *answer(); // ERROR
4839
4840 // Not mentioned in the RFC, but by-move captured variables are also Sized.
4841 let x: dyn Any = *answer();
4842 (move || { // ERROR
4843 let y = x;
4844 })();
4845
4846 // You CAN create a closure with unsized arguments,
4847 // but you CANNOT call it.
4848 // This is an implementation detail and may be changed in the future.
4849 let f = |x: dyn Any| {};
4850 f(*answer()); // ERROR
4851}
4852```
4853
4854## By-value trait objects
4855
4856With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
4857
4858```rust
4859#![feature(unsized_fn_params)]
4860
4861trait Foo {
4862 fn foo(self) {}
4863}
4864
4865impl<T: ?Sized> Foo for T {}
4866
4867fn main() {
4868 let slice: Box<[i32]> = Box::new([1, 2, 3]);
4869 <[i32] as Foo>::foo(*slice);
4870}
4871```
4872
4873And `Foo` will also be object-safe.
4874
4875```rust
4876#![feature(unsized_fn_params)]
4877
4878trait Foo {
4879 fn foo(self) {}
4880}
4881
4882impl<T: ?Sized> Foo for T {}
4883
4884fn main () {
4885 let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
4886 // doesn't compile yet
4887 <dyn Foo as Foo>::foo(*slice);
4888}
4889```
4890
4891One of the objectives of this feature is to allow `Box<dyn FnOnce>`.
4892
4893## Variable length arrays
4894
4895The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.
4896
4897```rust,ignore (not-yet-implemented)
4898#![feature(unsized_locals)]
4899
4900fn mergesort<T: Ord>(a: &mut [T]) {
4901 let mut tmp = [T; dyn a.len()];
4902 // ...
4903}
4904
4905fn main() {
4906 let mut a = [3, 1, 5, 6];
4907 mergesort(&mut a);
4908 assert_eq!(a, [1, 3, 5, 6]);
4909}
4910```
4911
4912VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.
4913
4914## Advisory on stack usage
4915
4916It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
4917
4918- When you need a by-value trait objects.
4919- When you really need a fast allocation of small temporary arrays.
4920
4921Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code
4922
4923```rust
4924#![feature(unsized_locals)]
4925
4926fn main() {
4927 let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
4928 let _x = {{{{{{{{{{*x}}}}}}}}}};
4929}
4930```
4931
4932and the code
4933
4934```rust
4935#![feature(unsized_locals)]
4936
4937fn main() {
4938 for _ in 0..10 {
4939 let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
4940 let _x = *x;
4941 }
4942}
4943```
4944
4945will unnecessarily extend the stack frame.
4946"##,
4947 },
4948 Lint {
4949 label: "unsized_tuple_coercion",
4950 description: r##"# `unsized_tuple_coercion`
4951
4952The tracking issue for this feature is: [#42877]
4953
4954[#42877]: https://github.com/rust-lang/rust/issues/42877
4955
4956------------------------
4957
4958This is a part of [RFC0401]. According to the RFC, there should be an implementation like this:
4959
4960```rust,ignore (partial-example)
4961impl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {}
4962```
4963
4964This implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this:
4965
4966```rust
4967#![feature(unsized_tuple_coercion)]
4968
4969fn main() {
4970 let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]);
4971 let y : &([i32; 3], [i32]) = &x;
4972 assert_eq!(y.1[0], 4);
4973}
4974```
4975
4976[RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
4977"##,
4978 },
4979 Lint {
4980 label: "update_panic_count",
4981 description: r##"# `update_panic_count`
4494 4982
4495This feature is internal to the Rust compiler and is not intended for general use. 4983This feature is internal to the Rust compiler and is not intended for general use.
4496 4984
4497------------------------ 4985------------------------
4498"##, 4986"##,
4499 }, 4987 },
4500 LintCompletion { 4988 Lint {
4501 label: "fd", 4989 label: "windows_c",
4502 description: r##"# `fd` 4990 description: r##"# `windows_c`
4503 4991
4504This feature is internal to the Rust compiler and is not intended for general use. 4992This feature is internal to the Rust compiler and is not intended for general use.
4505 4993
4506------------------------ 4994------------------------
4507"##, 4995"##,
4508 }, 4996 },
4509 LintCompletion { 4997 Lint {
4510 label: "libstd_thread_internals", 4998 label: "windows_handle",
4511 description: r##"# `libstd_thread_internals` 4999 description: r##"# `windows_handle`
5000
5001This feature is internal to the Rust compiler and is not intended for general use.
5002
5003------------------------
5004"##,
5005 },
5006 Lint {
5007 label: "windows_net",
5008 description: r##"# `windows_net`
5009
5010This feature is internal to the Rust compiler and is not intended for general use.
5011
5012------------------------
5013"##,
5014 },
5015 Lint {
5016 label: "windows_stdio",
5017 description: r##"# `windows_stdio`
4512 5018
4513This feature is internal to the Rust compiler and is not intended for general use. 5019This feature is internal to the Rust compiler and is not intended for general use.
4514 5020
@@ -4517,1864 +5023,2308 @@ This feature is internal to the Rust compiler and is not intended for general us
4517 }, 5023 },
4518]; 5024];
4519 5025
4520pub(super) const CLIPPY_LINTS: &[LintCompletion] = &[ 5026pub const CLIPPY_LINTS: &[Lint] = &[
4521 LintCompletion { 5027 Lint {
4522 label: "clippy::absurd_extreme_comparisons", 5028 label: "clippy::absurd_extreme_comparisons",
4523 description: r##"Checks for comparisons where one side of the relation is\neither the minimum or maximum value for its type and warns if it involves a\ncase that is always true or always false. Only integer and boolean types are\nchecked."##, 5029 description: r##"Checks for comparisons where one side of the relation is
5030either the minimum or maximum value for its type and warns if it involves a
5031case that is always true or always false. Only integer and boolean types are
5032checked."##,
4524 }, 5033 },
4525 LintCompletion { 5034 Lint {
4526 label: "clippy::almost_swapped", 5035 label: "clippy::almost_swapped",
4527 description: r##"Checks for `foo = bar; bar = foo` sequences."##, 5036 description: r##"Checks for `foo = bar; bar = foo` sequences."##,
4528 }, 5037 },
4529 LintCompletion { 5038 Lint {
4530 label: "clippy::approx_constant", 5039 label: "clippy::approx_constant",
4531 description: r##"Checks for floating point literals that approximate\nconstants which are defined in\n[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\nor\n[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\nrespectively, suggesting to use the predefined constant."##, 5040 description: r##"Checks for floating point literals that approximate
4532 }, 5041constants which are defined in
4533 LintCompletion { 5042[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
5043or
5044[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
5045respectively, suggesting to use the predefined constant."##,
5046 },
5047 Lint {
4534 label: "clippy::as_conversions", 5048 label: "clippy::as_conversions",
4535 description: r##"Checks for usage of `as` conversions.\n\nNote that this lint is specialized in linting *every single* use of `as`\nregardless of whether good alternatives exist or not.\nIf you want more precise lints for `as`, please consider using these separate lints:\n`unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,\n`fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.\nThere is a good explanation the reason why this lint should work in this way and how it is useful\n[in this issue](https://github.com/rust-lang/rust-clippy/issues/5122)."##, 5049 description: r##"Checks for usage of `as` conversions.
5050
5051Note that this lint is specialized in linting *every single* use of `as`
5052regardless of whether good alternatives exist or not.
5053If you want more precise lints for `as`, please consider using these separate lints:
5054`unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,
5055`fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
5056There is a good explanation the reason why this lint should work in this way and how it is useful
5057[in this issue](https://github.com/rust-lang/rust-clippy/issues/5122)."##,
4536 }, 5058 },
4537 LintCompletion { 5059 Lint {
4538 label: "clippy::assertions_on_constants", 5060 label: "clippy::assertions_on_constants",
4539 description: r##"Checks for `assert!(true)` and `assert!(false)` calls."##, 5061 description: r##"Checks for `assert!(true)` and `assert!(false)` calls."##,
4540 }, 5062 },
4541 LintCompletion { 5063 Lint {
4542 label: "clippy::assign_op_pattern", 5064 label: "clippy::assign_op_pattern",
4543 description: r##"Checks for `a = a op b` or `a = b commutative_op a`\npatterns."##, 5065 description: r##"Checks for `a = a op b` or `a = b commutative_op a`
5066patterns."##,
4544 }, 5067 },
4545 LintCompletion { 5068 Lint {
4546 label: "clippy::assign_ops", 5069 label: "clippy::assign_ops",
4547 description: r##"Nothing. This lint has been deprecated."##, 5070 description: r##"Nothing. This lint has been deprecated."##,
4548 }, 5071 },
4549 LintCompletion { 5072 Lint {
4550 label: "clippy::async_yields_async", 5073 label: "clippy::async_yields_async",
4551 description: r##"Checks for async blocks that yield values of types\nthat can themselves be awaited."##, 5074 description: r##"Checks for async blocks that yield values of types
5075that can themselves be awaited."##,
4552 }, 5076 },
4553 LintCompletion { 5077 Lint {
4554 label: "clippy::await_holding_lock", 5078 label: "clippy::await_holding_lock",
4555 description: r##"Checks for calls to await while holding a\nnon-async-aware MutexGuard."##, 5079 description: r##"Checks for calls to await while holding a
5080non-async-aware MutexGuard."##,
4556 }, 5081 },
4557 LintCompletion { 5082 Lint {
4558 label: "clippy::await_holding_refcell_ref", 5083 label: "clippy::await_holding_refcell_ref",
4559 description: r##"Checks for calls to await while holding a\n`RefCell` `Ref` or `RefMut`."##, 5084 description: r##"Checks for calls to await while holding a
5085`RefCell` `Ref` or `RefMut`."##,
4560 }, 5086 },
4561 LintCompletion { 5087 Lint {
4562 label: "clippy::bad_bit_mask", 5088 label: "clippy::bad_bit_mask",
4563 description: r##"Checks for incompatible bit masks in comparisons.\n\nThe formula for detecting if an expression of the type `_ <bit_op> m\n<cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of\n{`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\ntable:\n\n|Comparison |Bit Op|Example |is always|Formula |\n|------------|------|------------|---------|----------------------|\n|`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\n|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\n|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\n|`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\n|`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\n|`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |"##, 5089 description: r##"Checks for incompatible bit masks in comparisons.
4564 }, 5090
4565 LintCompletion { 5091The formula for detecting if an expression of the type `_ <bit_op> m
5092<cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
5093{`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
5094table:
5095
5096|Comparison |Bit Op|Example |is always|Formula |
5097|------------|------|------------|---------|----------------------|
5098|`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
5099|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
5100|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
5101|`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
5102|`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
5103|`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |"##,
5104 },
5105 Lint {
4566 label: "clippy::bind_instead_of_map", 5106 label: "clippy::bind_instead_of_map",
4567 description: r##"Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or\n`_.or_else(|x| Err(y))`."##, 5107 description: r##"Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
5108`_.or_else(|x| Err(y))`."##,
4568 }, 5109 },
4569 LintCompletion { 5110 Lint {
4570 label: "clippy::blacklisted_name", 5111 label: "clippy::blacklisted_name",
4571 description: r##"Checks for usage of blacklisted names for variables, such\nas `foo`."##, 5112 description: r##"Checks for usage of blacklisted names for variables, such
5113as `foo`."##,
4572 }, 5114 },
4573 LintCompletion { 5115 Lint {
4574 label: "clippy::blanket_clippy_restriction_lints", 5116 label: "clippy::blanket_clippy_restriction_lints",
4575 description: r##"Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category."##, 5117 description: r##"Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category."##,
4576 }, 5118 },
4577 LintCompletion { 5119 Lint {
4578 label: "clippy::blocks_in_if_conditions", 5120 label: "clippy::blocks_in_if_conditions",
4579 description: r##"Checks for `if` conditions that use blocks containing an\nexpression, statements or conditions that use closures with blocks."##, 5121 description: r##"Checks for `if` conditions that use blocks containing an
5122expression, statements or conditions that use closures with blocks."##,
5123 },
5124 Lint {
5125 label: "clippy::bool_assert_comparison",
5126 description: r##"This lint warns about boolean comparisons in assert-like macros."##,
4580 }, 5127 },
4581 LintCompletion { 5128 Lint {
4582 label: "clippy::bool_comparison", 5129 label: "clippy::bool_comparison",
4583 description: r##"Checks for expressions of the form `x == true`,\n`x != true` and order comparisons such as `x < true` (or vice versa) and\nsuggest using the variable directly."##, 5130 description: r##"Checks for expressions of the form `x == true`,
5131`x != true` and order comparisons such as `x < true` (or vice versa) and
5132suggest using the variable directly."##,
4584 }, 5133 },
4585 LintCompletion { 5134 Lint {
4586 label: "clippy::borrow_interior_mutable_const", 5135 label: "clippy::borrow_interior_mutable_const",
4587 description: r##"Checks if `const` items which is interior mutable (e.g.,\ncontains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly."##, 5136 description: r##"Checks if `const` items which is interior mutable (e.g.,
5137contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly."##,
4588 }, 5138 },
4589 LintCompletion { 5139 Lint {
4590 label: "clippy::borrowed_box", 5140 label: "clippy::borrowed_box",
4591 description: r##"Checks for use of `&Box<T>` anywhere in the code.\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, 5141 description: r##"Checks for use of `&Box<T>` anywhere in the code.
5142Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##,
4592 }, 5143 },
4593 LintCompletion { 5144 Lint {
4594 label: "clippy::box_vec", 5145 label: "clippy::box_vec",
4595 description: r##"Checks for use of `Box<Vec<_>>` anywhere in the code.\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, 5146 description: r##"Checks for use of `Box<Vec<_>>` anywhere in the code.
5147Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##,
4596 }, 5148 },
4597 LintCompletion { 5149 Lint {
4598 label: "clippy::boxed_local", 5150 label: "clippy::boxed_local",
4599 description: r##"Checks for usage of `Box<T>` where an unboxed `T` would\nwork fine."##, 5151 description: r##"Checks for usage of `Box<T>` where an unboxed `T` would
5152work fine."##,
5153 },
5154 Lint {
5155 label: "clippy::branches_sharing_code",
5156 description: r##"Checks if the `if` and `else` block contain shared code that can be
5157moved out of the blocks."##,
4600 }, 5158 },
4601 LintCompletion { 5159 Lint {
4602 label: "clippy::builtin_type_shadow", 5160 label: "clippy::builtin_type_shadow",
4603 description: r##"Warns if a generic shadows a built-in type."##, 5161 description: r##"Warns if a generic shadows a built-in type."##,
4604 }, 5162 },
4605 LintCompletion { 5163 Lint {
4606 label: "clippy::bytes_nth", 5164 label: "clippy::bytes_nth",
4607 description: r##"Checks for the use of `.bytes().nth()`."##, 5165 description: r##"Checks for the use of `.bytes().nth()`."##,
4608 }, 5166 },
4609 LintCompletion { 5167 Lint {
4610 label: "clippy::cargo_common_metadata", 5168 label: "clippy::cargo_common_metadata",
4611 description: r##"Checks to see if all common metadata is defined in\n`Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata"##, 5169 description: r##"Checks to see if all common metadata is defined in
5170`Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata"##,
4612 }, 5171 },
4613 LintCompletion { 5172 Lint {
4614 label: "clippy::case_sensitive_file_extension_comparisons", 5173 label: "clippy::case_sensitive_file_extension_comparisons",
4615 description: r##"Checks for calls to `ends_with` with possible file extensions\nand suggests to use a case-insensitive approach instead."##, 5174 description: r##"Checks for calls to `ends_with` with possible file extensions
5175and suggests to use a case-insensitive approach instead."##,
4616 }, 5176 },
4617 LintCompletion { 5177 Lint {
4618 label: "clippy::cast_lossless", 5178 label: "clippy::cast_lossless",
4619 description: r##"Checks for casts between numerical types that may\nbe replaced by safe conversion functions."##, 5179 description: r##"Checks for casts between numerical types that may
5180be replaced by safe conversion functions."##,
4620 }, 5181 },
4621 LintCompletion { 5182 Lint {
4622 label: "clippy::cast_possible_truncation", 5183 label: "clippy::cast_possible_truncation",
4623 description: r##"Checks for casts between numerical types that may\ntruncate large values. This is expected behavior, so the cast is `Allow` by\ndefault."##, 5184 description: r##"Checks for casts between numerical types that may
5185truncate large values. This is expected behavior, so the cast is `Allow` by
5186default."##,
4624 }, 5187 },
4625 LintCompletion { 5188 Lint {
4626 label: "clippy::cast_possible_wrap", 5189 label: "clippy::cast_possible_wrap",
4627 description: r##"Checks for casts from an unsigned type to a signed type of\nthe same size. Performing such a cast is a 'no-op' for the compiler,\ni.e., nothing is changed at the bit level, and the binary representation of\nthe value is reinterpreted. This can cause wrapping if the value is too big\nfor the target signed type. However, the cast works as defined, so this lint\nis `Allow` by default."##, 5190 description: r##"Checks for casts from an unsigned type to a signed type of
4628 }, 5191the same size. Performing such a cast is a 'no-op' for the compiler,
4629 LintCompletion { 5192i.e., nothing is changed at the bit level, and the binary representation of
5193the value is reinterpreted. This can cause wrapping if the value is too big
5194for the target signed type. However, the cast works as defined, so this lint
5195is `Allow` by default."##,
5196 },
5197 Lint {
4630 label: "clippy::cast_precision_loss", 5198 label: "clippy::cast_precision_loss",
4631 description: r##"Checks for casts from any numerical to a float type where\nthe receiving type cannot store all values from the original type without\nrounding errors. This possible rounding is to be expected, so this lint is\n`Allow` by default.\n\nBasically, this warns on casting any integer with 32 or more bits to `f32`\nor any 64-bit integer to `f64`."##, 5199 description: r##"Checks for casts from any numerical to a float type where
5200the receiving type cannot store all values from the original type without
5201rounding errors. This possible rounding is to be expected, so this lint is
5202`Allow` by default.
5203
5204Basically, this warns on casting any integer with 32 or more bits to `f32`
5205or any 64-bit integer to `f64`."##,
4632 }, 5206 },
4633 LintCompletion { 5207 Lint {
4634 label: "clippy::cast_ptr_alignment", 5208 label: "clippy::cast_ptr_alignment",
4635 description: r##"Checks for casts, using `as` or `pointer::cast`,\nfrom a less-strictly-aligned pointer to a more-strictly-aligned pointer"##, 5209 description: r##"Checks for casts, using `as` or `pointer::cast`,
5210from a less-strictly-aligned pointer to a more-strictly-aligned pointer"##,
4636 }, 5211 },
4637 LintCompletion { 5212 Lint {
4638 label: "clippy::cast_ref_to_mut", 5213 label: "clippy::cast_ref_to_mut",
4639 description: r##"Checks for casts of `&T` to `&mut T` anywhere in the code."##, 5214 description: r##"Checks for casts of `&T` to `&mut T` anywhere in the code."##,
4640 }, 5215 },
4641 LintCompletion { 5216 Lint {
4642 label: "clippy::cast_sign_loss", 5217 label: "clippy::cast_sign_loss",
4643 description: r##"Checks for casts from a signed to an unsigned numerical\ntype. In this case, negative values wrap around to large positive values,\nwhich can be quite surprising in practice. However, as the cast works as\ndefined, this lint is `Allow` by default."##, 5218 description: r##"Checks for casts from a signed to an unsigned numerical
5219type. In this case, negative values wrap around to large positive values,
5220which can be quite surprising in practice. However, as the cast works as
5221defined, this lint is `Allow` by default."##,
4644 }, 5222 },
4645 LintCompletion { 5223 Lint {
4646 label: "clippy::char_lit_as_u8", 5224 label: "clippy::char_lit_as_u8",
4647 description: r##"Checks for expressions where a character literal is cast\nto `u8` and suggests using a byte literal instead."##, 5225 description: r##"Checks for expressions where a character literal is cast
5226to `u8` and suggests using a byte literal instead."##,
4648 }, 5227 },
4649 LintCompletion { 5228 Lint {
4650 label: "clippy::chars_last_cmp", 5229 label: "clippy::chars_last_cmp",
4651 description: r##"Checks for usage of `_.chars().last()` or\n`_.chars().next_back()` on a `str` to check if it ends with a given char."##, 5230 description: r##"Checks for usage of `_.chars().last()` or
5231`_.chars().next_back()` on a `str` to check if it ends with a given char."##,
4652 }, 5232 },
4653 LintCompletion { 5233 Lint {
4654 label: "clippy::chars_next_cmp", 5234 label: "clippy::chars_next_cmp",
4655 description: r##"Checks for usage of `.chars().next()` on a `str` to check\nif it starts with a given char."##, 5235 description: r##"Checks for usage of `.chars().next()` on a `str` to check
5236if it starts with a given char."##,
4656 }, 5237 },
4657 LintCompletion { 5238 Lint {
4658 label: "clippy::checked_conversions", 5239 label: "clippy::checked_conversions",
4659 description: r##"Checks for explicit bounds checking when casting."##, 5240 description: r##"Checks for explicit bounds checking when casting."##,
4660 }, 5241 },
4661 LintCompletion { 5242 Lint {
4662 label: "clippy::clone_double_ref", 5243 label: "clippy::clone_double_ref",
4663 description: r##"Checks for usage of `.clone()` on an `&&T`."##, 5244 description: r##"Checks for usage of `.clone()` on an `&&T`."##,
4664 }, 5245 },
4665 LintCompletion { 5246 Lint {
4666 label: "clippy::clone_on_copy", 5247 label: "clippy::clone_on_copy",
4667 description: r##"Checks for usage of `.clone()` on a `Copy` type."##, 5248 description: r##"Checks for usage of `.clone()` on a `Copy` type."##,
4668 }, 5249 },
4669 LintCompletion { 5250 Lint {
4670 label: "clippy::clone_on_ref_ptr", 5251 label: "clippy::clone_on_ref_ptr",
4671 description: r##"Checks for usage of `.clone()` on a ref-counted pointer,\n(`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\nfunction syntax instead (e.g., `Rc::clone(foo)`)."##, 5252 description: r##"Checks for usage of `.clone()` on a ref-counted pointer,
5253(`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
5254function syntax instead (e.g., `Rc::clone(foo)`)."##,
4672 }, 5255 },
4673 LintCompletion { label: "clippy::cmp_nan", description: r##"Checks for comparisons to NaN."## }, 5256 Lint {
4674 LintCompletion { 5257 label: "clippy::cloned_instead_of_copied",
5258 description: r##"Checks for usages of `cloned()` on an `Iterator` or `Option` where
5259`copied()` could be used instead."##,
5260 },
5261 Lint { label: "clippy::cmp_nan", description: r##"Checks for comparisons to NaN."## },
5262 Lint {
4675 label: "clippy::cmp_null", 5263 label: "clippy::cmp_null",
4676 description: r##"This lint checks for equality comparisons with `ptr::null`"##, 5264 description: r##"This lint checks for equality comparisons with `ptr::null`"##,
4677 }, 5265 },
4678 LintCompletion { 5266 Lint {
4679 label: "clippy::cmp_owned", 5267 label: "clippy::cmp_owned",
4680 description: r##"Checks for conversions to owned values just for the sake\nof a comparison."##, 5268 description: r##"Checks for conversions to owned values just for the sake
5269of a comparison."##,
4681 }, 5270 },
4682 LintCompletion { 5271 Lint {
4683 label: "clippy::cognitive_complexity", 5272 label: "clippy::cognitive_complexity",
4684 description: r##"Checks for methods with high cognitive complexity."##, 5273 description: r##"Checks for methods with high cognitive complexity."##,
4685 }, 5274 },
4686 LintCompletion { 5275 Lint {
4687 label: "clippy::collapsible_else_if", 5276 label: "clippy::collapsible_else_if",
4688 description: r##"Checks for collapsible `else { if ... }` expressions\nthat can be collapsed to `else if ...`."##, 5277 description: r##"Checks for collapsible `else { if ... }` expressions
5278that can be collapsed to `else if ...`."##,
4689 }, 5279 },
4690 LintCompletion { 5280 Lint {
4691 label: "clippy::collapsible_if", 5281 label: "clippy::collapsible_if",
4692 description: r##"Checks for nested `if` statements which can be collapsed\nby `&&`-combining their conditions."##, 5282 description: r##"Checks for nested `if` statements which can be collapsed
5283by `&&`-combining their conditions."##,
4693 }, 5284 },
4694 LintCompletion { 5285 Lint {
4695 label: "clippy::collapsible_match", 5286 label: "clippy::collapsible_match",
4696 description: r##"Finds nested `match` or `if let` expressions where the patterns may be \"collapsed\" together\nwithout adding any branches.\n\nNote that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only\ncases where merging would most likely make the code more readable."##, 5287 description: r##"Finds nested `match` or `if let` expressions where the patterns may be collapsed together
5288without adding any branches.
5289
5290Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
5291cases where merging would most likely make the code more readable."##,
4697 }, 5292 },
4698 LintCompletion { 5293 Lint {
4699 label: "clippy::comparison_chain", 5294 label: "clippy::comparison_chain",
4700 description: r##"Checks comparison chains written with `if` that can be\nrewritten with `match` and `cmp`."##, 5295 description: r##"Checks comparison chains written with `if` that can be
5296rewritten with `match` and `cmp`."##,
4701 }, 5297 },
4702 LintCompletion { 5298 Lint {
4703 label: "clippy::comparison_to_empty", 5299 label: "clippy::comparison_to_empty",
4704 description: r##"Checks for comparing to an empty slice such as `\"\"` or `[]`,\nand suggests using `.is_empty()` where applicable."##, 5300 description: r##"Checks for comparing to an empty slice such as `` or `[]`,
5301and suggests using `.is_empty()` where applicable."##,
4705 }, 5302 },
4706 LintCompletion { 5303 Lint {
4707 label: "clippy::copy_iterator", 5304 label: "clippy::copy_iterator",
4708 description: r##"Checks for types that implement `Copy` as well as\n`Iterator`."##, 5305 description: r##"Checks for types that implement `Copy` as well as
5306`Iterator`."##,
4709 }, 5307 },
4710 LintCompletion { 5308 Lint {
4711 label: "clippy::create_dir", 5309 label: "clippy::create_dir",
4712 description: r##"Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead."##, 5310 description: r##"Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead."##,
4713 }, 5311 },
4714 LintCompletion { 5312 Lint {
4715 label: "clippy::crosspointer_transmute", 5313 label: "clippy::crosspointer_transmute",
4716 description: r##"Checks for transmutes between a type `T` and `*T`."##, 5314 description: r##"Checks for transmutes between a type `T` and `*T`."##,
4717 }, 5315 },
4718 LintCompletion { 5316 Lint { label: "clippy::dbg_macro", description: r##"Checks for usage of dbg!() macro."## },
4719 label: "clippy::dbg_macro", 5317 Lint {
4720 description: r##"Checks for usage of dbg!() macro."##,
4721 },
4722 LintCompletion {
4723 label: "clippy::debug_assert_with_mut_call", 5318 label: "clippy::debug_assert_with_mut_call",
4724 description: r##"Checks for function/method calls with a mutable\nparameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros."##, 5319 description: r##"Checks for function/method calls with a mutable
5320parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros."##,
4725 }, 5321 },
4726 LintCompletion { 5322 Lint {
4727 label: "clippy::decimal_literal_representation", 5323 label: "clippy::decimal_literal_representation",
4728 description: r##"Warns if there is a better representation for a numeric literal."##, 5324 description: r##"Warns if there is a better representation for a numeric literal."##,
4729 }, 5325 },
4730 LintCompletion { 5326 Lint {
4731 label: "clippy::declare_interior_mutable_const", 5327 label: "clippy::declare_interior_mutable_const",
4732 description: r##"Checks for declaration of `const` items which is interior\nmutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.)."##, 5328 description: r##"Checks for declaration of `const` items which is interior
5329mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.)."##,
4733 }, 5330 },
4734 LintCompletion { 5331 Lint {
4735 label: "clippy::default_numeric_fallback", 5332 label: "clippy::default_numeric_fallback",
4736 description: r##"Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type\ninference.\n\nDefault numeric fallback means that if numeric types have not yet been bound to concrete\ntypes at the end of type inference, then integer type is bound to `i32`, and similarly\nfloating type is bound to `f64`.\n\nSee [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback."##, 5333 description: r##"Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
5334inference.
5335
5336Default numeric fallback means that if numeric types have not yet been bound to concrete
5337types at the end of type inference, then integer type is bound to `i32`, and similarly
5338floating type is bound to `f64`.
5339
5340See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback."##,
4737 }, 5341 },
4738 LintCompletion { 5342 Lint {
4739 label: "clippy::default_trait_access", 5343 label: "clippy::default_trait_access",
4740 description: r##"Checks for literal calls to `Default::default()`."##, 5344 description: r##"Checks for literal calls to `Default::default()`."##,
4741 }, 5345 },
4742 LintCompletion { 5346 Lint {
4743 label: "clippy::deprecated_cfg_attr", 5347 label: "clippy::deprecated_cfg_attr",
4744 description: r##"Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it\nwith `#[rustfmt::skip]`."##, 5348 description: r##"Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
5349with `#[rustfmt::skip]`."##,
4745 }, 5350 },
4746 LintCompletion { 5351 Lint {
4747 label: "clippy::deprecated_semver", 5352 label: "clippy::deprecated_semver",
4748 description: r##"Checks for `#[deprecated]` annotations with a `since`\nfield that is not a valid semantic version."##, 5353 description: r##"Checks for `#[deprecated]` annotations with a `since`
5354field that is not a valid semantic version."##,
4749 }, 5355 },
4750 LintCompletion { 5356 Lint {
4751 label: "clippy::deref_addrof", 5357 label: "clippy::deref_addrof",
4752 description: r##"Checks for usage of `*&` and `*&mut` in expressions."##, 5358 description: r##"Checks for usage of `*&` and `*&mut` in expressions."##,
4753 }, 5359 },
4754 LintCompletion { 5360 Lint {
4755 label: "clippy::derive_hash_xor_eq", 5361 label: "clippy::derive_hash_xor_eq",
4756 description: r##"Checks for deriving `Hash` but implementing `PartialEq`\nexplicitly or vice versa."##, 5362 description: r##"Checks for deriving `Hash` but implementing `PartialEq`
5363explicitly or vice versa."##,
4757 }, 5364 },
4758 LintCompletion { 5365 Lint {
4759 label: "clippy::derive_ord_xor_partial_ord", 5366 label: "clippy::derive_ord_xor_partial_ord",
4760 description: r##"Checks for deriving `Ord` but implementing `PartialOrd`\nexplicitly or vice versa."##, 5367 description: r##"Checks for deriving `Ord` but implementing `PartialOrd`
5368explicitly or vice versa."##,
4761 }, 5369 },
4762 LintCompletion { 5370 Lint {
4763 label: "clippy::disallowed_method", 5371 label: "clippy::disallowed_method",
4764 description: r##"Denies the configured methods and functions in clippy.toml"##, 5372 description: r##"Denies the configured methods and functions in clippy.toml"##,
4765 }, 5373 },
4766 LintCompletion { 5374 Lint {
4767 label: "clippy::diverging_sub_expression", 5375 label: "clippy::diverging_sub_expression",
4768 description: r##"Checks for diverging calls that are not match arms or\nstatements."##, 5376 description: r##"Checks for diverging calls that are not match arms or
5377statements."##,
4769 }, 5378 },
4770 LintCompletion { 5379 Lint {
4771 label: "clippy::doc_markdown", 5380 label: "clippy::doc_markdown",
4772 description: r##"Checks for the presence of `_`, `::` or camel-case words\noutside ticks in documentation."##, 5381 description: r##"Checks for the presence of `_`, `::` or camel-case words
5382outside ticks in documentation."##,
4773 }, 5383 },
4774 LintCompletion { 5384 Lint {
4775 label: "clippy::double_comparisons", 5385 label: "clippy::double_comparisons",
4776 description: r##"Checks for double comparisons that could be simplified to a single expression."##, 5386 description: r##"Checks for double comparisons that could be simplified to a single expression."##,
4777 }, 5387 },
4778 LintCompletion { 5388 Lint {
4779 label: "clippy::double_must_use", 5389 label: "clippy::double_must_use",
4780 description: r##"Checks for a [`#[must_use]`] attribute without\nfurther information on functions and methods that return a type already\nmarked as `#[must_use]`.\n\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, 5390 description: r##"Checks for a [`#[must_use]`] attribute without
5391further information on functions and methods that return a type already
5392marked as `#[must_use]`.
5393
5394[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##,
4781 }, 5395 },
4782 LintCompletion { 5396 Lint {
4783 label: "clippy::double_neg", 5397 label: "clippy::double_neg",
4784 description: r##"Detects expressions of the form `--x`."##, 5398 description: r##"Detects expressions of the form `--x`."##,
4785 }, 5399 },
4786 LintCompletion { 5400 Lint {
4787 label: "clippy::double_parens", 5401 label: "clippy::double_parens",
4788 description: r##"Checks for unnecessary double parentheses."##, 5402 description: r##"Checks for unnecessary double parentheses."##,
4789 }, 5403 },
4790 LintCompletion { 5404 Lint {
4791 label: "clippy::drop_bounds",
4792 description: r##"Nothing. This lint has been deprecated."##,
4793 },
4794 LintCompletion {
4795 label: "clippy::drop_copy", 5405 label: "clippy::drop_copy",
4796 description: r##"Checks for calls to `std::mem::drop` with a value\nthat derives the Copy trait"##, 5406 description: r##"Checks for calls to `std::mem::drop` with a value
5407that derives the Copy trait"##,
4797 }, 5408 },
4798 LintCompletion { 5409 Lint {
4799 label: "clippy::drop_ref", 5410 label: "clippy::drop_ref",
4800 description: r##"Checks for calls to `std::mem::drop` with a reference\ninstead of an owned value."##, 5411 description: r##"Checks for calls to `std::mem::drop` with a reference
5412instead of an owned value."##,
4801 }, 5413 },
4802 LintCompletion { 5414 Lint {
4803 label: "clippy::duplicate_underscore_argument", 5415 label: "clippy::duplicate_underscore_argument",
4804 description: r##"Checks for function arguments having the similar names\ndiffering by an underscore."##, 5416 description: r##"Checks for function arguments having the similar names
5417differing by an underscore."##,
4805 }, 5418 },
4806 LintCompletion { 5419 Lint {
4807 label: "clippy::duration_subsec", 5420 label: "clippy::duration_subsec",
4808 description: r##"Checks for calculation of subsecond microseconds or milliseconds\nfrom other `Duration` methods."##, 5421 description: r##"Checks for calculation of subsecond microseconds or milliseconds
5422from other `Duration` methods."##,
4809 }, 5423 },
4810 LintCompletion { 5424 Lint {
4811 label: "clippy::else_if_without_else", 5425 label: "clippy::else_if_without_else",
4812 description: r##"Checks for usage of if expressions with an `else if` branch,\nbut without a final `else` branch."##, 5426 description: r##"Checks for usage of if expressions with an `else if` branch,
5427but without a final `else` branch."##,
4813 }, 5428 },
4814 LintCompletion { 5429 Lint {
4815 label: "clippy::empty_enum", 5430 label: "clippy::empty_enum",
4816 description: r##"Checks for `enum`s with no variants.\n\nAs of this writing, the `never_type` is still a\nnightly-only experimental API. Therefore, this lint is only triggered\nif the `never_type` is enabled."##, 5431 description: r##"Checks for `enum`s with no variants.
5432
5433As of this writing, the `never_type` is still a
5434nightly-only experimental API. Therefore, this lint is only triggered
5435if the `never_type` is enabled."##,
4817 }, 5436 },
4818 LintCompletion { 5437 Lint {
4819 label: "clippy::empty_line_after_outer_attr", 5438 label: "clippy::empty_line_after_outer_attr",
4820 description: r##"Checks for empty lines after outer attributes"##, 5439 description: r##"Checks for empty lines after outer attributes"##,
4821 }, 5440 },
4822 LintCompletion { 5441 Lint { label: "clippy::empty_loop", description: r##"Checks for empty `loop` expressions."## },
4823 label: "clippy::empty_loop", 5442 Lint {
4824 description: r##"Checks for empty `loop` expressions."##,
4825 },
4826 LintCompletion {
4827 label: "clippy::enum_clike_unportable_variant", 5443 label: "clippy::enum_clike_unportable_variant",
4828 description: r##"Checks for C-like enumerations that are\n`repr(isize/usize)` and have values that don't fit into an `i32`."##, 5444 description: r##"Checks for C-like enumerations that are
5445`repr(isize/usize)` and have values that don't fit into an `i32`."##,
4829 }, 5446 },
4830 LintCompletion { 5447 Lint { label: "clippy::enum_glob_use", description: r##"Checks for `use Enum::*`."## },
4831 label: "clippy::enum_glob_use", 5448 Lint {
4832 description: r##"Checks for `use Enum::*`."##,
4833 },
4834 LintCompletion {
4835 label: "clippy::enum_variant_names", 5449 label: "clippy::enum_variant_names",
4836 description: r##"Detects enumeration variants that are prefixed or suffixed\nby the same characters."##, 5450 description: r##"Detects enumeration variants that are prefixed or suffixed
5451by the same characters."##,
4837 }, 5452 },
4838 LintCompletion { 5453 Lint {
4839 label: "clippy::eq_op", 5454 label: "clippy::eq_op",
4840 description: r##"Checks for equal operands to comparison, logical and\nbitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\n`||`, `&`, `|`, `^`, `-` and `/`)."##, 5455 description: r##"Checks for equal operands to comparison, logical and
5456bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
5457`||`, `&`, `|`, `^`, `-` and `/`)."##,
4841 }, 5458 },
4842 LintCompletion { 5459 Lint {
4843 label: "clippy::erasing_op", 5460 label: "clippy::erasing_op",
4844 description: r##"Checks for erasing operations, e.g., `x * 0`."##, 5461 description: r##"Checks for erasing operations, e.g., `x * 0`."##,
4845 }, 5462 },
4846 LintCompletion { 5463 Lint {
4847 label: "clippy::eval_order_dependence", 5464 label: "clippy::eval_order_dependence",
4848 description: r##"Checks for a read and a write to the same variable where\nwhether the read occurs before or after the write depends on the evaluation\norder of sub-expressions."##, 5465 description: r##"Checks for a read and a write to the same variable where
5466whether the read occurs before or after the write depends on the evaluation
5467order of sub-expressions."##,
4849 }, 5468 },
4850 LintCompletion { 5469 Lint {
4851 label: "clippy::excessive_precision", 5470 label: "clippy::excessive_precision",
4852 description: r##"Checks for float literals with a precision greater\nthan that supported by the underlying type."##, 5471 description: r##"Checks for float literals with a precision greater
5472than that supported by the underlying type."##,
4853 }, 5473 },
4854 LintCompletion { 5474 Lint {
4855 label: "clippy::exhaustive_enums", 5475 label: "clippy::exhaustive_enums",
4856 description: r##"Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`"##, 5476 description: r##"Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`"##,
4857 }, 5477 },
4858 LintCompletion { 5478 Lint {
4859 label: "clippy::exhaustive_structs", 5479 label: "clippy::exhaustive_structs",
4860 description: r##"Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`"##, 5480 description: r##"Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`"##,
4861 }, 5481 },
4862 LintCompletion { 5482 Lint {
4863 label: "clippy::exit", 5483 label: "clippy::exit",
4864 description: r##"`exit()` terminates the program and doesn't provide a\nstack trace."##, 5484 description: r##"`exit()` terminates the program and doesn't provide a
5485stack trace."##,
4865 }, 5486 },
4866 LintCompletion { 5487 Lint {
4867 label: "clippy::expect_fun_call", 5488 label: "clippy::expect_fun_call",
4868 description: r##"Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,\netc., and suggests to use `unwrap_or_else` instead"##, 5489 description: r##"Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
5490etc., and suggests to use `unwrap_or_else` instead"##,
4869 }, 5491 },
4870 LintCompletion { 5492 Lint {
4871 label: "clippy::expect_used", 5493 label: "clippy::expect_used",
4872 description: r##"Checks for `.expect()` calls on `Option`s and `Result`s."##, 5494 description: r##"Checks for `.expect()` calls on `Option`s and `Result`s."##,
4873 }, 5495 },
4874 LintCompletion { 5496 Lint {
4875 label: "clippy::expl_impl_clone_on_copy", 5497 label: "clippy::expl_impl_clone_on_copy",
4876 description: r##"Checks for explicit `Clone` implementations for `Copy`\ntypes."##, 5498 description: r##"Checks for explicit `Clone` implementations for `Copy`
5499types."##,
4877 }, 5500 },
4878 LintCompletion { 5501 Lint {
4879 label: "clippy::explicit_counter_loop", 5502 label: "clippy::explicit_counter_loop",
4880 description: r##"Checks `for` loops over slices with an explicit counter\nand suggests the use of `.enumerate()`."##, 5503 description: r##"Checks `for` loops over slices with an explicit counter
5504and suggests the use of `.enumerate()`."##,
4881 }, 5505 },
4882 LintCompletion { 5506 Lint {
4883 label: "clippy::explicit_deref_methods", 5507 label: "clippy::explicit_deref_methods",
4884 description: r##"Checks for explicit `deref()` or `deref_mut()` method calls."##, 5508 description: r##"Checks for explicit `deref()` or `deref_mut()` method calls."##,
4885 }, 5509 },
4886 LintCompletion { 5510 Lint {
4887 label: "clippy::explicit_into_iter_loop", 5511 label: "clippy::explicit_into_iter_loop",
4888 description: r##"Checks for loops on `y.into_iter()` where `y` will do, and\nsuggests the latter."##, 5512 description: r##"Checks for loops on `y.into_iter()` where `y` will do, and
5513suggests the latter."##,
4889 }, 5514 },
4890 LintCompletion { 5515 Lint {
4891 label: "clippy::explicit_iter_loop", 5516 label: "clippy::explicit_iter_loop",
4892 description: r##"Checks for loops on `x.iter()` where `&x` will do, and\nsuggests the latter."##, 5517 description: r##"Checks for loops on `x.iter()` where `&x` will do, and
5518suggests the latter."##,
4893 }, 5519 },
4894 LintCompletion { 5520 Lint {
4895 label: "clippy::explicit_write", 5521 label: "clippy::explicit_write",
4896 description: r##"Checks for usage of `write!()` / `writeln()!` which can be\nreplaced with `(e)print!()` / `(e)println!()`"##, 5522 description: r##"Checks for usage of `write!()` / `writeln()!` which can be
5523replaced with `(e)print!()` / `(e)println!()`"##,
4897 }, 5524 },
4898 LintCompletion { 5525 Lint {
4899 label: "clippy::extend_from_slice", 5526 label: "clippy::extend_from_slice",
4900 description: r##"Nothing. This lint has been deprecated."##, 5527 description: r##"Nothing. This lint has been deprecated."##,
4901 }, 5528 },
4902 LintCompletion { 5529 Lint {
4903 label: "clippy::extra_unused_lifetimes", 5530 label: "clippy::extra_unused_lifetimes",
4904 description: r##"Checks for lifetimes in generics that are never used\nanywhere else."##, 5531 description: r##"Checks for lifetimes in generics that are never used
5532anywhere else."##,
4905 }, 5533 },
4906 LintCompletion { 5534 Lint {
4907 label: "clippy::fallible_impl_from", 5535 label: "clippy::fallible_impl_from",
4908 description: r##"Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`"##, 5536 description: r##"Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`"##,
4909 }, 5537 },
4910 LintCompletion { 5538 Lint {
4911 label: "clippy::field_reassign_with_default", 5539 label: "clippy::field_reassign_with_default",
4912 description: r##"Checks for immediate reassignment of fields initialized\nwith Default::default()."##, 5540 description: r##"Checks for immediate reassignment of fields initialized
5541with Default::default()."##,
4913 }, 5542 },
4914 LintCompletion { 5543 Lint {
4915 label: "clippy::filetype_is_file", 5544 label: "clippy::filetype_is_file",
4916 description: r##"Checks for `FileType::is_file()`."##, 5545 description: r##"Checks for `FileType::is_file()`."##,
4917 }, 5546 },
4918 LintCompletion { 5547 Lint {
4919 label: "clippy::filter_map", 5548 label: "clippy::filter_map",
4920 description: r##"Checks for usage of `_.filter(_).map(_)`,\n`_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar."##, 5549 description: r##"Nothing. This lint has been deprecated."##,
4921 }, 5550 },
4922 LintCompletion { 5551 Lint {
4923 label: "clippy::filter_map_identity", 5552 label: "clippy::filter_map_identity",
4924 description: r##"Checks for usage of `filter_map(|x| x)`."##, 5553 description: r##"Checks for usage of `filter_map(|x| x)`."##,
4925 }, 5554 },
4926 LintCompletion { 5555 Lint {
4927 label: "clippy::filter_map_next", 5556 label: "clippy::filter_map_next",
4928 description: r##"Checks for usage of `_.filter_map(_).next()`."##, 5557 description: r##"Checks for usage of `_.filter_map(_).next()`."##,
4929 }, 5558 },
4930 LintCompletion { 5559 Lint {
4931 label: "clippy::filter_next", 5560 label: "clippy::filter_next",
4932 description: r##"Checks for usage of `_.filter(_).next()`."##, 5561 description: r##"Checks for usage of `_.filter(_).next()`."##,
4933 }, 5562 },
4934 LintCompletion { 5563 Lint { label: "clippy::find_map", description: r##"Nothing. This lint has been deprecated."## },
4935 label: "clippy::find_map", 5564 Lint {
4936 description: r##"Nothing. This lint has been deprecated."##,
4937 },
4938 LintCompletion {
4939 label: "clippy::flat_map_identity", 5565 label: "clippy::flat_map_identity",
4940 description: r##"Checks for usage of `flat_map(|x| x)`."##, 5566 description: r##"Checks for usage of `flat_map(|x| x)`."##,
4941 }, 5567 },
4942 LintCompletion { 5568 Lint {
4943 label: "clippy::float_arithmetic", 5569 label: "clippy::flat_map_option",
4944 description: r##"Checks for float arithmetic."##, 5570 description: r##"Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
5571used instead."##,
4945 }, 5572 },
4946 LintCompletion { 5573 Lint { label: "clippy::float_arithmetic", description: r##"Checks for float arithmetic."## },
5574 Lint {
4947 label: "clippy::float_cmp", 5575 label: "clippy::float_cmp",
4948 description: r##"Checks for (in-)equality comparisons on floating-point\nvalues (apart from zero), except in functions called `*eq*` (which probably\nimplement equality for a type involving floats)."##, 5576 description: r##"Checks for (in-)equality comparisons on floating-point
5577values (apart from zero), except in functions called `*eq*` (which probably
5578implement equality for a type involving floats)."##,
4949 }, 5579 },
4950 LintCompletion { 5580 Lint {
4951 label: "clippy::float_cmp_const", 5581 label: "clippy::float_cmp_const",
4952 description: r##"Checks for (in-)equality comparisons on floating-point\nvalue and constant, except in functions called `*eq*` (which probably\nimplement equality for a type involving floats)."##, 5582 description: r##"Checks for (in-)equality comparisons on floating-point
5583value and constant, except in functions called `*eq*` (which probably
5584implement equality for a type involving floats)."##,
4953 }, 5585 },
4954 LintCompletion { 5586 Lint {
4955 label: "clippy::float_equality_without_abs", 5587 label: "clippy::float_equality_without_abs",
4956 description: r##"Checks for statements of the form `(a - b) < f32::EPSILON` or\n`(a - b) < f64::EPSILON`. Notes the missing `.abs()`."##, 5588 description: r##"Checks for statements of the form `(a - b) < f32::EPSILON` or
5589`(a - b) < f64::EPSILON`. Notes the missing `.abs()`."##,
4957 }, 5590 },
4958 LintCompletion { 5591 Lint {
4959 label: "clippy::fn_address_comparisons", 5592 label: "clippy::fn_address_comparisons",
4960 description: r##"Checks for comparisons with an address of a function item."##, 5593 description: r##"Checks for comparisons with an address of a function item."##,
4961 }, 5594 },
4962 LintCompletion { 5595 Lint {
4963 label: "clippy::fn_params_excessive_bools", 5596 label: "clippy::fn_params_excessive_bools",
4964 description: r##"Checks for excessive use of\nbools in function definitions."##, 5597 description: r##"Checks for excessive use of
5598bools in function definitions."##,
4965 }, 5599 },
4966 LintCompletion { 5600 Lint {
4967 label: "clippy::fn_to_numeric_cast", 5601 label: "clippy::fn_to_numeric_cast",
4968 description: r##"Checks for casts of function pointers to something other than usize"##, 5602 description: r##"Checks for casts of function pointers to something other than usize"##,
4969 }, 5603 },
4970 LintCompletion { 5604 Lint {
4971 label: "clippy::fn_to_numeric_cast_with_truncation", 5605 label: "clippy::fn_to_numeric_cast_with_truncation",
4972 description: r##"Checks for casts of a function pointer to a numeric type not wide enough to\nstore address."##, 5606 description: r##"Checks for casts of a function pointer to a numeric type not wide enough to
5607store address."##,
4973 }, 5608 },
4974 LintCompletion { 5609 Lint {
4975 label: "clippy::for_kv_map", 5610 label: "clippy::for_kv_map",
4976 description: r##"Checks for iterating a map (`HashMap` or `BTreeMap`) and\nignoring either the keys or values."##, 5611 description: r##"Checks for iterating a map (`HashMap` or `BTreeMap`) and
5612ignoring either the keys or values."##,
4977 }, 5613 },
4978 LintCompletion { 5614 Lint {
4979 label: "clippy::for_loops_over_fallibles", 5615 label: "clippy::for_loops_over_fallibles",
4980 description: r##"Checks for `for` loops over `Option` or `Result` values."##, 5616 description: r##"Checks for `for` loops over `Option` or `Result` values."##,
4981 }, 5617 },
4982 LintCompletion { 5618 Lint {
4983 label: "clippy::forget_copy", 5619 label: "clippy::forget_copy",
4984 description: r##"Checks for calls to `std::mem::forget` with a value that\nderives the Copy trait"##, 5620 description: r##"Checks for calls to `std::mem::forget` with a value that
5621derives the Copy trait"##,
4985 }, 5622 },
4986 LintCompletion { 5623 Lint {
4987 label: "clippy::forget_ref", 5624 label: "clippy::forget_ref",
4988 description: r##"Checks for calls to `std::mem::forget` with a reference\ninstead of an owned value."##, 5625 description: r##"Checks for calls to `std::mem::forget` with a reference
5626instead of an owned value."##,
4989 }, 5627 },
4990 LintCompletion { 5628 Lint {
4991 label: "clippy::from_iter_instead_of_collect", 5629 label: "clippy::from_iter_instead_of_collect",
4992 description: r##"Checks for `from_iter()` function calls on types that implement the `FromIterator`\ntrait."##, 5630 description: r##"Checks for `from_iter()` function calls on types that implement the `FromIterator`
5631trait."##,
4993 }, 5632 },
4994 LintCompletion { 5633 Lint {
4995 label: "clippy::from_over_into", 5634 label: "clippy::from_over_into",
4996 description: r##"Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead."##, 5635 description: r##"Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead."##,
4997 }, 5636 },
4998 LintCompletion { 5637 Lint {
4999 label: "clippy::from_str_radix_10", 5638 label: "clippy::from_str_radix_10",
5000 description: r##"Checks for function invocations of the form `primitive::from_str_radix(s, 10)`"##, 5639 description: r##"Checks for function invocations of the form `primitive::from_str_radix(s, 10)`"##,
5001 }, 5640 },
5002 LintCompletion { 5641 Lint {
5003 label: "clippy::future_not_send", 5642 label: "clippy::future_not_send",
5004 description: r##"This lint requires Future implementations returned from\nfunctions and methods to implement the `Send` marker trait. It is mostly\nused by library authors (public and internal) that target an audience where\nmultithreaded executors are likely to be used for running these Futures."##, 5643 description: r##"This lint requires Future implementations returned from
5644functions and methods to implement the `Send` marker trait. It is mostly
5645used by library authors (public and internal) that target an audience where
5646multithreaded executors are likely to be used for running these Futures."##,
5005 }, 5647 },
5006 LintCompletion { 5648 Lint {
5007 label: "clippy::get_last_with_len", 5649 label: "clippy::get_last_with_len",
5008 description: r##"Checks for using `x.get(x.len() - 1)` instead of\n`x.last()`."##, 5650 description: r##"Checks for using `x.get(x.len() - 1)` instead of
5651`x.last()`."##,
5009 }, 5652 },
5010 LintCompletion { 5653 Lint {
5011 label: "clippy::get_unwrap", 5654 label: "clippy::get_unwrap",
5012 description: r##"Checks for use of `.get().unwrap()` (or\n`.get_mut().unwrap`) on a standard library type which implements `Index`"##, 5655 description: r##"Checks for use of `.get().unwrap()` (or
5656`.get_mut().unwrap`) on a standard library type which implements `Index`"##,
5013 }, 5657 },
5014 LintCompletion { 5658 Lint {
5015 label: "clippy::identity_op", 5659 label: "clippy::identity_op",
5016 description: r##"Checks for identity operations, e.g., `x + 0`."##, 5660 description: r##"Checks for identity operations, e.g., `x + 0`."##,
5017 }, 5661 },
5018 LintCompletion { 5662 Lint {
5019 label: "clippy::if_let_mutex", 5663 label: "clippy::if_let_mutex",
5020 description: r##"Checks for `Mutex::lock` calls in `if let` expression\nwith lock calls in any of the else blocks."##, 5664 description: r##"Checks for `Mutex::lock` calls in `if let` expression
5665with lock calls in any of the else blocks."##,
5021 }, 5666 },
5022 LintCompletion { 5667 Lint {
5023 label: "clippy::if_let_redundant_pattern_matching", 5668 label: "clippy::if_let_redundant_pattern_matching",
5024 description: r##"Nothing. This lint has been deprecated."##, 5669 description: r##"Nothing. This lint has been deprecated."##,
5025 }, 5670 },
5026 LintCompletion { 5671 Lint {
5027 label: "clippy::if_let_some_result", 5672 label: "clippy::if_let_some_result",
5028 description: r##"* Checks for unnecessary `ok()` in if let."##, 5673 description: r##"* Checks for unnecessary `ok()` in if let."##,
5029 }, 5674 },
5030 LintCompletion { 5675 Lint {
5031 label: "clippy::if_not_else", 5676 label: "clippy::if_not_else",
5032 description: r##"Checks for usage of `!` or `!=` in an if condition with an\nelse branch."##, 5677 description: r##"Checks for usage of `!` or `!=` in an if condition with an
5678else branch."##,
5033 }, 5679 },
5034 LintCompletion { 5680 Lint {
5035 label: "clippy::if_same_then_else", 5681 label: "clippy::if_same_then_else",
5036 description: r##"Checks for `if/else` with the same body as the *then* part\nand the *else* part."##, 5682 description: r##"Checks for `if/else` with the same body as the *then* part
5683and the *else* part."##,
5684 },
5685 Lint {
5686 label: "clippy::if_then_some_else_none",
5687 description: r##"Checks for if-else that could be written to `bool::then`."##,
5037 }, 5688 },
5038 LintCompletion { 5689 Lint {
5039 label: "clippy::ifs_same_cond", 5690 label: "clippy::ifs_same_cond",
5040 description: r##"Checks for consecutive `if`s with the same condition."##, 5691 description: r##"Checks for consecutive `if`s with the same condition."##,
5041 }, 5692 },
5042 LintCompletion { 5693 Lint {
5043 label: "clippy::implicit_clone", 5694 label: "clippy::implicit_clone",
5044 description: r##"Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer."##, 5695 description: r##"Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer."##,
5045 }, 5696 },
5046 LintCompletion { 5697 Lint {
5047 label: "clippy::implicit_hasher", 5698 label: "clippy::implicit_hasher",
5048 description: r##"Checks for public `impl` or `fn` missing generalization\nover different hashers and implicitly defaulting to the default hashing\nalgorithm (`SipHash`)."##, 5699 description: r##"Checks for public `impl` or `fn` missing generalization
5700over different hashers and implicitly defaulting to the default hashing
5701algorithm (`SipHash`)."##,
5049 }, 5702 },
5050 LintCompletion { 5703 Lint {
5051 label: "clippy::implicit_return", 5704 label: "clippy::implicit_return",
5052 description: r##"Checks for missing return statements at the end of a block."##, 5705 description: r##"Checks for missing return statements at the end of a block."##,
5053 }, 5706 },
5054 LintCompletion { 5707 Lint {
5055 label: "clippy::implicit_saturating_sub", 5708 label: "clippy::implicit_saturating_sub",
5056 description: r##"Checks for implicit saturating subtraction."##, 5709 description: r##"Checks for implicit saturating subtraction."##,
5057 }, 5710 },
5058 LintCompletion { 5711 Lint {
5059 label: "clippy::imprecise_flops", 5712 label: "clippy::imprecise_flops",
5060 description: r##"Looks for floating-point expressions that\ncan be expressed using built-in methods to improve accuracy\nat the cost of performance."##, 5713 description: r##"Looks for floating-point expressions that
5714can be expressed using built-in methods to improve accuracy
5715at the cost of performance."##,
5061 }, 5716 },
5062 LintCompletion { 5717 Lint {
5063 label: "clippy::inconsistent_digit_grouping", 5718 label: "clippy::inconsistent_digit_grouping",
5064 description: r##"Warns if an integral or floating-point constant is\ngrouped inconsistently with underscores."##, 5719 description: r##"Warns if an integral or floating-point constant is
5720grouped inconsistently with underscores."##,
5065 }, 5721 },
5066 LintCompletion { 5722 Lint {
5067 label: "clippy::inconsistent_struct_constructor", 5723 label: "clippy::inconsistent_struct_constructor",
5068 description: r##"Checks for struct constructors where the order of the field init\nshorthand in the constructor is inconsistent with the order in the struct definition."##, 5724 description: r##"Checks for struct constructors where all fields are shorthand and
5725the order of the field init shorthand in the constructor is inconsistent
5726with the order in the struct definition."##,
5069 }, 5727 },
5070 LintCompletion { 5728 Lint {
5071 label: "clippy::indexing_slicing", 5729 label: "clippy::indexing_slicing",
5072 description: r##"Checks for usage of indexing or slicing. Arrays are special cases, this lint\ndoes report on arrays if we can tell that slicing operations are in bounds and does not\nlint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint."##, 5730 description: r##"Checks for usage of indexing or slicing. Arrays are special cases, this lint
5731does report on arrays if we can tell that slicing operations are in bounds and does not
5732lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint."##,
5073 }, 5733 },
5074 LintCompletion { 5734 Lint {
5075 label: "clippy::ineffective_bit_mask", 5735 label: "clippy::ineffective_bit_mask",
5076 description: r##"Checks for bit masks in comparisons which can be removed\nwithout changing the outcome. The basic structure can be seen in the\nfollowing table:\n\n|Comparison| Bit Op |Example |equals |\n|----------|---------|-----------|-------|\n|`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\n|`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|"##, 5736 description: r##"Checks for bit masks in comparisons which can be removed
5737without changing the outcome. The basic structure can be seen in the
5738following table:
5739
5740|Comparison| Bit Op |Example |equals |
5741|----------|---------|-----------|-------|
5742|`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
5743|`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|"##,
5077 }, 5744 },
5078 LintCompletion { 5745 Lint {
5079 label: "clippy::inefficient_to_string", 5746 label: "clippy::inefficient_to_string",
5080 description: r##"Checks for usage of `.to_string()` on an `&&T` where\n`T` implements `ToString` directly (like `&&str` or `&&String`)."##, 5747 description: r##"Checks for usage of `.to_string()` on an `&&T` where
5748`T` implements `ToString` directly (like `&&str` or `&&String`)."##,
5081 }, 5749 },
5082 LintCompletion { 5750 Lint {
5083 label: "clippy::infallible_destructuring_match", 5751 label: "clippy::infallible_destructuring_match",
5084 description: r##"Checks for matches being used to destructure a single-variant enum\nor tuple struct where a `let` will suffice."##, 5752 description: r##"Checks for matches being used to destructure a single-variant enum
5753or tuple struct where a `let` will suffice."##,
5085 }, 5754 },
5086 LintCompletion { 5755 Lint {
5087 label: "clippy::infinite_iter", 5756 label: "clippy::infinite_iter",
5088 description: r##"Checks for iteration that is guaranteed to be infinite."##, 5757 description: r##"Checks for iteration that is guaranteed to be infinite."##,
5089 }, 5758 },
5090 LintCompletion { 5759 Lint {
5091 label: "clippy::inherent_to_string", 5760 label: "clippy::inherent_to_string",
5092 description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`."##, 5761 description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`."##,
5093 }, 5762 },
5094 LintCompletion { 5763 Lint {
5095 label: "clippy::inherent_to_string_shadow_display", 5764 label: "clippy::inherent_to_string_shadow_display",
5096 description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait."##, 5765 description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait."##,
5097 }, 5766 },
5098 LintCompletion { 5767 Lint {
5099 label: "clippy::inline_always", 5768 label: "clippy::inline_always",
5100 description: r##"Checks for items annotated with `#[inline(always)]`,\nunless the annotated function is empty or simply panics."##, 5769 description: r##"Checks for items annotated with `#[inline(always)]`,
5770unless the annotated function is empty or simply panics."##,
5101 }, 5771 },
5102 LintCompletion { 5772 Lint {
5103 label: "clippy::inline_asm_x86_att_syntax", 5773 label: "clippy::inline_asm_x86_att_syntax",
5104 description: r##"Checks for usage of AT&T x86 assembly syntax."##, 5774 description: r##"Checks for usage of AT&T x86 assembly syntax."##,
5105 }, 5775 },
5106 LintCompletion { 5776 Lint {
5107 label: "clippy::inline_asm_x86_intel_syntax", 5777 label: "clippy::inline_asm_x86_intel_syntax",
5108 description: r##"Checks for usage of Intel x86 assembly syntax."##, 5778 description: r##"Checks for usage of Intel x86 assembly syntax."##,
5109 }, 5779 },
5110 LintCompletion { 5780 Lint {
5111 label: "clippy::inline_fn_without_body", 5781 label: "clippy::inline_fn_without_body",
5112 description: r##"Checks for `#[inline]` on trait methods without bodies"##, 5782 description: r##"Checks for `#[inline]` on trait methods without bodies"##,
5113 }, 5783 },
5114 LintCompletion { 5784 Lint {
5115 label: "clippy::inspect_for_each", 5785 label: "clippy::inspect_for_each",
5116 description: r##"Checks for usage of `inspect().for_each()`."##, 5786 description: r##"Checks for usage of `inspect().for_each()`."##,
5117 }, 5787 },
5118 LintCompletion { 5788 Lint {
5119 label: "clippy::int_plus_one", 5789 label: "clippy::int_plus_one",
5120 description: r##"Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block"##, 5790 description: r##"Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block"##,
5121 }, 5791 },
5122 LintCompletion { 5792 Lint {
5123 label: "clippy::integer_arithmetic", 5793 label: "clippy::integer_arithmetic",
5124 description: r##"Checks for integer arithmetic operations which could overflow or panic.\n\nSpecifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\nof overflowing according to the [Rust\nReference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\nor which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\nattempted."##, 5794 description: r##"Checks for integer arithmetic operations which could overflow or panic.
5125 }, 5795
5126 LintCompletion { 5796Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
5127 label: "clippy::integer_division", 5797of overflowing according to the [Rust
5128 description: r##"Checks for division of integers"##, 5798Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
5129 }, 5799or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
5130 LintCompletion { 5800attempted."##,
5131 label: "clippy::into_iter_on_array",
5132 description: r##"Nothing. This lint has been deprecated."##,
5133 }, 5801 },
5134 LintCompletion { 5802 Lint { label: "clippy::integer_division", description: r##"Checks for division of integers"## },
5803 Lint {
5135 label: "clippy::into_iter_on_ref", 5804 label: "clippy::into_iter_on_ref",
5136 description: r##"Checks for `into_iter` calls on references which should be replaced by `iter`\nor `iter_mut`."##, 5805 description: r##"Checks for `into_iter` calls on references which should be replaced by `iter`
5806or `iter_mut`."##,
5137 }, 5807 },
5138 LintCompletion { 5808 Lint {
5139 label: "clippy::invalid_atomic_ordering", 5809 label: "clippy::invalid_atomic_ordering",
5140 description: r##"Checks for usage of invalid atomic\nordering in atomic loads/stores/exchanges/updates and\nmemory fences."##, 5810 description: r##"Checks for usage of invalid atomic
5811ordering in atomic loads/stores/exchanges/updates and
5812memory fences."##,
5141 }, 5813 },
5142 LintCompletion { 5814 Lint {
5143 label: "clippy::invalid_ref", 5815 label: "clippy::invalid_null_ptr_usage",
5144 description: r##"Nothing. This lint has been deprecated."##, 5816 description: r##"This lint checks for invalid usages of `ptr::null`."##,
5145 }, 5817 },
5146 LintCompletion { 5818 Lint {
5147 label: "clippy::invalid_regex", 5819 label: "clippy::invalid_regex",
5148 description: r##"Checks [regex](https://crates.io/crates/regex) creation\n(with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct\nregex syntax."##, 5820 description: r##"Checks [regex](https://crates.io/crates/regex) creation
5821(with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct
5822regex syntax."##,
5149 }, 5823 },
5150 LintCompletion { 5824 Lint {
5151 label: "clippy::invalid_upcast_comparisons", 5825 label: "clippy::invalid_upcast_comparisons",
5152 description: r##"Checks for comparisons where the relation is always either\ntrue or false, but where one side has been upcast so that the comparison is\nnecessary. Only integer types are checked."##, 5826 description: r##"Checks for comparisons where the relation is always either
5827true or false, but where one side has been upcast so that the comparison is
5828necessary. Only integer types are checked."##,
5153 }, 5829 },
5154 LintCompletion { 5830 Lint {
5155 label: "clippy::invisible_characters", 5831 label: "clippy::invisible_characters",
5156 description: r##"Checks for invisible Unicode characters in the code."##, 5832 description: r##"Checks for invisible Unicode characters in the code."##,
5157 }, 5833 },
5158 LintCompletion { 5834 Lint {
5159 label: "clippy::items_after_statements", 5835 label: "clippy::items_after_statements",
5160 description: r##"Checks for items declared after some statement in a block."##, 5836 description: r##"Checks for items declared after some statement in a block."##,
5161 }, 5837 },
5162 LintCompletion { 5838 Lint {
5163 label: "clippy::iter_cloned_collect", 5839 label: "clippy::iter_cloned_collect",
5164 description: r##"Checks for the use of `.cloned().collect()` on slice to\ncreate a `Vec`."##, 5840 description: r##"Checks for the use of `.cloned().collect()` on slice to
5841create a `Vec`."##,
5165 }, 5842 },
5166 LintCompletion { 5843 Lint {
5167 label: "clippy::iter_next_loop", 5844 label: "clippy::iter_count",
5168 description: r##"Checks for loops on `x.next()`."##, 5845 description: r##"Checks for the use of `.iter().count()`."##,
5169 }, 5846 },
5170 LintCompletion { 5847 Lint { label: "clippy::iter_next_loop", description: r##"Checks for loops on `x.next()`."## },
5848 Lint {
5171 label: "clippy::iter_next_slice", 5849 label: "clippy::iter_next_slice",
5172 description: r##"Checks for usage of `iter().next()` on a Slice or an Array"##, 5850 description: r##"Checks for usage of `iter().next()` on a Slice or an Array"##,
5173 }, 5851 },
5174 LintCompletion { 5852 Lint {
5175 label: "clippy::iter_nth", 5853 label: "clippy::iter_nth",
5176 description: r##"Checks for use of `.iter().nth()` (and the related\n`.iter_mut().nth()`) on standard library types with O(1) element access."##, 5854 description: r##"Checks for use of `.iter().nth()` (and the related
5855`.iter_mut().nth()`) on standard library types with O(1) element access."##,
5177 }, 5856 },
5178 LintCompletion { 5857 Lint {
5179 label: "clippy::iter_nth_zero", 5858 label: "clippy::iter_nth_zero",
5180 description: r##"Checks for the use of `iter.nth(0)`."##, 5859 description: r##"Checks for the use of `iter.nth(0)`."##,
5181 }, 5860 },
5182 LintCompletion { 5861 Lint {
5183 label: "clippy::iter_skip_next", 5862 label: "clippy::iter_skip_next",
5184 description: r##"Checks for use of `.skip(x).next()` on iterators."##, 5863 description: r##"Checks for use of `.skip(x).next()` on iterators."##,
5185 }, 5864 },
5186 LintCompletion { 5865 Lint {
5187 label: "clippy::iterator_step_by_zero", 5866 label: "clippy::iterator_step_by_zero",
5188 description: r##"Checks for calling `.step_by(0)` on iterators which panics."##, 5867 description: r##"Checks for calling `.step_by(0)` on iterators which panics."##,
5189 }, 5868 },
5190 LintCompletion { 5869 Lint {
5191 label: "clippy::just_underscores_and_digits", 5870 label: "clippy::just_underscores_and_digits",
5192 description: r##"Checks if you have variables whose name consists of just\nunderscores and digits."##, 5871 description: r##"Checks if you have variables whose name consists of just
5872underscores and digits."##,
5193 }, 5873 },
5194 LintCompletion { 5874 Lint {
5195 label: "clippy::large_const_arrays", 5875 label: "clippy::large_const_arrays",
5196 description: r##"Checks for large `const` arrays that should\nbe defined as `static` instead."##, 5876 description: r##"Checks for large `const` arrays that should
5877be defined as `static` instead."##,
5197 }, 5878 },
5198 LintCompletion { 5879 Lint {
5199 label: "clippy::large_digit_groups", 5880 label: "clippy::large_digit_groups",
5200 description: r##"Warns if the digits of an integral or floating-point\nconstant are grouped into groups that\nare too large."##, 5881 description: r##"Warns if the digits of an integral or floating-point
5882constant are grouped into groups that
5883are too large."##,
5201 }, 5884 },
5202 LintCompletion { 5885 Lint {
5203 label: "clippy::large_enum_variant", 5886 label: "clippy::large_enum_variant",
5204 description: r##"Checks for large size differences between variants on\n`enum`s."##, 5887 description: r##"Checks for large size differences between variants on
5888`enum`s."##,
5205 }, 5889 },
5206 LintCompletion { 5890 Lint {
5207 label: "clippy::large_stack_arrays", 5891 label: "clippy::large_stack_arrays",
5208 description: r##"Checks for local arrays that may be too large."##, 5892 description: r##"Checks for local arrays that may be too large."##,
5209 }, 5893 },
5210 LintCompletion { 5894 Lint {
5211 label: "clippy::large_types_passed_by_value", 5895 label: "clippy::large_types_passed_by_value",
5212 description: r##"Checks for functions taking arguments by value, where\nthe argument type is `Copy` and large enough to be worth considering\npassing by reference. Does not trigger if the function is being exported,\nbecause that might induce API breakage, if the parameter is declared as mutable,\nor if the argument is a `self`."##, 5896 description: r##"Checks for functions taking arguments by value, where
5897the argument type is `Copy` and large enough to be worth considering
5898passing by reference. Does not trigger if the function is being exported,
5899because that might induce API breakage, if the parameter is declared as mutable,
5900or if the argument is a `self`."##,
5213 }, 5901 },
5214 LintCompletion { 5902 Lint {
5215 label: "clippy::len_without_is_empty", 5903 label: "clippy::len_without_is_empty",
5216 description: r##"Checks for items that implement `.len()` but not\n`.is_empty()`."##, 5904 description: r##"Checks for items that implement `.len()` but not
5905`.is_empty()`."##,
5217 }, 5906 },
5218 LintCompletion { 5907 Lint {
5219 label: "clippy::len_zero", 5908 label: "clippy::len_zero",
5220 description: r##"Checks for getting the length of something via `.len()`\njust to compare to zero, and suggests using `.is_empty()` where applicable."##, 5909 description: r##"Checks for getting the length of something via `.len()`
5910just to compare to zero, and suggests using `.is_empty()` where applicable."##,
5221 }, 5911 },
5222 LintCompletion { 5912 Lint {
5223 label: "clippy::let_and_return", 5913 label: "clippy::let_and_return",
5224 description: r##"Checks for `let`-bindings, which are subsequently\nreturned."##, 5914 description: r##"Checks for `let`-bindings, which are subsequently
5915returned."##,
5225 }, 5916 },
5226 LintCompletion { 5917 Lint {
5227 label: "clippy::let_underscore_drop", 5918 label: "clippy::let_underscore_drop",
5228 description: r##"Checks for `let _ = <expr>`\nwhere expr has a type that implements `Drop`"##, 5919 description: r##"Checks for `let _ = <expr>`
5920where expr has a type that implements `Drop`"##,
5229 }, 5921 },
5230 LintCompletion { 5922 Lint {
5231 label: "clippy::let_underscore_lock", 5923 label: "clippy::let_underscore_lock",
5232 description: r##"Checks for `let _ = sync_lock`"##, 5924 description: r##"Checks for `let _ = sync_lock`"##,
5233 }, 5925 },
5234 LintCompletion { 5926 Lint {
5235 label: "clippy::let_underscore_must_use", 5927 label: "clippy::let_underscore_must_use",
5236 description: r##"Checks for `let _ = <expr>`\nwhere expr is #[must_use]"##, 5928 description: r##"Checks for `let _ = <expr>`
5237 }, 5929where expr is #[must_use]"##,
5238 LintCompletion {
5239 label: "clippy::let_unit_value",
5240 description: r##"Checks for binding a unit value."##,
5241 }, 5930 },
5242 LintCompletion { 5931 Lint { label: "clippy::let_unit_value", description: r##"Checks for binding a unit value."## },
5932 Lint {
5243 label: "clippy::linkedlist", 5933 label: "clippy::linkedlist",
5244 description: r##"Checks for usage of any `LinkedList`, suggesting to use a\n`Vec` or a `VecDeque` (formerly called `RingBuf`)."##, 5934 description: r##"Checks for usage of any `LinkedList`, suggesting to use a
5935`Vec` or a `VecDeque` (formerly called `RingBuf`)."##,
5245 }, 5936 },
5246 LintCompletion { 5937 Lint {
5247 label: "clippy::logic_bug", 5938 label: "clippy::logic_bug",
5248 description: r##"Checks for boolean expressions that contain terminals that\ncan be eliminated."##, 5939 description: r##"Checks for boolean expressions that contain terminals that
5940can be eliminated."##,
5249 }, 5941 },
5250 LintCompletion { 5942 Lint {
5251 label: "clippy::lossy_float_literal", 5943 label: "clippy::lossy_float_literal",
5252 description: r##"Checks for whole number float literals that\ncannot be represented as the underlying type without loss."##, 5944 description: r##"Checks for whole number float literals that
5945cannot be represented as the underlying type without loss."##,
5253 }, 5946 },
5254 LintCompletion { 5947 Lint {
5255 label: "clippy::macro_use_imports", 5948 label: "clippy::macro_use_imports",
5256 description: r##"Checks for `#[macro_use] use...`."##, 5949 description: r##"Checks for `#[macro_use] use...`."##,
5257 }, 5950 },
5258 LintCompletion { 5951 Lint {
5259 label: "clippy::main_recursion", 5952 label: "clippy::main_recursion",
5260 description: r##"Checks for recursion using the entrypoint."##, 5953 description: r##"Checks for recursion using the entrypoint."##,
5261 }, 5954 },
5262 LintCompletion { 5955 Lint {
5263 label: "clippy::manual_async_fn", 5956 label: "clippy::manual_async_fn",
5264 description: r##"It checks for manual implementations of `async` functions."##, 5957 description: r##"It checks for manual implementations of `async` functions."##,
5265 }, 5958 },
5266 LintCompletion { 5959 Lint {
5267 label: "clippy::manual_filter_map", 5960 label: "clippy::manual_filter_map",
5268 description: r##"Checks for usage of `_.filter(_).map(_)` that can be written more simply\nas `filter_map(_)`."##, 5961 description: r##"Checks for usage of `_.filter(_).map(_)` that can be written more simply
5962as `filter_map(_)`."##,
5269 }, 5963 },
5270 LintCompletion { 5964 Lint {
5271 label: "clippy::manual_find_map", 5965 label: "clippy::manual_find_map",
5272 description: r##"Checks for usage of `_.find(_).map(_)` that can be written more simply\nas `find_map(_)`."##, 5966 description: r##"Checks for usage of `_.find(_).map(_)` that can be written more simply
5967as `find_map(_)`."##,
5273 }, 5968 },
5274 LintCompletion { 5969 Lint {
5275 label: "clippy::manual_flatten", 5970 label: "clippy::manual_flatten",
5276 description: r##"Check for unnecessary `if let` usage in a for loop\nwhere only the `Some` or `Ok` variant of the iterator element is used."##, 5971 description: r##"Check for unnecessary `if let` usage in a for loop
5972where only the `Some` or `Ok` variant of the iterator element is used."##,
5277 }, 5973 },
5278 LintCompletion { 5974 Lint {
5279 label: "clippy::manual_map", 5975 label: "clippy::manual_map",
5280 description: r##"Checks for usages of `match` which could be implemented using `map`"##, 5976 description: r##"Checks for usages of `match` which could be implemented using `map`"##,
5281 }, 5977 },
5282 LintCompletion { 5978 Lint {
5283 label: "clippy::manual_memcpy", 5979 label: "clippy::manual_memcpy",
5284 description: r##"Checks for for-loops that manually copy items between\nslices that could be optimized by having a memcpy."##, 5980 description: r##"Checks for for-loops that manually copy items between
5981slices that could be optimized by having a memcpy."##,
5285 }, 5982 },
5286 LintCompletion { 5983 Lint {
5287 label: "clippy::manual_non_exhaustive", 5984 label: "clippy::manual_non_exhaustive",
5288 description: r##"Checks for manual implementations of the non-exhaustive pattern."##, 5985 description: r##"Checks for manual implementations of the non-exhaustive pattern."##,
5289 }, 5986 },
5290 LintCompletion { 5987 Lint {
5291 label: "clippy::manual_ok_or", 5988 label: "clippy::manual_ok_or",
5292 description: r##"Finds patterns that reimplement `Option::ok_or`."##, 5989 description: r##"Finds patterns that reimplement `Option::ok_or`."##,
5293 }, 5990 },
5294 LintCompletion { 5991 Lint {
5295 label: "clippy::manual_range_contains", 5992 label: "clippy::manual_range_contains",
5296 description: r##"Checks for expressions like `x >= 3 && x < 8` that could\nbe more readably expressed as `(3..8).contains(x)`."##, 5993 description: r##"Checks for expressions like `x >= 3 && x < 8` that could
5994be more readably expressed as `(3..8).contains(x)`."##,
5297 }, 5995 },
5298 LintCompletion { 5996 Lint {
5299 label: "clippy::manual_saturating_arithmetic", 5997 label: "clippy::manual_saturating_arithmetic",
5300 description: r##"Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`."##, 5998 description: r##"Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`."##,
5301 }, 5999 },
5302 LintCompletion { 6000 Lint {
5303 label: "clippy::manual_strip", 6001 label: "clippy::manual_str_repeat",
5304 description: r##"Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\nthe pattern's length."##, 6002 description: r##"Checks for manual implementations of `str::repeat`"##,
5305 }, 6003 },
5306 LintCompletion { 6004 Lint {
5307 label: "clippy::manual_swap", 6005 label: "clippy::manual_strip",
5308 description: r##"Checks for manual swapping."##, 6006 description: r##"Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
6007the pattern's length."##,
5309 }, 6008 },
5310 LintCompletion { 6009 Lint { label: "clippy::manual_swap", description: r##"Checks for manual swapping."## },
6010 Lint {
5311 label: "clippy::manual_unwrap_or", 6011 label: "clippy::manual_unwrap_or",
5312 description: r##"Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`."##, 6012 description: r##"Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`."##,
5313 }, 6013 },
5314 LintCompletion { 6014 Lint {
5315 label: "clippy::many_single_char_names", 6015 label: "clippy::many_single_char_names",
5316 description: r##"Checks for too many variables whose name consists of a\nsingle character."##, 6016 description: r##"Checks for too many variables whose name consists of a
6017single character."##,
5317 }, 6018 },
5318 LintCompletion { 6019 Lint {
5319 label: "clippy::map_clone", 6020 label: "clippy::map_clone",
5320 description: r##"Checks for usage of `map(|x| x.clone())` or\ndereferencing closures for `Copy` types, on `Iterator` or `Option`,\nand suggests `cloned()` or `copied()` instead"##, 6021 description: r##"Checks for usage of `map(|x| x.clone())` or
6022dereferencing closures for `Copy` types, on `Iterator` or `Option`,
6023and suggests `cloned()` or `copied()` instead"##,
5321 }, 6024 },
5322 LintCompletion { 6025 Lint {
5323 label: "clippy::map_collect_result_unit", 6026 label: "clippy::map_collect_result_unit",
5324 description: r##"Checks for usage of `_.map(_).collect::<Result<(), _>()`."##, 6027 description: r##"Checks for usage of `_.map(_).collect::<Result<(), _>()`."##,
5325 }, 6028 },
5326 LintCompletion { 6029 Lint {
5327 label: "clippy::map_entry", 6030 label: "clippy::map_entry",
5328 description: r##"Checks for uses of `contains_key` + `insert` on `HashMap`\nor `BTreeMap`."##, 6031 description: r##"Checks for uses of `contains_key` + `insert` on `HashMap`
6032or `BTreeMap`."##,
5329 }, 6033 },
5330 LintCompletion { 6034 Lint {
5331 label: "clippy::map_err_ignore", 6035 label: "clippy::map_err_ignore",
5332 description: r##"Checks for instances of `map_err(|_| Some::Enum)`"##, 6036 description: r##"Checks for instances of `map_err(|_| Some::Enum)`"##,
5333 }, 6037 },
5334 LintCompletion { 6038 Lint {
5335 label: "clippy::map_flatten", 6039 label: "clippy::map_flatten",
5336 description: r##"Checks for usage of `_.map(_).flatten(_)`,"##, 6040 description: r##"Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`"##,
5337 }, 6041 },
5338 LintCompletion { 6042 Lint {
5339 label: "clippy::map_identity", 6043 label: "clippy::map_identity",
5340 description: r##"Checks for instances of `map(f)` where `f` is the identity function."##, 6044 description: r##"Checks for instances of `map(f)` where `f` is the identity function."##,
5341 }, 6045 },
5342 LintCompletion { 6046 Lint {
5343 label: "clippy::map_unwrap_or", 6047 label: "clippy::map_unwrap_or",
5344 description: r##"Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or\n`result.map(_).unwrap_or_else(_)`."##, 6048 description: r##"Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
6049`result.map(_).unwrap_or_else(_)`."##,
5345 }, 6050 },
5346 LintCompletion { 6051 Lint {
5347 label: "clippy::match_as_ref", 6052 label: "clippy::match_as_ref",
5348 description: r##"Checks for match which is used to add a reference to an\n`Option` value."##, 6053 description: r##"Checks for match which is used to add a reference to an
6054`Option` value."##,
5349 }, 6055 },
5350 LintCompletion { 6056 Lint {
5351 label: "clippy::match_bool", 6057 label: "clippy::match_bool",
5352 description: r##"Checks for matches where match expression is a `bool`. It\nsuggests to replace the expression with an `if...else` block."##, 6058 description: r##"Checks for matches where match expression is a `bool`. It
6059suggests to replace the expression with an `if...else` block."##,
5353 }, 6060 },
5354 LintCompletion { 6061 Lint {
5355 label: "clippy::match_like_matches_macro", 6062 label: "clippy::match_like_matches_macro",
5356 description: r##"Checks for `match` or `if let` expressions producing a\n`bool` that could be written using `matches!`"##, 6063 description: r##"Checks for `match` or `if let` expressions producing a
6064`bool` that could be written using `matches!`"##,
5357 }, 6065 },
5358 LintCompletion { 6066 Lint {
5359 label: "clippy::match_on_vec_items", 6067 label: "clippy::match_on_vec_items",
5360 description: r##"Checks for `match vec[idx]` or `match vec[n..m]`."##, 6068 description: r##"Checks for `match vec[idx]` or `match vec[n..m]`."##,
5361 }, 6069 },
5362 LintCompletion { 6070 Lint {
5363 label: "clippy::match_overlapping_arm", 6071 label: "clippy::match_overlapping_arm",
5364 description: r##"Checks for overlapping match arms."##, 6072 description: r##"Checks for overlapping match arms."##,
5365 }, 6073 },
5366 LintCompletion { 6074 Lint {
5367 label: "clippy::match_ref_pats", 6075 label: "clippy::match_ref_pats",
5368 description: r##"Checks for matches where all arms match a reference,\nsuggesting to remove the reference and deref the matched expression\ninstead. It also checks for `if let &foo = bar` blocks."##, 6076 description: r##"Checks for matches where all arms match a reference,
6077suggesting to remove the reference and deref the matched expression
6078instead. It also checks for `if let &foo = bar` blocks."##,
5369 }, 6079 },
5370 LintCompletion { 6080 Lint {
5371 label: "clippy::match_same_arms", 6081 label: "clippy::match_same_arms",
5372 description: r##"Checks for `match` with identical arm bodies."##, 6082 description: r##"Checks for `match` with identical arm bodies."##,
5373 }, 6083 },
5374 LintCompletion { 6084 Lint {
5375 label: "clippy::match_single_binding", 6085 label: "clippy::match_single_binding",
5376 description: r##"Checks for useless match that binds to only one value."##, 6086 description: r##"Checks for useless match that binds to only one value."##,
5377 }, 6087 },
5378 LintCompletion { 6088 Lint {
5379 label: "clippy::match_wild_err_arm", 6089 label: "clippy::match_wild_err_arm",
5380 description: r##"Checks for arm which matches all errors with `Err(_)`\nand take drastic actions like `panic!`."##, 6090 description: r##"Checks for arm which matches all errors with `Err(_)`
6091and take drastic actions like `panic!`."##,
5381 }, 6092 },
5382 LintCompletion { 6093 Lint {
5383 label: "clippy::match_wildcard_for_single_variants", 6094 label: "clippy::match_wildcard_for_single_variants",
5384 description: r##"Checks for wildcard enum matches for a single variant."##, 6095 description: r##"Checks for wildcard enum matches for a single variant."##,
5385 }, 6096 },
5386 LintCompletion { 6097 Lint {
5387 label: "clippy::maybe_infinite_iter", 6098 label: "clippy::maybe_infinite_iter",
5388 description: r##"Checks for iteration that may be infinite."##, 6099 description: r##"Checks for iteration that may be infinite."##,
5389 }, 6100 },
5390 LintCompletion { 6101 Lint {
5391 label: "clippy::mem_discriminant_non_enum", 6102 label: "clippy::mem_discriminant_non_enum",
5392 description: r##"Checks for calls of `mem::discriminant()` on a non-enum type."##, 6103 description: r##"Checks for calls of `mem::discriminant()` on a non-enum type."##,
5393 }, 6104 },
5394 LintCompletion { 6105 Lint {
5395 label: "clippy::mem_forget", 6106 label: "clippy::mem_forget",
5396 description: r##"Checks for usage of `std::mem::forget(t)` where `t` is\n`Drop`."##, 6107 description: r##"Checks for usage of `std::mem::forget(t)` where `t` is
6108`Drop`."##,
5397 }, 6109 },
5398 LintCompletion { 6110 Lint {
5399 label: "clippy::mem_replace_option_with_none", 6111 label: "clippy::mem_replace_option_with_none",
5400 description: r##"Checks for `mem::replace()` on an `Option` with\n`None`."##, 6112 description: r##"Checks for `mem::replace()` on an `Option` with
6113`None`."##,
5401 }, 6114 },
5402 LintCompletion { 6115 Lint {
5403 label: "clippy::mem_replace_with_default", 6116 label: "clippy::mem_replace_with_default",
5404 description: r##"Checks for `std::mem::replace` on a value of type\n`T` with `T::default()`."##, 6117 description: r##"Checks for `std::mem::replace` on a value of type
6118`T` with `T::default()`."##,
5405 }, 6119 },
5406 LintCompletion { 6120 Lint {
5407 label: "clippy::mem_replace_with_uninit", 6121 label: "clippy::mem_replace_with_uninit",
5408 description: r##"Checks for `mem::replace(&mut _, mem::uninitialized())`\nand `mem::replace(&mut _, mem::zeroed())`."##, 6122 description: r##"Checks for `mem::replace(&mut _, mem::uninitialized())`
6123and `mem::replace(&mut _, mem::zeroed())`."##,
5409 }, 6124 },
5410 LintCompletion { 6125 Lint {
5411 label: "clippy::min_max", 6126 label: "clippy::min_max",
5412 description: r##"Checks for expressions where `std::cmp::min` and `max` are\nused to clamp values, but switched so that the result is constant."##, 6127 description: r##"Checks for expressions where `std::cmp::min` and `max` are
6128used to clamp values, but switched so that the result is constant."##,
5413 }, 6129 },
5414 LintCompletion { 6130 Lint {
5415 label: "clippy::misaligned_transmute", 6131 label: "clippy::misaligned_transmute",
5416 description: r##"Nothing. This lint has been deprecated."##, 6132 description: r##"Nothing. This lint has been deprecated."##,
5417 }, 6133 },
5418 LintCompletion { 6134 Lint {
5419 label: "clippy::mismatched_target_os", 6135 label: "clippy::mismatched_target_os",
5420 description: r##"Checks for cfg attributes having operating systems used in target family position."##, 6136 description: r##"Checks for cfg attributes having operating systems used in target family position."##,
5421 }, 6137 },
5422 LintCompletion { 6138 Lint {
5423 label: "clippy::misrefactored_assign_op", 6139 label: "clippy::misrefactored_assign_op",
5424 description: r##"Checks for `a op= a op b` or `a op= b op a` patterns."##, 6140 description: r##"Checks for `a op= a op b` or `a op= b op a` patterns."##,
5425 }, 6141 },
5426 LintCompletion { 6142 Lint {
5427 label: "clippy::missing_const_for_fn", 6143 label: "clippy::missing_const_for_fn",
5428 description: r##"Suggests the use of `const` in functions and methods where possible."##, 6144 description: r##"Suggests the use of `const` in functions and methods where possible."##,
5429 }, 6145 },
5430 LintCompletion { 6146 Lint {
5431 label: "clippy::missing_docs_in_private_items", 6147 label: "clippy::missing_docs_in_private_items",
5432 description: r##"Warns if there is missing doc for any documentable item\n(public or private)."##, 6148 description: r##"Warns if there is missing doc for any documentable item
6149(public or private)."##,
5433 }, 6150 },
5434 LintCompletion { 6151 Lint {
5435 label: "clippy::missing_errors_doc", 6152 label: "clippy::missing_errors_doc",
5436 description: r##"Checks the doc comments of publicly visible functions that\nreturn a `Result` type and warns if there is no `# Errors` section."##, 6153 description: r##"Checks the doc comments of publicly visible functions that
6154return a `Result` type and warns if there is no `# Errors` section."##,
5437 }, 6155 },
5438 LintCompletion { 6156 Lint {
5439 label: "clippy::missing_inline_in_public_items", 6157 label: "clippy::missing_inline_in_public_items",
5440 description: r##"it lints if an exported function, method, trait method with default impl,\nor trait method impl is not `#[inline]`."##, 6158 description: r##"it lints if an exported function, method, trait method with default impl,
6159or trait method impl is not `#[inline]`."##,
5441 }, 6160 },
5442 LintCompletion { 6161 Lint {
5443 label: "clippy::missing_panics_doc", 6162 label: "clippy::missing_panics_doc",
5444 description: r##"Checks the doc comments of publicly visible functions that\nmay panic and warns if there is no `# Panics` section."##, 6163 description: r##"Checks the doc comments of publicly visible functions that
6164may panic and warns if there is no `# Panics` section."##,
5445 }, 6165 },
5446 LintCompletion { 6166 Lint {
5447 label: "clippy::missing_safety_doc", 6167 label: "clippy::missing_safety_doc",
5448 description: r##"Checks for the doc comments of publicly visible\nunsafe functions and warns if there is no `# Safety` section."##, 6168 description: r##"Checks for the doc comments of publicly visible
6169unsafe functions and warns if there is no `# Safety` section."##,
5449 }, 6170 },
5450 LintCompletion { 6171 Lint {
5451 label: "clippy::mistyped_literal_suffixes", 6172 label: "clippy::mistyped_literal_suffixes",
5452 description: r##"Warns for mistyped suffix in literals"##, 6173 description: r##"Warns for mistyped suffix in literals"##,
5453 }, 6174 },
5454 LintCompletion { 6175 Lint {
5455 label: "clippy::mixed_case_hex_literals", 6176 label: "clippy::mixed_case_hex_literals",
5456 description: r##"Warns on hexadecimal literals with mixed-case letter\ndigits."##, 6177 description: r##"Warns on hexadecimal literals with mixed-case letter
6178digits."##,
5457 }, 6179 },
5458 LintCompletion { 6180 Lint {
5459 label: "clippy::module_inception", 6181 label: "clippy::module_inception",
5460 description: r##"Checks for modules that have the same name as their\nparent module"##, 6182 description: r##"Checks for modules that have the same name as their
6183parent module"##,
5461 }, 6184 },
5462 LintCompletion { 6185 Lint {
5463 label: "clippy::module_name_repetitions", 6186 label: "clippy::module_name_repetitions",
5464 description: r##"Detects type names that are prefixed or suffixed by the\ncontaining module's name."##, 6187 description: r##"Detects type names that are prefixed or suffixed by the
5465 }, 6188containing module's name."##,
5466 LintCompletion {
5467 label: "clippy::modulo_arithmetic",
5468 description: r##"Checks for modulo arithmetic."##,
5469 }, 6189 },
5470 LintCompletion { 6190 Lint { label: "clippy::modulo_arithmetic", description: r##"Checks for modulo arithmetic."## },
6191 Lint {
5471 label: "clippy::modulo_one", 6192 label: "clippy::modulo_one",
5472 description: r##"Checks for getting the remainder of a division by one or minus\none."##, 6193 description: r##"Checks for getting the remainder of a division by one or minus
6194one."##,
5473 }, 6195 },
5474 LintCompletion { 6196 Lint {
5475 label: "clippy::multiple_crate_versions", 6197 label: "clippy::multiple_crate_versions",
5476 description: r##"Checks to see if multiple versions of a crate are being\nused."##, 6198 description: r##"Checks to see if multiple versions of a crate are being
6199used."##,
5477 }, 6200 },
5478 LintCompletion { 6201 Lint {
5479 label: "clippy::multiple_inherent_impl", 6202 label: "clippy::multiple_inherent_impl",
5480 description: r##"Checks for multiple inherent implementations of a struct"##, 6203 description: r##"Checks for multiple inherent implementations of a struct"##,
5481 }, 6204 },
5482 LintCompletion { 6205 Lint {
5483 label: "clippy::must_use_candidate", 6206 label: "clippy::must_use_candidate",
5484 description: r##"Checks for public functions that have no\n[`#[must_use]`] attribute, but return something not already marked\nmust-use, have no mutable arg and mutate no statics.\n\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, 6207 description: r##"Checks for public functions that have no
6208[`#[must_use]`] attribute, but return something not already marked
6209must-use, have no mutable arg and mutate no statics.
6210
6211[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##,
5485 }, 6212 },
5486 LintCompletion { 6213 Lint {
5487 label: "clippy::must_use_unit", 6214 label: "clippy::must_use_unit",
5488 description: r##"Checks for a [`#[must_use]`] attribute on\nunit-returning functions and methods.\n\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, 6215 description: r##"Checks for a [`#[must_use]`] attribute on
6216unit-returning functions and methods.
6217
6218[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##,
5489 }, 6219 },
5490 LintCompletion { 6220 Lint {
5491 label: "clippy::mut_from_ref", 6221 label: "clippy::mut_from_ref",
5492 description: r##"This lint checks for functions that take immutable\nreferences and return mutable ones."##, 6222 description: r##"This lint checks for functions that take immutable
6223references and return mutable ones."##,
5493 }, 6224 },
5494 LintCompletion { 6225 Lint {
5495 label: "clippy::mut_mut", 6226 label: "clippy::mut_mut",
5496 description: r##"Checks for instances of `mut mut` references."##, 6227 description: r##"Checks for instances of `mut mut` references."##,
5497 }, 6228 },
5498 LintCompletion { 6229 Lint {
5499 label: "clippy::mut_mutex_lock", 6230 label: "clippy::mut_mutex_lock",
5500 description: r##"Checks for `&mut Mutex::lock` calls"##, 6231 description: r##"Checks for `&mut Mutex::lock` calls"##,
5501 }, 6232 },
5502 LintCompletion { 6233 Lint {
5503 label: "clippy::mut_range_bound", 6234 label: "clippy::mut_range_bound",
5504 description: r##"Checks for loops which have a range bound that is a mutable variable"##, 6235 description: r##"Checks for loops which have a range bound that is a mutable variable"##,
5505 }, 6236 },
5506 LintCompletion { 6237 Lint {
5507 label: "clippy::mutable_key_type", 6238 label: "clippy::mutable_key_type",
5508 description: r##"Checks for sets/maps with mutable key types."##, 6239 description: r##"Checks for sets/maps with mutable key types."##,
5509 }, 6240 },
5510 LintCompletion { 6241 Lint {
5511 label: "clippy::mutex_atomic", 6242 label: "clippy::mutex_atomic",
5512 description: r##"Checks for usages of `Mutex<X>` where an atomic will do."##, 6243 description: r##"Checks for usages of `Mutex<X>` where an atomic will do."##,
5513 }, 6244 },
5514 LintCompletion { 6245 Lint {
5515 label: "clippy::mutex_integer", 6246 label: "clippy::mutex_integer",
5516 description: r##"Checks for usages of `Mutex<X>` where `X` is an integral\ntype."##, 6247 description: r##"Checks for usages of `Mutex<X>` where `X` is an integral
5517 }, 6248type."##,
5518 LintCompletion {
5519 label: "clippy::naive_bytecount",
5520 description: r##"Checks for naive byte counts"##,
5521 }, 6249 },
5522 LintCompletion { 6250 Lint { label: "clippy::naive_bytecount", description: r##"Checks for naive byte counts"## },
6251 Lint {
5523 label: "clippy::needless_arbitrary_self_type", 6252 label: "clippy::needless_arbitrary_self_type",
5524 description: r##"The lint checks for `self` in fn parameters that\nspecify the `Self`-type explicitly"##, 6253 description: r##"The lint checks for `self` in fn parameters that
6254specify the `Self`-type explicitly"##,
5525 }, 6255 },
5526 LintCompletion { 6256 Lint {
6257 label: "clippy::needless_bitwise_bool",
6258 description: r##"Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
6259a lazy and."##,
6260 },
6261 Lint {
5527 label: "clippy::needless_bool", 6262 label: "clippy::needless_bool",
5528 description: r##"Checks for expressions of the form `if c { true } else {\nfalse }` (or vice versa) and suggests using the condition directly."##, 6263 description: r##"Checks for expressions of the form `if c { true } else {
6264false }` (or vice versa) and suggests using the condition directly."##,
5529 }, 6265 },
5530 LintCompletion { 6266 Lint {
5531 label: "clippy::needless_borrow", 6267 label: "clippy::needless_borrow",
5532 description: r##"Checks for address of operations (`&`) that are going to\nbe dereferenced immediately by the compiler."##, 6268 description: r##"Checks for address of operations (`&`) that are going to
6269be dereferenced immediately by the compiler."##,
5533 }, 6270 },
5534 LintCompletion { 6271 Lint {
5535 label: "clippy::needless_borrowed_reference", 6272 label: "clippy::needless_borrowed_reference",
5536 description: r##"Checks for useless borrowed references."##, 6273 description: r##"Checks for bindings that destructure a reference and borrow the inner
6274value with `&ref`."##,
5537 }, 6275 },
5538 LintCompletion { 6276 Lint {
5539 label: "clippy::needless_collect", 6277 label: "clippy::needless_collect",
5540 description: r##"Checks for functions collecting an iterator when collect\nis not needed."##, 6278 description: r##"Checks for functions collecting an iterator when collect
6279is not needed."##,
5541 }, 6280 },
5542 LintCompletion { 6281 Lint {
5543 label: "clippy::needless_continue", 6282 label: "clippy::needless_continue",
5544 description: r##"The lint checks for `if`-statements appearing in loops\nthat contain a `continue` statement in either their main blocks or their\n`else`-blocks, when omitting the `else`-block possibly with some\nrearrangement of code can make the code easier to understand."##, 6283 description: r##"The lint checks for `if`-statements appearing in loops
6284that contain a `continue` statement in either their main blocks or their
6285`else`-blocks, when omitting the `else`-block possibly with some
6286rearrangement of code can make the code easier to understand."##,
5545 }, 6287 },
5546 LintCompletion { 6288 Lint {
5547 label: "clippy::needless_doctest_main", 6289 label: "clippy::needless_doctest_main",
5548 description: r##"Checks for `fn main() { .. }` in doctests"##, 6290 description: r##"Checks for `fn main() { .. }` in doctests"##,
5549 }, 6291 },
5550 LintCompletion { 6292 Lint {
6293 label: "clippy::needless_for_each",
6294 description: r##"Checks for usage of `for_each` that would be more simply written as a
6295`for` loop."##,
6296 },
6297 Lint {
5551 label: "clippy::needless_lifetimes", 6298 label: "clippy::needless_lifetimes",
5552 description: r##"Checks for lifetime annotations which can be removed by\nrelying on lifetime elision."##, 6299 description: r##"Checks for lifetime annotations which can be removed by
6300relying on lifetime elision."##,
5553 }, 6301 },
5554 LintCompletion { 6302 Lint {
5555 label: "clippy::needless_pass_by_value", 6303 label: "clippy::needless_pass_by_value",
5556 description: r##"Checks for functions taking arguments by value, but not\nconsuming them in its\nbody."##, 6304 description: r##"Checks for functions taking arguments by value, but not
6305consuming them in its
6306body."##,
5557 }, 6307 },
5558 LintCompletion { 6308 Lint {
5559 label: "clippy::needless_question_mark", 6309 label: "clippy::needless_question_mark",
5560 description: r##"Suggests alternatives for useless applications of `?` in terminating expressions"##, 6310 description: r##"Suggests alternatives for useless applications of `?` in terminating expressions"##,
5561 }, 6311 },
5562 LintCompletion { 6312 Lint {
5563 label: "clippy::needless_range_loop", 6313 label: "clippy::needless_range_loop",
5564 description: r##"Checks for looping over the range of `0..len` of some\ncollection just to get the values by index."##, 6314 description: r##"Checks for looping over the range of `0..len` of some
6315collection just to get the values by index."##,
5565 }, 6316 },
5566 LintCompletion { 6317 Lint {
5567 label: "clippy::needless_return", 6318 label: "clippy::needless_return",
5568 description: r##"Checks for return statements at the end of a block."##, 6319 description: r##"Checks for return statements at the end of a block."##,
5569 }, 6320 },
5570 LintCompletion { 6321 Lint {
5571 label: "clippy::needless_update", 6322 label: "clippy::needless_update",
5572 description: r##"Checks for needlessly including a base struct on update\nwhen all fields are changed anyway.\n\nThis lint is not applied to structs marked with\n[non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html)."##, 6323 description: r##"Checks for needlessly including a base struct on update
6324when all fields are changed anyway.
6325
6326This lint is not applied to structs marked with
6327[non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html)."##,
5573 }, 6328 },
5574 LintCompletion { 6329 Lint {
5575 label: "clippy::neg_cmp_op_on_partial_ord", 6330 label: "clippy::neg_cmp_op_on_partial_ord",
5576 description: r##"Checks for the usage of negated comparison operators on types which only implement\n`PartialOrd` (e.g., `f64`)."##, 6331 description: r##"Checks for the usage of negated comparison operators on types which only implement
6332`PartialOrd` (e.g., `f64`)."##,
5577 }, 6333 },
5578 LintCompletion { 6334 Lint {
5579 label: "clippy::neg_multiply", 6335 label: "clippy::neg_multiply",
5580 description: r##"Checks for multiplication by -1 as a form of negation."##, 6336 description: r##"Checks for multiplication by -1 as a form of negation."##,
5581 }, 6337 },
5582 LintCompletion { 6338 Lint {
5583 label: "clippy::never_loop", 6339 label: "clippy::never_loop",
5584 description: r##"Checks for loops that will always `break`, `return` or\n`continue` an outer loop."##, 6340 description: r##"Checks for loops that will always `break`, `return` or
6341`continue` an outer loop."##,
5585 }, 6342 },
5586 LintCompletion { 6343 Lint {
5587 label: "clippy::new_ret_no_self", 6344 label: "clippy::new_ret_no_self",
5588 description: r##"Checks for `new` not returning a type that contains `Self`."##, 6345 description: r##"Checks for `new` not returning a type that contains `Self`."##,
5589 }, 6346 },
5590 LintCompletion { 6347 Lint {
5591 label: "clippy::new_without_default", 6348 label: "clippy::new_without_default",
5592 description: r##"Checks for types with a `fn new() -> Self` method and no\nimplementation of\n[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)."##, 6349 description: r##"Checks for types with a `fn new() -> Self` method and no
6350implementation of
6351[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)."##,
5593 }, 6352 },
5594 LintCompletion { 6353 Lint {
5595 label: "clippy::no_effect", 6354 label: "clippy::no_effect",
5596 description: r##"Checks for statements which have no effect."##, 6355 description: r##"Checks for statements which have no effect."##,
5597 }, 6356 },
5598 LintCompletion { 6357 Lint {
5599 label: "clippy::non_ascii_literal", 6358 label: "clippy::non_ascii_literal",
5600 description: r##"Checks for non-ASCII characters in string literals."##, 6359 description: r##"Checks for non-ASCII characters in string literals."##,
5601 }, 6360 },
5602 LintCompletion { 6361 Lint {
6362 label: "clippy::non_octal_unix_permissions",
6363 description: r##"Checks for non-octal values used to set Unix file permissions."##,
6364 },
6365 Lint {
5603 label: "clippy::nonminimal_bool", 6366 label: "clippy::nonminimal_bool",
5604 description: r##"Checks for boolean expressions that can be written more\nconcisely."##, 6367 description: r##"Checks for boolean expressions that can be written more
6368concisely."##,
5605 }, 6369 },
5606 LintCompletion { 6370 Lint {
5607 label: "clippy::nonsensical_open_options", 6371 label: "clippy::nonsensical_open_options",
5608 description: r##"Checks for duplicate open options as well as combinations\nthat make no sense."##, 6372 description: r##"Checks for duplicate open options as well as combinations
6373that make no sense."##,
5609 }, 6374 },
5610 LintCompletion { 6375 Lint {
5611 label: "clippy::not_unsafe_ptr_arg_deref", 6376 label: "clippy::not_unsafe_ptr_arg_deref",
5612 description: r##"Checks for public functions that dereference raw pointer\narguments but are not marked unsafe."##, 6377 description: r##"Checks for public functions that dereference raw pointer
5613 }, 6378arguments but are not marked `unsafe`."##,
5614 LintCompletion {
5615 label: "clippy::ok_expect",
5616 description: r##"Checks for usage of `ok().expect(..)`."##,
5617 }, 6379 },
5618 LintCompletion { 6380 Lint { label: "clippy::ok_expect", description: r##"Checks for usage of `ok().expect(..)`."## },
6381 Lint {
5619 label: "clippy::op_ref", 6382 label: "clippy::op_ref",
5620 description: r##"Checks for arguments to `==` which have their address\ntaken to satisfy a bound\nand suggests to dereference the other argument instead"##, 6383 description: r##"Checks for arguments to `==` which have their address
6384taken to satisfy a bound
6385and suggests to dereference the other argument instead"##,
5621 }, 6386 },
5622 LintCompletion { 6387 Lint {
5623 label: "clippy::option_as_ref_deref", 6388 label: "clippy::option_as_ref_deref",
5624 description: r##"Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str)."##, 6389 description: r##"Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str)."##,
5625 }, 6390 },
5626 LintCompletion { 6391 Lint {
5627 label: "clippy::option_env_unwrap", 6392 label: "clippy::option_env_unwrap",
5628 description: r##"Checks for usage of `option_env!(...).unwrap()` and\nsuggests usage of the `env!` macro."##, 6393 description: r##"Checks for usage of `option_env!(...).unwrap()` and
6394suggests usage of the `env!` macro."##,
5629 }, 6395 },
5630 LintCompletion { 6396 Lint {
6397 label: "clippy::option_filter_map",
6398 description: r##"Checks for indirect collection of populated `Option`"##,
6399 },
6400 Lint {
5631 label: "clippy::option_if_let_else", 6401 label: "clippy::option_if_let_else",
5632 description: r##"Lints usage of `if let Some(v) = ... { y } else { x }` which is more\nidiomatically done with `Option::map_or` (if the else bit is a pure\nexpression) or `Option::map_or_else` (if the else bit is an impure\nexpression)."##, 6402 description: r##"Lints usage of `if let Some(v) = ... { y } else { x }` which is more
6403idiomatically done with `Option::map_or` (if the else bit is a pure
6404expression) or `Option::map_or_else` (if the else bit is an impure
6405expression)."##,
5633 }, 6406 },
5634 LintCompletion { 6407 Lint {
5635 label: "clippy::option_map_or_none", 6408 label: "clippy::option_map_or_none",
5636 description: r##"Checks for usage of `_.map_or(None, _)`."##, 6409 description: r##"Checks for usage of `_.map_or(None, _)`."##,
5637 }, 6410 },
5638 LintCompletion { 6411 Lint {
5639 label: "clippy::option_map_unit_fn", 6412 label: "clippy::option_map_unit_fn",
5640 description: r##"Checks for usage of `option.map(f)` where f is a function\nor closure that returns the unit type `()`."##, 6413 description: r##"Checks for usage of `option.map(f)` where f is a function
6414or closure that returns the unit type `()`."##,
5641 }, 6415 },
5642 LintCompletion { 6416 Lint {
5643 label: "clippy::option_option", 6417 label: "clippy::option_option",
5644 description: r##"Checks for use of `Option<Option<_>>` in function signatures and type\ndefinitions"##, 6418 description: r##"Checks for use of `Option<Option<_>>` in function signatures and type
6419definitions"##,
5645 }, 6420 },
5646 LintCompletion { 6421 Lint {
5647 label: "clippy::or_fun_call", 6422 label: "clippy::or_fun_call",
5648 description: r##"Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,\netc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n`unwrap_or_default` instead."##, 6423 description: r##"Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
6424etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
6425`unwrap_or_default` instead."##,
5649 }, 6426 },
5650 LintCompletion { 6427 Lint {
5651 label: "clippy::out_of_bounds_indexing", 6428 label: "clippy::out_of_bounds_indexing",
5652 description: r##"Checks for out of bounds array indexing with a constant\nindex."##, 6429 description: r##"Checks for out of bounds array indexing with a constant
6430index."##,
5653 }, 6431 },
5654 LintCompletion { 6432 Lint {
5655 label: "clippy::overflow_check_conditional", 6433 label: "clippy::overflow_check_conditional",
5656 description: r##"Detects classic underflow/overflow checks."##, 6434 description: r##"Detects classic underflow/overflow checks."##,
5657 }, 6435 },
5658 LintCompletion { label: "clippy::panic", description: r##"Checks for usage of `panic!`."## }, 6436 Lint { label: "clippy::panic", description: r##"Checks for usage of `panic!`."## },
5659 LintCompletion { 6437 Lint {
5660 label: "clippy::panic_in_result_fn", 6438 label: "clippy::panic_in_result_fn",
5661 description: r##"Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result."##, 6439 description: r##"Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result."##,
5662 }, 6440 },
5663 LintCompletion { 6441 Lint {
5664 label: "clippy::panic_params",
5665 description: r##"Nothing. This lint has been deprecated."##,
5666 },
5667 LintCompletion {
5668 label: "clippy::panicking_unwrap", 6442 label: "clippy::panicking_unwrap",
5669 description: r##"Checks for calls of `unwrap[_err]()` that will always fail."##, 6443 description: r##"Checks for calls of `unwrap[_err]()` that will always fail."##,
5670 }, 6444 },
5671 LintCompletion { 6445 Lint {
5672 label: "clippy::partialeq_ne_impl", 6446 label: "clippy::partialeq_ne_impl",
5673 description: r##"Checks for manual re-implementations of `PartialEq::ne`."##, 6447 description: r##"Checks for manual re-implementations of `PartialEq::ne`."##,
5674 }, 6448 },
5675 LintCompletion { 6449 Lint {
5676 label: "clippy::path_buf_push_overwrite", 6450 label: "clippy::path_buf_push_overwrite",
5677 description: r##"* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)\ncalls on `PathBuf` that can cause overwrites."##, 6451 description: r##"* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
6452calls on `PathBuf` that can cause overwrites."##,
5678 }, 6453 },
5679 LintCompletion { 6454 Lint {
5680 label: "clippy::pattern_type_mismatch", 6455 label: "clippy::pattern_type_mismatch",
5681 description: r##"Checks for patterns that aren't exact representations of the types\nthey are applied to.\n\nTo satisfy this lint, you will have to adjust either the expression that is matched\nagainst or the pattern itself, as well as the bindings that are introduced by the\nadjusted patterns. For matching you will have to either dereference the expression\nwith the `*` operator, or amend the patterns to explicitly match against `&<pattern>`\nor `&mut <pattern>` depending on the reference mutability. For the bindings you need\nto use the inverse. You can leave them as plain bindings if you wish for the value\nto be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct\na reference into the matched structure.\n\nIf you are looking for a way to learn about ownership semantics in more detail, it\nis recommended to look at IDE options available to you to highlight types, lifetimes\nand reference semantics in your code. The available tooling would expose these things\nin a general way even outside of the various pattern matching mechanics. Of course\nthis lint can still be used to highlight areas of interest and ensure a good understanding\nof ownership semantics."##, 6456 description: r##"Checks for patterns that aren't exact representations of the types
5682 }, 6457they are applied to.
5683 LintCompletion { 6458
6459To satisfy this lint, you will have to adjust either the expression that is matched
6460against or the pattern itself, as well as the bindings that are introduced by the
6461adjusted patterns. For matching you will have to either dereference the expression
6462with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
6463or `&mut <pattern>` depending on the reference mutability. For the bindings you need
6464to use the inverse. You can leave them as plain bindings if you wish for the value
6465to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
6466a reference into the matched structure.
6467
6468If you are looking for a way to learn about ownership semantics in more detail, it
6469is recommended to look at IDE options available to you to highlight types, lifetimes
6470and reference semantics in your code. The available tooling would expose these things
6471in a general way even outside of the various pattern matching mechanics. Of course
6472this lint can still be used to highlight areas of interest and ensure a good understanding
6473of ownership semantics."##,
6474 },
6475 Lint {
5684 label: "clippy::possible_missing_comma", 6476 label: "clippy::possible_missing_comma",
5685 description: r##"Checks for possible missing comma in an array. It lints if\nan array element is a binary operator expression and it lies on two lines."##, 6477 description: r##"Checks for possible missing comma in an array. It lints if
6478an array element is a binary operator expression and it lies on two lines."##,
5686 }, 6479 },
5687 LintCompletion { 6480 Lint {
5688 label: "clippy::precedence", 6481 label: "clippy::precedence",
5689 description: r##"Checks for operations where precedence may be unclear\nand suggests to add parentheses. Currently it catches the following:\n* mixed usage of arithmetic and bit shifting/combining operators without\nparentheses\n* a \"negative\" numeric literal (which is really a unary `-` followed by a\nnumeric literal)\n followed by a method call"##, 6482 description: r##"Checks for operations where precedence may be unclear
5690 }, 6483and suggests to add parentheses. Currently it catches the following:
5691 LintCompletion { 6484* mixed usage of arithmetic and bit shifting/combining operators without
6485parentheses
6486* a negative numeric literal (which is really a unary `-` followed by a
6487numeric literal)
6488 followed by a method call"##,
6489 },
6490 Lint {
5692 label: "clippy::print_literal", 6491 label: "clippy::print_literal",
5693 description: r##"This lint warns about the use of literals as `print!`/`println!` args."##, 6492 description: r##"This lint warns about the use of literals as `print!`/`println!` args."##,
5694 }, 6493 },
5695 LintCompletion { 6494 Lint {
5696 label: "clippy::print_stderr", 6495 label: "clippy::print_stderr",
5697 description: r##"Checks for printing on *stderr*. The purpose of this lint\nis to catch debugging remnants."##, 6496 description: r##"Checks for printing on *stderr*. The purpose of this lint
6497is to catch debugging remnants."##,
5698 }, 6498 },
5699 LintCompletion { 6499 Lint {
5700 label: "clippy::print_stdout", 6500 label: "clippy::print_stdout",
5701 description: r##"Checks for printing on *stdout*. The purpose of this lint\nis to catch debugging remnants."##, 6501 description: r##"Checks for printing on *stdout*. The purpose of this lint
6502is to catch debugging remnants."##,
5702 }, 6503 },
5703 LintCompletion { 6504 Lint {
5704 label: "clippy::print_with_newline", 6505 label: "clippy::print_with_newline",
5705 description: r##"This lint warns when you use `print!()` with a format\nstring that ends in a newline."##, 6506 description: r##"This lint warns when you use `print!()` with a format
6507string that ends in a newline."##,
5706 }, 6508 },
5707 LintCompletion { 6509 Lint {
5708 label: "clippy::println_empty_string", 6510 label: "clippy::println_empty_string",
5709 description: r##"This lint warns when you use `println!(\"\")` to\nprint a newline."##, 6511 description: r##"This lint warns when you use `println!()` to
6512print a newline."##,
5710 }, 6513 },
5711 LintCompletion { 6514 Lint {
5712 label: "clippy::ptr_arg", 6515 label: "clippy::ptr_arg",
5713 description: r##"This lint checks for function arguments of type `&String`\nor `&Vec` unless the references are mutable. It will also suggest you\nreplace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\ncalls."##, 6516 description: r##"This lint checks for function arguments of type `&String`
6517or `&Vec` unless the references are mutable. It will also suggest you
6518replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
6519calls."##,
5714 }, 6520 },
5715 LintCompletion { 6521 Lint {
5716 label: "clippy::ptr_as_ptr", 6522 label: "clippy::ptr_as_ptr",
5717 description: r##"Checks for `as` casts between raw pointers without changing its mutability,\nnamely `*const T` to `*const U` and `*mut T` to `*mut U`."##, 6523 description: r##"Checks for `as` casts between raw pointers without changing its mutability,
6524namely `*const T` to `*const U` and `*mut T` to `*mut U`."##,
5718 }, 6525 },
5719 LintCompletion { 6526 Lint { label: "clippy::ptr_eq", description: r##"Use `std::ptr::eq` when applicable"## },
5720 label: "clippy::ptr_eq", 6527 Lint {
5721 description: r##"Use `std::ptr::eq` when applicable"##,
5722 },
5723 LintCompletion {
5724 label: "clippy::ptr_offset_with_cast", 6528 label: "clippy::ptr_offset_with_cast",
5725 description: r##"Checks for usage of the `offset` pointer method with a `usize` casted to an\n`isize`."##, 6529 description: r##"Checks for usage of the `offset` pointer method with a `usize` casted to an
6530`isize`."##,
5726 }, 6531 },
5727 LintCompletion { 6532 Lint {
5728 label: "clippy::pub_enum_variant_names", 6533 label: "clippy::pub_enum_variant_names",
5729 description: r##"Detects public enumeration variants that are\nprefixed or suffixed by the same characters."##, 6534 description: r##"Nothing. This lint has been deprecated."##,
5730 }, 6535 },
5731 LintCompletion { 6536 Lint {
5732 label: "clippy::question_mark", 6537 label: "clippy::question_mark",
5733 description: r##"Checks for expressions that could be replaced by the question mark operator."##, 6538 description: r##"Checks for expressions that could be replaced by the question mark operator."##,
5734 }, 6539 },
5735 LintCompletion { 6540 Lint {
5736 label: "clippy::range_minus_one", 6541 label: "clippy::range_minus_one",
5737 description: r##"Checks for inclusive ranges where 1 is subtracted from\nthe upper bound, e.g., `x..=(y-1)`."##, 6542 description: r##"Checks for inclusive ranges where 1 is subtracted from
6543the upper bound, e.g., `x..=(y-1)`."##,
5738 }, 6544 },
5739 LintCompletion { 6545 Lint {
5740 label: "clippy::range_plus_one", 6546 label: "clippy::range_plus_one",
5741 description: r##"Checks for exclusive ranges where 1 is added to the\nupper bound, e.g., `x..(y+1)`."##, 6547 description: r##"Checks for exclusive ranges where 1 is added to the
6548upper bound, e.g., `x..(y+1)`."##,
5742 }, 6549 },
5743 LintCompletion { 6550 Lint {
5744 label: "clippy::range_step_by_zero", 6551 label: "clippy::range_step_by_zero",
5745 description: r##"Nothing. This lint has been deprecated."##, 6552 description: r##"Nothing. This lint has been deprecated."##,
5746 }, 6553 },
5747 LintCompletion { 6554 Lint {
5748 label: "clippy::range_zip_with_len", 6555 label: "clippy::range_zip_with_len",
5749 description: r##"Checks for zipping a collection with the range of\n`0.._.len()`."##, 6556 description: r##"Checks for zipping a collection with the range of
6557`0.._.len()`."##,
5750 }, 6558 },
5751 LintCompletion { 6559 Lint {
5752 label: "clippy::rc_buffer", 6560 label: "clippy::rc_buffer",
5753 description: r##"Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`."##, 6561 description: r##"Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`."##,
5754 }, 6562 },
5755 LintCompletion { 6563 Lint {
5756 label: "clippy::redundant_allocation", 6564 label: "clippy::redundant_allocation",
5757 description: r##"Checks for use of redundant allocations anywhere in the code."##, 6565 description: r##"Checks for use of redundant allocations anywhere in the code."##,
5758 }, 6566 },
5759 LintCompletion { 6567 Lint {
5760 label: "clippy::redundant_clone", 6568 label: "clippy::redundant_clone",
5761 description: r##"Checks for a redundant `clone()` (and its relatives) which clones an owned\nvalue that is going to be dropped without further use."##, 6569 description: r##"Checks for a redundant `clone()` (and its relatives) which clones an owned
6570value that is going to be dropped without further use."##,
5762 }, 6571 },
5763 LintCompletion { 6572 Lint {
5764 label: "clippy::redundant_closure", 6573 label: "clippy::redundant_closure",
5765 description: r##"Checks for closures which just call another function where\nthe function can be called directly. `unsafe` functions or calls where types\nget adjusted are ignored."##, 6574 description: r##"Checks for closures which just call another function where
6575the function can be called directly. `unsafe` functions or calls where types
6576get adjusted are ignored."##,
5766 }, 6577 },
5767 LintCompletion { 6578 Lint {
5768 label: "clippy::redundant_closure_call", 6579 label: "clippy::redundant_closure_call",
5769 description: r##"Detects closures called in the same expression where they\nare defined."##, 6580 description: r##"Detects closures called in the same expression where they
6581are defined."##,
5770 }, 6582 },
5771 LintCompletion { 6583 Lint {
5772 label: "clippy::redundant_closure_for_method_calls", 6584 label: "clippy::redundant_closure_for_method_calls",
5773 description: r##"Checks for closures which only invoke a method on the closure\nargument and can be replaced by referencing the method directly."##, 6585 description: r##"Checks for closures which only invoke a method on the closure
6586argument and can be replaced by referencing the method directly."##,
5774 }, 6587 },
5775 LintCompletion { 6588 Lint {
5776 label: "clippy::redundant_else", 6589 label: "clippy::redundant_else",
5777 description: r##"Checks for `else` blocks that can be removed without changing semantics."##, 6590 description: r##"Checks for `else` blocks that can be removed without changing semantics."##,
5778 }, 6591 },
5779 LintCompletion { 6592 Lint {
5780 label: "clippy::redundant_field_names", 6593 label: "clippy::redundant_field_names",
5781 description: r##"Checks for fields in struct literals where shorthands\ncould be used."##, 6594 description: r##"Checks for fields in struct literals where shorthands
6595could be used."##,
5782 }, 6596 },
5783 LintCompletion { 6597 Lint {
5784 label: "clippy::redundant_pattern", 6598 label: "clippy::redundant_pattern",
5785 description: r##"Checks for patterns in the form `name @ _`."##, 6599 description: r##"Checks for patterns in the form `name @ _`."##,
5786 }, 6600 },
5787 LintCompletion { 6601 Lint {
5788 label: "clippy::redundant_pattern_matching", 6602 label: "clippy::redundant_pattern_matching",
5789 description: r##"Lint for redundant pattern matching over `Result`, `Option`,\n`std::task::Poll` or `std::net::IpAddr`"##, 6603 description: r##"Lint for redundant pattern matching over `Result`, `Option`,
6604`std::task::Poll` or `std::net::IpAddr`"##,
5790 }, 6605 },
5791 LintCompletion { 6606 Lint {
5792 label: "clippy::redundant_pub_crate", 6607 label: "clippy::redundant_pub_crate",
5793 description: r##"Checks for items declared `pub(crate)` that are not crate visible because they\nare inside a private module."##, 6608 description: r##"Checks for items declared `pub(crate)` that are not crate visible because they
6609are inside a private module."##,
5794 }, 6610 },
5795 LintCompletion { 6611 Lint {
5796 label: "clippy::redundant_slicing", 6612 label: "clippy::redundant_slicing",
5797 description: r##"Checks for redundant slicing expressions which use the full range, and\ndo not change the type."##, 6613 description: r##"Checks for redundant slicing expressions which use the full range, and
6614do not change the type."##,
5798 }, 6615 },
5799 LintCompletion { 6616 Lint {
5800 label: "clippy::redundant_static_lifetimes", 6617 label: "clippy::redundant_static_lifetimes",
5801 description: r##"Checks for constants and statics with an explicit `'static` lifetime."##, 6618 description: r##"Checks for constants and statics with an explicit `'static` lifetime."##,
5802 }, 6619 },
5803 LintCompletion { 6620 Lint {
6621 label: "clippy::ref_binding_to_reference",
6622 description: r##"Checks for `ref` bindings which create a reference to a reference."##,
6623 },
6624 Lint {
5804 label: "clippy::ref_in_deref", 6625 label: "clippy::ref_in_deref",
5805 description: r##"Checks for references in expressions that use\nauto dereference."##, 6626 description: r##"Checks for references in expressions that use
6627auto dereference."##,
5806 }, 6628 },
5807 LintCompletion { 6629 Lint {
5808 label: "clippy::ref_option_ref", 6630 label: "clippy::ref_option_ref",
5809 description: r##"Checks for usage of `&Option<&T>`."##, 6631 description: r##"Checks for usage of `&Option<&T>`."##,
5810 }, 6632 },
5811 LintCompletion { 6633 Lint {
5812 label: "clippy::regex_macro", 6634 label: "clippy::regex_macro",
5813 description: r##"Nothing. This lint has been deprecated."##, 6635 description: r##"Nothing. This lint has been deprecated."##,
5814 }, 6636 },
5815 LintCompletion { 6637 Lint {
5816 label: "clippy::repeat_once", 6638 label: "clippy::repeat_once",
5817 description: r##"Checks for usage of `.repeat(1)` and suggest the following method for each types.\n- `.to_string()` for `str`\n- `.clone()` for `String`\n- `.to_vec()` for `slice`"##, 6639 description: r##"Checks for usage of `.repeat(1)` and suggest the following method for each types.
6640- `.to_string()` for `str`
6641- `.clone()` for `String`
6642- `.to_vec()` for `slice`"##,
5818 }, 6643 },
5819 LintCompletion { 6644 Lint {
5820 label: "clippy::replace_consts", 6645 label: "clippy::replace_consts",
5821 description: r##"Nothing. This lint has been deprecated."##, 6646 description: r##"Nothing. This lint has been deprecated."##,
5822 }, 6647 },
5823 LintCompletion { 6648 Lint {
5824 label: "clippy::rest_pat_in_fully_bound_structs", 6649 label: "clippy::rest_pat_in_fully_bound_structs",
5825 description: r##"Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched."##, 6650 description: r##"Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched."##,
5826 }, 6651 },
5827 LintCompletion { 6652 Lint {
5828 label: "clippy::result_map_or_into_option", 6653 label: "clippy::result_map_or_into_option",
5829 description: r##"Checks for usage of `_.map_or(None, Some)`."##, 6654 description: r##"Checks for usage of `_.map_or(None, Some)`."##,
5830 }, 6655 },
5831 LintCompletion { 6656 Lint {
5832 label: "clippy::result_map_unit_fn", 6657 label: "clippy::result_map_unit_fn",
5833 description: r##"Checks for usage of `result.map(f)` where f is a function\nor closure that returns the unit type `()`."##, 6658 description: r##"Checks for usage of `result.map(f)` where f is a function
6659or closure that returns the unit type `()`."##,
5834 }, 6660 },
5835 LintCompletion { 6661 Lint {
5836 label: "clippy::result_unit_err", 6662 label: "clippy::result_unit_err",
5837 description: r##"Checks for public functions that return a `Result`\nwith an `Err` type of `()`. It suggests using a custom type that\nimplements [`std::error::Error`]."##, 6663 description: r##"Checks for public functions that return a `Result`
6664with an `Err` type of `()`. It suggests using a custom type that
6665implements `std::error::Error`."##,
5838 }, 6666 },
5839 LintCompletion { 6667 Lint {
5840 label: "clippy::reversed_empty_ranges", 6668 label: "clippy::reversed_empty_ranges",
5841 description: r##"Checks for range expressions `x..y` where both `x` and `y`\nare constant and `x` is greater or equal to `y`."##, 6669 description: r##"Checks for range expressions `x..y` where both `x` and `y`
6670are constant and `x` is greater or equal to `y`."##,
5842 }, 6671 },
5843 LintCompletion { 6672 Lint {
5844 label: "clippy::same_functions_in_if_condition", 6673 label: "clippy::same_functions_in_if_condition",
5845 description: r##"Checks for consecutive `if`s with the same function call."##, 6674 description: r##"Checks for consecutive `if`s with the same function call."##,
5846 }, 6675 },
5847 LintCompletion { 6676 Lint {
5848 label: "clippy::same_item_push", 6677 label: "clippy::same_item_push",
5849 description: r##"Checks whether a for loop is being used to push a constant\nvalue into a Vec."##, 6678 description: r##"Checks whether a for loop is being used to push a constant
6679value into a Vec."##,
5850 }, 6680 },
5851 LintCompletion { 6681 Lint {
5852 label: "clippy::search_is_some", 6682 label: "clippy::search_is_some",
5853 description: r##"Checks for an iterator or string search (such as `find()`,\n`position()`, or `rposition()`) followed by a call to `is_some()`."##, 6683 description: r##"Checks for an iterator or string search (such as `find()`,
6684`position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`."##,
5854 }, 6685 },
5855 LintCompletion { 6686 Lint {
5856 label: "clippy::self_assignment", 6687 label: "clippy::self_assignment",
5857 description: r##"Checks for explicit self-assignments."##, 6688 description: r##"Checks for explicit self-assignments."##,
5858 }, 6689 },
5859 LintCompletion { 6690 Lint {
5860 label: "clippy::semicolon_if_nothing_returned", 6691 label: "clippy::semicolon_if_nothing_returned",
5861 description: r##"Looks for blocks of expressions and fires if the last expression returns `()`\nbut is not followed by a semicolon."##, 6692 description: r##"Looks for blocks of expressions and fires if the last expression returns
6693`()` but is not followed by a semicolon."##,
5862 }, 6694 },
5863 LintCompletion { 6695 Lint {
5864 label: "clippy::serde_api_misuse", 6696 label: "clippy::serde_api_misuse",
5865 description: r##"Checks for mis-uses of the serde API."##, 6697 description: r##"Checks for mis-uses of the serde API."##,
5866 }, 6698 },
5867 LintCompletion { 6699 Lint {
5868 label: "clippy::shadow_reuse", 6700 label: "clippy::shadow_reuse",
5869 description: r##"Checks for bindings that shadow other bindings already in\nscope, while reusing the original value."##, 6701 description: r##"Checks for bindings that shadow other bindings already in
6702scope, while reusing the original value."##,
5870 }, 6703 },
5871 LintCompletion { 6704 Lint {
5872 label: "clippy::shadow_same", 6705 label: "clippy::shadow_same",
5873 description: r##"Checks for bindings that shadow other bindings already in\nscope, while just changing reference level or mutability."##, 6706 description: r##"Checks for bindings that shadow other bindings already in
6707scope, while just changing reference level or mutability."##,
5874 }, 6708 },
5875 LintCompletion { 6709 Lint {
5876 label: "clippy::shadow_unrelated", 6710 label: "clippy::shadow_unrelated",
5877 description: r##"Checks for bindings that shadow other bindings already in\nscope, either without a initialization or with one that does not even use\nthe original value."##, 6711 description: r##"Checks for bindings that shadow other bindings already in
6712scope, either without a initialization or with one that does not even use
6713the original value."##,
5878 }, 6714 },
5879 LintCompletion { 6715 Lint {
5880 label: "clippy::short_circuit_statement", 6716 label: "clippy::short_circuit_statement",
5881 description: r##"Checks for the use of short circuit boolean conditions as\na\nstatement."##, 6717 description: r##"Checks for the use of short circuit boolean conditions as
6718a
6719statement."##,
5882 }, 6720 },
5883 LintCompletion { 6721 Lint {
5884 label: "clippy::should_assert_eq", 6722 label: "clippy::should_assert_eq",
5885 description: r##"Nothing. This lint has been deprecated."##, 6723 description: r##"Nothing. This lint has been deprecated."##,
5886 }, 6724 },
5887 LintCompletion { 6725 Lint {
5888 label: "clippy::should_implement_trait", 6726 label: "clippy::should_implement_trait",
5889 description: r##"Checks for methods that should live in a trait\nimplementation of a `std` trait (see [llogiq's blog\npost](http://llogiq.github.io/2015/07/30/traits.html) for further\ninformation) instead of an inherent implementation."##, 6727 description: r##"Checks for methods that should live in a trait
6728implementation of a `std` trait (see [llogiq's blog
6729post](http://llogiq.github.io/2015/07/30/traits.html) for further
6730information) instead of an inherent implementation."##,
5890 }, 6731 },
5891 LintCompletion { 6732 Lint {
5892 label: "clippy::similar_names", 6733 label: "clippy::similar_names",
5893 description: r##"Checks for names that are very similar and thus confusing."##, 6734 description: r##"Checks for names that are very similar and thus confusing."##,
5894 }, 6735 },
5895 LintCompletion { 6736 Lint {
5896 label: "clippy::single_char_add_str", 6737 label: "clippy::single_char_add_str",
5897 description: r##"Warns when using `push_str`/`insert_str` with a single-character string literal\nwhere `push`/`insert` with a `char` would work fine."##, 6738 description: r##"Warns when using `push_str`/`insert_str` with a single-character string literal
6739where `push`/`insert` with a `char` would work fine."##,
5898 }, 6740 },
5899 LintCompletion { 6741 Lint {
5900 label: "clippy::single_char_pattern", 6742 label: "clippy::single_char_pattern",
5901 description: r##"Checks for string methods that receive a single-character\n`str` as an argument, e.g., `_.split(\"x\")`."##, 6743 description: r##"Checks for string methods that receive a single-character
6744`str` as an argument, e.g., `_.split(x)`."##,
5902 }, 6745 },
5903 LintCompletion { 6746 Lint {
5904 label: "clippy::single_component_path_imports", 6747 label: "clippy::single_component_path_imports",
5905 description: r##"Checking for imports with single component use path."##, 6748 description: r##"Checking for imports with single component use path."##,
5906 }, 6749 },
5907 LintCompletion { 6750 Lint {
5908 label: "clippy::single_element_loop", 6751 label: "clippy::single_element_loop",
5909 description: r##"Checks whether a for loop has a single element."##, 6752 description: r##"Checks whether a for loop has a single element."##,
5910 }, 6753 },
5911 LintCompletion { 6754 Lint {
5912 label: "clippy::single_match", 6755 label: "clippy::single_match",
5913 description: r##"Checks for matches with a single arm where an `if let`\nwill usually suffice."##, 6756 description: r##"Checks for matches with a single arm where an `if let`
6757will usually suffice."##,
5914 }, 6758 },
5915 LintCompletion { 6759 Lint {
5916 label: "clippy::single_match_else", 6760 label: "clippy::single_match_else",
5917 description: r##"Checks for matches with two arms where an `if let else` will\nusually suffice."##, 6761 description: r##"Checks for matches with two arms where an `if let else` will
6762usually suffice."##,
5918 }, 6763 },
5919 LintCompletion { 6764 Lint {
5920 label: "clippy::size_of_in_element_count", 6765 label: "clippy::size_of_in_element_count",
5921 description: r##"Detects expressions where\n`size_of::<T>` or `size_of_val::<T>` is used as a\ncount of elements of type `T`"##, 6766 description: r##"Detects expressions where
6767`size_of::<T>` or `size_of_val::<T>` is used as a
6768count of elements of type `T`"##,
5922 }, 6769 },
5923 LintCompletion { 6770 Lint {
5924 label: "clippy::skip_while_next", 6771 label: "clippy::skip_while_next",
5925 description: r##"Checks for usage of `_.skip_while(condition).next()`."##, 6772 description: r##"Checks for usage of `_.skip_while(condition).next()`."##,
5926 }, 6773 },
5927 LintCompletion { 6774 Lint {
5928 label: "clippy::slow_vector_initialization", 6775 label: "clippy::slow_vector_initialization",
5929 description: r##"Checks slow zero-filled vector initialization"##, 6776 description: r##"Checks slow zero-filled vector initialization"##,
5930 }, 6777 },
5931 LintCompletion { 6778 Lint {
5932 label: "clippy::stable_sort_primitive", 6779 label: "clippy::stable_sort_primitive",
5933 description: r##"When sorting primitive values (integers, bools, chars, as well\nas arrays, slices, and tuples of such items), it is better to\nuse an unstable sort than a stable sort."##, 6780 description: r##"When sorting primitive values (integers, bools, chars, as well
6781as arrays, slices, and tuples of such items), it is better to
6782use an unstable sort than a stable sort."##,
5934 }, 6783 },
5935 LintCompletion { 6784 Lint {
5936 label: "clippy::str_to_string", 6785 label: "clippy::str_to_string",
5937 description: r##"This lint checks for `.to_string()` method calls on values of type `&str`."##, 6786 description: r##"This lint checks for `.to_string()` method calls on values of type `&str`."##,
5938 }, 6787 },
5939 LintCompletion { 6788 Lint {
5940 label: "clippy::string_add", 6789 label: "clippy::string_add",
5941 description: r##"Checks for all instances of `x + _` where `x` is of type\n`String`, but only if [`string_add_assign`](#string_add_assign) does *not*\nmatch."##, 6790 description: r##"Checks for all instances of `x + _` where `x` is of type
6791`String`, but only if [`string_add_assign`](#string_add_assign) does *not*
6792match."##,
5942 }, 6793 },
5943 LintCompletion { 6794 Lint {
5944 label: "clippy::string_add_assign", 6795 label: "clippy::string_add_assign",
5945 description: r##"Checks for string appends of the form `x = x + y` (without\n`let`!)."##, 6796 description: r##"Checks for string appends of the form `x = x + y` (without
6797`let`!)."##,
5946 }, 6798 },
5947 LintCompletion { 6799 Lint {
5948 label: "clippy::string_extend_chars", 6800 label: "clippy::string_extend_chars",
5949 description: r##"Checks for the use of `.extend(s.chars())` where s is a\n`&str` or `String`."##, 6801 description: r##"Checks for the use of `.extend(s.chars())` where s is a
6802`&str` or `String`."##,
5950 }, 6803 },
5951 LintCompletion { 6804 Lint {
5952 label: "clippy::string_from_utf8_as_bytes", 6805 label: "clippy::string_from_utf8_as_bytes",
5953 description: r##"Check if the string is transformed to byte array and casted back to string."##, 6806 description: r##"Check if the string is transformed to byte array and casted back to string."##,
5954 }, 6807 },
5955 LintCompletion { 6808 Lint {
5956 label: "clippy::string_lit_as_bytes", 6809 label: "clippy::string_lit_as_bytes",
5957 description: r##"Checks for the `as_bytes` method called on string literals\nthat contain only ASCII characters."##, 6810 description: r##"Checks for the `as_bytes` method called on string literals
6811that contain only ASCII characters."##,
5958 }, 6812 },
5959 LintCompletion { 6813 Lint {
5960 label: "clippy::string_to_string", 6814 label: "clippy::string_to_string",
5961 description: r##"This lint checks for `.to_string()` method calls on values of type `String`."##, 6815 description: r##"This lint checks for `.to_string()` method calls on values of type `String`."##,
5962 }, 6816 },
5963 LintCompletion { 6817 Lint {
5964 label: "clippy::struct_excessive_bools", 6818 label: "clippy::struct_excessive_bools",
5965 description: r##"Checks for excessive\nuse of bools in structs."##, 6819 description: r##"Checks for excessive
6820use of bools in structs."##,
5966 }, 6821 },
5967 LintCompletion { 6822 Lint {
5968 label: "clippy::suboptimal_flops", 6823 label: "clippy::suboptimal_flops",
5969 description: r##"Looks for floating-point expressions that\ncan be expressed using built-in methods to improve both\naccuracy and performance."##, 6824 description: r##"Looks for floating-point expressions that
6825can be expressed using built-in methods to improve both
6826accuracy and performance."##,
5970 }, 6827 },
5971 LintCompletion { 6828 Lint {
5972 label: "clippy::suspicious_arithmetic_impl", 6829 label: "clippy::suspicious_arithmetic_impl",
5973 description: r##"Lints for suspicious operations in impls of arithmetic operators, e.g.\nsubtracting elements in an Add impl."##, 6830 description: r##"Lints for suspicious operations in impls of arithmetic operators, e.g.
6831subtracting elements in an Add impl."##,
5974 }, 6832 },
5975 LintCompletion { 6833 Lint {
5976 label: "clippy::suspicious_assignment_formatting", 6834 label: "clippy::suspicious_assignment_formatting",
5977 description: r##"Checks for use of the non-existent `=*`, `=!` and `=-`\noperators."##, 6835 description: r##"Checks for use of the non-existent `=*`, `=!` and `=-`
6836operators."##,
5978 }, 6837 },
5979 LintCompletion { 6838 Lint {
5980 label: "clippy::suspicious_else_formatting", 6839 label: "clippy::suspicious_else_formatting",
5981 description: r##"Checks for formatting of `else`. It lints if the `else`\nis followed immediately by a newline or the `else` seems to be missing."##, 6840 description: r##"Checks for formatting of `else`. It lints if the `else`
6841is followed immediately by a newline or the `else` seems to be missing."##,
5982 }, 6842 },
5983 LintCompletion { 6843 Lint {
5984 label: "clippy::suspicious_map", 6844 label: "clippy::suspicious_map",
5985 description: r##"Checks for calls to `map` followed by a `count`."##, 6845 description: r##"Checks for calls to `map` followed by a `count`."##,
5986 }, 6846 },
5987 LintCompletion { 6847 Lint {
5988 label: "clippy::suspicious_op_assign_impl", 6848 label: "clippy::suspicious_op_assign_impl",
5989 description: r##"Lints for suspicious operations in impls of OpAssign, e.g.\nsubtracting elements in an AddAssign impl."##, 6849 description: r##"Lints for suspicious operations in impls of OpAssign, e.g.
6850subtracting elements in an AddAssign impl."##,
5990 }, 6851 },
5991 LintCompletion { 6852 Lint {
5992 label: "clippy::suspicious_operation_groupings", 6853 label: "clippy::suspicious_operation_groupings",
5993 description: r##"Checks for unlikely usages of binary operators that are almost\ncertainly typos and/or copy/paste errors, given the other usages\nof binary operators nearby."##, 6854 description: r##"Checks for unlikely usages of binary operators that are almost
6855certainly typos and/or copy/paste errors, given the other usages
6856of binary operators nearby."##,
6857 },
6858 Lint {
6859 label: "clippy::suspicious_splitn",
6860 description: r##"Checks for calls to [`splitn`]
6861(https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
6862related functions with either zero or one splits."##,
5994 }, 6863 },
5995 LintCompletion { 6864 Lint {
5996 label: "clippy::suspicious_unary_op_formatting", 6865 label: "clippy::suspicious_unary_op_formatting",
5997 description: r##"Checks the formatting of a unary operator on the right hand side\nof a binary operator. It lints if there is no space between the binary and unary operators,\nbut there is a space between the unary and its operand."##, 6866 description: r##"Checks the formatting of a unary operator on the right hand side
6867of a binary operator. It lints if there is no space between the binary and unary operators,
6868but there is a space between the unary and its operand."##,
5998 }, 6869 },
5999 LintCompletion { 6870 Lint {
6000 label: "clippy::tabs_in_doc_comments", 6871 label: "clippy::tabs_in_doc_comments",
6001 description: r##"Checks doc comments for usage of tab characters."##, 6872 description: r##"Checks doc comments for usage of tab characters."##,
6002 }, 6873 },
6003 LintCompletion { 6874 Lint {
6004 label: "clippy::temporary_assignment", 6875 label: "clippy::temporary_assignment",
6005 description: r##"Checks for construction of a structure or tuple just to\nassign a value in it."##, 6876 description: r##"Checks for construction of a structure or tuple just to
6877assign a value in it."##,
6006 }, 6878 },
6007 LintCompletion { 6879 Lint {
6008 label: "clippy::temporary_cstring_as_ptr",
6009 description: r##"Nothing. This lint has been deprecated."##,
6010 },
6011 LintCompletion {
6012 label: "clippy::to_digit_is_some", 6880 label: "clippy::to_digit_is_some",
6013 description: r##"Checks for `.to_digit(..).is_some()` on `char`s."##, 6881 description: r##"Checks for `.to_digit(..).is_some()` on `char`s."##,
6014 }, 6882 },
6015 LintCompletion { 6883 Lint {
6016 label: "clippy::to_string_in_display", 6884 label: "clippy::to_string_in_display",
6017 description: r##"Checks for uses of `to_string()` in `Display` traits."##, 6885 description: r##"Checks for uses of `to_string()` in `Display` traits."##,
6018 }, 6886 },
6019 LintCompletion { label: "clippy::todo", description: r##"Checks for usage of `todo!`."## }, 6887 Lint { label: "clippy::todo", description: r##"Checks for usage of `todo!`."## },
6020 LintCompletion { 6888 Lint {
6021 label: "clippy::too_many_arguments", 6889 label: "clippy::too_many_arguments",
6022 description: r##"Checks for functions with too many parameters."##, 6890 description: r##"Checks for functions with too many parameters."##,
6023 }, 6891 },
6024 LintCompletion { 6892 Lint {
6025 label: "clippy::too_many_lines", 6893 label: "clippy::too_many_lines",
6026 description: r##"Checks for functions with a large amount of lines."##, 6894 description: r##"Checks for functions with a large amount of lines."##,
6027 }, 6895 },
6028 LintCompletion { 6896 Lint {
6029 label: "clippy::toplevel_ref_arg", 6897 label: "clippy::toplevel_ref_arg",
6030 description: r##"Checks for function arguments and let bindings denoted as\n`ref`."##, 6898 description: r##"Checks for function arguments and let bindings denoted as
6899`ref`."##,
6031 }, 6900 },
6032 LintCompletion { 6901 Lint {
6033 label: "clippy::trait_duplication_in_bounds", 6902 label: "clippy::trait_duplication_in_bounds",
6034 description: r##"Checks for cases where generics are being used and multiple\nsyntax specifications for trait bounds are used simultaneously."##, 6903 description: r##"Checks for cases where generics are being used and multiple
6904syntax specifications for trait bounds are used simultaneously."##,
6035 }, 6905 },
6036 LintCompletion { 6906 Lint {
6037 label: "clippy::transmute_bytes_to_str", 6907 label: "clippy::transmute_bytes_to_str",
6038 description: r##"Checks for transmutes from a `&[u8]` to a `&str`."##, 6908 description: r##"Checks for transmutes from a `&[u8]` to a `&str`."##,
6039 }, 6909 },
6040 LintCompletion { 6910 Lint {
6041 label: "clippy::transmute_float_to_int", 6911 label: "clippy::transmute_float_to_int",
6042 description: r##"Checks for transmutes from a float to an integer."##, 6912 description: r##"Checks for transmutes from a float to an integer."##,
6043 }, 6913 },
6044 LintCompletion { 6914 Lint {
6045 label: "clippy::transmute_int_to_bool", 6915 label: "clippy::transmute_int_to_bool",
6046 description: r##"Checks for transmutes from an integer to a `bool`."##, 6916 description: r##"Checks for transmutes from an integer to a `bool`."##,
6047 }, 6917 },
6048 LintCompletion { 6918 Lint {
6049 label: "clippy::transmute_int_to_char", 6919 label: "clippy::transmute_int_to_char",
6050 description: r##"Checks for transmutes from an integer to a `char`."##, 6920 description: r##"Checks for transmutes from an integer to a `char`."##,
6051 }, 6921 },
6052 LintCompletion { 6922 Lint {
6053 label: "clippy::transmute_int_to_float", 6923 label: "clippy::transmute_int_to_float",
6054 description: r##"Checks for transmutes from an integer to a float."##, 6924 description: r##"Checks for transmutes from an integer to a float."##,
6055 }, 6925 },
6056 LintCompletion { 6926 Lint {
6057 label: "clippy::transmute_ptr_to_ptr", 6927 label: "clippy::transmute_ptr_to_ptr",
6058 description: r##"Checks for transmutes from a pointer to a pointer, or\nfrom a reference to a reference."##, 6928 description: r##"Checks for transmutes from a pointer to a pointer, or
6929from a reference to a reference."##,
6059 }, 6930 },
6060 LintCompletion { 6931 Lint {
6061 label: "clippy::transmute_ptr_to_ref", 6932 label: "clippy::transmute_ptr_to_ref",
6062 description: r##"Checks for transmutes from a pointer to a reference."##, 6933 description: r##"Checks for transmutes from a pointer to a reference."##,
6063 }, 6934 },
6064 LintCompletion { 6935 Lint {
6065 label: "clippy::transmutes_expressible_as_ptr_casts", 6936 label: "clippy::transmutes_expressible_as_ptr_casts",
6066 description: r##"Checks for transmutes that could be a pointer cast."##, 6937 description: r##"Checks for transmutes that could be a pointer cast."##,
6067 }, 6938 },
6068 LintCompletion { 6939 Lint {
6069 label: "clippy::transmuting_null", 6940 label: "clippy::transmuting_null",
6070 description: r##"Checks for transmute calls which would receive a null pointer."##, 6941 description: r##"Checks for transmute calls which would receive a null pointer."##,
6071 }, 6942 },
6072 LintCompletion { 6943 Lint {
6073 label: "clippy::trivial_regex", 6944 label: "clippy::trivial_regex",
6074 description: r##"Checks for trivial [regex](https://crates.io/crates/regex)\ncreation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`)."##, 6945 description: r##"Checks for trivial [regex](https://crates.io/crates/regex)
6946creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`)."##,
6075 }, 6947 },
6076 LintCompletion { 6948 Lint {
6077 label: "clippy::trivially_copy_pass_by_ref", 6949 label: "clippy::trivially_copy_pass_by_ref",
6078 description: r##"Checks for functions taking arguments by reference, where\nthe argument type is `Copy` and small enough to be more efficient to always\npass by value."##, 6950 description: r##"Checks for functions taking arguments by reference, where
6079 }, 6951the argument type is `Copy` and small enough to be more efficient to always
6080 LintCompletion { 6952pass by value."##,
6081 label: "clippy::try_err",
6082 description: r##"Checks for usages of `Err(x)?`."##,
6083 }, 6953 },
6084 LintCompletion { 6954 Lint { label: "clippy::try_err", description: r##"Checks for usages of `Err(x)?`."## },
6955 Lint {
6085 label: "clippy::type_complexity", 6956 label: "clippy::type_complexity",
6086 description: r##"Checks for types used in structs, parameters and `let`\ndeclarations above a certain complexity threshold."##, 6957 description: r##"Checks for types used in structs, parameters and `let`
6958declarations above a certain complexity threshold."##,
6087 }, 6959 },
6088 LintCompletion { 6960 Lint {
6089 label: "clippy::type_repetition_in_bounds", 6961 label: "clippy::type_repetition_in_bounds",
6090 description: r##"This lint warns about unnecessary type repetitions in trait bounds"##, 6962 description: r##"This lint warns about unnecessary type repetitions in trait bounds"##,
6091 }, 6963 },
6092 LintCompletion { 6964 Lint {
6093 label: "clippy::undropped_manually_drops", 6965 label: "clippy::undropped_manually_drops",
6094 description: r##"Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`."##, 6966 description: r##"Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`."##,
6095 }, 6967 },
6096 LintCompletion { 6968 Lint {
6097 label: "clippy::unicode_not_nfc", 6969 label: "clippy::unicode_not_nfc",
6098 description: r##"Checks for string literals that contain Unicode in a form\nthat is not equal to its\n[NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms)."##, 6970 description: r##"Checks for string literals that contain Unicode in a form
6971that is not equal to its
6972[NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms)."##,
6099 }, 6973 },
6100 LintCompletion { 6974 Lint {
6101 label: "clippy::unimplemented", 6975 label: "clippy::unimplemented",
6102 description: r##"Checks for usage of `unimplemented!`."##, 6976 description: r##"Checks for usage of `unimplemented!`."##,
6103 }, 6977 },
6104 LintCompletion { 6978 Lint {
6105 label: "clippy::uninit_assumed_init", 6979 label: "clippy::uninit_assumed_init",
6106 description: r##"Checks for `MaybeUninit::uninit().assume_init()`."##, 6980 description: r##"Checks for `MaybeUninit::uninit().assume_init()`."##,
6107 }, 6981 },
6108 LintCompletion { 6982 Lint {
6109 label: "clippy::unit_arg", 6983 label: "clippy::unit_arg",
6110 description: r##"Checks for passing a unit value as an argument to a function without using a\nunit literal (`()`)."##, 6984 description: r##"Checks for passing a unit value as an argument to a function without using a
6985unit literal (`()`)."##,
6111 }, 6986 },
6112 LintCompletion { 6987 Lint {
6113 label: "clippy::unit_cmp", 6988 label: "clippy::unit_cmp",
6114 description: r##"Checks for comparisons to unit. This includes all binary\ncomparisons (like `==` and `<`) and asserts."##, 6989 description: r##"Checks for comparisons to unit. This includes all binary
6990comparisons (like `==` and `<`) and asserts."##,
6115 }, 6991 },
6116 LintCompletion { 6992 Lint {
6117 label: "clippy::unit_return_expecting_ord", 6993 label: "clippy::unit_return_expecting_ord",
6118 description: r##"Checks for functions that expect closures of type\nFn(...) -> Ord where the implemented closure returns the unit type.\nThe lint also suggests to remove the semi-colon at the end of the statement if present."##, 6994 description: r##"Checks for functions that expect closures of type
6995Fn(...) -> Ord where the implemented closure returns the unit type.
6996The lint also suggests to remove the semi-colon at the end of the statement if present."##,
6119 }, 6997 },
6120 LintCompletion { 6998 Lint {
6121 label: "clippy::unknown_clippy_lints",
6122 description: r##"Nothing. This lint has been deprecated."##,
6123 },
6124 LintCompletion {
6125 label: "clippy::unnecessary_cast", 6999 label: "clippy::unnecessary_cast",
6126 description: r##"Checks for casts to the same type, casts of int literals to integer types\nand casts of float literals to float types."##, 7000 description: r##"Checks for casts to the same type, casts of int literals to integer types
7001and casts of float literals to float types."##,
6127 }, 7002 },
6128 LintCompletion { 7003 Lint {
6129 label: "clippy::unnecessary_filter_map", 7004 label: "clippy::unnecessary_filter_map",
6130 description: r##"Checks for `filter_map` calls which could be replaced by `filter` or `map`.\nMore specifically it checks if the closure provided is only performing one of the\nfilter or map operations and suggests the appropriate option."##, 7005 description: r##"Checks for `filter_map` calls which could be replaced by `filter` or `map`.
7006More specifically it checks if the closure provided is only performing one of the
7007filter or map operations and suggests the appropriate option."##,
6131 }, 7008 },
6132 LintCompletion { 7009 Lint {
6133 label: "clippy::unnecessary_fold", 7010 label: "clippy::unnecessary_fold",
6134 description: r##"Checks for using `fold` when a more succinct alternative exists.\nSpecifically, this checks for `fold`s which could be replaced by `any`, `all`,\n`sum` or `product`."##, 7011 description: r##"Checks for using `fold` when a more succinct alternative exists.
7012Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
7013`sum` or `product`."##,
6135 }, 7014 },
6136 LintCompletion { 7015 Lint {
6137 label: "clippy::unnecessary_lazy_evaluations", 7016 label: "clippy::unnecessary_lazy_evaluations",
6138 description: r##"As the counterpart to `or_fun_call`, this lint looks for unnecessary\nlazily evaluated closures on `Option` and `Result`.\n\nThis lint suggests changing the following functions, when eager evaluation results in\nsimpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`"##, 7017 description: r##"As the counterpart to `or_fun_call`, this lint looks for unnecessary
6139 }, 7018lazily evaluated closures on `Option` and `Result`.
6140 LintCompletion { 7019
7020This lint suggests changing the following functions, when eager evaluation results in
7021simpler code:
7022 - `unwrap_or_else` to `unwrap_or`
7023 - `and_then` to `and`
7024 - `or_else` to `or`
7025 - `get_or_insert_with` to `get_or_insert`
7026 - `ok_or_else` to `ok_or`"##,
7027 },
7028 Lint {
6141 label: "clippy::unnecessary_mut_passed", 7029 label: "clippy::unnecessary_mut_passed",
6142 description: r##"Detects passing a mutable reference to a function that only\nrequires an immutable reference."##, 7030 description: r##"Detects passing a mutable reference to a function that only
7031requires an immutable reference."##,
6143 }, 7032 },
6144 LintCompletion { 7033 Lint {
6145 label: "clippy::unnecessary_operation", 7034 label: "clippy::unnecessary_operation",
6146 description: r##"Checks for expression statements that can be reduced to a\nsub-expression."##, 7035 description: r##"Checks for expression statements that can be reduced to a
7036sub-expression."##,
6147 }, 7037 },
6148 LintCompletion { 7038 Lint {
7039 label: "clippy::unnecessary_self_imports",
7040 description: r##"Checks for imports ending in `::{self}`."##,
7041 },
7042 Lint {
6149 label: "clippy::unnecessary_sort_by", 7043 label: "clippy::unnecessary_sort_by",
6150 description: r##"Detects uses of `Vec::sort_by` passing in a closure\nwhich compares the two arguments, either directly or indirectly."##, 7044 description: r##"Detects uses of `Vec::sort_by` passing in a closure
7045which compares the two arguments, either directly or indirectly."##,
6151 }, 7046 },
6152 LintCompletion { 7047 Lint {
6153 label: "clippy::unnecessary_unwrap", 7048 label: "clippy::unnecessary_unwrap",
6154 description: r##"Checks for calls of `unwrap[_err]()` that cannot fail."##, 7049 description: r##"Checks for calls of `unwrap[_err]()` that cannot fail."##,
6155 }, 7050 },
6156 LintCompletion { 7051 Lint {
6157 label: "clippy::unnecessary_wraps", 7052 label: "clippy::unnecessary_wraps",
6158 description: r##"Checks for private functions that only return `Ok` or `Some`."##, 7053 description: r##"Checks for private functions that only return `Ok` or `Some`."##,
6159 }, 7054 },
6160 LintCompletion { 7055 Lint {
6161 label: "clippy::unneeded_field_pattern", 7056 label: "clippy::unneeded_field_pattern",
6162 description: r##"Checks for structure field patterns bound to wildcards."##, 7057 description: r##"Checks for structure field patterns bound to wildcards."##,
6163 }, 7058 },
6164 LintCompletion { 7059 Lint {
6165 label: "clippy::unneeded_wildcard_pattern", 7060 label: "clippy::unneeded_wildcard_pattern",
6166 description: r##"Checks for tuple patterns with a wildcard\npattern (`_`) is next to a rest pattern (`..`).\n\n_NOTE_: While `_, ..` means there is at least one element left, `..`\nmeans there are 0 or more elements left. This can make a difference\nwhen refactoring, but shouldn't result in errors in the refactored code,\nsince the wildcard pattern isn't used anyway."##, 7061 description: r##"Checks for tuple patterns with a wildcard
7062pattern (`_`) is next to a rest pattern (`..`).
7063
7064_NOTE_: While `_, ..` means there is at least one element left, `..`
7065means there are 0 or more elements left. This can make a difference
7066when refactoring, but shouldn't result in errors in the refactored code,
7067since the wildcard pattern isn't used anyway."##,
6167 }, 7068 },
6168 LintCompletion { 7069 Lint {
6169 label: "clippy::unnested_or_patterns", 7070 label: "clippy::unnested_or_patterns",
6170 description: r##"Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\nsuggests replacing the pattern with a nested one, `Some(0 | 2)`.\n\nAnother way to think of this is that it rewrites patterns in\n*disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*."##, 7071 description: r##"Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and
6171 }, 7072suggests replacing the pattern with a nested one, `Some(0 | 2)`.
6172 LintCompletion { 7073
6173 label: "clippy::unreachable", 7074Another way to think of this is that it rewrites patterns in
6174 description: r##"Checks for usage of `unreachable!`."##, 7075*disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*."##,
6175 }, 7076 },
6176 LintCompletion { 7077 Lint { label: "clippy::unreachable", description: r##"Checks for usage of `unreachable!`."## },
7078 Lint {
6177 label: "clippy::unreadable_literal", 7079 label: "clippy::unreadable_literal",
6178 description: r##"Warns if a long integral or floating-point constant does\nnot contain underscores."##, 7080 description: r##"Warns if a long integral or floating-point constant does
7081not contain underscores."##,
6179 }, 7082 },
6180 LintCompletion { 7083 Lint {
6181 label: "clippy::unsafe_derive_deserialize", 7084 label: "clippy::unsafe_derive_deserialize",
6182 description: r##"Checks for deriving `serde::Deserialize` on a type that\nhas methods using `unsafe`."##, 7085 description: r##"Checks for deriving `serde::Deserialize` on a type that
7086has methods using `unsafe`."##,
6183 }, 7087 },
6184 LintCompletion { 7088 Lint {
6185 label: "clippy::unsafe_removed_from_name", 7089 label: "clippy::unsafe_removed_from_name",
6186 description: r##"Checks for imports that remove \"unsafe\" from an item's\nname."##, 7090 description: r##"Checks for imports that remove unsafe from an item's
7091name."##,
6187 }, 7092 },
6188 LintCompletion { 7093 Lint {
6189 label: "clippy::unsafe_vector_initialization", 7094 label: "clippy::unsafe_vector_initialization",
6190 description: r##"Nothing. This lint has been deprecated."##, 7095 description: r##"Nothing. This lint has been deprecated."##,
6191 }, 7096 },
6192 LintCompletion { 7097 Lint {
6193 label: "clippy::unseparated_literal_suffix", 7098 label: "clippy::unseparated_literal_suffix",
6194 description: r##"Warns if literal suffixes are not separated by an\nunderscore."##, 7099 description: r##"Warns if literal suffixes are not separated by an
7100underscore."##,
6195 }, 7101 },
6196 LintCompletion { 7102 Lint {
6197 label: "clippy::unsound_collection_transmute", 7103 label: "clippy::unsound_collection_transmute",
6198 description: r##"Checks for transmutes between collections whose\ntypes have different ABI, size or alignment."##, 7104 description: r##"Checks for transmutes between collections whose
7105types have different ABI, size or alignment."##,
6199 }, 7106 },
6200 LintCompletion { 7107 Lint {
6201 label: "clippy::unstable_as_mut_slice", 7108 label: "clippy::unstable_as_mut_slice",
6202 description: r##"Nothing. This lint has been deprecated."##, 7109 description: r##"Nothing. This lint has been deprecated."##,
6203 }, 7110 },
6204 LintCompletion { 7111 Lint {
6205 label: "clippy::unstable_as_slice", 7112 label: "clippy::unstable_as_slice",
6206 description: r##"Nothing. This lint has been deprecated."##, 7113 description: r##"Nothing. This lint has been deprecated."##,
6207 }, 7114 },
6208 LintCompletion { 7115 Lint {
7116 label: "clippy::unused_async",
7117 description: r##"Checks for functions that are declared `async` but have no `.await`s inside of them."##,
7118 },
7119 Lint {
6209 label: "clippy::unused_collect", 7120 label: "clippy::unused_collect",
6210 description: r##"Nothing. This lint has been deprecated."##, 7121 description: r##"Nothing. This lint has been deprecated."##,
6211 }, 7122 },
6212 LintCompletion { 7123 Lint {
6213 label: "clippy::unused_io_amount", 7124 label: "clippy::unused_io_amount",
6214 description: r##"Checks for unused written/read amount."##, 7125 description: r##"Checks for unused written/read amount."##,
6215 }, 7126 },
6216 LintCompletion { 7127 Lint {
6217 label: "clippy::unused_label",
6218 description: r##"Nothing. This lint has been deprecated."##,
6219 },
6220 LintCompletion {
6221 label: "clippy::unused_self", 7128 label: "clippy::unused_self",
6222 description: r##"Checks methods that contain a `self` argument but don't use it"##, 7129 description: r##"Checks methods that contain a `self` argument but don't use it"##,
6223 }, 7130 },
6224 LintCompletion { 7131 Lint {
6225 label: "clippy::unused_unit", 7132 label: "clippy::unused_unit",
6226 description: r##"Checks for unit (`()`) expressions that can be removed."##, 7133 description: r##"Checks for unit (`()`) expressions that can be removed."##,
6227 }, 7134 },
6228 LintCompletion { 7135 Lint {
6229 label: "clippy::unusual_byte_groupings", 7136 label: "clippy::unusual_byte_groupings",
6230 description: r##"Warns if hexadecimal or binary literals are not grouped\nby nibble or byte."##, 7137 description: r##"Warns if hexadecimal or binary literals are not grouped
7138by nibble or byte."##,
6231 }, 7139 },
6232 LintCompletion { 7140 Lint {
6233 label: "clippy::unwrap_in_result", 7141 label: "clippy::unwrap_in_result",
6234 description: r##"Checks for functions of type Result that contain `expect()` or `unwrap()`"##, 7142 description: r##"Checks for functions of type Result that contain `expect()` or `unwrap()`"##,
6235 }, 7143 },
6236 LintCompletion { 7144 Lint {
6237 label: "clippy::unwrap_used", 7145 label: "clippy::unwrap_used",
6238 description: r##"Checks for `.unwrap()` calls on `Option`s and on `Result`s."##, 7146 description: r##"Checks for `.unwrap()` calls on `Option`s and on `Result`s."##,
6239 }, 7147 },
6240 LintCompletion { 7148 Lint {
6241 label: "clippy::upper_case_acronyms", 7149 label: "clippy::upper_case_acronyms",
6242 description: r##"Checks for fully capitalized names and optionally names containing a capitalized acronym."##, 7150 description: r##"Checks for fully capitalized names and optionally names containing a capitalized acronym."##,
6243 }, 7151 },
6244 LintCompletion { 7152 Lint {
6245 label: "clippy::use_debug", 7153 label: "clippy::use_debug",
6246 description: r##"Checks for use of `Debug` formatting. The purpose of this\nlint is to catch debugging remnants."##, 7154 description: r##"Checks for use of `Debug` formatting. The purpose of this
7155lint is to catch debugging remnants."##,
6247 }, 7156 },
6248 LintCompletion { 7157 Lint {
6249 label: "clippy::use_self", 7158 label: "clippy::use_self",
6250 description: r##"Checks for unnecessary repetition of structure name when a\nreplacement with `Self` is applicable."##, 7159 description: r##"Checks for unnecessary repetition of structure name when a
7160replacement with `Self` is applicable."##,
6251 }, 7161 },
6252 LintCompletion { 7162 Lint {
6253 label: "clippy::used_underscore_binding", 7163 label: "clippy::used_underscore_binding",
6254 description: r##"Checks for the use of bindings with a single leading\nunderscore."##, 7164 description: r##"Checks for the use of bindings with a single leading
7165underscore."##,
6255 }, 7166 },
6256 LintCompletion { 7167 Lint {
6257 label: "clippy::useless_asref", 7168 label: "clippy::useless_asref",
6258 description: r##"Checks for usage of `.as_ref()` or `.as_mut()` where the\ntypes before and after the call are the same."##, 7169 description: r##"Checks for usage of `.as_ref()` or `.as_mut()` where the
7170types before and after the call are the same."##,
6259 }, 7171 },
6260 LintCompletion { 7172 Lint {
6261 label: "clippy::useless_attribute", 7173 label: "clippy::useless_attribute",
6262 description: r##"Checks for `extern crate` and `use` items annotated with\nlint attributes.\n\nThis lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\n`#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\n`#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\n`extern crate` items with a `#[macro_use]` attribute."##, 7174 description: r##"Checks for `extern crate` and `use` items annotated with
7175lint attributes.
7176
7177This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
7178`#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
7179`#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
7180`extern crate` items with a `#[macro_use]` attribute."##,
6263 }, 7181 },
6264 LintCompletion { 7182 Lint {
6265 label: "clippy::useless_conversion", 7183 label: "clippy::useless_conversion",
6266 description: r##"Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls\nwhich uselessly convert to the same type."##, 7184 description: r##"Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls
7185which uselessly convert to the same type."##,
6267 }, 7186 },
6268 LintCompletion { 7187 Lint {
6269 label: "clippy::useless_format", 7188 label: "clippy::useless_format",
6270 description: r##"Checks for the use of `format!(\"string literal with no\nargument\")` and `format!(\"{}\", foo)` where `foo` is a string."##, 7189 description: r##"Checks for the use of `format!(string literal with no
7190argument)` and `format!({}, foo)` where `foo` is a string."##,
6271 }, 7191 },
6272 LintCompletion { 7192 Lint {
6273 label: "clippy::useless_let_if_seq", 7193 label: "clippy::useless_let_if_seq",
6274 description: r##"Checks for variable declarations immediately followed by a\nconditional affectation."##, 7194 description: r##"Checks for variable declarations immediately followed by a
7195conditional affectation."##,
6275 }, 7196 },
6276 LintCompletion { 7197 Lint {
6277 label: "clippy::useless_transmute", 7198 label: "clippy::useless_transmute",
6278 description: r##"Checks for transmutes to the original type of the object\nand transmutes that could be a cast."##, 7199 description: r##"Checks for transmutes to the original type of the object
7200and transmutes that could be a cast."##,
6279 }, 7201 },
6280 LintCompletion { 7202 Lint {
6281 label: "clippy::useless_vec", 7203 label: "clippy::useless_vec",
6282 description: r##"Checks for usage of `&vec![..]` when using `&[..]` would\nbe possible."##, 7204 description: r##"Checks for usage of `&vec![..]` when using `&[..]` would
7205be possible."##,
6283 }, 7206 },
6284 LintCompletion { 7207 Lint {
6285 label: "clippy::vec_box", 7208 label: "clippy::vec_box",
6286 description: r##"Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, 7209 description: r##"Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
7210Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##,
6287 }, 7211 },
6288 LintCompletion { 7212 Lint {
6289 label: "clippy::vec_init_then_push", 7213 label: "clippy::vec_init_then_push",
6290 description: r##"Checks for calls to `push` immediately after creating a new `Vec`."##, 7214 description: r##"Checks for calls to `push` immediately after creating a new `Vec`."##,
6291 }, 7215 },
6292 LintCompletion { 7216 Lint {
6293 label: "clippy::vec_resize_to_zero", 7217 label: "clippy::vec_resize_to_zero",
6294 description: r##"Finds occurrences of `Vec::resize(0, an_int)`"##, 7218 description: r##"Finds occurrences of `Vec::resize(0, an_int)`"##,
6295 }, 7219 },
6296 LintCompletion { 7220 Lint {
6297 label: "clippy::verbose_bit_mask", 7221 label: "clippy::verbose_bit_mask",
6298 description: r##"Checks for bit masks that can be replaced by a call\nto `trailing_zeros`"##, 7222 description: r##"Checks for bit masks that can be replaced by a call
7223to `trailing_zeros`"##,
6299 }, 7224 },
6300 LintCompletion { 7225 Lint {
6301 label: "clippy::verbose_file_reads", 7226 label: "clippy::verbose_file_reads",
6302 description: r##"Checks for use of File::read_to_end and File::read_to_string."##, 7227 description: r##"Checks for use of File::read_to_end and File::read_to_string."##,
6303 }, 7228 },
6304 LintCompletion { 7229 Lint {
6305 label: "clippy::vtable_address_comparisons", 7230 label: "clippy::vtable_address_comparisons",
6306 description: r##"Checks for comparisons with an address of a trait vtable."##, 7231 description: r##"Checks for comparisons with an address of a trait vtable."##,
6307 }, 7232 },
6308 LintCompletion { 7233 Lint {
6309 label: "clippy::while_immutable_condition", 7234 label: "clippy::while_immutable_condition",
6310 description: r##"Checks whether variables used within while loop condition\ncan be (and are) mutated in the body."##, 7235 description: r##"Checks whether variables used within while loop condition
7236can be (and are) mutated in the body."##,
6311 }, 7237 },
6312 LintCompletion { 7238 Lint {
6313 label: "clippy::while_let_loop", 7239 label: "clippy::while_let_loop",
6314 description: r##"Detects `loop + match` combinations that are easier\nwritten as a `while let` loop."##, 7240 description: r##"Detects `loop + match` combinations that are easier
7241written as a `while let` loop."##,
6315 }, 7242 },
6316 LintCompletion { 7243 Lint {
6317 label: "clippy::while_let_on_iterator", 7244 label: "clippy::while_let_on_iterator",
6318 description: r##"Checks for `while let` expressions on iterators."##, 7245 description: r##"Checks for `while let` expressions on iterators."##,
6319 }, 7246 },
6320 LintCompletion { 7247 Lint {
6321 label: "clippy::wildcard_dependencies", 7248 label: "clippy::wildcard_dependencies",
6322 description: r##"Checks for wildcard dependencies in the `Cargo.toml`."##, 7249 description: r##"Checks for wildcard dependencies in the `Cargo.toml`."##,
6323 }, 7250 },
6324 LintCompletion { 7251 Lint {
6325 label: "clippy::wildcard_enum_match_arm", 7252 label: "clippy::wildcard_enum_match_arm",
6326 description: r##"Checks for wildcard enum matches using `_`."##, 7253 description: r##"Checks for wildcard enum matches using `_`."##,
6327 }, 7254 },
6328 LintCompletion { 7255 Lint {
6329 label: "clippy::wildcard_imports", 7256 label: "clippy::wildcard_imports",
6330 description: r##"Checks for wildcard imports `use _::*`."##, 7257 description: r##"Checks for wildcard imports `use _::*`."##,
6331 }, 7258 },
6332 LintCompletion { 7259 Lint {
6333 label: "clippy::wildcard_in_or_patterns", 7260 label: "clippy::wildcard_in_or_patterns",
6334 description: r##"Checks for wildcard pattern used with others patterns in same match arm."##, 7261 description: r##"Checks for wildcard pattern used with others patterns in same match arm."##,
6335 }, 7262 },
6336 LintCompletion { 7263 Lint {
6337 label: "clippy::write_literal", 7264 label: "clippy::write_literal",
6338 description: r##"This lint warns about the use of literals as `write!`/`writeln!` args."##, 7265 description: r##"This lint warns about the use of literals as `write!`/`writeln!` args."##,
6339 }, 7266 },
6340 LintCompletion { 7267 Lint {
6341 label: "clippy::write_with_newline", 7268 label: "clippy::write_with_newline",
6342 description: r##"This lint warns when you use `write!()` with a format\nstring that\nends in a newline."##, 7269 description: r##"This lint warns when you use `write!()` with a format
7270string that
7271ends in a newline."##,
6343 }, 7272 },
6344 LintCompletion { 7273 Lint {
6345 label: "clippy::writeln_empty_string", 7274 label: "clippy::writeln_empty_string",
6346 description: r##"This lint warns when you use `writeln!(buf, \"\")` to\nprint a newline."##, 7275 description: r##"This lint warns when you use `writeln!(buf, )` to
7276print a newline."##,
6347 }, 7277 },
6348 LintCompletion { 7278 Lint {
6349 label: "clippy::wrong_pub_self_convention", 7279 label: "clippy::wrong_pub_self_convention",
6350 description: r##"This is the same as\n[`wrong_self_convention`](#wrong_self_convention), but for public items."##, 7280 description: r##"Nothing. This lint has been deprecated."##,
6351 }, 7281 },
6352 LintCompletion { 7282 Lint {
6353 label: "clippy::wrong_self_convention", 7283 label: "clippy::wrong_self_convention",
6354 description: r##"Checks for methods with certain name prefixes and which\ndoesn't match how self is taken. The actual rules are:\n\n|Prefix |`self` taken |\n|-------|----------------------|\n|`as_` |`&self` or `&mut self`|\n|`from_`| none |\n|`into_`|`self` |\n|`is_` |`&self` or none |\n|`to_` |`&self` |"##, 7284 description: r##"Checks for methods with certain name prefixes and which
6355 }, 7285doesn't match how self is taken. The actual rules are:
6356 LintCompletion { 7286
7287|Prefix |Postfix |`self` taken | `self` type |
7288|-------|------------|-----------------------|--------------|
7289|`as_` | none |`&self` or `&mut self` | any |
7290|`from_`| none | none | any |
7291|`into_`| none |`self` | any |
7292|`is_` | none |`&self` or none | any |
7293|`to_` | `_mut` |`&mut self` | any |
7294|`to_` | not `_mut` |`self` | `Copy` |
7295|`to_` | not `_mut` |`&self` | not `Copy` |
7296
7297Note: Clippy doesn't trigger methods with `to_` prefix in:
7298- Traits definition.
7299Clippy can not tell if a type that implements a trait is `Copy` or not.
7300- Traits implementation, when `&self` is taken.
7301The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
7302(see e.g. the `std::string::ToString` trait).
7303
7304Please find more info here:
7305https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv"##,
7306 },
7307 Lint {
6357 label: "clippy::wrong_transmute", 7308 label: "clippy::wrong_transmute",
6358 description: r##"Checks for transmutes that can't ever be correct on any\narchitecture."##, 7309 description: r##"Checks for transmutes that can't ever be correct on any
6359 }, 7310architecture."##,
6360 LintCompletion {
6361 label: "clippy::zero_divided_by_zero",
6362 description: r##"Checks for `0.0 / 0.0`."##,
6363 }, 7311 },
6364 LintCompletion { 7312 Lint { label: "clippy::zero_divided_by_zero", description: r##"Checks for `0.0 / 0.0`."## },
7313 Lint {
6365 label: "clippy::zero_prefixed_literal", 7314 label: "clippy::zero_prefixed_literal",
6366 description: r##"Warns if an integral constant literal starts with `0`."##, 7315 description: r##"Warns if an integral constant literal starts with `0`."##,
6367 }, 7316 },
6368 LintCompletion { 7317 Lint {
6369 label: "clippy::zero_ptr", 7318 label: "clippy::zero_ptr",
6370 description: r##"Catch casts from `0` to some pointer type"##, 7319 description: r##"Catch casts from `0` to some pointer type"##,
6371 }, 7320 },
6372 LintCompletion { 7321 Lint {
6373 label: "clippy::zero_sized_map_values", 7322 label: "clippy::zero_sized_map_values",
6374 description: r##"Checks for maps with zero-sized value types anywhere in the code."##, 7323 description: r##"Checks for maps with zero-sized value types anywhere in the code."##,
6375 }, 7324 },
6376 LintCompletion { 7325 Lint {
6377 label: "clippy::zst_offset", 7326 label: "clippy::zst_offset",
6378 description: r##"Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to\nzero-sized types"##, 7327 description: r##"Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
7328zero-sized types"##,
6379 }, 7329 },
6380]; 7330];
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index ae52dd8bb..9634d872e 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -323,7 +323,7 @@ fn import_for_item(
323 } 323 }
324 324
325 let segment_import = 325 let segment_import =
326 find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?; 326 find_import_for_segment(db, original_item_candidate, unresolved_first_segment)?;
327 let trait_item_to_import = item_as_assoc(db, original_item) 327 let trait_item_to_import = item_as_assoc(db, original_item)
328 .and_then(|assoc| assoc.containing_trait(db)) 328 .and_then(|assoc| assoc.containing_trait(db))
329 .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); 329 .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
@@ -383,7 +383,7 @@ fn find_import_for_segment(
383 original_item 383 original_item
384 } else { 384 } else {
385 let matching_module = 385 let matching_module =
386 module_with_segment_name(db, &unresolved_first_segment, original_item)?; 386 module_with_segment_name(db, unresolved_first_segment, original_item)?;
387 ItemInNs::from(ModuleDef::from(matching_module)) 387 ItemInNs::from(ModuleDef::from(matching_module))
388 }) 388 })
389} 389}
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index aa61c5bcb..10bbafe77 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -120,15 +120,19 @@ impl ImportScope {
120 if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) 120 if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone())
121 { 121 {
122 if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) { 122 if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) {
123 if let Some(_) = common_prefix(&prev_path, &curr_path) { 123 if let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) {
124 if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { 124 if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() {
125 // Same prefix but no use tree lists so this has to be of item style. 125 let prefix_c = prev_prefix.qualifiers().count();
126 break ImportGranularityGuess::Item; // this overwrites CrateOrModule, technically the file doesn't adhere to anything here. 126 let curr_c = curr_path.qualifiers().count() - prefix_c;
127 } else { 127 let prev_c = prev_path.qualifiers().count() - prefix_c;
128 // Same prefix with item tree lists, has to be module style as it 128 if curr_c <= 1 || prev_c <= 1 {
129 // can't be crate style since the trees wouldn't share a prefix then. 129 // Same prefix but no use tree lists so this has to be of item style.
130 break ImportGranularityGuess::Module; 130 break ImportGranularityGuess::Item; // this overwrites CrateOrModule, technically the file doesn't adhere to anything here.
131 }
131 } 132 }
133 // Same prefix with item tree lists, has to be module style as it
134 // can't be crate style since the trees wouldn't share a prefix then.
135 break ImportGranularityGuess::Module;
132 } 136 }
133 } 137 }
134 } 138 }
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index 78a2a87b3..70b11bf81 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -663,6 +663,13 @@ use foo::bar::qux;
663", 663",
664 ImportGranularityGuess::Item, 664 ImportGranularityGuess::Item,
665 ); 665 );
666 check_guess(
667 r"
668use foo::bar::Bar;
669use foo::baz;
670",
671 ImportGranularityGuess::Item,
672 );
666} 673}
667 674
668#[test] 675#[test]
@@ -682,6 +689,14 @@ use foo::{baz::{qux, quux}, bar};
682", 689",
683 ImportGranularityGuess::Module, 690 ImportGranularityGuess::Module,
684 ); 691 );
692 check_guess(
693 r"
694use foo::bar::Bar;
695use foo::baz::Baz;
696use foo::{Foo, Qux};
697",
698 ImportGranularityGuess::Module,
699 );
685} 700}
686 701
687#[test] 702#[test]
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index 0dbabb44f..ec29476a4 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -124,7 +124,7 @@ fn recursive_merge(
124 .map(|tree_list| tree_list.use_trees().any(tree_is_self)) 124 .map(|tree_list| tree_list.use_trees().any(tree_is_self))
125 .unwrap_or(false) 125 .unwrap_or(false)
126 }; 126 };
127 match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) { 127 match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) {
128 (true, false) => continue, 128 (true, false) => continue,
129 (false, true) => { 129 (false, true) => {
130 *lhs_t = rhs_t; 130 *lhs_t = rhs_t;
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 1f900aef4..105607dca 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -93,6 +93,7 @@ impl RootDatabase {
93 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 93 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
94 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 94 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
95 db.set_library_roots_with_durability(Default::default(), Durability::HIGH); 95 db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
96 db.set_enable_proc_attr_macros(Default::default());
96 db.update_lru_capacity(lru_capacity); 97 db.update_lru_capacity(lru_capacity);
97 db 98 db
98 } 99 }
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 67840602b..a840e06a6 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -8,7 +8,8 @@ use std::{convert::TryInto, mem};
8 8
9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; 9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
10use hir::{ 10use hir::{
11 DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, Visibility, 11 AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics,
12 Visibility,
12}; 13};
13use once_cell::unsync::Lazy; 14use once_cell::unsync::Lazy;
14use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
@@ -303,13 +304,13 @@ impl Definition {
303 } 304 }
304 } 305 }
305 306
306 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { 307 pub fn usages<'a>(self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> {
307 FindUsages { def: self, sema, scope: None, include_self_kw_refs: None } 308 FindUsages { def: self, sema, scope: None, include_self_kw_refs: None }
308 } 309 }
309} 310}
310 311
311pub struct FindUsages<'a> { 312pub struct FindUsages<'a> {
312 def: &'a Definition, 313 def: Definition,
313 sema: &'a Semantics<'a, RootDatabase>, 314 sema: &'a Semantics<'a, RootDatabase>,
314 scope: Option<SearchScope>, 315 scope: Option<SearchScope>,
315 include_self_kw_refs: Option<hir::Type>, 316 include_self_kw_refs: Option<hir::Type>,
@@ -318,7 +319,7 @@ pub struct FindUsages<'a> {
318impl<'a> FindUsages<'a> { 319impl<'a> FindUsages<'a> {
319 /// Enable searching for `Self` when the definition is a type. 320 /// Enable searching for `Self` when the definition is a type.
320 pub fn include_self_refs(mut self) -> FindUsages<'a> { 321 pub fn include_self_refs(mut self) -> FindUsages<'a> {
321 self.include_self_kw_refs = def_to_ty(self.sema, self.def); 322 self.include_self_kw_refs = def_to_ty(self.sema, &self.def);
322 self 323 self
323 } 324 }
324 325
@@ -408,7 +409,7 @@ impl<'a> FindUsages<'a> {
408 if let Some(ast::NameLike::NameRef(name_ref)) = 409 if let Some(ast::NameLike::NameRef(name_ref)) =
409 sema.find_node_at_offset_with_descend(&tree, offset) 410 sema.find_node_at_offset_with_descend(&tree, offset)
410 { 411 {
411 if self.found_self_ty_name_ref(&self_ty, &name_ref, sink) { 412 if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
412 return; 413 return;
413 } 414 }
414 } 415 }
@@ -423,7 +424,7 @@ impl<'a> FindUsages<'a> {
423 name_ref: &ast::NameRef, 424 name_ref: &ast::NameRef,
424 sink: &mut dyn FnMut(FileId, FileReference) -> bool, 425 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
425 ) -> bool { 426 ) -> bool {
426 match NameRefClass::classify(self.sema, &name_ref) { 427 match NameRefClass::classify(self.sema, name_ref) {
427 Some(NameRefClass::Definition(Definition::SelfType(impl_))) 428 Some(NameRefClass::Definition(Definition::SelfType(impl_)))
428 if impl_.self_ty(self.sema.db) == *self_ty => 429 if impl_.self_ty(self.sema.db) == *self_ty =>
429 { 430 {
@@ -445,7 +446,7 @@ impl<'a> FindUsages<'a> {
445 sink: &mut dyn FnMut(FileId, FileReference) -> bool, 446 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
446 ) -> bool { 447 ) -> bool {
447 match NameRefClass::classify_lifetime(self.sema, lifetime) { 448 match NameRefClass::classify_lifetime(self.sema, lifetime) {
448 Some(NameRefClass::Definition(def)) if &def == self.def => { 449 Some(NameRefClass::Definition(def)) if def == self.def => {
449 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); 450 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
450 let reference = FileReference { 451 let reference = FileReference {
451 range, 452 range,
@@ -463,13 +464,13 @@ impl<'a> FindUsages<'a> {
463 name_ref: &ast::NameRef, 464 name_ref: &ast::NameRef,
464 sink: &mut dyn FnMut(FileId, FileReference) -> bool, 465 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
465 ) -> bool { 466 ) -> bool {
466 match NameRefClass::classify(self.sema, &name_ref) { 467 match NameRefClass::classify(self.sema, name_ref) {
467 Some(NameRefClass::Definition(def)) if &def == self.def => { 468 Some(NameRefClass::Definition(def)) if def == self.def => {
468 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 469 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
469 let reference = FileReference { 470 let reference = FileReference {
470 range, 471 range,
471 name: ast::NameLike::NameRef(name_ref.clone()), 472 name: ast::NameLike::NameRef(name_ref.clone()),
472 access: reference_access(&def, &name_ref), 473 access: reference_access(&def, name_ref),
473 }; 474 };
474 sink(file_id, reference) 475 sink(file_id, reference)
475 } 476 }
@@ -479,7 +480,7 @@ impl<'a> FindUsages<'a> {
479 let reference = FileReference { 480 let reference = FileReference {
480 range, 481 range,
481 name: ast::NameLike::NameRef(name_ref.clone()), 482 name: ast::NameLike::NameRef(name_ref.clone()),
482 access: reference_access(&def, &name_ref), 483 access: reference_access(&def, name_ref),
483 }; 484 };
484 sink(file_id, reference) 485 sink(file_id, reference)
485 } else { 486 } else {
@@ -489,11 +490,9 @@ impl<'a> FindUsages<'a> {
489 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 490 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
490 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 491 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
491 let access = match self.def { 492 let access = match self.def {
492 Definition::Field(_) if &field == self.def => { 493 Definition::Field(_) if field == self.def => reference_access(&field, name_ref),
493 reference_access(&field, &name_ref) 494 Definition::Local(l) if local == l => {
494 } 495 reference_access(&Definition::Local(local), name_ref)
495 Definition::Local(l) if &local == l => {
496 reference_access(&Definition::Local(local), &name_ref)
497 } 496 }
498 _ => return false, 497 _ => return false,
499 }; 498 };
@@ -513,7 +512,7 @@ impl<'a> FindUsages<'a> {
513 match NameClass::classify(self.sema, name) { 512 match NameClass::classify(self.sema, name) {
514 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) 513 Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
515 if matches!( 514 if matches!(
516 self.def, Definition::Field(_) if &field_ref == self.def 515 self.def, Definition::Field(_) if field_ref == self.def
517 ) => 516 ) =>
518 { 517 {
519 let FileRange { file_id, range } = self.sema.original_range(name.syntax()); 518 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
@@ -525,12 +524,38 @@ impl<'a> FindUsages<'a> {
525 }; 524 };
526 sink(file_id, reference) 525 sink(file_id, reference)
527 } 526 }
528 Some(NameClass::ConstReference(def)) if *self.def == def => { 527 Some(NameClass::ConstReference(def)) if self.def == def => {
529 let FileRange { file_id, range } = self.sema.original_range(name.syntax()); 528 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
530 let reference = 529 let reference =
531 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; 530 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None };
532 sink(file_id, reference) 531 sink(file_id, reference)
533 } 532 }
533 // Resolve trait impl function definitions to the trait definition's version if self.def is the trait definition's
534 Some(NameClass::Definition(Definition::ModuleDef(mod_def))) => {
535 /* poor man's try block */
536 (|| {
537 let this = match self.def {
538 Definition::ModuleDef(this) if this != mod_def => this,
539 _ => return None,
540 };
541 let this_trait = this
542 .as_assoc_item(self.sema.db)?
543 .containing_trait_or_trait_impl(self.sema.db)?;
544 let trait_ = mod_def
545 .as_assoc_item(self.sema.db)?
546 .containing_trait_or_trait_impl(self.sema.db)?;
547 (trait_ == this_trait).then(|| {
548 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
549 let reference = FileReference {
550 range,
551 name: ast::NameLike::Name(name.clone()),
552 access: None,
553 };
554 sink(file_id, reference)
555 })
556 })()
557 .unwrap_or(false)
558 }
534 _ => false, 559 _ => false,
535 } 560 }
536 } 561 }
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 5c372a7e5..000f87a85 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -197,6 +197,7 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
197} 197}
198 198
199pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> { 199pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
200 let _p = profile::span("crate_symbols").detail(|| format!("{:?}", query));
200 // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from 201 // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from
201 // that instead? 202 // that instead?
202 203
@@ -321,6 +322,7 @@ impl SymbolIndex {
321 322
322impl Query { 323impl Query {
323 pub(crate) fn search(self, indices: &[&SymbolIndex]) -> Vec<FileSymbol> { 324 pub(crate) fn search(self, indices: &[&SymbolIndex]) -> Vec<FileSymbol> {
325 let _p = profile::span("symbol_index::Query::search");
324 let mut op = fst::map::OpBuilder::new(); 326 let mut op = fst::map::OpBuilder::new();
325 for file_symbols in indices.iter() { 327 for file_symbols in indices.iter() {
326 let automaton = fst::automaton::Subsequence::new(&self.lowercased); 328 let automaton = fst::automaton::Subsequence::new(&self.lowercased);
diff --git a/crates/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml
index 5d2221ebc..727d17bac 100644
--- a/crates/ide_ssr/Cargo.toml
+++ b/crates/ide_ssr/Cargo.toml
@@ -11,7 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = "2.0.0-pre.1"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16itertools = "0.10.0" 16itertools = "0.10.0"
17 17
diff --git a/crates/ide_ssr/src/matching.rs b/crates/ide_ssr/src/matching.rs
index b3072fb9f..fb92a0ccc 100644
--- a/crates/ide_ssr/src/matching.rs
+++ b/crates/ide_ssr/src/matching.rs
@@ -382,7 +382,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
382 code: Option<T>, 382 code: Option<T>,
383 ) -> Result<(), MatchFailed> { 383 ) -> Result<(), MatchFailed> {
384 match (pattern, code) { 384 match (pattern, code) {
385 (Some(p), Some(c)) => self.attempt_match_node(phase, &p.syntax(), &c.syntax()), 385 (Some(p), Some(c)) => self.attempt_match_node(phase, p.syntax(), c.syntax()),
386 (None, None) => Ok(()), 386 (None, None) => Ok(()),
387 (Some(p), None) => fail_match!("Pattern `{}` had nothing to match", p.syntax().text()), 387 (Some(p), None) => fail_match!("Pattern `{}` had nothing to match", p.syntax().text()),
388 (None, Some(c)) => { 388 (None, Some(c)) => {
@@ -478,7 +478,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
478 if Some(first_token.text()) == next_pattern_token.as_deref() { 478 if Some(first_token.text()) == next_pattern_token.as_deref() {
479 if let Some(SyntaxElement::Node(p)) = pattern.next() { 479 if let Some(SyntaxElement::Node(p)) = pattern.next() {
480 // We have a subtree that starts with the next token in our pattern. 480 // We have a subtree that starts with the next token in our pattern.
481 self.attempt_match_token_tree(phase, &p, &n)?; 481 self.attempt_match_token_tree(phase, &p, n)?;
482 break; 482 break;
483 } 483 }
484 } 484 }
@@ -609,7 +609,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
609 expr: &ast::Expr, 609 expr: &ast::Expr,
610 ) -> Result<usize, MatchFailed> { 610 ) -> Result<usize, MatchFailed> {
611 use hir::HirDisplay; 611 use hir::HirDisplay;
612 let code_type = self.sema.type_of_expr(&expr).ok_or_else(|| { 612 let code_type = self.sema.type_of_expr(expr).ok_or_else(|| {
613 match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) 613 match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
614 })?; 614 })?;
615 // Temporary needed to make the borrow checker happy. 615 // Temporary needed to make the borrow checker happy.
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs
index c9ccc1961..9265af7c1 100644
--- a/crates/ide_ssr/src/replacing.rs
+++ b/crates/ide_ssr/src/replacing.rs
@@ -84,16 +84,16 @@ impl ReplacementRenderer<'_> {
84 fn render_node_or_token(&mut self, node_or_token: &SyntaxElement) { 84 fn render_node_or_token(&mut self, node_or_token: &SyntaxElement) {
85 match node_or_token { 85 match node_or_token {
86 SyntaxElement::Token(token) => { 86 SyntaxElement::Token(token) => {
87 self.render_token(&token); 87 self.render_token(token);
88 } 88 }
89 SyntaxElement::Node(child_node) => { 89 SyntaxElement::Node(child_node) => {
90 self.render_node(&child_node); 90 self.render_node(child_node);
91 } 91 }
92 } 92 }
93 } 93 }
94 94
95 fn render_node(&mut self, node: &SyntaxNode) { 95 fn render_node(&mut self, node: &SyntaxNode) {
96 if let Some(mod_path) = self.match_info.rendered_template_paths.get(&node) { 96 if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) {
97 self.out.push_str(&mod_path.to_string()); 97 self.out.push_str(&mod_path.to_string());
98 // Emit everything except for the segment's name-ref, since we already effectively 98 // Emit everything except for the segment's name-ref, since we already effectively
99 // emitted that as part of `mod_path`. 99 // emitted that as part of `mod_path`.
@@ -107,12 +107,12 @@ impl ReplacementRenderer<'_> {
107 } 107 }
108 } 108 }
109 } else { 109 } else {
110 self.render_node_children(&node); 110 self.render_node_children(node);
111 } 111 }
112 } 112 }
113 113
114 fn render_token(&mut self, token: &SyntaxToken) { 114 fn render_token(&mut self, token: &SyntaxToken) {
115 if let Some(placeholder) = self.rule.get_placeholder(&token) { 115 if let Some(placeholder) = self.rule.get_placeholder(token) {
116 if let Some(placeholder_value) = 116 if let Some(placeholder_value) =
117 self.match_info.placeholder_values.get(&placeholder.ident) 117 self.match_info.placeholder_values.get(&placeholder.ident)
118 { 118 {
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 541da4122..a66a7a4a8 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -211,7 +211,7 @@ impl<'db> ResolutionScope<'db> {
211 // First try resolving the whole path. This will work for things like 211 // First try resolving the whole path. This will work for things like
212 // `std::collections::HashMap`, but will fail for things like 212 // `std::collections::HashMap`, but will fail for things like
213 // `std::collections::HashMap::new`. 213 // `std::collections::HashMap::new`.
214 if let Some(resolution) = self.scope.speculative_resolve(&path) { 214 if let Some(resolution) = self.scope.speculative_resolve(path) {
215 return Some(resolution); 215 return Some(resolution);
216 } 216 }
217 // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if 217 // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if
diff --git a/crates/ide_ssr/src/search.rs b/crates/ide_ssr/src/search.rs
index 28cef742c..f2056919e 100644
--- a/crates/ide_ssr/src/search.rs
+++ b/crates/ide_ssr/src/search.rs
@@ -173,7 +173,7 @@ impl<'db> MatchFinder<'db> {
173 if !is_search_permitted(code) { 173 if !is_search_permitted(code) {
174 return; 174 return;
175 } 175 }
176 self.try_add_match(rule, &code, restrict_range, matches_out); 176 self.try_add_match(rule, code, restrict_range, matches_out);
177 // If we've got a macro call, we already tried matching it pre-expansion, which is the only 177 // If we've got a macro call, we already tried matching it pre-expansion, which is the only
178 // way to match the whole macro, now try expanding it and matching the expansion. 178 // way to match the whole macro, now try expanding it and matching the expansion.
179 if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { 179 if let Some(macro_call) = ast::MacroCall::cast(code.clone()) {
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs
index 1d8565dc0..444c6b0af 100644
--- a/crates/ide_ssr/src/tests.rs
+++ b/crates/ide_ssr/src/tests.rs
@@ -75,7 +75,7 @@ pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Ve
75 match range_or_offset { 75 match range_or_offset {
76 RangeOrOffset::Range(range) => { 76 RangeOrOffset::Range(range) => {
77 position = FilePosition { file_id, offset: range.start() }; 77 position = FilePosition { file_id, offset: range.start() };
78 selections = vec![FileRange { file_id, range: range }]; 78 selections = vec![FileRange { file_id, range }];
79 } 79 }
80 RangeOrOffset::Offset(offset) => { 80 RangeOrOffset::Offset(offset) => {
81 position = FilePosition { file_id, offset }; 81 position = FilePosition { file_id, offset };
@@ -129,7 +129,7 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
129 let matched_strings: Vec<String> = 129 let matched_strings: Vec<String> =
130 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); 130 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect();
131 if matched_strings != expected && !expected.is_empty() { 131 if matched_strings != expected && !expected.is_empty() {
132 print_match_debug_info(&match_finder, position.file_id, &expected[0]); 132 print_match_debug_info(&match_finder, position.file_id, expected[0]);
133 } 133 }
134 assert_eq!(matched_strings, expected); 134 assert_eq!(matched_strings, expected);
135} 135}
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index 8856787c0..f3092d9aa 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = "2.0.0-pre.1"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15smallvec = "1.2.0" 15smallvec = "1.2.0"
16log = "0.4.8" 16log = "0.4.8"
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index 38707ffa5..18eb97f0d 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -187,7 +187,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt
187 let a = 1664525; 187 let a = 1664525;
188 let c = 1013904223; 188 let c = 1013904223;
189 *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c); 189 *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
190 return *seed; 190 *seed
191 } 191 }
192 fn make_ident(ident: &str) -> tt::TokenTree { 192 fn make_ident(ident: &str) -> tt::TokenTree {
193 tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) }) 193 tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) })
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 84ca3ff87..c2a9a38c9 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -1,6 +1,6 @@
1//! An NFA-based parser, which is porting from rustc mbe parsing code 1//! An NFA-based parser, which is porting from rustc mbe parsing code
2//! 2//!
3//! See https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs 3//! See <https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs>
4//! Here is a quick intro to how the parser works, copied from rustc: 4//! Here is a quick intro to how the parser works, copied from rustc:
5//! 5//!
6//! A 'position' is a dot in the middle of a matcher, usually represented as a 6//! A 'position' is a dot in the middle of a matcher, usually represented as a
@@ -121,7 +121,7 @@ impl Match {
121 121
122/// Matching errors are added to the `Match`. 122/// Matching errors are added to the `Match`.
123pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { 123pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
124 let mut res = match_loop(pattern, &input); 124 let mut res = match_loop(pattern, input);
125 res.bound_count = count(res.bindings.bindings()); 125 res.bound_count = count(res.bindings.bindings());
126 return res; 126 return res;
127 127
@@ -202,7 +202,7 @@ impl BindingsBuilder {
202 } 202 }
203 203
204 fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) { 204 fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) {
205 let BindingsIdx(idx, nidx) = self.copy(&child); 205 let BindingsIdx(idx, nidx) = self.copy(child);
206 self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx)))); 206 self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx))));
207 } 207 }
208 208
@@ -219,9 +219,9 @@ impl BindingsBuilder {
219 bindings 219 bindings
220 } 220 }
221 221
222 fn build_inner(&self, bindings: &mut Bindings, link_nodes: &Vec<LinkNode<Rc<BindingKind>>>) { 222 fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[LinkNode<Rc<BindingKind>>]) {
223 let mut nodes = Vec::new(); 223 let mut nodes = Vec::new();
224 self.collect_nodes(&link_nodes, &mut nodes); 224 self.collect_nodes(link_nodes, &mut nodes);
225 225
226 for cmd in nodes { 226 for cmd in nodes {
227 match &**cmd { 227 match &**cmd {
@@ -282,7 +282,7 @@ impl BindingsBuilder {
282 282
283 nested_refs.into_iter().for_each(|iter| { 283 nested_refs.into_iter().for_each(|iter| {
284 let mut child_bindings = Bindings::default(); 284 let mut child_bindings = Bindings::default();
285 self.build_inner(&mut child_bindings, &iter); 285 self.build_inner(&mut child_bindings, iter);
286 nested.push(child_bindings) 286 nested.push(child_bindings)
287 }) 287 })
288 } 288 }
@@ -301,7 +301,7 @@ impl BindingsBuilder {
301 301
302 fn collect_nodes<'a>( 302 fn collect_nodes<'a>(
303 &'a self, 303 &'a self,
304 link_nodes: &'a Vec<LinkNode<Rc<BindingKind>>>, 304 link_nodes: &'a [LinkNode<Rc<BindingKind>>],
305 nodes: &mut Vec<&'a Rc<BindingKind>>, 305 nodes: &mut Vec<&'a Rc<BindingKind>>,
306 ) { 306 ) {
307 link_nodes.iter().for_each(|it| match it { 307 link_nodes.iter().for_each(|it| match it {
@@ -417,7 +417,7 @@ fn match_loop_inner<'t>(
417 let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count); 417 let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count);
418 if item.sep.is_some() && sep_idx != sep_len { 418 if item.sep.is_some() && sep_idx != sep_len {
419 let sep = item.sep.as_ref().unwrap(); 419 let sep = item.sep.as_ref().unwrap();
420 if src.clone().expect_separator(&sep, sep_idx) { 420 if src.clone().expect_separator(sep, sep_idx) {
421 item.dot.next(); 421 item.dot.next();
422 item.sep_parsed = Some(sep_idx + 1); 422 item.sep_parsed = Some(sep_idx + 1);
423 try_push!(next_items, item); 423 try_push!(next_items, item);
@@ -487,22 +487,15 @@ fn match_loop_inner<'t>(
487 item.meta_result = Some((fork, match_res)); 487 item.meta_result = Some((fork, match_res));
488 try_push!(bb_items, item); 488 try_push!(bb_items, item);
489 } else { 489 } else {
490 bindings_builder.push_optional(&mut item.bindings, &name); 490 bindings_builder.push_optional(&mut item.bindings, name);
491 item.dot.next(); 491 item.dot.next();
492 cur_items.push(item); 492 cur_items.push(item);
493 } 493 }
494 } 494 }
495 Some(err) => { 495 Some(err) => {
496 res.add_err(err); 496 res.add_err(err);
497 match match_res.value { 497 if let Some(fragment) = match_res.value {
498 Some(fragment) => { 498 bindings_builder.push_fragment(&mut item.bindings, name, fragment);
499 bindings_builder.push_fragment(
500 &mut item.bindings,
501 &name,
502 fragment,
503 );
504 }
505 _ => {}
506 } 499 }
507 item.is_error = true; 500 item.is_error = true;
508 error_items.push(item); 501 error_items.push(item);
@@ -511,7 +504,7 @@ fn match_loop_inner<'t>(
511 } 504 }
512 } 505 }
513 OpDelimited::Op(Op::Leaf(leaf)) => { 506 OpDelimited::Op(Op::Leaf(leaf)) => {
514 if let Err(err) = match_leaf(&leaf, &mut src.clone()) { 507 if let Err(err) = match_leaf(leaf, &mut src.clone()) {
515 res.add_err(err); 508 res.add_err(err);
516 item.is_error = true; 509 item.is_error = true;
517 } else { 510 } else {
@@ -578,9 +571,9 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
578 ); 571 );
579 stdx::always!(cur_items.is_empty()); 572 stdx::always!(cur_items.is_empty());
580 573
581 if error_items.len() > 0 { 574 if !error_items.is_empty() {
582 error_recover_item = error_items.pop().map(|it| it.bindings); 575 error_recover_item = error_items.pop().map(|it| it.bindings);
583 } else if eof_items.len() > 0 { 576 } else if !eof_items.is_empty() {
584 error_recover_item = Some(eof_items[0].bindings.clone()); 577 error_recover_item = Some(eof_items[0].bindings.clone());
585 } 578 }
586 579
@@ -647,10 +640,10 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
647 let (iter, match_res) = item.meta_result.take().unwrap(); 640 let (iter, match_res) = item.meta_result.take().unwrap();
648 match match_res.value { 641 match match_res.value {
649 Some(fragment) => { 642 Some(fragment) => {
650 bindings_builder.push_fragment(&mut item.bindings, &name, fragment); 643 bindings_builder.push_fragment(&mut item.bindings, name, fragment);
651 } 644 }
652 None if match_res.err.is_none() => { 645 None if match_res.err.is_none() => {
653 bindings_builder.push_optional(&mut item.bindings, &name); 646 bindings_builder.push_optional(&mut item.bindings, name);
654 } 647 }
655 _ => {} 648 _ => {}
656 } 649 }
@@ -793,7 +786,7 @@ impl<'a> TtIter<'a> {
793 _ => (), 786 _ => (),
794 } 787 }
795 788
796 let tt = self.next().ok_or_else(|| ())?.clone(); 789 let tt = self.next().ok_or(())?.clone();
797 let punct = match tt { 790 let punct = match tt {
798 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.spacing == tt::Spacing::Joint => { 791 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.spacing == tt::Spacing::Joint => {
799 punct 792 punct
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index dd7fa97d7..49a137577 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -55,7 +55,7 @@ pub(super) fn transcribe(
55 template: &MetaTemplate, 55 template: &MetaTemplate,
56 bindings: &Bindings, 56 bindings: &Bindings,
57) -> ExpandResult<tt::Subtree> { 57) -> ExpandResult<tt::Subtree> {
58 let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; 58 let mut ctx = ExpandCtx { bindings, nesting: Vec::new() };
59 let mut arena: Vec<tt::TokenTree> = Vec::new(); 59 let mut arena: Vec<tt::TokenTree> = Vec::new();
60 expand_subtree(&mut ctx, template, None, &mut arena) 60 expand_subtree(&mut ctx, template, None, &mut arena)
61} 61}
@@ -91,12 +91,12 @@ fn expand_subtree(
91 Op::Leaf(tt) => arena.push(tt.clone().into()), 91 Op::Leaf(tt) => arena.push(tt.clone().into()),
92 Op::Subtree { tokens, delimiter } => { 92 Op::Subtree { tokens, delimiter } => {
93 let ExpandResult { value: tt, err: e } = 93 let ExpandResult { value: tt, err: e } =
94 expand_subtree(ctx, &tokens, *delimiter, arena); 94 expand_subtree(ctx, tokens, *delimiter, arena);
95 err = err.or(e); 95 err = err.or(e);
96 arena.push(tt.into()); 96 arena.push(tt.into());
97 } 97 }
98 Op::Var { name, id, .. } => { 98 Op::Var { name, id, .. } => {
99 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id); 99 let ExpandResult { value: fragment, err: e } = expand_var(ctx, name, *id);
100 err = err.or(e); 100 err = err.or(e);
101 push_fragment(arena, fragment); 101 push_fragment(arena, fragment);
102 } 102 }
@@ -141,7 +141,7 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult
141 .into(); 141 .into();
142 ExpandResult::ok(Fragment::Tokens(tt)) 142 ExpandResult::ok(Fragment::Tokens(tt))
143 } else { 143 } else {
144 ctx.bindings.get(&v, &mut ctx.nesting).map_or_else( 144 ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
145 |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) }, 145 |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) },
146 |b| ExpandResult::ok(b.clone()), 146 |b| ExpandResult::ok(b.clone()),
147 ) 147 )
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index b95374b76..8c8528aaf 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -280,8 +280,8 @@ impl Rule {
280 .expect_subtree() 280 .expect_subtree()
281 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; 281 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
282 282
283 let lhs = MetaTemplate(parse_pattern(&lhs)?); 283 let lhs = MetaTemplate(parse_pattern(lhs)?);
284 let rhs = MetaTemplate(parse_template(&rhs)?); 284 let rhs = MetaTemplate(parse_template(rhs)?);
285 285
286 Ok(crate::Rule { lhs, rhs }) 286 Ok(crate::Rule { lhs, rhs })
287 } 287 }
@@ -290,13 +290,13 @@ impl Rule {
290fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { 290fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
291 for op in pattern.iter() { 291 for op in pattern.iter() {
292 match op { 292 match op {
293 Op::Subtree { tokens, .. } => validate(&tokens)?, 293 Op::Subtree { tokens, .. } => validate(tokens)?,
294 Op::Repeat { tokens: subtree, separator, .. } => { 294 Op::Repeat { tokens: subtree, separator, .. } => {
295 // Checks that no repetition which could match an empty token 295 // Checks that no repetition which could match an empty token
296 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 296 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
297 297
298 if separator.is_none() { 298 if separator.is_none()
299 if subtree.iter().all(|child_op| { 299 && subtree.iter().all(|child_op| {
300 match child_op { 300 match child_op {
301 Op::Var { kind, .. } => { 301 Op::Var { kind, .. } => {
302 // vis is optional 302 // vis is optional
@@ -314,9 +314,9 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
314 Op::Subtree { .. } => {} 314 Op::Subtree { .. } => {}
315 } 315 }
316 false 316 false
317 }) { 317 })
318 return Err(ParseError::RepetitionEmptyTokenTree); 318 {
319 } 319 return Err(ParseError::RepetitionEmptyTokenTree);
320 } 320 }
321 validate(subtree)? 321 validate(subtree)?
322 } 322 }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 61b2a4955..deed884d2 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -42,7 +42,7 @@ impl<'a> OpDelimitedIter<'a> {
42 } 42 }
43 43
44 pub(crate) fn reset(&self) -> Self { 44 pub(crate) fn reset(&self) -> Self {
45 Self { inner: &self.inner, idx: 0, delimited: self.delimited } 45 Self { inner: self.inner, idx: 0, delimited: self.delimited }
46 } 46 }
47} 47}
48 48
@@ -126,11 +126,11 @@ impl Separator {
126} 126}
127 127
128pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> { 128pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
129 parse_inner(&template, Mode::Template).into_iter().collect() 129 parse_inner(template, Mode::Template).into_iter().collect()
130} 130}
131 131
132pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> { 132pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
133 parse_inner(&pattern, Mode::Pattern).into_iter().collect() 133 parse_inner(pattern, Mode::Pattern).into_iter().collect()
134} 134}
135 135
136#[derive(Clone, Copy)] 136#[derive(Clone, Copy)]
@@ -140,7 +140,7 @@ enum Mode {
140} 140}
141 141
142fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> { 142fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> {
143 let mut src = TtIter::new(&tt); 143 let mut src = TtIter::new(tt);
144 std::iter::from_fn(move || { 144 std::iter::from_fn(move || {
145 let first = src.next()?; 145 let first = src.next()?;
146 Some(next_op(first, &mut src, mode)) 146 Some(next_op(first, &mut src, mode))
@@ -171,7 +171,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
171 match second { 171 match second {
172 tt::TokenTree::Subtree(subtree) => { 172 tt::TokenTree::Subtree(subtree) => {
173 let (separator, kind) = parse_repeat(src)?; 173 let (separator, kind) = parse_repeat(src)?;
174 let tokens = parse_inner(&subtree, mode) 174 let tokens = parse_inner(subtree, mode)
175 .into_iter() 175 .into_iter()
176 .collect::<Result<Vec<Op>, ParseError>>()?; 176 .collect::<Result<Vec<Op>, ParseError>>()?;
177 Op::Repeat { tokens: MetaTemplate(tokens), separator, kind } 177 Op::Repeat { tokens: MetaTemplate(tokens), separator, kind }
@@ -191,7 +191,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
191 Op::Var { name, kind, id } 191 Op::Var { name, kind, id }
192 } 192 }
193 tt::Leaf::Literal(lit) => { 193 tt::Leaf::Literal(lit) => {
194 if is_boolean_literal(&lit) { 194 if is_boolean_literal(lit) {
195 let name = lit.text.clone(); 195 let name = lit.text.clone();
196 let kind = eat_fragment_kind(src, mode)?; 196 let kind = eat_fragment_kind(src, mode)?;
197 let id = lit.id; 197 let id = lit.id;
@@ -206,14 +206,14 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
206 tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), 206 tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()),
207 tt::TokenTree::Subtree(subtree) => { 207 tt::TokenTree::Subtree(subtree) => {
208 let tokens = 208 let tokens =
209 parse_inner(&subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?; 209 parse_inner(subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?;
210 Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter } 210 Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter }
211 } 211 }
212 }; 212 };
213 Ok(res) 213 Ok(res)
214} 214}
215 215
216fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ParseError> { 216fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<SmolStr>, ParseError> {
217 if let Mode::Pattern = mode { 217 if let Mode::Pattern = mode {
218 src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; 218 src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?;
219 let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; 219 let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?;
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
index bde370fdb..ee80807ad 100644
--- a/crates/mbe/src/subtree_source.rs
+++ b/crates/mbe/src/subtree_source.rs
@@ -22,7 +22,7 @@ impl<'a> SubtreeTokenSource {
22 #[cfg(test)] 22 #[cfg(test)]
23 pub(crate) fn text(&self) -> SmolStr { 23 pub(crate) fn text(&self) -> SmolStr {
24 match self.cached.get(self.curr.1) { 24 match self.cached.get(self.curr.1) {
25 Some(ref tt) => tt.text.clone(), 25 Some(tt) => tt.text.clone(),
26 _ => SmolStr::new(""), 26 _ => SmolStr::new(""),
27 } 27 }
28 } 28 }
@@ -59,7 +59,7 @@ impl<'a> SubtreeTokenSource {
59 59
60 current = match tt { 60 current = match tt {
61 Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { 61 Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
62 cached.push(convert_leaf(&leaf)); 62 cached.push(convert_leaf(leaf));
63 cursor.bump() 63 cursor.bump()
64 } 64 }
65 Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { 65 Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
@@ -114,7 +114,7 @@ impl<'a> TokenSource for SubtreeTokenSource {
114 /// Is the current token a specified keyword? 114 /// Is the current token a specified keyword?
115 fn is_keyword(&self, kw: &str) -> bool { 115 fn is_keyword(&self, kw: &str) -> bool {
116 match self.cached.get(self.curr.1) { 116 match self.cached.get(self.curr.1) {
117 Some(ref t) => t.text == *kw, 117 Some(t) => t.text == *kw,
118 _ => false, 118 _ => false,
119 } 119 }
120 } 120 }
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index b11172caf..cdc22425d 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -243,8 +243,7 @@ trait TokenConvertor {
243 type Token: SrcToken; 243 type Token: SrcToken;
244 244
245 fn go(&mut self) -> tt::Subtree { 245 fn go(&mut self) -> tt::Subtree {
246 let mut subtree = tt::Subtree::default(); 246 let mut subtree = tt::Subtree { delimiter: None, ..Default::default() };
247 subtree.delimiter = None;
248 while self.peek().is_some() { 247 while self.peek().is_some() {
249 self.collect_leaf(&mut subtree.token_trees); 248 self.collect_leaf(&mut subtree.token_trees);
250 } 249 }
@@ -506,7 +505,7 @@ impl TokenConvertor for Convertor {
506 505
507 fn peek(&self) -> Option<Self::Token> { 506 fn peek(&self) -> Option<Self::Token> {
508 if let Some((punct, mut offset)) = self.punct_offset.clone() { 507 if let Some((punct, mut offset)) = self.punct_offset.clone() {
509 offset = offset + TextSize::of('.'); 508 offset += TextSize::of('.');
510 if usize::from(offset) < punct.text().len() { 509 if usize::from(offset) < punct.text().len() {
511 return Some(SynToken::Punch(punct, offset)); 510 return Some(SynToken::Punch(punct, offset));
512 } 511 }
@@ -634,7 +633,7 @@ impl<'a> TreeSink for TtTreeSink<'a> {
634 } 633 }
635 } 634 }
636 }; 635 };
637 self.buf += &text; 636 self.buf += text;
638 self.text_pos += TextSize::of(text); 637 self.text_pos += TextSize::of(text);
639 } 638 }
640 639
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs
index 5f173f513..c788e427e 100644
--- a/crates/mbe/src/tests/expand.rs
+++ b/crates/mbe/src/tests/expand.rs
@@ -490,7 +490,7 @@ [email protected]
490 490
491fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { 491fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree {
492 if let tt::TokenTree::Subtree(subtree) = tt { 492 if let tt::TokenTree::Subtree(subtree) = tt {
493 return &subtree; 493 return subtree;
494 } 494 }
495 unreachable!("It is not a subtree"); 495 unreachable!("It is not a subtree");
496} 496}
@@ -1846,16 +1846,17 @@ fn test_no_space_after_semi_colon() {
1846 [email protected] 1846 [email protected]
1847 [email protected] "#" 1847 [email protected] "#"
1848 [email protected] "[" 1848 [email protected] "["
1849 [email protected] 1849 [email protected]
1850 [email protected] 1850 [email protected]
1851 [email protected] 1851 [email protected]
1852 [email protected] "cfg" 1852 [email protected]
1853 [email protected] 1853 [email protected] "cfg"
1854 [email protected] "(" 1854 [email protected]
1855 [email protected] "feature" 1855 [email protected] "("
1856 [email protected] "=" 1856 [email protected] "feature"
1857 [email protected] "\"std\"" 1857 [email protected] "="
1858 [email protected] ")" 1858 [email protected] "\"std\""
1859 [email protected] ")"
1859 [email protected] "]" 1860 [email protected] "]"
1860 [email protected] "mod" 1861 [email protected] "mod"
1861 [email protected] 1862 [email protected]
@@ -1865,16 +1866,17 @@ fn test_no_space_after_semi_colon() {
1865 [email protected] 1866 [email protected]
1866 [email protected] "#" 1867 [email protected] "#"
1867 [email protected] "[" 1868 [email protected] "["
1868 [email protected] 1869 [email protected]
1869 [email protected] 1870 [email protected]
1870 [email protected] 1871 [email protected]
1871 [email protected] "cfg" 1872 [email protected]
1872 [email protected] 1873 [email protected] "cfg"
1873 [email protected] "(" 1874 [email protected]
1874 [email protected] "feature" 1875 [email protected] "("
1875 [email protected] "=" 1876 [email protected] "feature"
1876 [email protected] "\"std\"" 1877 [email protected] "="
1877 [email protected] ")" 1878 [email protected] "\"std\""
1879 [email protected] ")"
1878 [email protected] "]" 1880 [email protected] "]"
1879 [email protected] "mod" 1881 [email protected] "mod"
1880 [email protected] 1882 [email protected]
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index 99a8d250b..5a4eca7bf 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -115,7 +115,7 @@ impl<'a> TtIter<'a> {
115 } 115 }
116 } 116 }
117 117
118 let buffer = TokenBuffer::from_tokens(&self.inner.as_slice()); 118 let buffer = TokenBuffer::from_tokens(self.inner.as_slice());
119 let mut src = SubtreeTokenSource::new(&buffer); 119 let mut src = SubtreeTokenSource::new(&buffer);
120 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; 120 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
121 121
@@ -138,7 +138,7 @@ impl<'a> TtIter<'a> {
138 } 138 }
139 } 139 }
140 self.inner = self.inner.as_slice()[res.len()..].iter(); 140 self.inner = self.inner.as_slice()[res.len()..].iter();
141 if res.len() == 0 && err.is_none() { 141 if res.is_empty() && err.is_none() {
142 err = Some(err!("no tokens consumed")); 142 err = Some(err!("no tokens consumed"));
143 } 143 }
144 let res = match res.len() { 144 let res = match res.len() {
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index b8242cd2f..a44c5e484 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -13,6 +13,7 @@ pub(super) fn outer_attrs(p: &mut Parser) {
13} 13}
14 14
15pub(super) fn meta(p: &mut Parser) { 15pub(super) fn meta(p: &mut Parser) {
16 let meta = p.start();
16 paths::use_path(p); 17 paths::use_path(p);
17 18
18 match p.current() { 19 match p.current() {
@@ -25,6 +26,8 @@ pub(super) fn meta(p: &mut Parser) {
25 T!['('] | T!['['] | T!['{'] => items::token_tree(p), 26 T!['('] | T!['['] | T!['{'] => items::token_tree(p),
26 _ => {} 27 _ => {}
27 } 28 }
29
30 meta.complete(p, META);
28} 31}
29 32
30fn attr(p: &mut Parser, inner: bool) { 33fn attr(p: &mut Parser, inner: bool) {
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 9d22e1950..686a64345 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -208,7 +208,7 @@ struct Restrictions {
208 208
209/// Binding powers of operators for a Pratt parser. 209/// Binding powers of operators for a Pratt parser.
210/// 210///
211/// See https://www.oilshell.org/blog/2016/11/03.html 211/// See <https://www.oilshell.org/blog/2016/11/03.html>
212#[rustfmt::skip] 212#[rustfmt::skip]
213fn current_op(p: &Parser) -> (u8, SyntaxKind) { 213fn current_op(p: &Parser) -> (u8, SyntaxKind) {
214 const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]); 214 const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]);
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 269f223e6..abdfca1fe 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -252,12 +252,10 @@ fn closure_expr(p: &mut Parser) -> CompletedMarker {
252 // test lambda_ret_block 252 // test lambda_ret_block
253 // fn main() { || -> i32 { 92 }(); } 253 // fn main() { || -> i32 { 92 }(); }
254 block_expr(p); 254 block_expr(p);
255 } else if p.at_ts(EXPR_FIRST) {
256 expr(p);
255 } else { 257 } else {
256 if p.at_ts(EXPR_FIRST) { 258 p.error("expected expression");
257 expr(p);
258 } else {
259 p.error("expected expression");
260 }
261 } 259 }
262 m.complete(p, CLOSURE_EXPR) 260 m.complete(p, CLOSURE_EXPR)
263} 261}
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index bcefd183a..5f10b82de 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -220,7 +220,7 @@ pub enum SyntaxKind {
220 ITEM_LIST, 220 ITEM_LIST,
221 ASSOC_ITEM_LIST, 221 ASSOC_ITEM_LIST,
222 ATTR, 222 ATTR,
223 META_ITEM, 223 META,
224 USE_TREE, 224 USE_TREE,
225 USE_TREE_LIST, 225 USE_TREE_LIST,
226 PATH, 226 PATH,
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index f09ad37e3..48dac14c4 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -244,7 +244,7 @@ impl RelPath {
244 } 244 }
245} 245}
246 246
247/// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 247/// Taken from <https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85>
248fn normalize_path(path: &Path) -> PathBuf { 248fn normalize_path(path: &Path) -> PathBuf {
249 let mut components = path.components().peekable(); 249 let mut components = path.components().peekable();
250 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { 250 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index f525df152..899895578 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -59,7 +59,7 @@ pub trait Message: Serialize + DeserializeOwned {
59 Ok(match read_json(inp, buf)? { 59 Ok(match read_json(inp, buf)? {
60 None => None, 60 None => None,
61 Some(text) => { 61 Some(text) => {
62 let mut deserializer = serde_json::Deserializer::from_str(&text); 62 let mut deserializer = serde_json::Deserializer::from_str(text);
63 // Note that some proc-macro generate very deep syntax tree 63 // Note that some proc-macro generate very deep syntax tree
64 // We have to disable the current limit of serde here 64 // We have to disable the current limit of serde here
65 deserializer.disable_recursion_limit(); 65 deserializer.disable_recursion_limit();
@@ -92,7 +92,7 @@ fn read_json<'a>(
92 92
93 // Some ill behaved macro try to use stdout for debugging 93 // Some ill behaved macro try to use stdout for debugging
94 // We ignore it here 94 // We ignore it here
95 if !buf.starts_with("{") { 95 if !buf.starts_with('{') {
96 log::error!("proc-macro tried to print : {}", buf); 96 log::error!("proc-macro tried to print : {}", buf);
97 continue; 97 continue;
98 } 98 }
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index 99d05aef3..38eac6c17 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -76,9 +76,7 @@ impl ProcMacroProcessSrv {
76 .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?; 76 .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?;
77 77
78 match res { 78 match res {
79 Some(Response::Error(err)) => { 79 Some(Response::Error(err)) => Err(tt::ExpansionError::ExpansionError(err.message)),
80 return Err(tt::ExpansionError::ExpansionError(err.message));
81 }
82 Some(res) => Ok(res.try_into().map_err(|err| { 80 Some(res) => Ok(res.try_into().map_err(|err| {
83 tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err)) 81 tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err))
84 })?), 82 })?),
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
index 6dbac50b4..28a4ac086 100644
--- a/crates/proc_macro_api/src/version.rs
+++ b/crates/proc_macro_api/src/version.rs
@@ -95,7 +95,7 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
95/// * [version string bytes encoded in utf8] <- GET THIS BOI 95/// * [version string bytes encoded in utf8] <- GET THIS BOI
96/// * [some more bytes that we don really care but still there] :-) 96/// * [some more bytes that we don really care but still there] :-)
97/// Check this issue for more about the bytes layout: 97/// Check this issue for more about the bytes layout:
98/// https://github.com/rust-analyzer/rust-analyzer/issues/6174 98/// <https://github.com/rust-analyzer/rust-analyzer/issues/6174>
99fn read_version(dylib_path: &Path) -> io::Result<String> { 99fn read_version(dylib_path: &Path) -> io::Result<String> {
100 let dylib_file = File::open(dylib_path)?; 100 let dylib_file = File::open(dylib_path)?;
101 let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?; 101 let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?;
diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs
index cccc53220..5133e7c50 100644
--- a/crates/proc_macro_srv/src/dylib.rs
+++ b/crates/proc_macro_srv/src/dylib.rs
@@ -188,7 +188,9 @@ impl Expander {
188/// Copy the dylib to temp directory to prevent locking in Windows 188/// Copy the dylib to temp directory to prevent locking in Windows
189#[cfg(windows)] 189#[cfg(windows)]
190fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> { 190fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
191 use std::{ffi::OsString, time::SystemTime}; 191 use std::collections::hash_map::RandomState;
192 use std::ffi::OsString;
193 use std::hash::{BuildHasher, Hasher};
192 194
193 let mut to = std::env::temp_dir(); 195 let mut to = std::env::temp_dir();
194 196
@@ -199,10 +201,11 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
199 ) 201 )
200 })?; 202 })?;
201 203
202 // generate a time deps unique number 204 // Generate a unique number by abusing `HashMap`'s hasher.
203 let t = SystemTime::now().duration_since(std::time::UNIX_EPOCH).expect("Time went backwards"); 205 // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
206 let t = RandomState::new().build_hasher().finish();
204 207
205 let mut unique_name = OsString::from(t.as_millis().to_string()); 208 let mut unique_name = OsString::from(t.to_string());
206 unique_name.push(file_name); 209 unique_name.push(file_name);
207 210
208 to.push(unique_name); 211 to.push(unique_name);
diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs
index d4f04ee06..f54cbcd61 100644
--- a/crates/proc_macro_srv/src/lib.rs
+++ b/crates/proc_macro_srv/src/lib.rs
@@ -1,7 +1,7 @@
1//! RA Proc Macro Server 1//! RA Proc Macro Server
2//! 2//!
3//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code. 3//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code.
4//! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander. 4//! The general idea here is based on <https://github.com/fedochet/rust-proc-macro-expander>.
5//! 5//!
6//! But we adapt it to better fit RA needs: 6//! But we adapt it to better fit RA needs:
7//! 7//!
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs b/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs
index dae6ff1d1..3b2afe01f 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Buffer management for same-process client<->server communication. 1//! lib-proc-macro Buffer management for same-process client<->server communication.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/buffer.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/buffer.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use std::io::{self, Write}; 6use std::io::{self, Write};
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
index b036d4e20..c135cf7a2 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Client-side types. 1//! lib-proc-macro Client-side types.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use super::*; 6use super::*;
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs b/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs
index 273a97715..f5b6d897e 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. 1//! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/closure.rs# 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/closure.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6#[repr(C)] 6#[repr(C)]
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs b/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs
index a2f77b5ac..d2a65d249 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Server-side handles and storage for per-handle data. 1//! lib-proc-macro Server-side handles and storage for per-handle data.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/handle.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/handle.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use std::collections::{BTreeMap, HashMap}; 6use std::collections::{BTreeMap, HashMap};
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
index e67902682..375396d1b 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Internal interface for communicating between a `proc_macro` client 1//! lib-proc-macro Internal interface for communicating between a `proc_macro` client
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/mod.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/mod.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5//! 5//!
6//! Internal interface for communicating between a `proc_macro` client 6//! Internal interface for communicating between a `proc_macro` client
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
index bd1e7c2fc..69928ec84 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro Serialization for client-server communication. 1//! lib-proc-macro Serialization for client-server communication.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/rpc.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/rpc.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5//! 5//!
6//! Serialization for client-server communication. 6//! Serialization for client-server communication.
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs b/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs
index 6ef7ea43c..0436bc418 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro `Cell` variant for (scoped) existential lifetimes. 1//! lib-proc-macro `Cell` variant for (scoped) existential lifetimes.
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/scoped_cell.rs#L1 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/scoped_cell.rs#L1>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use std::cell::Cell; 6use std::cell::Cell;
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
index 88fbdc078..cc9afd84b 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro server-side traits 1//! lib-proc-macro server-side traits
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use super::*; 6use super::*;
diff --git a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs b/crates/proc_macro_srv/src/proc_macro/diagnostic.rs
index 55d93917c..3c5b7bc01 100644
--- a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs
+++ b/crates/proc_macro_srv/src/proc_macro/diagnostic.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro diagnostic 1//! lib-proc-macro diagnostic
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6use crate::proc_macro::Span; 6use crate::proc_macro::Span;
@@ -91,7 +91,7 @@ impl<'a> Iterator for Children<'a> {
91impl Diagnostic { 91impl Diagnostic {
92 /// Creates a new diagnostic with the given `level` and `message`. 92 /// Creates a new diagnostic with the given `level` and `message`.
93 pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic { 93 pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
94 Diagnostic { level: level, message: message.into(), spans: vec![], children: vec![] } 94 Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
95 } 95 }
96 96
97 /// Creates a new diagnostic with the given `level` and `message` pointing to 97 /// Creates a new diagnostic with the given `level` and `message` pointing to
@@ -101,12 +101,7 @@ impl Diagnostic {
101 S: MultiSpan, 101 S: MultiSpan,
102 T: Into<String>, 102 T: Into<String>,
103 { 103 {
104 Diagnostic { 104 Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] }
105 level: level,
106 message: message.into(),
107 spans: spans.into_spans(),
108 children: vec![],
109 }
110 } 105 }
111 106
112 diagnostic_child_methods!(span_error, error, Level::Error); 107 diagnostic_child_methods!(span_error, error, Level::Error);
diff --git a/crates/proc_macro_srv/src/proc_macro/mod.rs b/crates/proc_macro_srv/src/proc_macro/mod.rs
index fc6e7344f..b7c1d04b5 100644
--- a/crates/proc_macro_srv/src/proc_macro/mod.rs
+++ b/crates/proc_macro_srv/src/proc_macro/mod.rs
@@ -1,6 +1,6 @@
1//! lib-proc-macro main module 1//! lib-proc-macro main module
2//! 2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/lib.rs 3//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/lib.rs>
4//! augmented with removing unstable features 4//! augmented with removing unstable features
5 5
6// NOTE(@edwin0cheng): 6// NOTE(@edwin0cheng):
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs
index 5d765f6e2..65ca3eb6c 100644
--- a/crates/proc_macro_srv/src/rustc_server.rs
+++ b/crates/proc_macro_srv/src/rustc_server.rs
@@ -1,6 +1,6 @@
1//! Rustc proc-macro server implementation with tt 1//! Rustc proc-macro server implementation with tt
2//! 2//!
3//! Based on idea from https://github.com/fedochet/rust-proc-macro-expander 3//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that 4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
5//! we could provide any TokenStream implementation. 5//! we could provide any TokenStream implementation.
6//! The original idea from fedochet is using proc-macro2 as backend, 6//! The original idea from fedochet is using proc-macro2 as backend,
@@ -539,7 +539,7 @@ impl server::Literal for Rustc {
539 } else { 539 } else {
540 n.parse::<u128>().unwrap().to_string() 540 n.parse::<u128>().unwrap().to_string()
541 }; 541 };
542 return Literal { text: n.into(), id: tt::TokenId::unspecified() }; 542 Literal { text: n.into(), id: tt::TokenId::unspecified() }
543 } 543 }
544 544
545 fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { 545 fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
diff --git a/crates/proc_macro_srv/src/tests/utils.rs b/crates/proc_macro_srv/src/tests/utils.rs
index 2e950c113..2c093aa0a 100644
--- a/crates/proc_macro_srv/src/tests/utils.rs
+++ b/crates/proc_macro_srv/src/tests/utils.rs
@@ -7,35 +7,8 @@ use proc_macro_api::ListMacrosTask;
7use std::str::FromStr; 7use std::str::FromStr;
8 8
9pub mod fixtures { 9pub mod fixtures {
10 use cargo_metadata::Message;
11 use std::path::PathBuf;
12 use std::process::Command;
13
14 // Use current project metadata to get the proc-macro dylib path
15 pub fn proc_macro_test_dylib_path() -> std::path::PathBuf { 10 pub fn proc_macro_test_dylib_path() -> std::path::PathBuf {
16 let name = "proc_macro_test"; 11 proc_macro_test::PROC_MACRO_TEST_LOCATION.into()
17 let version = "0.0.0";
18 let command = Command::new(toolchain::cargo())
19 .args(&["check", "--tests", "--message-format", "json"])
20 .output()
21 .unwrap()
22 .stdout;
23
24 for message in Message::parse_stream(command.as_slice()) {
25 match message.unwrap() {
26 Message::CompilerArtifact(artifact) => {
27 if artifact.target.kind.contains(&"proc-macro".to_string()) {
28 let repr = format!("{} {}", name, version);
29 if artifact.package_id.repr.starts_with(&repr) {
30 return PathBuf::from(&artifact.filenames[0]);
31 }
32 }
33 }
34 _ => (), // Unknown message
35 }
36 }
37
38 panic!("No proc-macro dylib for {} found!", name);
39 } 12 }
40} 13}
41 14
diff --git a/crates/proc_macro_test/Cargo.toml b/crates/proc_macro_test/Cargo.toml
index 753443be2..1a88e361e 100644
--- a/crates/proc_macro_test/Cargo.toml
+++ b/crates/proc_macro_test/Cargo.toml
@@ -8,4 +8,8 @@ publish = false
8 8
9[lib] 9[lib]
10doctest = false 10doctest = false
11proc-macro = true 11
12[build-dependencies]
13proc_macro_test_impl = { path = "imp", version = "0.0.0" }
14toolchain = { path = "../toolchain", version = "0.0.0" }
15cargo_metadata = "0.13"
diff --git a/crates/proc_macro_test/build.rs b/crates/proc_macro_test/build.rs
new file mode 100644
index 000000000..4653a93dd
--- /dev/null
+++ b/crates/proc_macro_test/build.rs
@@ -0,0 +1,48 @@
1//! This will build the proc macro in `imp`, and copy the resulting dylib artifact into the
2//! `OUT_DIR`.
3//!
4//! `proc_macro_test` itself contains only a path to that artifact.
5
6use std::{
7 env, fs,
8 path::{Path, PathBuf},
9 process::Command,
10};
11
12use cargo_metadata::Message;
13
14fn main() {
15 let out_dir = env::var_os("OUT_DIR").unwrap();
16 let out_dir = Path::new(&out_dir);
17
18 let name = "proc_macro_test_impl";
19 let version = "0.0.0";
20 let output = Command::new(toolchain::cargo())
21 .current_dir("imp")
22 .args(&["build", "-p", "proc_macro_test_impl", "--message-format", "json"])
23 .output()
24 .unwrap();
25 assert!(output.status.success());
26
27 let mut artifact_path = None;
28 for message in Message::parse_stream(output.stdout.as_slice()) {
29 match message.unwrap() {
30 Message::CompilerArtifact(artifact) => {
31 if artifact.target.kind.contains(&"proc-macro".to_string()) {
32 let repr = format!("{} {}", name, version);
33 if artifact.package_id.repr.starts_with(&repr) {
34 artifact_path = Some(PathBuf::from(&artifact.filenames[0]));
35 }
36 }
37 }
38 _ => (), // Unknown message
39 }
40 }
41
42 let src_path = artifact_path.expect("no dylib for proc_macro_test_impl found");
43 let dest_path = out_dir.join(src_path.file_name().unwrap());
44 fs::copy(src_path, &dest_path).unwrap();
45
46 let info_path = out_dir.join("proc_macro_test_location.txt");
47 fs::write(info_path, dest_path.to_str().unwrap()).unwrap();
48}
diff --git a/crates/proc_macro_test/imp/.gitignore b/crates/proc_macro_test/imp/.gitignore
new file mode 100644
index 000000000..2c96eb1b6
--- /dev/null
+++ b/crates/proc_macro_test/imp/.gitignore
@@ -0,0 +1,2 @@
1target/
2Cargo.lock
diff --git a/crates/proc_macro_test/imp/Cargo.toml b/crates/proc_macro_test/imp/Cargo.toml
new file mode 100644
index 000000000..1c2e75401
--- /dev/null
+++ b/crates/proc_macro_test/imp/Cargo.toml
@@ -0,0 +1,17 @@
1[package]
2name = "proc_macro_test_impl"
3version = "0.0.0"
4license = "MIT OR Apache-2.0"
5authors = ["rust-analyzer developers"]
6edition = "2018"
7publish = false
8
9[lib]
10doctest = false
11proc-macro = true
12
13[workspace]
14
15[dependencies]
16# this crate should not have any dependencies, since it uses its own workspace,
17# and its own `Cargo.lock`
diff --git a/crates/proc_macro_test/imp/src/lib.rs b/crates/proc_macro_test/imp/src/lib.rs
new file mode 100644
index 000000000..4b26d2472
--- /dev/null
+++ b/crates/proc_macro_test/imp/src/lib.rs
@@ -0,0 +1,48 @@
1//! Exports a few trivial procedural macros for testing.
2
3use proc_macro::TokenStream;
4
5#[proc_macro]
6pub fn fn_like_noop(args: TokenStream) -> TokenStream {
7 args
8}
9
10#[proc_macro]
11pub fn fn_like_panic(args: TokenStream) -> TokenStream {
12 panic!("fn_like_panic!({})", args);
13}
14
15#[proc_macro]
16pub fn fn_like_error(args: TokenStream) -> TokenStream {
17 format!("compile_error!(\"fn_like_error!({})\");", args).parse().unwrap()
18}
19
20#[proc_macro_attribute]
21pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream {
22 item
23}
24
25#[proc_macro_attribute]
26pub fn attr_panic(args: TokenStream, item: TokenStream) -> TokenStream {
27 panic!("#[attr_panic {}] {}", args, item);
28}
29
30#[proc_macro_attribute]
31pub fn attr_error(args: TokenStream, item: TokenStream) -> TokenStream {
32 format!("compile_error!(\"#[attr_error({})] {}\");", args, item).parse().unwrap()
33}
34
35#[proc_macro_derive(DeriveEmpty)]
36pub fn derive_empty(_item: TokenStream) -> TokenStream {
37 TokenStream::new()
38}
39
40#[proc_macro_derive(DerivePanic)]
41pub fn derive_panic(item: TokenStream) -> TokenStream {
42 panic!("#[derive(DerivePanic)] {}", item);
43}
44
45#[proc_macro_derive(DeriveError)]
46pub fn derive_error(item: TokenStream) -> TokenStream {
47 format!("compile_error!(\"#[derive(DeriveError)] {}\");", item).parse().unwrap()
48}
diff --git a/crates/proc_macro_test/src/lib.rs b/crates/proc_macro_test/src/lib.rs
index 4b26d2472..2edf23a63 100644
--- a/crates/proc_macro_test/src/lib.rs
+++ b/crates/proc_macro_test/src/lib.rs
@@ -1,48 +1,4 @@
1//! Exports a few trivial procedural macros for testing. 1//! Exports a few trivial procedural macros for testing.
2 2
3use proc_macro::TokenStream; 3pub static PROC_MACRO_TEST_LOCATION: &str =
4 4 include_str!(concat!(env!("OUT_DIR"), "/proc_macro_test_location.txt"));
5#[proc_macro]
6pub fn fn_like_noop(args: TokenStream) -> TokenStream {
7 args
8}
9
10#[proc_macro]
11pub fn fn_like_panic(args: TokenStream) -> TokenStream {
12 panic!("fn_like_panic!({})", args);
13}
14
15#[proc_macro]
16pub fn fn_like_error(args: TokenStream) -> TokenStream {
17 format!("compile_error!(\"fn_like_error!({})\");", args).parse().unwrap()
18}
19
20#[proc_macro_attribute]
21pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream {
22 item
23}
24
25#[proc_macro_attribute]
26pub fn attr_panic(args: TokenStream, item: TokenStream) -> TokenStream {
27 panic!("#[attr_panic {}] {}", args, item);
28}
29
30#[proc_macro_attribute]
31pub fn attr_error(args: TokenStream, item: TokenStream) -> TokenStream {
32 format!("compile_error!(\"#[attr_error({})] {}\");", args, item).parse().unwrap()
33}
34
35#[proc_macro_derive(DeriveEmpty)]
36pub fn derive_empty(_item: TokenStream) -> TokenStream {
37 TokenStream::new()
38}
39
40#[proc_macro_derive(DerivePanic)]
41pub fn derive_panic(item: TokenStream) -> TokenStream {
42 panic!("#[derive(DerivePanic)] {}", item);
43}
44
45#[proc_macro_derive(DeriveError)]
46pub fn derive_error(item: TokenStream) -> TokenStream {
47 format!("compile_error!(\"#[derive(DeriveError)] {}\");", item).parse().unwrap()
48}
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 1a8c8f862..653d3d983 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -20,6 +20,9 @@ jemalloc-ctl = { version = "0.4.1", package = "tikv-jemalloc-ctl", optional = tr
20[target.'cfg(target_os = "linux")'.dependencies] 20[target.'cfg(target_os = "linux")'.dependencies]
21perf-event = "0.4" 21perf-event = "0.4"
22 22
23[target.'cfg(windows)'.dependencies]
24winapi = { version = "0.3.8", features = ["psapi"] }
25
23[features] 26[features]
24cpu_profiler = [] 27cpu_profiler = []
25jemalloc = ["jemalloc-ctl"] 28jemalloc = ["jemalloc-ctl"]
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs
index 7cd01a7df..5ea5039db 100644
--- a/crates/profile/src/lib.rs
+++ b/crates/profile/src/lib.rs
@@ -50,10 +50,10 @@ impl Drop for Scope {
50/// A wrapper around google_cpu_profiler. 50/// A wrapper around google_cpu_profiler.
51/// 51///
52/// Usage: 52/// Usage:
53/// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro. 53/// 1. Install gpref_tools (<https://github.com/gperftools/gperftools>), probably packaged with your Linux distro.
54/// 2. Build with `cpu_profiler` feature. 54/// 2. Build with `cpu_profiler` feature.
55/// 3. Run the code, the *raw* output would be in the `./out.profile` file. 55/// 3. Run the code, the *raw* output would be in the `./out.profile` file.
56/// 4. Install pprof for visualization (https://github.com/google/pprof). 56/// 4. Install pprof for visualization (<https://github.com/google/pprof>).
57/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000` 57/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
58/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. 58/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
59/// 59///
@@ -75,7 +75,7 @@ impl Drop for Scope {
75/// 75///
76/// See this diff for how to profile completions: 76/// See this diff for how to profile completions:
77/// 77///
78/// https://github.com/rust-analyzer/rust-analyzer/pull/5306 78/// <https://github.com/rust-analyzer/rust-analyzer/pull/5306>
79#[derive(Debug)] 79#[derive(Debug)]
80pub struct CpuSpan { 80pub struct CpuSpan {
81 _private: (), 81 _private: (),
diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs
index 2917ded60..fbcb9e3c2 100644
--- a/crates/profile/src/memory_usage.rs
+++ b/crates/profile/src/memory_usage.rs
@@ -32,9 +32,23 @@ impl MemoryUsage {
32 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), 32 allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize),
33 } 33 }
34 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { 34 } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
35 // Note: This is incredibly slow. 35 memusage_linux()
36 let alloc = unsafe { libc::mallinfo() }.uordblks as isize; 36 } else if #[cfg(windows)] {
37 MemoryUsage { allocated: Bytes(alloc) } 37 // There doesn't seem to be an API for determining heap usage, so we try to
38 // approximate that by using the Commit Charge value.
39
40 use winapi::um::processthreadsapi::*;
41 use winapi::um::psapi::*;
42 use std::mem::{MaybeUninit, size_of};
43
44 let proc = unsafe { GetCurrentProcess() };
45 let mut mem_counters = MaybeUninit::uninit();
46 let cb = size_of::<PROCESS_MEMORY_COUNTERS>();
47 let ret = unsafe { GetProcessMemoryInfo(proc, mem_counters.as_mut_ptr(), cb as u32) };
48 assert!(ret != 0);
49
50 let usage = unsafe { mem_counters.assume_init().PagefileUsage };
51 MemoryUsage { allocated: Bytes(usage as isize) }
38 } else { 52 } else {
39 MemoryUsage { allocated: Bytes(0) } 53 MemoryUsage { allocated: Bytes(0) }
40 } 54 }
@@ -42,6 +56,37 @@ impl MemoryUsage {
42 } 56 }
43} 57}
44 58
59#[cfg(all(target_os = "linux", target_env = "gnu", not(feature = "jemalloc")))]
60fn memusage_linux() -> MemoryUsage {
61 // Linux/glibc has 2 APIs for allocator introspection that we can use: mallinfo and mallinfo2.
62 // mallinfo uses `int` fields and cannot handle memory usage exceeding 2 GB.
63 // mallinfo2 is very recent, so its presence needs to be detected at runtime.
64 // Both are abysmally slow.
65
66 use std::ffi::CStr;
67 use std::sync::atomic::{AtomicUsize, Ordering};
68
69 static MALLINFO2: AtomicUsize = AtomicUsize::new(1);
70
71 let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed);
72 if mallinfo2 == 1 {
73 let cstr = CStr::from_bytes_with_nul(b"mallinfo2\0").unwrap();
74 mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, cstr.as_ptr()) } as usize;
75 // NB: races don't matter here, since they'll always store the same value
76 MALLINFO2.store(mallinfo2, Ordering::Relaxed);
77 }
78
79 if mallinfo2 == 0 {
80 // mallinfo2 does not exist, use mallinfo.
81 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
82 MemoryUsage { allocated: Bytes(alloc) }
83 } else {
84 let mallinfo2: fn() -> libc::mallinfo2 = unsafe { std::mem::transmute(mallinfo2) };
85 let alloc = mallinfo2().uordblks as isize;
86 MemoryUsage { allocated: Bytes(alloc) }
87 }
88}
89
45#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] 90#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
46pub struct Bytes(isize); 91pub struct Bytes(isize);
47 92
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index 33a4f8168..53cb4bae7 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -184,7 +184,7 @@ impl WorkspaceBuildData {
184 184
185 // Copy-pasted from existing cargo_metadata. It seems like we 185 // Copy-pasted from existing cargo_metadata. It seems like we
186 // should be using sered_stacker here? 186 // should be using sered_stacker here?
187 let mut deserializer = serde_json::Deserializer::from_str(&line); 187 let mut deserializer = serde_json::Deserializer::from_str(line);
188 deserializer.disable_recursion_limit(); 188 deserializer.disable_recursion_limit();
189 let message = Message::deserialize(&mut deserializer) 189 let message = Message::deserialize(&mut deserializer)
190 .unwrap_or(Message::TextLine(line.to_string())); 190 .unwrap_or(Message::TextLine(line.to_string()));
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index b8ad08364..ac079f83e 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -278,7 +278,7 @@ impl CargoWorkspace {
278 id, edition, name, manifest_path, version, metadata, .. 278 id, edition, name, manifest_path, version, metadata, ..
279 } = meta_pkg; 279 } = meta_pkg;
280 let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default(); 280 let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
281 let is_member = ws_members.contains(&id); 281 let is_member = ws_members.contains(id);
282 let edition = edition 282 let edition = edition
283 .parse::<Edition>() 283 .parse::<Edition>()
284 .with_context(|| format!("Failed to parse edition {}", edition))?; 284 .with_context(|| format!("Failed to parse edition {}", edition))?;
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs
index 4e39d6dd3..a22f79c15 100644
--- a/crates/project_model/src/sysroot.rs
+++ b/crates/project_model/src/sysroot.rs
@@ -142,12 +142,12 @@ fn discover_sysroot_src_dir(
142 log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); 142 log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
143 } 143 }
144 144
145 get_rust_src(&sysroot_path) 145 get_rust_src(sysroot_path)
146 .or_else(|| { 146 .or_else(|| {
147 let mut rustup = Command::new(toolchain::rustup()); 147 let mut rustup = Command::new(toolchain::rustup());
148 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); 148 rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
149 utf8_stdout(rustup).ok()?; 149 utf8_stdout(rustup).ok()?;
150 get_rust_src(&sysroot_path) 150 get_rust_src(sysroot_path)
151 }) 151 })
152 .ok_or_else(|| { 152 .ok_or_else(|| {
153 format_err!( 153 format_err!(
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 84990075f..ef0f3c9e4 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -185,7 +185,7 @@ impl ProjectWorkspace {
185 185
186 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> { 186 pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
187 let sysroot = Sysroot::discover( 187 let sysroot = Sysroot::discover(
188 &detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?, 188 detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?,
189 )?; 189 )?;
190 let rustc_cfg = rustc_cfg::get(None, None); 190 let rustc_cfg = rustc_cfg::get(None, None);
191 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) 191 Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
@@ -324,7 +324,7 @@ impl ProjectWorkspace {
324 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { 324 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
325 match self { 325 match self {
326 ProjectWorkspace::Cargo { cargo, .. } => { 326 ProjectWorkspace::Cargo { cargo, .. } => {
327 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); 327 collector.add_config(cargo.workspace_root(), cargo.build_data_config().clone());
328 } 328 }
329 _ => {} 329 _ => {}
330 } 330 }
@@ -348,7 +348,7 @@ fn project_json_to_crate_graph(
348 .crates() 348 .crates()
349 .filter_map(|(crate_id, krate)| { 349 .filter_map(|(crate_id, krate)| {
350 let file_path = &krate.root_module; 350 let file_path = &krate.root_module;
351 let file_id = load(&file_path)?; 351 let file_id = load(file_path)?;
352 Some((crate_id, krate, file_id)) 352 Some((crate_id, krate, file_id))
353 }) 353 })
354 .map(|(crate_id, krate, file_id)| { 354 .map(|(crate_id, krate, file_id)| {
@@ -534,7 +534,7 @@ fn detached_files_to_crate_graph(
534 cfg_options.extend(rustc_cfg); 534 cfg_options.extend(rustc_cfg);
535 535
536 for detached_file in detached_files { 536 for detached_file in detached_files {
537 let file_id = match load(&detached_file) { 537 let file_id = match load(detached_file) {
538 Some(file_id) => file_id, 538 Some(file_id) => file_id,
539 None => { 539 None => {
540 log::error!("Failed to load detached file {:?}", detached_file); 540 log::error!("Failed to load detached file {:?}", detached_file);
@@ -602,7 +602,7 @@ fn handle_rustc_crates(
602 crate_graph, 602 crate_graph,
603 &rustc_workspace[pkg], 603 &rustc_workspace[pkg],
604 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), 604 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
605 &cfg_options, 605 cfg_options,
606 proc_macro_loader, 606 proc_macro_loader,
607 file_id, 607 file_id,
608 &rustc_workspace[tgt].name, 608 &rustc_workspace[tgt].name,
@@ -685,7 +685,7 @@ fn add_target_crate_root(
685 let proc_macro = build_data 685 let proc_macro = build_data
686 .as_ref() 686 .as_ref()
687 .and_then(|it| it.proc_macro_dylib_path.as_ref()) 687 .and_then(|it| it.proc_macro_dylib_path.as_ref())
688 .map(|it| proc_macro_loader(&it)) 688 .map(|it| proc_macro_loader(it))
689 .unwrap_or_default(); 689 .unwrap_or_default();
690 690
691 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); 691 let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs
index 63953098a..19173241b 100644
--- a/crates/rust-analyzer/src/bin/flags.rs
+++ b/crates/rust-analyzer/src/bin/flags.rs
@@ -67,10 +67,10 @@ xflags::xflags! {
67 /// Don't load sysroot crates (`std`, `core` & friends). 67 /// Don't load sysroot crates (`std`, `core` & friends).
68 optional --no-sysroot 68 optional --no-sysroot
69 69
70 /// Load OUT_DIR values by running `cargo check` before analysis. 70 /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis.
71 optional --load-output-dirs 71 optional --disable-build-scripts
72 /// Use proc-macro-srv for proc-macro expanding. 72 /// Don't use expand proc macros.
73 optional --with-proc-macro 73 optional --disable-proc-macros
74 /// Only resolve names, don't run type inference. 74 /// Only resolve names, don't run type inference.
75 optional --skip-inference 75 optional --skip-inference
76 } 76 }
@@ -79,10 +79,10 @@ xflags::xflags! {
79 /// Directory with Cargo.toml. 79 /// Directory with Cargo.toml.
80 required path: PathBuf 80 required path: PathBuf
81 { 81 {
82 /// Load OUT_DIR values by running `cargo check` before analysis. 82 /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis.
83 optional --load-output-dirs 83 optional --disable-build-scripts
84 /// Use proc-macro-srv for proc-macro expanding. 84 /// Don't use expand proc macros.
85 optional --with-proc-macro 85 optional --disable-proc-macros
86 } 86 }
87 87
88 cmd ssr 88 cmd ssr
@@ -158,8 +158,8 @@ pub struct AnalysisStats {
158 pub only: Option<String>, 158 pub only: Option<String>,
159 pub with_deps: bool, 159 pub with_deps: bool,
160 pub no_sysroot: bool, 160 pub no_sysroot: bool,
161 pub load_output_dirs: bool, 161 pub disable_build_scripts: bool,
162 pub with_proc_macro: bool, 162 pub disable_proc_macros: bool,
163 pub skip_inference: bool, 163 pub skip_inference: bool,
164} 164}
165 165
@@ -167,8 +167,8 @@ pub struct AnalysisStats {
167pub struct Diagnostics { 167pub struct Diagnostics {
168 pub path: PathBuf, 168 pub path: PathBuf,
169 169
170 pub load_output_dirs: bool, 170 pub disable_build_scripts: bool,
171 pub with_proc_macro: bool, 171 pub disable_proc_macros: bool,
172} 172}
173 173
174#[derive(Debug)] 174#[derive(Debug)]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 2b842d393..afc96505f 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -91,14 +91,14 @@ fn try_main() -> Result<()> {
91 with_deps: cmd.with_deps, 91 with_deps: cmd.with_deps,
92 no_sysroot: cmd.no_sysroot, 92 no_sysroot: cmd.no_sysroot,
93 path: cmd.path, 93 path: cmd.path,
94 load_output_dirs: cmd.load_output_dirs, 94 enable_build_scripts: !cmd.disable_build_scripts,
95 with_proc_macro: cmd.with_proc_macro, 95 enable_proc_macros: !cmd.disable_proc_macros,
96 skip_inference: cmd.skip_inference, 96 skip_inference: cmd.skip_inference,
97 } 97 }
98 .run(verbosity)?, 98 .run(verbosity)?,
99 99
100 flags::RustAnalyzerCmd::Diagnostics(cmd) => { 100 flags::RustAnalyzerCmd::Diagnostics(cmd) => {
101 cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)? 101 cli::diagnostics(&cmd.path, !cmd.disable_build_scripts, !cmd.disable_proc_macros)?
102 } 102 }
103 flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?, 103 flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?,
104 flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?, 104 flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?,
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index f4cd43448..5d8547152 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -123,7 +123,7 @@ impl CargoTargetSpec {
123 let res = CargoTargetSpec { 123 let res = CargoTargetSpec {
124 workspace_root: cargo_ws.workspace_root().to_path_buf(), 124 workspace_root: cargo_ws.workspace_root().to_path_buf(),
125 cargo_toml: package_data.manifest.clone(), 125 cargo_toml: package_data.manifest.clone(),
126 package: cargo_ws.package_flag(&package_data), 126 package: cargo_ws.package_flag(package_data),
127 target: target_data.name.clone(), 127 target: target_data.name.clone(),
128 target_kind: target_data.kind, 128 target_kind: target_data.kind,
129 }; 129 };
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index 76b666dc2..25ebcc0ec 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -16,7 +16,6 @@ use vfs::Vfs;
16pub use self::{ 16pub use self::{
17 analysis_stats::AnalysisStatsCmd, 17 analysis_stats::AnalysisStatsCmd,
18 diagnostics::diagnostics, 18 diagnostics::diagnostics,
19 load_cargo::{load_workspace, load_workspace_at, LoadCargoConfig},
20 ssr::{apply_ssr_rules, search_for_patterns}, 19 ssr::{apply_ssr_rules, search_for_patterns},
21}; 20};
22 21
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 14dbbb20d..843c0ca37 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -51,8 +51,8 @@ pub struct AnalysisStatsCmd {
51 pub with_deps: bool, 51 pub with_deps: bool,
52 pub no_sysroot: bool, 52 pub no_sysroot: bool,
53 pub path: PathBuf, 53 pub path: PathBuf,
54 pub load_output_dirs: bool, 54 pub enable_build_scripts: bool,
55 pub with_proc_macro: bool, 55 pub enable_proc_macros: bool,
56 pub skip_inference: bool, 56 pub skip_inference: bool,
57} 57}
58 58
@@ -67,9 +67,10 @@ impl AnalysisStatsCmd {
67 let mut cargo_config = CargoConfig::default(); 67 let mut cargo_config = CargoConfig::default();
68 cargo_config.no_sysroot = self.no_sysroot; 68 cargo_config.no_sysroot = self.no_sysroot;
69 let load_cargo_config = LoadCargoConfig { 69 let load_cargo_config = LoadCargoConfig {
70 load_out_dirs_from_check: self.load_output_dirs, 70 load_out_dirs_from_check: self.enable_build_scripts,
71 wrap_rustc: false, 71 wrap_rustc: false,
72 with_proc_macro: self.with_proc_macro, 72 with_proc_macro: self.enable_proc_macros,
73 prefill_caches: false,
73 }; 74 };
74 let (host, vfs, _proc_macro) = 75 let (host, vfs, _proc_macro) =
75 load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; 76 load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index c33c8179c..dc9a390fe 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -34,8 +34,12 @@ pub fn diagnostics(
34 with_proc_macro: bool, 34 with_proc_macro: bool,
35) -> Result<()> { 35) -> Result<()> {
36 let cargo_config = Default::default(); 36 let cargo_config = Default::default();
37 let load_cargo_config = 37 let load_cargo_config = LoadCargoConfig {
38 LoadCargoConfig { load_out_dirs_from_check, with_proc_macro, wrap_rustc: false }; 38 load_out_dirs_from_check,
39 with_proc_macro,
40 wrap_rustc: false,
41 prefill_caches: false,
42 };
39 let (host, _vfs, _proc_macro) = 43 let (host, _vfs, _proc_macro) =
40 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; 44 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?;
41 let db = host.raw_database(); 45 let db = host.raw_database();
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 75bad1112..b5f5519b4 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -4,6 +4,7 @@ use std::{path::Path, sync::Arc};
4 4
5use anyhow::Result; 5use anyhow::Result;
6use crossbeam_channel::{unbounded, Receiver}; 6use crossbeam_channel::{unbounded, Receiver};
7use hir::db::DefDatabase;
7use ide::{AnalysisHost, Change}; 8use ide::{AnalysisHost, Change};
8use ide_db::base_db::CrateGraph; 9use ide_db::base_db::CrateGraph;
9use project_model::{ 10use project_model::{
@@ -13,13 +14,14 @@ use vfs::{loader::Handle, AbsPath, AbsPathBuf};
13 14
14use crate::reload::{ProjectFolders, SourceRootConfig}; 15use crate::reload::{ProjectFolders, SourceRootConfig};
15 16
16pub struct LoadCargoConfig { 17pub(crate) struct LoadCargoConfig {
17 pub load_out_dirs_from_check: bool, 18 pub(crate) load_out_dirs_from_check: bool,
18 pub wrap_rustc: bool, 19 pub(crate) wrap_rustc: bool,
19 pub with_proc_macro: bool, 20 pub(crate) with_proc_macro: bool,
21 pub(crate) prefill_caches: bool,
20} 22}
21 23
22pub fn load_workspace_at( 24pub(crate) fn load_workspace_at(
23 root: &Path, 25 root: &Path,
24 cargo_config: &CargoConfig, 26 cargo_config: &CargoConfig,
25 load_config: &LoadCargoConfig, 27 load_config: &LoadCargoConfig,
@@ -32,7 +34,7 @@ pub fn load_workspace_at(
32 load_workspace(workspace, load_config, progress) 34 load_workspace(workspace, load_config, progress)
33} 35}
34 36
35pub fn load_workspace( 37fn load_workspace(
36 ws: ProjectWorkspace, 38 ws: ProjectWorkspace,
37 config: &LoadCargoConfig, 39 config: &LoadCargoConfig,
38 progress: &dyn Fn(String), 40 progress: &dyn Fn(String),
@@ -81,6 +83,10 @@ pub fn load_workspace(
81 log::debug!("crate graph: {:?}", crate_graph); 83 log::debug!("crate graph: {:?}", crate_graph);
82 let host = 84 let host =
83 load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver); 85 load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver);
86
87 if config.prefill_caches {
88 host.analysis().prime_caches(|_| {})?;
89 }
84 Ok((host, vfs, proc_macro_client)) 90 Ok((host, vfs, proc_macro_client))
85} 91}
86 92
@@ -94,6 +100,8 @@ fn load_crate_graph(
94 let mut host = AnalysisHost::new(lru_cap); 100 let mut host = AnalysisHost::new(lru_cap);
95 let mut analysis_change = Change::new(); 101 let mut analysis_change = Change::new();
96 102
103 host.raw_database_mut().set_enable_proc_attr_macros(true);
104
97 // wait until Vfs has loaded all roots 105 // wait until Vfs has loaded all roots
98 for task in receiver { 106 for task in receiver {
99 match task { 107 match task {
@@ -118,7 +126,7 @@ fn load_crate_graph(
118 } 126 }
119 } 127 }
120 } 128 }
121 let source_roots = source_root_config.partition(&vfs); 129 let source_roots = source_root_config.partition(vfs);
122 analysis_change.set_roots(source_roots); 130 analysis_change.set_roots(source_roots);
123 131
124 analysis_change.set_crate_graph(crate_graph); 132 analysis_change.set_crate_graph(crate_graph);
@@ -141,6 +149,7 @@ mod tests {
141 load_out_dirs_from_check: false, 149 load_out_dirs_from_check: false,
142 wrap_rustc: false, 150 wrap_rustc: false,
143 with_proc_macro: false, 151 with_proc_macro: false,
152 prefill_caches: false,
144 }; 153 };
145 let (host, _vfs, _proc_macro) = 154 let (host, _vfs, _proc_macro) =
146 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; 155 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs
index 1fd9b5a9b..0b3475e09 100644
--- a/crates/rust-analyzer/src/cli/ssr.rs
+++ b/crates/rust-analyzer/src/cli/ssr.rs
@@ -13,6 +13,7 @@ pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> {
13 load_out_dirs_from_check: true, 13 load_out_dirs_from_check: true,
14 wrap_rustc: false, 14 wrap_rustc: false,
15 with_proc_macro: true, 15 with_proc_macro: true,
16 prefill_caches: false,
16 }; 17 };
17 let (host, vfs, _proc_macro) = 18 let (host, vfs, _proc_macro) =
18 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; 19 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?;
@@ -39,8 +40,12 @@ pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<Stri
39 use ide_db::base_db::SourceDatabaseExt; 40 use ide_db::base_db::SourceDatabaseExt;
40 use ide_db::symbol_index::SymbolsDatabase; 41 use ide_db::symbol_index::SymbolsDatabase;
41 let cargo_config = Default::default(); 42 let cargo_config = Default::default();
42 let load_cargo_config = 43 let load_cargo_config = LoadCargoConfig {
43 LoadCargoConfig { load_out_dirs_from_check: true, wrap_rustc: true, with_proc_macro: true }; 44 load_out_dirs_from_check: true,
45 wrap_rustc: true,
46 with_proc_macro: true,
47 prefill_caches: false,
48 };
44 let (host, _vfs, _proc_macro) = 49 let (host, _vfs, _proc_macro) =
45 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; 50 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?;
46 let db = host.raw_database(); 51 let db = host.raw_database();
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index ae78fd4f6..3b20d741a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -92,6 +92,7 @@ config_data! {
92 checkOnSave_overrideCommand: Option<Vec<String>> = "null", 92 checkOnSave_overrideCommand: Option<Vec<String>> = "null",
93 93
94 /// Whether to add argument snippets when completing functions. 94 /// Whether to add argument snippets when completing functions.
95 /// Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set.
95 completion_addCallArgumentSnippets: bool = "true", 96 completion_addCallArgumentSnippets: bool = "true",
96 /// Whether to add parenthesis when completing functions. 97 /// Whether to add parenthesis when completing functions.
97 completion_addCallParenthesis: bool = "true", 98 completion_addCallParenthesis: bool = "true",
@@ -125,6 +126,9 @@ config_data! {
125 /// and a blue icon in the `Problems Panel`. 126 /// and a blue icon in the `Problems Panel`.
126 diagnostics_warningsAsInfo: Vec<String> = "[]", 127 diagnostics_warningsAsInfo: Vec<String> = "[]",
127 128
129 /// Expand attribute macros.
130 experimental_procAttrMacros: bool = "false",
131
128 /// Controls file watching implementation. 132 /// Controls file watching implementation.
129 files_watcher: String = "\"client\"", 133 files_watcher: String = "\"client\"",
130 /// These directories will be ignored by rust-analyzer. 134 /// These directories will be ignored by rust-analyzer.
@@ -148,6 +152,9 @@ config_data! {
148 /// Whether to show `Implementations` action. Only applies when 152 /// Whether to show `Implementations` action. Only applies when
149 /// `#rust-analyzer.hoverActions.enable#` is set. 153 /// `#rust-analyzer.hoverActions.enable#` is set.
150 hoverActions_implementations: bool = "true", 154 hoverActions_implementations: bool = "true",
155 /// Whether to show `References` action. Only applies when
156 /// `#rust-analyzer.hoverActions.enable#` is set.
157 hoverActions_references: bool = "false",
151 /// Whether to show `Run` action. Only applies when 158 /// Whether to show `Run` action. Only applies when
152 /// `#rust-analyzer.hoverActions.enable#` is set. 159 /// `#rust-analyzer.hoverActions.enable#` is set.
153 hoverActions_run: bool = "true", 160 hoverActions_run: bool = "true",
@@ -545,6 +552,9 @@ impl Config {
545 let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?; 552 let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?;
546 Some((path, vec!["proc-macro".into()])) 553 Some((path, vec!["proc-macro".into()]))
547 } 554 }
555 pub fn expand_proc_attr_macros(&self) -> bool {
556 self.data.experimental_procAttrMacros
557 }
548 pub fn files(&self) -> FilesConfig { 558 pub fn files(&self) -> FilesConfig {
549 FilesConfig { 559 FilesConfig {
550 watcher: match self.data.files_watcher.as_str() { 560 watcher: match self.data.files_watcher.as_str() {
@@ -712,6 +722,7 @@ impl Config {
712 HoverConfig { 722 HoverConfig {
713 implementations: self.data.hoverActions_enable 723 implementations: self.data.hoverActions_enable
714 && self.data.hoverActions_implementations, 724 && self.data.hoverActions_implementations,
725 references: self.data.hoverActions_enable && self.data.hoverActions_references,
715 run: self.data.hoverActions_enable && self.data.hoverActions_run, 726 run: self.data.hoverActions_enable && self.data.hoverActions_run,
716 debug: self.data.hoverActions_enable && self.data.hoverActions_debug, 727 debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
717 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef, 728 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index d4b9db362..2f63c26ce 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -47,7 +47,7 @@ impl DiagnosticCollection {
47 ) { 47 ) {
48 let diagnostics = self.check.entry(file_id).or_default(); 48 let diagnostics = self.check.entry(file_id).or_default();
49 for existing_diagnostic in diagnostics.iter() { 49 for existing_diagnostic in diagnostics.iter() {
50 if are_diagnostics_equal(&existing_diagnostic, &diagnostic) { 50 if are_diagnostics_equal(existing_diagnostic, &diagnostic) {
51 return; 51 return;
52 } 52 }
53 } 53 }
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 82dd0da9a..8594d923c 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -224,7 +224,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
224 224
225 let mut message = rd.message.clone(); 225 let mut message = rd.message.clone();
226 for child in &rd.children { 226 for child in &rd.children {
227 let child = map_rust_child_diagnostic(config, workspace_root, &child); 227 let child = map_rust_child_diagnostic(config, workspace_root, child);
228 match child { 228 match child {
229 MappedRustChildDiagnostic::SubDiagnostic(sub) => { 229 MappedRustChildDiagnostic::SubDiagnostic(sub) => {
230 subdiagnostics.push(sub); 230 subdiagnostics.push(sub);
@@ -268,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
268 primary_spans 268 primary_spans
269 .iter() 269 .iter()
270 .flat_map(|primary_span| { 270 .flat_map(|primary_span| {
271 let primary_location = primary_location(config, workspace_root, &primary_span); 271 let primary_location = primary_location(config, workspace_root, primary_span);
272 272
273 let mut message = message.clone(); 273 let mut message = message.clone();
274 if needs_primary_span_label { 274 if needs_primary_span_label {
@@ -298,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
298 // generated that code. 298 // generated that code.
299 let is_in_macro_call = i != 0; 299 let is_in_macro_call = i != 0;
300 300
301 let secondary_location = location(config, workspace_root, &span); 301 let secondary_location = location(config, workspace_root, span);
302 if secondary_location == primary_location { 302 if secondary_location == primary_location {
303 continue; 303 continue;
304 } 304 }
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index 2011a4132..5991e72b5 100644
--- a/crates/rust-analyzer/src/dispatch.rs
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -104,7 +104,7 @@ impl<'a> RequestDispatcher<'a> {
104 104
105 let res = crate::from_json(R::METHOD, req.params); 105 let res = crate::from_json(R::METHOD, req.params);
106 match res { 106 match res {
107 Ok(params) => return Some((req.id, params)), 107 Ok(params) => Some((req.id, params)),
108 Err(err) => { 108 Err(err) => {
109 let response = lsp_server::Response::new_err( 109 let response = lsp_server::Response::new_err(
110 req.id, 110 req.id,
@@ -112,7 +112,7 @@ impl<'a> RequestDispatcher<'a> {
112 err.to_string(), 112 err.to_string(),
113 ); 113 );
114 self.global_state.respond(response); 114 self.global_state.respond(response);
115 return None; 115 None
116 } 116 }
117 } 117 }
118 } 118 }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index ea9dbf7fc..583900cfe 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -119,12 +119,12 @@ impl GlobalState {
119 119
120 let analysis_host = AnalysisHost::new(config.lru_capacity()); 120 let analysis_host = AnalysisHost::new(config.lru_capacity());
121 let (flycheck_sender, flycheck_receiver) = unbounded(); 121 let (flycheck_sender, flycheck_receiver) = unbounded();
122 GlobalState { 122 let mut this = GlobalState {
123 sender, 123 sender,
124 req_queue: ReqQueue::default(), 124 req_queue: ReqQueue::default(),
125 task_pool, 125 task_pool,
126 loader, 126 loader,
127 config: Arc::new(config), 127 config: Arc::new(config.clone()),
128 analysis_host, 128 analysis_host,
129 diagnostics: Default::default(), 129 diagnostics: Default::default(),
130 mem_docs: FxHashMap::default(), 130 mem_docs: FxHashMap::default(),
@@ -151,7 +151,10 @@ impl GlobalState {
151 151
152 fetch_build_data_queue: OpQueue::default(), 152 fetch_build_data_queue: OpQueue::default(),
153 latest_requests: Default::default(), 153 latest_requests: Default::default(),
154 } 154 };
155 // Apply any required database inputs from the config.
156 this.update_configuration(config);
157 this
155 } 158 }
156 159
157 pub(crate) fn process_changes(&mut self) -> bool { 160 pub(crate) fn process_changes(&mut self) -> bool {
@@ -191,7 +194,7 @@ impl GlobalState {
191 change.change_file(file.file_id, text); 194 change.change_file(file.file_id, text);
192 } 195 }
193 if has_fs_changes { 196 if has_fs_changes {
194 let roots = self.source_root_config.partition(&vfs); 197 let roots = self.source_root_config.partition(vfs);
195 change.set_roots(roots); 198 change.set_roots(roots);
196 } 199 }
197 change 200 change
@@ -288,7 +291,7 @@ impl GlobalStateSnapshot {
288 } 291 }
289 292
290 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { 293 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
291 let path = from_proto::vfs_path(&url).ok()?; 294 let path = from_proto::vfs_path(url).ok()?;
292 Some(self.mem_docs.get(&path)?.version) 295 Some(self.mem_docs.get(&path)?.version)
293 } 296 }
294 297
@@ -297,7 +300,7 @@ impl GlobalStateSnapshot {
297 base.pop(); 300 base.pop();
298 let path = base.join(&path.path).unwrap(); 301 let path = base.join(&path.path).unwrap();
299 let path = path.as_path().unwrap(); 302 let path = path.as_path().unwrap();
300 url_from_abs_path(&path) 303 url_from_abs_path(path)
301 } 304 }
302 305
303 pub(crate) fn cargo_target_for_crate_root( 306 pub(crate) fn cargo_target_for_crate_root(
@@ -309,7 +312,7 @@ impl GlobalStateSnapshot {
309 let path = path.as_path()?; 312 let path = path.as_path()?;
310 self.workspaces.iter().find_map(|ws| match ws { 313 self.workspaces.iter().find_map(|ws| match ws {
311 ProjectWorkspace::Cargo { cargo, .. } => { 314 ProjectWorkspace::Cargo { cargo, .. } => {
312 cargo.target_by_root(&path).map(|it| (cargo, it)) 315 cargo.target_by_root(path).map(|it| (cargo, it))
313 } 316 }
314 ProjectWorkspace::Json { .. } => None, 317 ProjectWorkspace::Json { .. } => None,
315 ProjectWorkspace::DetachedFiles { .. } => None, 318 ProjectWorkspace::DetachedFiles { .. } => None,
@@ -320,7 +323,7 @@ impl GlobalStateSnapshot {
320pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { 323pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
321 let path = vfs.file_path(id); 324 let path = vfs.file_path(id);
322 let path = path.as_path().unwrap(); 325 let path = path.as_path().unwrap();
323 url_from_abs_path(&path) 326 url_from_abs_path(path)
324} 327}
325 328
326pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> { 329pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 456744603..ccf66294f 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -781,7 +781,7 @@ pub(crate) fn handle_completion_resolve(
781 let resolve_data = match original_completion 781 let resolve_data = match original_completion
782 .data 782 .data
783 .take() 783 .take()
784 .map(|data| serde_json::from_value::<CompletionResolveData>(data)) 784 .map(serde_json::from_value::<CompletionResolveData>)
785 .transpose()? 785 .transpose()?
786 { 786 {
787 Some(data) => data, 787 Some(data) => data,
@@ -1229,14 +1229,13 @@ pub(crate) fn publish_diagnostics(
1229 .map(|d| Diagnostic { 1229 .map(|d| Diagnostic {
1230 range: to_proto::range(&line_index, d.range), 1230 range: to_proto::range(&line_index, d.range),
1231 severity: Some(to_proto::diagnostic_severity(d.severity)), 1231 severity: Some(to_proto::diagnostic_severity(d.severity)),
1232 code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String), 1232 code: Some(NumberOrString::String(d.code.as_str().to_string())),
1233 code_description: d.code.and_then(|code| { 1233 code_description: Some(lsp_types::CodeDescription {
1234 lsp_types::Url::parse(&format!( 1234 href: lsp_types::Url::parse(&format!(
1235 "https://rust-analyzer.github.io/manual.html#{}", 1235 "https://rust-analyzer.github.io/manual.html#{}",
1236 code.as_str() 1236 d.code.as_str()
1237 )) 1237 ))
1238 .ok() 1238 .unwrap(),
1239 .map(|href| lsp_types::CodeDescription { href })
1240 }), 1239 }),
1241 source: Some("rust-analyzer".to_string()), 1240 source: Some("rust-analyzer".to_string()),
1242 message: d.message, 1241 message: d.message,
@@ -1396,7 +1395,7 @@ pub(crate) fn handle_semantic_tokens_full_delta(
1396 1395
1397 if let Some(prev_id) = &cached_tokens.result_id { 1396 if let Some(prev_id) = &cached_tokens.result_id {
1398 if *prev_id == params.previous_result_id { 1397 if *prev_id == params.previous_result_id {
1399 let delta = to_proto::semantic_token_delta(&cached_tokens, &semantic_tokens); 1398 let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens);
1400 *cached_tokens = semantic_tokens; 1399 *cached_tokens = semantic_tokens;
1401 return Ok(Some(delta.into())); 1400 return Ok(Some(delta.into()));
1402 } 1401 }
@@ -1506,11 +1505,41 @@ fn show_impl_command_link(
1506 None 1505 None
1507} 1506}
1508 1507
1508fn show_ref_command_link(
1509 snap: &GlobalStateSnapshot,
1510 position: &FilePosition,
1511) -> Option<lsp_ext::CommandLinkGroup> {
1512 if snap.config.hover().references {
1513 if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
1514 let uri = to_proto::url(snap, position.file_id);
1515 let line_index = snap.file_line_index(position.file_id).ok()?;
1516 let position = to_proto::position(&line_index, position.offset);
1517 let locations: Vec<_> = ref_search_res
1518 .references
1519 .into_iter()
1520 .flat_map(|(file_id, ranges)| {
1521 ranges.into_iter().filter_map(move |(range, _)| {
1522 to_proto::location(snap, FileRange { file_id, range }).ok()
1523 })
1524 })
1525 .collect();
1526 let title = to_proto::reference_title(locations.len());
1527 let command = to_proto::command::show_references(title, &uri, position, locations);
1528
1529 return Some(lsp_ext::CommandLinkGroup {
1530 commands: vec![to_command_link(command, "Go to references".into())],
1531 ..Default::default()
1532 });
1533 }
1534 }
1535 None
1536}
1537
1509fn runnable_action_links( 1538fn runnable_action_links(
1510 snap: &GlobalStateSnapshot, 1539 snap: &GlobalStateSnapshot,
1511 runnable: Runnable, 1540 runnable: Runnable,
1512) -> Option<lsp_ext::CommandLinkGroup> { 1541) -> Option<lsp_ext::CommandLinkGroup> {
1513 let cargo_spec = CargoTargetSpec::for_file(&snap, runnable.nav.file_id).ok()?; 1542 let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?;
1514 let hover_config = snap.config.hover(); 1543 let hover_config = snap.config.hover();
1515 if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { 1544 if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1516 return None; 1545 return None;
@@ -1566,6 +1595,7 @@ fn prepare_hover_actions(
1566 .iter() 1595 .iter()
1567 .filter_map(|it| match it { 1596 .filter_map(|it| match it {
1568 HoverAction::Implementation(position) => show_impl_command_link(snap, position), 1597 HoverAction::Implementation(position) => show_impl_command_link(snap, position),
1598 HoverAction::Reference(position) => show_ref_command_link(snap, position),
1569 HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()), 1599 HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()),
1570 HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), 1600 HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
1571 }) 1601 })
@@ -1593,7 +1623,7 @@ fn run_rustfmt(
1593 text_document: TextDocumentIdentifier, 1623 text_document: TextDocumentIdentifier,
1594 range: Option<lsp_types::Range>, 1624 range: Option<lsp_types::Range>,
1595) -> Result<Option<Vec<lsp_types::TextEdit>>> { 1625) -> Result<Option<Vec<lsp_types::TextEdit>>> {
1596 let file_id = from_proto::file_id(&snap, &text_document.uri)?; 1626 let file_id = from_proto::file_id(snap, &text_document.uri)?;
1597 let file = snap.analysis.file_text(file_id)?; 1627 let file = snap.analysis.file_text(file_id)?;
1598 let crate_ids = snap.analysis.crate_for(file_id)?; 1628 let crate_ids = snap.analysis.crate_for(file_id)?;
1599 1629
@@ -1640,7 +1670,7 @@ fn run_rustfmt(
1640 .into()); 1670 .into());
1641 } 1671 }
1642 1672
1643 let frange = from_proto::file_range(&snap, text_document.clone(), range)?; 1673 let frange = from_proto::file_range(snap, text_document, range)?;
1644 let start_line = line_index.index.line_col(frange.range.start()).line; 1674 let start_line = line_index.index.line_col(frange.range.start()).line;
1645 let end_line = line_index.index.line_col(frange.range.end()).line; 1675 let end_line = line_index.index.line_col(frange.range.end()).line;
1646 1676
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index ec36a5f5c..8ddeb59f7 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -37,6 +37,7 @@ fn integrated_highlighting_benchmark() {
37 load_out_dirs_from_check: true, 37 load_out_dirs_from_check: true,
38 wrap_rustc: false, 38 wrap_rustc: false,
39 with_proc_macro: false, 39 with_proc_macro: false,
40 prefill_caches: false,
40 }; 41 };
41 42
42 let (mut host, vfs, _proc_macro) = { 43 let (mut host, vfs, _proc_macro) = {
@@ -91,6 +92,7 @@ fn integrated_completion_benchmark() {
91 load_out_dirs_from_check: true, 92 load_out_dirs_from_check: true,
92 wrap_rustc: false, 93 wrap_rustc: false,
93 with_proc_macro: false, 94 with_proc_macro: false,
95 prefill_caches: true,
94 }; 96 };
95 97
96 let (mut host, vfs, _proc_macro) = { 98 let (mut host, vfs, _proc_macro) = {
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 8000b5490..087c26a71 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -124,7 +124,7 @@ pub(crate) fn apply_document_changes(
124 match change.range { 124 match change.range {
125 Some(range) => { 125 Some(range) => {
126 if !index_valid.covers(range.end.line) { 126 if !index_valid.covers(range.end.line) {
127 line_index.index = Arc::new(ide::LineIndex::new(&old_text)); 127 line_index.index = Arc::new(ide::LineIndex::new(old_text));
128 } 128 }
129 index_valid = IndexValid::UpToLineExclusive(range.start.line); 129 index_valid = IndexValid::UpToLineExclusive(range.start.line);
130 let range = from_proto::text_range(&line_index, range); 130 let range = from_proto::text_range(&line_index, range);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 31d8ea9e7..1417d8379 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -701,7 +701,7 @@ impl GlobalState {
701 }, 701 },
702 ); 702 );
703 703
704 return Ok(()); 704 Ok(())
705 })? 705 })?
706 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| { 706 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
707 for change in params.changes { 707 for change in params.changes {
@@ -740,7 +740,7 @@ impl GlobalState {
740 let subscriptions = self 740 let subscriptions = self
741 .mem_docs 741 .mem_docs
742 .keys() 742 .keys()
743 .map(|path| self.vfs.read().0.file_id(&path).unwrap()) 743 .map(|path| self.vfs.read().0.file_id(path).unwrap())
744 .collect::<Vec<_>>(); 744 .collect::<Vec<_>>();
745 745
746 log::trace!("updating notifications for {:?}", subscriptions); 746 log::trace!("updating notifications for {:?}", subscriptions);
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 93b5ff55f..bd31d1d13 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -2,6 +2,7 @@
2use std::{mem, sync::Arc}; 2use std::{mem, sync::Arc};
3 3
4use flycheck::{FlycheckConfig, FlycheckHandle}; 4use flycheck::{FlycheckConfig, FlycheckHandle};
5use hir::db::DefDatabase;
5use ide::Change; 6use ide::Change;
6use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; 7use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath};
7use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace}; 8use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace};
@@ -47,6 +48,11 @@ impl GlobalState {
47 } else if self.config.flycheck() != old_config.flycheck() { 48 } else if self.config.flycheck() != old_config.flycheck() {
48 self.reload_flycheck(); 49 self.reload_flycheck();
49 } 50 }
51
52 // Apply experimental feature flags.
53 self.analysis_host
54 .raw_database_mut()
55 .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros());
50 } 56 }
51 pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) { 57 pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
52 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { 58 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index db216d951..c20642231 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -112,7 +112,7 @@ impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
112 112
113/// Tokens are encoded relative to each other. 113/// Tokens are encoded relative to each other.
114/// 114///
115/// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45 115/// This is a direct port of <https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45>
116pub(crate) struct SemanticTokensBuilder { 116pub(crate) struct SemanticTokensBuilder {
117 id: String, 117 id: String,
118 prev_line: u32, 118 prev_line: u32,
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 7428a3043..e53cd3c7b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -405,7 +405,7 @@ pub(crate) fn semantic_tokens(
405 text_range = 405 text_range =
406 TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); 406 TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n'));
407 } 407 }
408 let range = range(&line_index, text_range); 408 let range = range(line_index, text_range);
409 builder.push(range, token_index, modifier_bitset); 409 builder.push(range, token_index, modifier_bitset);
410 } 410 }
411 } 411 }
@@ -781,7 +781,7 @@ pub(crate) fn snippet_workspace_edit(
781 document_changes.extend_from_slice(&ops); 781 document_changes.extend_from_slice(&ops);
782 } 782 }
783 for (file_id, edit) in source_change.source_file_edits { 783 for (file_id, edit) in source_change.source_file_edits {
784 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; 784 let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?;
785 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 785 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
786 } 786 }
787 let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { 787 let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
@@ -957,7 +957,7 @@ pub(crate) fn code_lens(
957 let annotation_range = range(&line_index, annotation.range); 957 let annotation_range = range(&line_index, annotation.range);
958 958
959 let action = run.action(); 959 let action = run.action();
960 let r = runnable(&snap, run)?; 960 let r = runnable(snap, run)?;
961 961
962 let command = if debug { 962 let command = if debug {
963 command::debug_single(&r) 963 command::debug_single(&r)
@@ -1236,12 +1236,12 @@ fn main() {
1236 assert_eq!(folds.len(), 4); 1236 assert_eq!(folds.len(), 4);
1237 1237
1238 let line_index = LineIndex { 1238 let line_index = LineIndex {
1239 index: Arc::new(ide::LineIndex::new(&text)), 1239 index: Arc::new(ide::LineIndex::new(text)),
1240 endings: LineEndings::Unix, 1240 endings: LineEndings::Unix,
1241 encoding: OffsetEncoding::Utf16, 1241 encoding: OffsetEncoding::Utf16,
1242 }; 1242 };
1243 let converted: Vec<lsp_types::FoldingRange> = 1243 let converted: Vec<lsp_types::FoldingRange> =
1244 folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); 1244 folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();
1245 1245
1246 let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; 1246 let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
1247 assert_eq!(converted.len(), expected_lines.len()); 1247 assert_eq!(converted.len(), expected_lines.len());
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 9e89209ea..3585132d4 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -493,7 +493,7 @@ fn preserves_dos_line_endings() {
493 } 493 }
494 494
495 let server = Project::with_fixture( 495 let server = Project::with_fixture(
496 &" 496 "
497//- /Cargo.toml 497//- /Cargo.toml
498[package] 498[package]
499name = \"foo\" 499name = \"foo\"
@@ -758,7 +758,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
758 ```rust 758 ```rust
759 fn bar() 759 fn bar()
760 ```"#]] 760 ```"#]]
761 .assert_eq(&value); 761 .assert_eq(value);
762} 762}
763 763
764#[test] 764#[test]
@@ -795,7 +795,7 @@ fn main() {}
795 795
796"#; 796"#;
797 let server = 797 let server =
798 Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); 798 Project::with_fixture(code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
799 799
800 //rename same level file 800 //rename same level file
801 server.request::<WillRenameFiles>( 801 server.request::<WillRenameFiles>(
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 75e677762..e22c295f9 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -323,7 +323,7 @@ fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Valu
323 323
324 if !l.is_empty() { 324 if !l.is_empty() {
325 assert!(!r.is_empty()); 325 assert!(!r.is_empty());
326 Some((&l[0], &r[0])) 326 Some((l[0], r[0]))
327 } else { 327 } else {
328 assert_eq!(r.len(), 0); 328 assert_eq!(r.len(), 0);
329 None 329 None
diff --git a/crates/stdx/src/panic_context.rs b/crates/stdx/src/panic_context.rs
index 8d51e20d3..26debf3c4 100644
--- a/crates/stdx/src/panic_context.rs
+++ b/crates/stdx/src/panic_context.rs
@@ -1,6 +1,6 @@
1//! A micro-crate to enhance panic messages with context info. 1//! A micro-crate to enhance panic messages with context info.
2//! 2//!
3//! FIXME: upstream to https://github.com/kriomant/panic-context ? 3//! FIXME: upstream to <https://github.com/kriomant/panic-context> ?
4 4
5use std::{cell::RefCell, panic, sync::Once}; 5use std::{cell::RefCell, panic, sync::Once};
6 6
diff --git a/crates/stdx/src/process.rs b/crates/stdx/src/process.rs
index b0fa12f76..692a2ab3d 100644
--- a/crates/stdx/src/process.rs
+++ b/crates/stdx/src/process.rs
@@ -1,7 +1,7 @@
1//! Read both stdout and stderr of child without deadlocks. 1//! Read both stdout and stderr of child without deadlocks.
2//! 2//!
3//! https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs 3//! <https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs>
4//! https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231 4//! <https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231>
5 5
6use std::{ 6use std::{
7 io, 7 io,
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 2106732cd..f1525a649 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -11,7 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = "2.0.0-pre.1"
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "=0.13.0-pre.6" 16rowan = "=0.13.0-pre.6"
17rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" }
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 19107ee38..2663c0759 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -30,7 +30,7 @@ impl ast::UseTree {
30 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { 30 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() {
31 make::path_unqualified(make::path_segment_self()) 31 make::path_unqualified(make::path_segment_self())
32 } else { 32 } else {
33 match split_path_prefix(&prefix) { 33 match split_path_prefix(prefix) {
34 Some(it) => it, 34 Some(it) => it,
35 None => return self.clone(), 35 None => return self.clone(),
36 } 36 }
@@ -95,7 +95,7 @@ impl fmt::Display for IndentLevel {
95 let indent = if len <= spaces.len() { 95 let indent = if len <= spaces.len() {
96 &spaces[..len] 96 &spaces[..len]
97 } else { 97 } else {
98 buf = iter::repeat(' ').take(len).collect::<String>(); 98 buf = " ".repeat(len);
99 &buf 99 &buf
100 }; 100 };
101 fmt::Display::fmt(indent, f) 101 fmt::Display::fmt(indent, f)
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 9a88fdb56..702de59a9 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -150,10 +150,7 @@ impl Attr {
150 pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } 150 pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
151 pub fn excl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) } 151 pub fn excl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) }
152 pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) } 152 pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
153 pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } 153 pub fn meta(&self) -> Option<Meta> { support::child(&self.syntax) }
154 pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
155 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
156 pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) }
157 pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) } 154 pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
158} 155}
159#[derive(Debug, Clone, PartialEq, Eq, Hash)] 156#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -632,6 +629,16 @@ impl WherePred {
632 pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } 629 pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
633} 630}
634#[derive(Debug, Clone, PartialEq, Eq, Hash)] 631#[derive(Debug, Clone, PartialEq, Eq, Hash)]
632pub struct Meta {
633 pub(crate) syntax: SyntaxNode,
634}
635impl Meta {
636 pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
637 pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
638 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
639 pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) }
640}
641#[derive(Debug, Clone, PartialEq, Eq, Hash)]
635pub struct ExprStmt { 642pub struct ExprStmt {
636 pub(crate) syntax: SyntaxNode, 643 pub(crate) syntax: SyntaxNode,
637} 644}
@@ -2072,6 +2079,17 @@ impl AstNode for WherePred {
2072 } 2079 }
2073 fn syntax(&self) -> &SyntaxNode { &self.syntax } 2080 fn syntax(&self) -> &SyntaxNode { &self.syntax }
2074} 2081}
2082impl AstNode for Meta {
2083 fn can_cast(kind: SyntaxKind) -> bool { kind == META }
2084 fn cast(syntax: SyntaxNode) -> Option<Self> {
2085 if Self::can_cast(syntax.kind()) {
2086 Some(Self { syntax })
2087 } else {
2088 None
2089 }
2090 }
2091 fn syntax(&self) -> &SyntaxNode { &self.syntax }
2092}
2075impl AstNode for ExprStmt { 2093impl AstNode for ExprStmt {
2076 fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT } 2094 fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT }
2077 fn cast(syntax: SyntaxNode) -> Option<Self> { 2095 fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3887,6 +3905,11 @@ impl std::fmt::Display for WherePred {
3887 std::fmt::Display::fmt(self.syntax(), f) 3905 std::fmt::Display::fmt(self.syntax(), f)
3888 } 3906 }
3889} 3907}
3908impl std::fmt::Display for Meta {
3909 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3910 std::fmt::Display::fmt(self.syntax(), f)
3911 }
3912}
3890impl std::fmt::Display for ExprStmt { 3913impl std::fmt::Display for ExprStmt {
3891 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 3914 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3892 std::fmt::Display::fmt(self.syntax(), f) 3915 std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 0cf170626..4c3c9661d 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -580,12 +580,11 @@ pub fn fn_(
580pub fn struct_( 580pub fn struct_(
581 visibility: Option<ast::Visibility>, 581 visibility: Option<ast::Visibility>,
582 strukt_name: ast::Name, 582 strukt_name: ast::Name,
583 type_params: Option<ast::GenericParamList>, 583 generic_param_list: Option<ast::GenericParamList>,
584 field_list: ast::FieldList, 584 field_list: ast::FieldList,
585) -> ast::Struct { 585) -> ast::Struct {
586 let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; 586 let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" };
587 let type_params = 587 let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string());
588 if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() };
589 let visibility = match visibility { 588 let visibility = match visibility {
590 None => String::new(), 589 None => String::new(),
591 Some(it) => format!("{} ", it), 590 Some(it) => format!("{} ", it),
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 884fe0739..3d27d2c1a 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -144,19 +144,20 @@ impl AttrKind {
144 144
145impl ast::Attr { 145impl ast::Attr {
146 pub fn as_simple_atom(&self) -> Option<SmolStr> { 146 pub fn as_simple_atom(&self) -> Option<SmolStr> {
147 if self.eq_token().is_some() || self.token_tree().is_some() { 147 let meta = self.meta()?;
148 if meta.eq_token().is_some() || meta.token_tree().is_some() {
148 return None; 149 return None;
149 } 150 }
150 self.simple_name() 151 self.simple_name()
151 } 152 }
152 153
153 pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> { 154 pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
154 let tt = self.token_tree()?; 155 let tt = self.meta()?.token_tree()?;
155 Some((self.simple_name()?, tt)) 156 Some((self.simple_name()?, tt))
156 } 157 }
157 158
158 pub fn simple_name(&self) -> Option<SmolStr> { 159 pub fn simple_name(&self) -> Option<SmolStr> {
159 let path = self.path()?; 160 let path = self.meta()?.path()?;
160 match (path.segment(), path.qualifier()) { 161 match (path.segment(), path.qualifier()) {
161 (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()), 162 (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
162 _ => None, 163 _ => None,
@@ -174,6 +175,18 @@ impl ast::Attr {
174 _ => AttrKind::Outer, 175 _ => AttrKind::Outer,
175 } 176 }
176 } 177 }
178
179 pub fn path(&self) -> Option<ast::Path> {
180 self.meta()?.path()
181 }
182
183 pub fn expr(&self) -> Option<ast::Expr> {
184 self.meta()?.expr()
185 }
186
187 pub fn token_tree(&self) -> Option<ast::TokenTree> {
188 self.meta()?.token_tree()
189 }
177} 190}
178 191
179#[derive(Debug, Clone, PartialEq, Eq)] 192#[derive(Debug, Clone, PartialEq, Eq)]
@@ -259,11 +272,14 @@ impl ast::Path {
259 } 272 }
260 273
261 pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone { 274 pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
262 // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone
263 successors(self.first_segment(), |p| { 275 successors(self.first_segment(), |p| {
264 p.parent_path().parent_path().and_then(|p| p.segment()) 276 p.parent_path().parent_path().and_then(|p| p.segment())
265 }) 277 })
266 } 278 }
279
280 pub fn qualifiers(&self) -> impl Iterator<Item = ast::Path> + Clone {
281 successors(self.qualifier(), |p| p.qualifier())
282 }
267} 283}
268impl ast::UseTree { 284impl ast::UseTree {
269 pub fn is_simple_path(&self) -> bool { 285 pub fn is_simple_path(&self) -> bool {
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 4b1e1ccee..ad52d9f54 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -242,7 +242,7 @@ impl ast::ByteString {
242 (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), 242 (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (),
243 (Ok(c), true) => { 243 (Ok(c), true) => {
244 buf.reserve_exact(text.len()); 244 buf.reserve_exact(text.len());
245 buf.extend_from_slice(&text[..char_range.start].as_bytes()); 245 buf.extend_from_slice(text[..char_range.start].as_bytes());
246 buf.push(c as u8); 246 buf.push(c as u8);
247 } 247 }
248 (Err(_), _) => has_error = true, 248 (Err(_), _) => has_error = true,
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs
index 431ed0699..001921343 100644
--- a/crates/syntax/src/parsing.rs
+++ b/crates/syntax/src/parsing.rs
@@ -15,7 +15,7 @@ use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
15pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse}; 15pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse};
16 16
17pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { 17pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
18 let (tokens, lexer_errors) = tokenize(&text); 18 let (tokens, lexer_errors) = tokenize(text);
19 19
20 let mut token_source = TextTokenSource::new(text, &tokens); 20 let mut token_source = TextTokenSource::new(text, &tokens);
21 let mut tree_sink = TextTreeSink::new(text, &tokens); 21 let mut tree_sink = TextTreeSink::new(text, &tokens);
@@ -33,7 +33,7 @@ pub(crate) fn parse_text_fragment<T: AstNode>(
33 text: &str, 33 text: &str,
34 fragment_kind: parser::FragmentKind, 34 fragment_kind: parser::FragmentKind,
35) -> Result<T, ()> { 35) -> Result<T, ()> {
36 let (tokens, lexer_errors) = tokenize(&text); 36 let (tokens, lexer_errors) = tokenize(text);
37 if !lexer_errors.is_empty() { 37 if !lexer_errors.is_empty() {
38 return Err(()); 38 return Err(());
39 } 39 }
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs
index 7c8d0a4c4..ae4844e48 100644
--- a/crates/syntax/src/parsing/lexer.rs
+++ b/crates/syntax/src/parsing/lexer.rs
@@ -144,7 +144,7 @@ fn rustc_token_kind_to_syntax_kind(
144 } 144 }
145 145
146 rustc_lexer::TokenKind::RawIdent => IDENT, 146 rustc_lexer::TokenKind::RawIdent => IDENT,
147 rustc_lexer::TokenKind::Literal { kind, .. } => return match_literal_kind(&kind), 147 rustc_lexer::TokenKind::Literal { kind, .. } => return match_literal_kind(kind),
148 148
149 rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME_IDENT, 149 rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME_IDENT,
150 rustc_lexer::TokenKind::Lifetime { starts_with_number: true } => { 150 rustc_lexer::TokenKind::Lifetime { starts_with_number: true } => {
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 4ad50ab72..186cc9e74 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -26,18 +26,18 @@ pub(crate) fn incremental_reparse(
26 edit: &Indel, 26 edit: &Indel,
27 errors: Vec<SyntaxError>, 27 errors: Vec<SyntaxError>,
28) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 28) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
29 if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) { 29 if let Some((green, new_errors, old_range)) = reparse_token(node, edit) {
30 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); 30 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
31 } 31 }
32 32
33 if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) { 33 if let Some((green, new_errors, old_range)) = reparse_block(node, edit) {
34 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); 34 return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
35 } 35 }
36 None 36 None
37} 37}
38 38
39fn reparse_token<'node>( 39fn reparse_token(
40 root: &'node SyntaxNode, 40 root: &SyntaxNode,
41 edit: &Indel, 41 edit: &Indel,
42) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 42) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
43 let prev_token = root.covering_element(edit.delete).as_token()?.clone(); 43 let prev_token = root.covering_element(edit.delete).as_token()?.clone();
@@ -52,7 +52,7 @@ fn reparse_token<'node>(
52 } 52 }
53 } 53 }
54 54
55 let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit); 55 let mut new_text = get_text_after_edit(prev_token.clone().into(), edit);
56 let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?; 56 let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?;
57 57
58 if new_token_kind != prev_token_kind 58 if new_token_kind != prev_token_kind
@@ -84,8 +84,8 @@ fn reparse_token<'node>(
84 } 84 }
85} 85}
86 86
87fn reparse_block<'node>( 87fn reparse_block(
88 root: &'node SyntaxNode, 88 root: &SyntaxNode,
89 edit: &Indel, 89 edit: &Indel,
90) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { 90) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
91 let (node, reparser) = find_reparsable_node(root, edit.delete)?; 91 let (node, reparser) = find_reparsable_node(root, edit.delete)?;
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs
index 45f3c800f..4961ca08d 100644
--- a/crates/syntax/src/tests.rs
+++ b/crates/syntax/src/tests.rs
@@ -69,13 +69,13 @@ fn parser_tests() {
69 dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], "rast", |text, path| { 69 dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], "rast", |text, path| {
70 let parse = SourceFile::parse(text); 70 let parse = SourceFile::parse(text);
71 let errors = parse.errors(); 71 let errors = parse.errors();
72 assert_errors_are_absent(&errors, path); 72 assert_errors_are_absent(errors, path);
73 parse.debug_dump() 73 parse.debug_dump()
74 }); 74 });
75 dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], "rast", |text, path| { 75 dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], "rast", |text, path| {
76 let parse = SourceFile::parse(text); 76 let parse = SourceFile::parse(text);
77 let errors = parse.errors(); 77 let errors = parse.errors();
78 assert_errors_are_present(&errors, path); 78 assert_errors_are_present(errors, path);
79 parse.debug_dump() 79 parse.debug_dump()
80 }); 80 });
81} 81}
@@ -236,7 +236,7 @@ where
236 } 236 }
237 }); 237 });
238 dir_tests(&test_data_dir(), err_paths, "rast", |text, path| { 238 dir_tests(&test_data_dir(), err_paths, "rast", |text, path| {
239 if let Ok(_) = f(text) { 239 if f(text).is_ok() {
240 panic!("'{:?}' successfully parsed when it should have errored", path); 240 panic!("'{:?}' successfully parsed when it should have errored", path);
241 } else { 241 } else {
242 "ERROR\n".to_owned() 242 "ERROR\n".to_owned()
diff --git a/crates/syntax/test_data/parser/err/0005_attribute_recover.rast b/crates/syntax/test_data/parser/err/0005_attribute_recover.rast
index 4845a6563..6202c8bfe 100644
--- a/crates/syntax/test_data/parser/err/0005_attribute_recover.rast
+++ b/crates/syntax/test_data/parser/err/0005_attribute_recover.rast
@@ -3,20 +3,21 @@ [email protected]
3 [email protected] 3 [email protected]
4 [email protected] "#" 4 [email protected] "#"
5 [email protected] "[" 5 [email protected] "["
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] 8 [email protected]
9 [email protected] "foo" 9 [email protected]
10 [email protected] 10 [email protected] "foo"
11 [email protected] "(" 11 [email protected]
12 [email protected] "foo" 12 [email protected] "("
13 [email protected] "," 13 [email protected] "foo"
14 [email protected] " " 14 [email protected] ","
15 [email protected] "+" 15 [email protected] " "
16 [email protected] "," 16 [email protected] "+"
17 [email protected] " " 17 [email protected] ","
18 [email protected] "92" 18 [email protected] " "
19 [email protected] ")" 19 [email protected] "92"
20 [email protected] ")"
20 [email protected] "]" 21 [email protected] "]"
21 [email protected] "\n" 22 [email protected] "\n"
22 [email protected] "fn" 23 [email protected] "fn"
@@ -35,24 +36,25 @@ [email protected]
35 [email protected] 36 [email protected]
36 [email protected] "#" 37 [email protected] "#"
37 [email protected] "[" 38 [email protected] "["
38 [email protected] 39 [email protected]
39 [email protected] 40 [email protected]
40 [email protected] 41 [email protected]
41 [email protected] "foo" 42 [email protected]
42 [email protected] 43 [email protected] "foo"
43 [email protected] "(" 44 [email protected]
44 [email protected] "\n" 45 [email protected] "("
45 [email protected] "fn" 46 [email protected] "\n"
46 [email protected] " " 47 [email protected] "fn"
47 [email protected] "foo" 48 [email protected] " "
48 [email protected] 49 [email protected] "foo"
49 [email protected] "(" 50 [email protected]
50 [email protected] ")" 51 [email protected] "("
51 [email protected] " " 52 [email protected] ")"
52 [email protected] 53 [email protected] " "
53 [email protected] "{" 54 [email protected]
54 [email protected] "\n" 55 [email protected] "{"
55 [email protected] "}" 56 [email protected] "\n"
57 [email protected] "}"
56 [email protected] "\n" 58 [email protected] "\n"
57error 53..53: expected R_PAREN 59error 53..53: expected R_PAREN
58error 53..53: expected `]` 60error 53..53: expected `]`
diff --git a/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast
index a443b37db..846279748 100644
--- a/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast
+++ b/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast
@@ -27,14 +27,15 @@ [email protected]
27 [email protected] "#" 27 [email protected] "#"
28 [email protected] "!" 28 [email protected] "!"
29 [email protected] "[" 29 [email protected] "["
30 [email protected] 30 [email protected]
31 [email protected] 31 [email protected]
32 [email protected] 32 [email protected]
33 [email protected] "doc" 33 [email protected]
34 [email protected] 34 [email protected] "doc"
35 [email protected] "(" 35 [email protected]
36 [email protected] "\"Inner attributes not ..." 36 [email protected] "("
37 [email protected] ")" 37 [email protected] "\"Inner attributes not ..."
38 [email protected] ")"
38 [email protected] "]" 39 [email protected] "]"
39 [email protected] "\n " 40 [email protected] "\n "
40 [email protected] "//! Nor are ModuleDoc ..." 41 [email protected] "//! Nor are ModuleDoc ..."
@@ -57,28 +58,30 @@ [email protected]
57 [email protected] "#" 58 [email protected] "#"
58 [email protected] "!" 59 [email protected] "!"
59 [email protected] "[" 60 [email protected] "["
60 [email protected] 61 [email protected]
61 [email protected] 62 [email protected]
62 [email protected] 63 [email protected]
63 [email protected] "doc" 64 [email protected]
64 [email protected] 65 [email protected] "doc"
65 [email protected] "(" 66 [email protected]
66 [email protected] "\"Nor here\"" 67 [email protected] "("
67 [email protected] ")" 68 [email protected] "\"Nor here\""
69 [email protected] ")"
68 [email protected] "]" 70 [email protected] "]"
69 [email protected] "\n " 71 [email protected] "\n "
70 [email protected] 72 [email protected]
71 [email protected] "#" 73 [email protected] "#"
72 [email protected] "!" 74 [email protected] "!"
73 [email protected] "[" 75 [email protected] "["
74 [email protected] 76 [email protected]
75 [email protected] 77 [email protected]
76 [email protected] 78 [email protected]
77 [email protected] "doc" 79 [email protected]
78 [email protected] 80 [email protected] "doc"
79 [email protected] "(" 81 [email protected]
80 [email protected] "\"We error on each attr\"" 82 [email protected] "("
81 [email protected] ")" 83 [email protected] "\"We error on each attr\""
84 [email protected] ")"
82 [email protected] "]" 85 [email protected] "]"
83 [email protected] "\n " 86 [email protected] "\n "
84 [email protected] "//! Nor are ModuleDoc ..." 87 [email protected] "//! Nor are ModuleDoc ..."
@@ -99,14 +102,15 @@ [email protected]
99 [email protected] "#" 102 [email protected] "#"
100 [email protected] "!" 103 [email protected] "!"
101 [email protected] "[" 104 [email protected] "["
102 [email protected] 105 [email protected]
103 [email protected] 106 [email protected]
104 [email protected] 107 [email protected]
105 [email protected] "doc" 108 [email protected]
106 [email protected] 109 [email protected] "doc"
107 [email protected] "(" 110 [email protected]
108 [email protected] "\"Nor here\"" 111 [email protected] "("
109 [email protected] ")" 112 [email protected] "\"Nor here\""
113 [email protected] ")"
110 [email protected] "]" 114 [email protected] "]"
111 [email protected] "\n " 115 [email protected] "\n "
112 [email protected] "//! Nor are ModuleDoc ..." 116 [email protected] "//! Nor are ModuleDoc ..."
diff --git a/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast b/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast
index 672dd054a..b6209639d 100644
--- a/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast
+++ b/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast
@@ -135,14 +135,15 @@ [email protected]
135 [email protected] 135 [email protected]
136 [email protected] "#" 136 [email protected] "#"
137 [email protected] "[" 137 [email protected] "["
138 [email protected] 138 [email protected]
139 [email protected] 139 [email protected]
140 [email protected] 140 [email protected]
141 [email protected] "cfg" 141 [email protected]
142 [email protected] 142 [email protected] "cfg"
143 [email protected] "(" 143 [email protected]
144 [email protected] "test" 144 [email protected] "("
145 [email protected] ")" 145 [email protected] "test"
146 [email protected] ")"
146 [email protected] "]" 147 [email protected] "]"
147 [email protected] "\n " 148 [email protected] "\n "
148 [email protected] 149 [email protected]
diff --git a/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast b/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast
index 33bb085e9..84c8e9ee7 100644
--- a/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast
+++ b/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast
@@ -47,14 +47,15 @@ [email protected]
47 [email protected] 47 [email protected]
48 [email protected] "#" 48 [email protected] "#"
49 [email protected] "[" 49 [email protected] "["
50 [email protected] 50 [email protected]
51 [email protected] 51 [email protected]
52 [email protected] 52 [email protected]
53 [email protected] "cfg" 53 [email protected]
54 [email protected] 54 [email protected] "cfg"
55 [email protected] "(" 55 [email protected]
56 [email protected] "test" 56 [email protected] "("
57 [email protected] ")" 57 [email protected] "test"
58 [email protected] ")"
58 [email protected] "]" 59 [email protected] "]"
59 [email protected] "\n " 60 [email protected] "\n "
60 [email protected] "}" 61 [email protected] "}"
diff --git a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast b/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
index 8fd8d5e59..7b8b7284f 100644
--- a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
+++ b/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
@@ -15,10 +15,11 @@ [email protected]
15 [email protected] 15 [email protected]
16 [email protected] "#" 16 [email protected] "#"
17 [email protected] "[" 17 [email protected] "["
18 [email protected] 18 [email protected]
19 [email protected] 19 [email protected]
20 [email protected] 20 [email protected]
21 [email protected] "A" 21 [email protected]
22 [email protected] "A"
22 [email protected] "]" 23 [email protected] "]"
23 [email protected] " " 24 [email protected] " "
24 [email protected] 25 [email protected]
@@ -35,10 +36,11 @@ [email protected]
35 [email protected] 36 [email protected]
36 [email protected] "#" 37 [email protected] "#"
37 [email protected] "[" 38 [email protected] "["
38 [email protected] 39 [email protected]
39 [email protected] 40 [email protected]
40 [email protected] 41 [email protected]
41 [email protected] "B" 42 [email protected]
43 [email protected] "B"
42 [email protected] "]" 44 [email protected] "]"
43 [email protected] " " 45 [email protected] " "
44 [email protected] 46 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast
index 9ae271817..402950bcc 100644
--- a/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast
@@ -12,18 +12,19 @@ [email protected]
12 [email protected] 12 [email protected]
13 [email protected] "#" 13 [email protected] "#"
14 [email protected] "[" 14 [email protected] "["
15 [email protected] 15 [email protected]
16 [email protected] 16 [email protected]
17 [email protected] 17 [email protected]
18 [email protected] "serde" 18 [email protected]
19 [email protected] 19 [email protected] "serde"
20 [email protected] "(" 20 [email protected]
21 [email protected] "with" 21 [email protected] "("
22 [email protected] " " 22 [email protected] "with"
23 [email protected] "=" 23 [email protected] " "
24 [email protected] " " 24 [email protected] "="
25 [email protected] "\"url_serde\"" 25 [email protected] " "
26 [email protected] ")" 26 [email protected] "\"url_serde\""
27 [email protected] ")"
27 [email protected] "]" 28 [email protected] "]"
28 [email protected] "\n " 29 [email protected] "\n "
29 [email protected] 30 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast
index 4d09c9f50..db2b645b0 100644
--- a/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast
@@ -12,18 +12,19 @@ [email protected]
12 [email protected] 12 [email protected]
13 [email protected] "#" 13 [email protected] "#"
14 [email protected] "[" 14 [email protected] "["
15 [email protected] 15 [email protected]
16 [email protected] 16 [email protected]
17 [email protected] 17 [email protected]
18 [email protected] "serde" 18 [email protected]
19 [email protected] 19 [email protected] "serde"
20 [email protected] "(" 20 [email protected]
21 [email protected] "with" 21 [email protected] "("
22 [email protected] " " 22 [email protected] "with"
23 [email protected] "=" 23 [email protected] " "
24 [email protected] " " 24 [email protected] "="
25 [email protected] "\"url_serde\"" 25 [email protected] " "
26 [email protected] ")" 26 [email protected] "\"url_serde\""
27 [email protected] ")"
27 [email protected] "]" 28 [email protected] "]"
28 [email protected] "\n " 29 [email protected] "\n "
29 [email protected] 30 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast b/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast
index 141a7b203..24ac1d66a 100644
--- a/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast
@@ -26,14 +26,15 @@ [email protected]
26 [email protected] "#" 26 [email protected] "#"
27 [email protected] "!" 27 [email protected] "!"
28 [email protected] "[" 28 [email protected] "["
29 [email protected] 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] 31 [email protected]
32 [email protected] "doc" 32 [email protected]
33 [email protected] 33 [email protected] "doc"
34 [email protected] "(" 34 [email protected]
35 [email protected] "\"This is also a doc c ..." 35 [email protected] "("
36 [email protected] ")" 36 [email protected] "\"This is also a doc c ..."
37 [email protected] ")"
37 [email protected] "]" 38 [email protected] "]"
38 [email protected] "\n" 39 [email protected] "\n"
39 [email protected] "}" 40 [email protected] "}"
diff --git a/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast b/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast
index ec7a00f1d..7fbeee203 100644
--- a/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast
@@ -25,42 +25,45 @@ [email protected]
25 [email protected] "#" 25 [email protected] "#"
26 [email protected] "!" 26 [email protected] "!"
27 [email protected] "[" 27 [email protected] "["
28 [email protected] 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] "doc" 31 [email protected]
32 [email protected] 32 [email protected] "doc"
33 [email protected] "(" 33 [email protected]
34 [email protected] "\"Inner attribute\"" 34 [email protected] "("
35 [email protected] ")" 35 [email protected] "\"Inner attribute\""
36 [email protected] ")"
36 [email protected] "]" 37 [email protected] "]"
37 [email protected] "\n " 38 [email protected] "\n "
38 [email protected] 39 [email protected]
39 [email protected] "#" 40 [email protected] "#"
40 [email protected] "!" 41 [email protected] "!"
41 [email protected] "[" 42 [email protected] "["
42 [email protected] 43 [email protected]
43 [email protected] 44 [email protected]
44 [email protected] 45 [email protected]
45 [email protected] "doc" 46 [email protected]
46 [email protected] 47 [email protected] "doc"
47 [email protected] "(" 48 [email protected]
48 [email protected] "\"Can be\"" 49 [email protected] "("
49 [email protected] ")" 50 [email protected] "\"Can be\""
51 [email protected] ")"
50 [email protected] "]" 52 [email protected] "]"
51 [email protected] "\n " 53 [email protected] "\n "
52 [email protected] 54 [email protected]
53 [email protected] "#" 55 [email protected] "#"
54 [email protected] "!" 56 [email protected] "!"
55 [email protected] "[" 57 [email protected] "["
56 [email protected] 58 [email protected]
57 [email protected] 59 [email protected]
58 [email protected] 60 [email protected]
59 [email protected] "doc" 61 [email protected]
60 [email protected] 62 [email protected] "doc"
61 [email protected] "(" 63 [email protected]
62 [email protected] "\"Stacked\"" 64 [email protected] "("
63 [email protected] ")" 65 [email protected] "\"Stacked\""
66 [email protected] ")"
64 [email protected] "]" 67 [email protected] "]"
65 [email protected] "\n " 68 [email protected] "\n "
66 [email protected] 69 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast b/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast
index 97924da05..40852f514 100644
--- a/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast
@@ -25,18 +25,19 @@ [email protected]
25 [email protected] 25 [email protected]
26 [email protected] "#" 26 [email protected] "#"
27 [email protected] "[" 27 [email protected] "["
28 [email protected] 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] "cfg" 31 [email protected]
32 [email protected] 32 [email protected] "cfg"
33 [email protected] "(" 33 [email protected]
34 [email protected] "feature" 34 [email protected] "("
35 [email protected] " " 35 [email protected] "feature"
36 [email protected] "=" 36 [email protected] " "
37 [email protected] " " 37 [email protected] "="
38 [email protected] "\"some\"" 38 [email protected] " "
39 [email protected] ")" 39 [email protected] "\"some\""
40 [email protected] ")"
40 [email protected] "]" 41 [email protected] "]"
41 [email protected] "\n " 42 [email protected] "\n "
42 [email protected] 43 [email protected]
@@ -53,18 +54,19 @@ [email protected]
53 [email protected] 54 [email protected]
54 [email protected] "#" 55 [email protected] "#"
55 [email protected] "[" 56 [email protected] "["
56 [email protected] 57 [email protected]
57 [email protected] 58 [email protected]
58 [email protected] 59 [email protected]
59 [email protected] "cfg" 60 [email protected]
60 [email protected] 61 [email protected] "cfg"
61 [email protected] "(" 62 [email protected]
62 [email protected] "feature" 63 [email protected] "("
63 [email protected] " " 64 [email protected] "feature"
64 [email protected] "=" 65 [email protected] " "
65 [email protected] " " 66 [email protected] "="
66 [email protected] "\"other\"" 67 [email protected] " "
67 [email protected] ")" 68 [email protected] "\"other\""
69 [email protected] ")"
68 [email protected] "]" 70 [email protected] "]"
69 [email protected] "\n " 71 [email protected] "\n "
70 [email protected] 72 [email protected]
@@ -81,52 +83,55 @@ [email protected]
81 [email protected] 83 [email protected]
82 [email protected] "#" 84 [email protected] "#"
83 [email protected] "[" 85 [email protected] "["
84 [email protected] 86 [email protected]
85 [email protected] 87 [email protected]
86 [email protected] 88 [email protected]
87 [email protected] "cfg" 89 [email protected]
88 [email protected] 90 [email protected] "cfg"
89 [email protected] "(" 91 [email protected]
90 [email protected] "feature" 92 [email protected] "("
91 [email protected] " " 93 [email protected] "feature"
92 [email protected] "=" 94 [email protected] " "
93 [email protected] " " 95 [email protected] "="
94 [email protected] "\"many\"" 96 [email protected] " "
95 [email protected] ")" 97 [email protected] "\"many\""
98 [email protected] ")"
96 [email protected] "]" 99 [email protected] "]"
97 [email protected] "\n " 100 [email protected] "\n "
98 [email protected] 101 [email protected]
99 [email protected] "#" 102 [email protected] "#"
100 [email protected] "[" 103 [email protected] "["
101 [email protected] 104 [email protected]
102 [email protected] 105 [email protected]
103 [email protected] 106 [email protected]
104 [email protected] "cfg" 107 [email protected]
105 [email protected] 108 [email protected] "cfg"
106 [email protected] "(" 109 [email protected]
107 [email protected] "feature" 110 [email protected] "("
108 [email protected] " " 111 [email protected] "feature"
109 [email protected] "=" 112 [email protected] " "
110 [email protected] " " 113 [email protected] "="
111 [email protected] "\"attributes\"" 114 [email protected] " "
112 [email protected] ")" 115 [email protected] "\"attributes\""
116 [email protected] ")"
113 [email protected] "]" 117 [email protected] "]"
114 [email protected] "\n " 118 [email protected] "\n "
115 [email protected] 119 [email protected]
116 [email protected] "#" 120 [email protected] "#"
117 [email protected] "[" 121 [email protected] "["
118 [email protected] 122 [email protected]
119 [email protected] 123 [email protected]
120 [email protected] 124 [email protected]
121 [email protected] "cfg" 125 [email protected]
122 [email protected] 126 [email protected] "cfg"
123 [email protected] "(" 127 [email protected]
124 [email protected] "feature" 128 [email protected] "("
125 [email protected] " " 129 [email protected] "feature"
126 [email protected] "=" 130 [email protected] " "
127 [email protected] " " 131 [email protected] "="
128 [email protected] "\"before\"" 132 [email protected] " "
129 [email protected] ")" 133 [email protected] "\"before\""
134 [email protected] ")"
130 [email protected] "]" 135 [email protected] "]"
131 [email protected] "\n " 136 [email protected] "\n "
132 [email protected] 137 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast
index 616aa984e..840181383 100644
--- a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast
@@ -10,14 +10,15 @@ [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "#" 11 [email protected] "#"
12 [email protected] "[" 12 [email protected] "["
13 [email protected] 13 [email protected]
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected]
16 [email protected] "derive" 16 [email protected]
17 [email protected] 17 [email protected] "derive"
18 [email protected] "(" 18 [email protected]
19 [email protected] "Lifetime" 19 [email protected] "("
20 [email protected] ")" 20 [email protected] "Lifetime"
21 [email protected] ")"
21 [email protected] "]" 22 [email protected] "]"
22 [email protected] " " 23 [email protected] " "
23 [email protected] 24 [email protected]
@@ -28,14 +29,15 @@ [email protected]
28 [email protected] 29 [email protected]
29 [email protected] "#" 30 [email protected] "#"
30 [email protected] "[" 31 [email protected] "["
31 [email protected] 32 [email protected]
32 [email protected] 33 [email protected]
33 [email protected] 34 [email protected]
34 [email protected] "derive" 35 [email protected]
35 [email protected] 36 [email protected] "derive"
36 [email protected] "(" 37 [email protected]
37 [email protected] "Type" 38 [email protected] "("
38 [email protected] ")" 39 [email protected] "Type"
40 [email protected] ")"
39 [email protected] "]" 41 [email protected] "]"
40 [email protected] " " 42 [email protected] " "
41 [email protected] 43 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast b/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast
index 54ea2c7c6..5e82214c0 100644
--- a/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast
@@ -24,14 +24,15 @@ [email protected]
24 [email protected] 24 [email protected]
25 [email protected] "#" 25 [email protected] "#"
26 [email protected] "[" 26 [email protected] "["
27 [email protected] 27 [email protected]
28 [email protected] 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] "cfg" 30 [email protected]
31 [email protected] 31 [email protected] "cfg"
32 [email protected] "(" 32 [email protected]
33 [email protected] "test" 33 [email protected] "("
34 [email protected] ")" 34 [email protected] "test"
35 [email protected] ")"
35 [email protected] "]" 36 [email protected] "]"
36 [email protected] " " 37 [email protected] " "
37 [email protected] 38 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast b/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast
index 0342e64f3..178204fec 100644
--- a/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast
@@ -15,10 +15,11 @@ [email protected]
15 [email protected] 15 [email protected]
16 [email protected] "#" 16 [email protected] "#"
17 [email protected] "[" 17 [email protected] "["
18 [email protected] 18 [email protected]
19 [email protected] 19 [email protected]
20 [email protected] 20 [email protected]
21 [email protected] "A" 21 [email protected]
22 [email protected] "A"
22 [email protected] "]" 23 [email protected] "]"
23 [email protected] " " 24 [email protected] " "
24 [email protected] 25 [email protected]
@@ -36,10 +37,11 @@ [email protected]
36 [email protected] 37 [email protected]
37 [email protected] "#" 38 [email protected] "#"
38 [email protected] "[" 39 [email protected] "["
39 [email protected] 40 [email protected]
40 [email protected] 41 [email protected]
41 [email protected] 42 [email protected]
42 [email protected] "B" 43 [email protected]
44 [email protected] "B"
43 [email protected] "]" 45 [email protected] "]"
44 [email protected] " " 46 [email protected] " "
45 [email protected] 47 [email protected]
@@ -56,19 +58,21 @@ [email protected]
56 [email protected] 58 [email protected]
57 [email protected] "#" 59 [email protected] "#"
58 [email protected] "[" 60 [email protected] "["
59 [email protected] 61 [email protected]
60 [email protected] 62 [email protected]
61 [email protected] 63 [email protected]
62 [email protected] "C" 64 [email protected]
65 [email protected] "C"
63 [email protected] "]" 66 [email protected] "]"
64 [email protected] " " 67 [email protected] " "
65 [email protected] 68 [email protected]
66 [email protected] "#" 69 [email protected] "#"
67 [email protected] "[" 70 [email protected] "["
68 [email protected] 71 [email protected]
69 [email protected] 72 [email protected]
70 [email protected] 73 [email protected]
71 [email protected] "D" 74 [email protected]
75 [email protected] "D"
72 [email protected] "]" 76 [email protected] "]"
73 [email protected] " " 77 [email protected] " "
74 [email protected] 78 [email protected]
@@ -79,10 +83,11 @@ [email protected]
79 [email protected] 83 [email protected]
80 [email protected] "#" 84 [email protected] "#"
81 [email protected] "[" 85 [email protected] "["
82 [email protected] 86 [email protected]
83 [email protected] 87 [email protected]
84 [email protected] 88 [email protected]
85 [email protected] "D" 89 [email protected]
90 [email protected] "D"
86 [email protected] "]" 91 [email protected] "]"
87 [email protected] " " 92 [email protected] " "
88 [email protected] 93 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast b/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast
index 3b46e5b47..9daac234a 100644
--- a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast
@@ -19,10 +19,11 @@ [email protected]
19 [email protected] 19 [email protected]
20 [email protected] "#" 20 [email protected] "#"
21 [email protected] "[" 21 [email protected] "["
22 [email protected] 22 [email protected]
23 [email protected] 23 [email protected]
24 [email protected] 24 [email protected]
25 [email protected] "A" 25 [email protected]
26 [email protected] "A"
26 [email protected] "]" 27 [email protected] "]"
27 [email protected] " " 28 [email protected] " "
28 [email protected] 29 [email protected]
@@ -42,10 +43,11 @@ [email protected]
42 [email protected] 43 [email protected]
43 [email protected] "#" 44 [email protected] "#"
44 [email protected] "[" 45 [email protected] "["
45 [email protected] 46 [email protected]
46 [email protected] 47 [email protected]
47 [email protected] 48 [email protected]
48 [email protected] "B" 49 [email protected]
50 [email protected] "B"
49 [email protected] "]" 51 [email protected] "]"
50 [email protected] " " 52 [email protected] " "
51 [email protected] "&" 53 [email protected] "&"
diff --git a/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast b/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast
index 4c07cefa6..c3a79836a 100644
--- a/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast
@@ -109,10 +109,11 @@ [email protected]
109 [email protected] 109 [email protected]
110 [email protected] "#" 110 [email protected] "#"
111 [email protected] "[" 111 [email protected] "["
112 [email protected] 112 [email protected]
113 [email protected] 113 [email protected]
114 [email protected] 114 [email protected]
115 [email protected] "attr" 115 [email protected]
116 [email protected] "attr"
116 [email protected] "]" 117 [email protected] "]"
117 [email protected] 118 [email protected]
118 [email protected] "|" 119 [email protected] "|"
diff --git a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
index c54e64e3f..891eace59 100644
--- a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
@@ -10,10 +10,11 @@ [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "#" 11 [email protected] "#"
12 [email protected] "[" 12 [email protected] "["
13 [email protected] 13 [email protected]
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected]
16 [email protected] "must_use" 16 [email protected]
17 [email protected] "must_use"
17 [email protected] "]" 18 [email protected] "]"
18 [email protected] " " 19 [email protected] " "
19 [email protected] 20 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast
index a84088bf3..a363e592b 100644
--- a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast
@@ -10,10 +10,11 @@ [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "#" 11 [email protected] "#"
12 [email protected] "[" 12 [email protected] "["
13 [email protected] 13 [email protected]
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected]
16 [email protected] "attr1" 16 [email protected]
17 [email protected] "attr1"
17 [email protected] "]" 18 [email protected] "]"
18 [email protected] " " 19 [email protected] " "
19 [email protected] 20 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
index e9202a612..c606a7c9b 100644
--- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
@@ -87,17 +87,18 @@ [email protected]
87 [email protected] 87 [email protected]
88 [email protected] "#" 88 [email protected] "#"
89 [email protected] "[" 89 [email protected] "["
90 [email protected] 90 [email protected]
91 [email protected] 91 [email protected]
92 [email protected] 92 [email protected]
93 [email protected] "cfg" 93 [email protected]
94 [email protected] 94 [email protected] "cfg"
95 [email protected] "(" 95 [email protected]
96 [email protected] "any" 96 [email protected] "("
97 [email protected] 97 [email protected] "any"
98 [email protected] "(" 98 [email protected]
99 [email protected] ")" 99 [email protected] "("
100 [email protected] ")" 100 [email protected] ")"
101 [email protected] ")"
101 [email protected] "]" 102 [email protected] "]"
102 [email protected] " " 103 [email protected] " "
103 [email protected] 104 [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast
index f284aafcd..26cdc2945 100644
--- a/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast
@@ -31,14 +31,15 @@ [email protected]
31 [email protected] 31 [email protected]
32 [email protected] "#" 32 [email protected] "#"
33 [email protected] "[" 33 [email protected] "["
34 [email protected] 34 [email protected]
35 [email protected] 35 [email protected]
36 [email protected] 36 [email protected]
37 [email protected] "cfg" 37 [email protected]
38 [email protected] 38 [email protected] "cfg"
39 [email protected] "(" 39 [email protected]
40 [email protected] "test" 40 [email protected] "("
41 [email protected] ")" 41 [email protected] "test"
42 [email protected] ")"
42 [email protected] "]" 43 [email protected] "]"
43 [email protected] " " 44 [email protected] " "
44 [email protected] "2" 45 [email protected] "2"
diff --git a/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast b/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast
index 2905c5f1a..1d20765b0 100644
--- a/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast
@@ -23,10 +23,11 @@ [email protected]
23 [email protected] 23 [email protected]
24 [email protected] "#" 24 [email protected] "#"
25 [email protected] "[" 25 [email protected] "["
26 [email protected] 26 [email protected]
27 [email protected] 27 [email protected]
28 [email protected] 28 [email protected]
29 [email protected] "attr" 29 [email protected]
30 [email protected] "attr"
30 [email protected] "]" 31 [email protected] "]"
31 [email protected] " " 32 [email protected] " "
32 [email protected] "92" 33 [email protected] "92"
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast
index d34b21abe..3b6612677 100644
--- a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast
@@ -34,14 +34,15 @@ [email protected]
34 [email protected] 34 [email protected]
35 [email protected] "#" 35 [email protected] "#"
36 [email protected] "[" 36 [email protected] "["
37 [email protected] 37 [email protected]
38 [email protected] 38 [email protected]
39 [email protected] 39 [email protected]
40 [email protected] "cfg" 40 [email protected]
41 [email protected] 41 [email protected] "cfg"
42 [email protected] "(" 42 [email protected]
43 [email protected] "test" 43 [email protected] "("
44 [email protected] ")" 44 [email protected] "test"
45 [email protected] ")"
45 [email protected] "]" 46 [email protected] "]"
46 [email protected] " " 47 [email protected] " "
47 [email protected] "2" 48 [email protected] "2"
diff --git a/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast b/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast
index 42587243a..be2d1dc12 100644
--- a/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast
+++ b/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast
@@ -3,182 +3,192 @@ [email protected]
3 [email protected] "#" 3 [email protected] "#"
4 [email protected] "!" 4 [email protected] "!"
5 [email protected] "[" 5 [email protected] "["
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] 8 [email protected]
9 [email protected] "attr" 9 [email protected]
10 [email protected] "attr"
10 [email protected] "]" 11 [email protected] "]"
11 [email protected] "\n" 12 [email protected] "\n"
12 [email protected] 13 [email protected]
13 [email protected] "#" 14 [email protected] "#"
14 [email protected] "!" 15 [email protected] "!"
15 [email protected] "[" 16 [email protected] "["
16 [email protected] 17 [email protected]
17 [email protected] 18 [email protected]
18 [email protected] 19 [email protected]
19 [email protected] "attr" 20 [email protected]
20 [email protected] 21 [email protected] "attr"
21 [email protected] "(" 22 [email protected]
22 [email protected] "true" 23 [email protected] "("
23 [email protected] ")" 24 [email protected] "true"
25 [email protected] ")"
24 [email protected] "]" 26 [email protected] "]"
25 [email protected] "\n" 27 [email protected] "\n"
26 [email protected] 28 [email protected]
27 [email protected] "#" 29 [email protected] "#"
28 [email protected] "!" 30 [email protected] "!"
29 [email protected] "[" 31 [email protected] "["
30 [email protected] 32 [email protected]
31 [email protected] 33 [email protected]
32 [email protected] 34 [email protected]
33 [email protected] "attr" 35 [email protected]
34 [email protected] 36 [email protected] "attr"
35 [email protected] "(" 37 [email protected]
36 [email protected] "ident" 38 [email protected] "("
37 [email protected] ")" 39 [email protected] "ident"
40 [email protected] ")"
38 [email protected] "]" 41 [email protected] "]"
39 [email protected] "\n" 42 [email protected] "\n"
40 [email protected] 43 [email protected]
41 [email protected] "#" 44 [email protected] "#"
42 [email protected] "!" 45 [email protected] "!"
43 [email protected] "[" 46 [email protected] "["
44 [email protected] 47 [email protected]
45 [email protected] 48 [email protected]
46 [email protected] 49 [email protected]
47 [email protected] "attr" 50 [email protected]
48 [email protected] 51 [email protected] "attr"
49 [email protected] "(" 52 [email protected]
50 [email protected] "ident" 53 [email protected] "("
51 [email protected] "," 54 [email protected] "ident"
52 [email protected] " " 55 [email protected] ","
53 [email protected] "100" 56 [email protected] " "
54 [email protected] "," 57 [email protected] "100"
55 [email protected] " " 58 [email protected] ","
56 [email protected] "true" 59 [email protected] " "
57 [email protected] "," 60 [email protected] "true"
58 [email protected] " " 61 [email protected] ","
59 [email protected] "\"true\"" 62 [email protected] " "
60 [email protected] "," 63 [email protected] "\"true\""
61 [email protected] " " 64 [email protected] ","
62 [email protected] "ident" 65 [email protected] " "
63 [email protected] " " 66 [email protected] "ident"
64 [email protected] "=" 67 [email protected] " "
65 [email protected] " " 68 [email protected] "="
66 [email protected] "100" 69 [email protected] " "
67 [email protected] "," 70 [email protected] "100"
68 [email protected] " " 71 [email protected] ","
69 [email protected] "ident" 72 [email protected] " "
70 [email protected] " " 73 [email protected] "ident"
71 [email protected] "=" 74 [email protected] " "
72 [email protected] " " 75 [email protected] "="
73 [email protected] "\"hello\"" 76 [email protected] " "
74 [email protected] "," 77 [email protected] "\"hello\""
75 [email protected] " " 78 [email protected] ","
76 [email protected] "ident" 79 [email protected] " "
77 [email protected] 80 [email protected] "ident"
78 [email protected] "(" 81 [email protected]
79 [email protected] "100" 82 [email protected] "("
80 [email protected] ")" 83 [email protected] "100"
81 [email protected] ")" 84 [email protected] ")"
85 [email protected] ")"
82 [email protected] "]" 86 [email protected] "]"
83 [email protected] "\n" 87 [email protected] "\n"
84 [email protected] 88 [email protected]
85 [email protected] "#" 89 [email protected] "#"
86 [email protected] "!" 90 [email protected] "!"
87 [email protected] "[" 91 [email protected] "["
88 [email protected] 92 [email protected]
89 [email protected] 93 [email protected]
90 [email protected] 94 [email protected]
91 [email protected] "attr" 95 [email protected]
92 [email protected] 96 [email protected] "attr"
93 [email protected] "(" 97 [email protected]
94 [email protected] "100" 98 [email protected] "("
95 [email protected] ")" 99 [email protected] "100"
100 [email protected] ")"
96 [email protected] "]" 101 [email protected] "]"
97 [email protected] "\n" 102 [email protected] "\n"
98 [email protected] 103 [email protected]
99 [email protected] "#" 104 [email protected] "#"
100 [email protected] "!" 105 [email protected] "!"
101 [email protected] "[" 106 [email protected] "["
102 [email protected] 107 [email protected]
103 [email protected] 108 [email protected]
104 [email protected] 109 [email protected]
105 [email protected] "attr" 110 [email protected]
106 [email protected] 111 [email protected] "attr"
107 [email protected] "(" 112 [email protected]
108 [email protected] "enabled" 113 [email protected] "("
109 [email protected] " " 114 [email protected] "enabled"
110 [email protected] "=" 115 [email protected] " "
111 [email protected] " " 116 [email protected] "="
112 [email protected] "true" 117 [email protected] " "
113 [email protected] ")" 118 [email protected] "true"
119 [email protected] ")"
114 [email protected] "]" 120 [email protected] "]"
115 [email protected] "\n" 121 [email protected] "\n"
116 [email protected] 122 [email protected]
117 [email protected] "#" 123 [email protected] "#"
118 [email protected] "!" 124 [email protected] "!"
119 [email protected] "[" 125 [email protected] "["
120 [email protected] 126 [email protected]
121 [email protected] 127 [email protected]
122 [email protected] 128 [email protected]
123 [email protected] "enabled" 129 [email protected]
124 [email protected] 130 [email protected] "enabled"
125 [email protected] "(" 131 [email protected]
126 [email protected] "true" 132 [email protected] "("
127 [email protected] ")" 133 [email protected] "true"
134 [email protected] ")"
128 [email protected] "]" 135 [email protected] "]"
129 [email protected] "\n" 136 [email protected] "\n"
130 [email protected] 137 [email protected]
131 [email protected] "#" 138 [email protected] "#"
132 [email protected] "!" 139 [email protected] "!"
133 [email protected] "[" 140 [email protected] "["
134 [email protected] 141 [email protected]
135 [email protected] 142 [email protected]
136 [email protected] 143 [email protected]
137 [email protected] "attr" 144 [email protected]
138 [email protected] 145 [email protected] "attr"
139 [email protected] "(" 146 [email protected]
140 [email protected] "\"hello\"" 147 [email protected] "("
141 [email protected] ")" 148 [email protected] "\"hello\""
149 [email protected] ")"
142 [email protected] "]" 150 [email protected] "]"
143 [email protected] "\n" 151 [email protected] "\n"
144 [email protected] 152 [email protected]
145 [email protected] "#" 153 [email protected] "#"
146 [email protected] "!" 154 [email protected] "!"
147 [email protected] "[" 155 [email protected] "["
148 [email protected] 156 [email protected]
149 [email protected] 157 [email protected]
150 [email protected] 158 [email protected]
151 [email protected] "repr" 159 [email protected]
152 [email protected] 160 [email protected] "repr"
153 [email protected] "(" 161 [email protected]
154 [email protected] "C" 162 [email protected] "("
155 [email protected] "," 163 [email protected] "C"
156 [email protected] " " 164 [email protected] ","
157 [email protected] "align" 165 [email protected] " "
158 [email protected] " " 166 [email protected] "align"
159 [email protected] "=" 167 [email protected] " "
160 [email protected] " " 168 [email protected] "="
161 [email protected] "4" 169 [email protected] " "
162 [email protected] ")" 170 [email protected] "4"
171 [email protected] ")"
163 [email protected] "]" 172 [email protected] "]"
164 [email protected] "\n" 173 [email protected] "\n"
165 [email protected] 174 [email protected]
166 [email protected] "#" 175 [email protected] "#"
167 [email protected] "!" 176 [email protected] "!"
168 [email protected] "[" 177 [email protected] "["
169 [email protected] 178 [email protected]
170 [email protected] 179 [email protected]
171 [email protected] 180 [email protected]
172 [email protected] "repr" 181 [email protected]
173 [email protected] 182 [email protected] "repr"
174 [email protected] "(" 183 [email protected]
175 [email protected] "C" 184 [email protected] "("
176 [email protected] "," 185 [email protected] "C"
177 [email protected] " " 186 [email protected] ","
178 [email protected] "align" 187 [email protected] " "
179 [email protected] 188 [email protected] "align"
180 [email protected] "(" 189 [email protected]
181 [email protected] "4" 190 [email protected] "("
182 [email protected] ")" 191 [email protected] "4"
183 [email protected] ")" 192 [email protected] ")"
193 [email protected] ")"
184 [email protected] "]" 194 [email protected] "]"
diff --git a/crates/syntax/test_data/parser/ok/0008_mod_item.rast b/crates/syntax/test_data/parser/ok/0008_mod_item.rast
index b2c1d791f..8b1e0a52d 100644
--- a/crates/syntax/test_data/parser/ok/0008_mod_item.rast
+++ b/crates/syntax/test_data/parser/ok/0008_mod_item.rast
@@ -65,10 +65,11 @@ [email protected]
65 [email protected] "#" 65 [email protected] "#"
66 [email protected] "!" 66 [email protected] "!"
67 [email protected] "[" 67 [email protected] "["
68 [email protected] 68 [email protected]
69 [email protected] 69 [email protected]
70 [email protected] 70 [email protected]
71 [email protected] "attr" 71 [email protected]
72 [email protected] "attr"
72 [email protected] "]" 73 [email protected] "]"
73 [email protected] "\n " 74 [email protected] "\n "
74 [email protected] 75 [email protected]
diff --git a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast
index 478fdba75..ff5877a7b 100644
--- a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast
+++ b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast
@@ -3,23 +3,25 @@ [email protected]
3 [email protected] 3 [email protected]
4 [email protected] "#" 4 [email protected] "#"
5 [email protected] "[" 5 [email protected] "["
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] 8 [email protected]
9 [email protected] "cfg" 9 [email protected]
10 [email protected] 10 [email protected] "cfg"
11 [email protected] "(" 11 [email protected]
12 [email protected] "test" 12 [email protected] "("
13 [email protected] ")" 13 [email protected] "test"
14 [email protected] ")"
14 [email protected] "]" 15 [email protected] "]"
15 [email protected] "\n" 16 [email protected] "\n"
16 [email protected] 17 [email protected]
17 [email protected] "#" 18 [email protected] "#"
18 [email protected] "[" 19 [email protected] "["
19 [email protected] 20 [email protected]
20 [email protected] 21 [email protected]
21 [email protected] 22 [email protected]
22 [email protected] "ignore" 23 [email protected]
24 [email protected] "ignore"
23 [email protected] "]" 25 [email protected] "]"
24 [email protected] "\n" 26 [email protected] "\n"
25 [email protected] "fn" 27 [email protected] "fn"
@@ -38,15 +40,16 @@ [email protected]
38 [email protected] 40 [email protected]
39 [email protected] "#" 41 [email protected] "#"
40 [email protected] "[" 42 [email protected] "["
41 [email protected] 43 [email protected]
42 [email protected] 44 [email protected]
43 [email protected] 45 [email protected]
44 [email protected] "path" 46 [email protected]
45 [email protected] " " 47 [email protected] "path"
46 [email protected] "=" 48 [email protected] " "
47 [email protected] " " 49 [email protected] "="
48 [email protected] 50 [email protected] " "
49 [email protected] "\"a.rs\"" 51 [email protected]
52 [email protected] "\"a.rs\""
50 [email protected] "]" 53 [email protected] "]"
51 [email protected] "\n" 54 [email protected] "\n"
52 [email protected] "mod" 55 [email protected] "mod"
diff --git a/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast b/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast
index a3e091ad3..94791f771 100644
--- a/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast
+++ b/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast
@@ -3,15 +3,16 @@ [email protected]
3 [email protected] 3 [email protected]
4 [email protected] "#" 4 [email protected] "#"
5 [email protected] "[" 5 [email protected] "["
6 [email protected] 6 [email protected]
7 [email protected] 7 [email protected]
8 [email protected] 8 [email protected]
9 [email protected] "foo" 9 [email protected]
10 [email protected] 10 [email protected] "foo"
11 [email protected] "(" 11 [email protected]
12 [email protected] "a" 12 [email protected] "("
13 [email protected] "," 13 [email protected] "a"
14 [email protected] ")" 14 [email protected] ","
15 [email protected] ")"
15 [email protected] "]" 16 [email protected] "]"
16 [email protected] "\n" 17 [email protected] "\n"
17 [email protected] "fn" 18 [email protected] "fn"
diff --git a/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast b/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast
index 46b192dc1..20675dbf5 100644
--- a/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast
+++ b/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast
@@ -11,71 +11,76 @@ [email protected]
11 [email protected] "#" 11 [email protected] "#"
12 [email protected] "!" 12 [email protected] "!"
13 [email protected] "[" 13 [email protected] "["
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected]
16 [email protected] 16 [email protected]
17 [email protected] "allow" 17 [email protected]
18 [email protected] 18 [email protected] "allow"
19 [email protected] "(" 19 [email protected]
20 [email protected] "non_camel_case_types" 20 [email protected] "("
21 [email protected] ")" 21 [email protected] "non_camel_case_types"
22 [email protected] ")"
22 [email protected] "]" 23 [email protected] "]"
23 [email protected] "\n" 24 [email protected] "\n"
24 [email protected] 25 [email protected]
25 [email protected] "#" 26 [email protected] "#"
26 [email protected] "!" 27 [email protected] "!"
27 [email protected] "[" 28 [email protected] "["
28 [email protected] 29 [email protected]
29 [email protected] 30 [email protected]
30 [email protected] 31 [email protected]
31 [email protected] "allow" 32 [email protected]
32 [email protected] 33 [email protected] "allow"
33 [email protected] "(" 34 [email protected]
34 [email protected] "dead_code" 35 [email protected] "("
35 [email protected] ")" 36 [email protected] "dead_code"
37 [email protected] ")"
36 [email protected] "]" 38 [email protected] "]"
37 [email protected] "\n" 39 [email protected] "\n"
38 [email protected] 40 [email protected]
39 [email protected] "#" 41 [email protected] "#"
40 [email protected] "!" 42 [email protected] "!"
41 [email protected] "[" 43 [email protected] "["
42 [email protected] 44 [email protected]
43 [email protected] 45 [email protected]
44 [email protected] 46 [email protected]
45 [email protected] "allow" 47 [email protected]
46 [email protected] 48 [email protected] "allow"
47 [email protected] "(" 49 [email protected]
48 [email protected] "unreachable_code" 50 [email protected] "("
49 [email protected] ")" 51 [email protected] "unreachable_code"
52 [email protected] ")"
50 [email protected] "]" 53 [email protected] "]"
51 [email protected] "\n" 54 [email protected] "\n"
52 [email protected] 55 [email protected]
53 [email protected] "#" 56 [email protected] "#"
54 [email protected] "!" 57 [email protected] "!"
55 [email protected] "[" 58 [email protected] "["
56 [email protected] 59 [email protected]
57 [email protected] 60 [email protected]
58 [email protected] 61 [email protected]
59 [email protected] "allow" 62 [email protected]
60 [email protected] 63 [email protected] "allow"
61 [email protected] "(" 64 [email protected]
62 [email protected] "unused_parens" 65 [email protected] "("
63 [email protected] ")" 66 [email protected] "unused_parens"
67 [email protected] ")"
64 [email protected] "]" 68 [email protected] "]"
65 [email protected] "\n\n" 69 [email protected] "\n\n"
66 [email protected] 70 [email protected]
67 [email protected] "#" 71 [email protected] "#"
68 [email protected] "!" 72 [email protected] "!"
69 [email protected] "[" 73 [email protected] "["
70 [email protected] 74 [email protected]
71 [email protected] 75 [email protected]
72 [email protected] 76 [email protected]
73 [email protected] "recursion_limit" 77 [email protected]
74 [email protected] " " 78 [email protected] "recursion_limit"
75 [email protected] "=" 79 [email protected] " "
76 [email protected] " " 80 [email protected] "="
77 [email protected] 81 [email protected] " "
78 [email protected] "\"128\"" 82 [email protected]
83 [email protected] "\"128\""
79 [email protected] "]" 84 [email protected] "]"
80 [email protected] "\n\n" 85 [email protected] "\n\n"
81 [email protected] 86 [email protected]
diff --git a/crates/syntax/test_data/parser/ok/0044_let_attrs.rast b/crates/syntax/test_data/parser/ok/0044_let_attrs.rast
index af44a4dbe..d0e7a1dbe 100644
--- a/crates/syntax/test_data/parser/ok/0044_let_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0044_let_attrs.rast
@@ -17,18 +17,19 @@ [email protected]
17 [email protected] 17 [email protected]
18 [email protected] "#" 18 [email protected] "#"
19 [email protected] "[" 19 [email protected] "["
20 [email protected] 20 [email protected]
21 [email protected] 21 [email protected]
22 [email protected] 22 [email protected]
23 [email protected] "cfg" 23 [email protected]
24 [email protected] 24 [email protected] "cfg"
25 [email protected] "(" 25 [email protected]
26 [email protected] "feature" 26 [email protected] "("
27 [email protected] " " 27 [email protected] "feature"
28 [email protected] "=" 28 [email protected] " "
29 [email protected] " " 29 [email protected] "="
30 [email protected] "\"backtrace\"" 30 [email protected] " "
31 [email protected] ")" 31 [email protected] "\"backtrace\""
32 [email protected] ")"
32 [email protected] "]" 33 [email protected] "]"
33 [email protected] "\n " 34 [email protected] "\n "
34 [email protected] "let" 35 [email protected] "let"
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
index 5e50b4e0b..7fbd635d0 100644
--- a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
@@ -15,14 +15,15 @@ [email protected]
15 [email protected] "#" 15 [email protected] "#"
16 [email protected] "!" 16 [email protected] "!"
17 [email protected] "[" 17 [email protected] "["
18 [email protected] 18 [email protected]
19 [email protected] 19 [email protected]
20 [email protected] 20 [email protected]
21 [email protected] "doc" 21 [email protected]
22 [email protected] 22 [email protected] "doc"
23 [email protected] "(" 23 [email protected]
24 [email protected] "\"Inner attributes all ..." 24 [email protected] "("
25 [email protected] ")" 25 [email protected] "\"Inner attributes all ..."
26 [email protected] ")"
26 [email protected] "]" 27 [email protected] "]"
27 [email protected] "\n " 28 [email protected] "\n "
28 [email protected] "//! As are ModuleDoc ..." 29 [email protected] "//! As are ModuleDoc ..."
@@ -35,28 +36,30 @@ [email protected]
35 [email protected] "#" 36 [email protected] "#"
36 [email protected] "!" 37 [email protected] "!"
37 [email protected] "[" 38 [email protected] "["
38 [email protected] 39 [email protected]
39 [email protected] 40 [email protected]
40 [email protected] 41 [email protected]
41 [email protected] "doc" 42 [email protected]
42 [email protected] 43 [email protected] "doc"
43 [email protected] "(" 44 [email protected]
44 [email protected] "\"Inner attributes are ..." 45 [email protected] "("
45 [email protected] ")" 46 [email protected] "\"Inner attributes are ..."
47 [email protected] ")"
46 [email protected] "]" 48 [email protected] "]"
47 [email protected] "\n " 49 [email protected] "\n "
48 [email protected] 50 [email protected]
49 [email protected] "#" 51 [email protected] "#"
50 [email protected] "!" 52 [email protected] "!"
51 [email protected] "[" 53 [email protected] "["
52 [email protected] 54 [email protected]
53 [email protected] 55 [email protected]
54 [email protected] 56 [email protected]
55 [email protected] "doc" 57 [email protected]
56 [email protected] 58 [email protected] "doc"
57 [email protected] "(" 59 [email protected]
58 [email protected] "\"Being validated is n ..." 60 [email protected] "("
59 [email protected] ")" 61 [email protected] "\"Being validated is n ..."
62 [email protected] ")"
60 [email protected] "]" 63 [email protected] "]"
61 [email protected] "\n " 64 [email protected] "\n "
62 [email protected] "//! As are ModuleDoc ..." 65 [email protected] "//! As are ModuleDoc ..."
@@ -71,14 +74,15 @@ [email protected]
71 [email protected] "#" 74 [email protected] "#"
72 [email protected] "!" 75 [email protected] "!"
73 [email protected] "[" 76 [email protected] "["
74 [email protected] 77 [email protected]
75 [email protected] 78 [email protected]
76 [email protected] 79 [email protected]
77 [email protected] "doc" 80 [email protected]
78 [email protected] 81 [email protected] "doc"
79 [email protected] "(" 82 [email protected]
80 [email protected] "\"Inner attributes are ..." 83 [email protected] "("
81 [email protected] ")" 84 [email protected] "\"Inner attributes are ..."
85 [email protected] ")"
82 [email protected] "]" 86 [email protected] "]"
83 [email protected] "\n " 87 [email protected] "\n "
84 [email protected] "//! As are ModuleDoc ..." 88 [email protected] "//! As are ModuleDoc ..."
@@ -111,14 +115,15 @@ [email protected]
111 [email protected] 115 [email protected]
112 [email protected] "#" 116 [email protected] "#"
113 [email protected] "[" 117 [email protected] "["
114 [email protected] 118 [email protected]
115 [email protected] 119 [email protected]
116 [email protected] 120 [email protected]
117 [email protected] "doc" 121 [email protected]
118 [email protected] 122 [email protected] "doc"
119 [email protected] "(" 123 [email protected]
120 [email protected] "\"Outer attributes are ..." 124 [email protected] "("
121 [email protected] ")" 125 [email protected] "\"Outer attributes are ..."
126 [email protected] ")"
122 [email protected] "]" 127 [email protected] "]"
123 [email protected] " " 128 [email protected] " "
124 [email protected] "{" 129 [email protected] "{"
@@ -200,14 +205,15 @@ [email protected]
200 [email protected] "#" 205 [email protected] "#"
201 [email protected] "!" 206 [email protected] "!"
202 [email protected] "[" 207 [email protected] "["
203 [email protected] 208 [email protected]
204 [email protected] 209 [email protected]
205 [email protected] 210 [email protected]
206 [email protected] "allow" 211 [email protected]
207 [email protected] 212 [email protected] "allow"
208 [email protected] "(" 213 [email protected]
209 [email protected] "unused_variables" 214 [email protected] "("
210 [email protected] ")" 215 [email protected] "unused_variables"
216 [email protected] ")"
211 [email protected] "]" 217 [email protected] "]"
212 [email protected] " " 218 [email protected] " "
213 [email protected] "// this is `inner_at ..." 219 [email protected] "// this is `inner_at ..."
diff --git a/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast b/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast
index 37594769a..854ff9d56 100644
--- a/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast
+++ b/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast
@@ -14,14 +14,15 @@ [email protected]
14 [email protected] "#" 14 [email protected] "#"
15 [email protected] "!" 15 [email protected] "!"
16 [email protected] "[" 16 [email protected] "["
17 [email protected] 17 [email protected]
18 [email protected] 18 [email protected]
19 [email protected] 19 [email protected]
20 [email protected] "doc" 20 [email protected]
21 [email protected] 21 [email protected] "doc"
22 [email protected] "(" 22 [email protected]
23 [email protected] "\"This is also a doc c ..." 23 [email protected] "("
24 [email protected] ")" 24 [email protected] "\"This is also a doc c ..."
25 [email protected] ")"
25 [email protected] "]" 26 [email protected] "]"
26 [email protected] "\n" 27 [email protected] "\n"
27 [email protected] "}" 28 [email protected] "}"
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
index 88470c41c..f935a0df5 100644
--- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
@@ -10,19 +10,21 @@ [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "#" 11 [email protected] "#"
12 [email protected] "[" 12 [email protected] "["
13 [email protected] 13 [email protected]
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected]
16 [email protected] "attr1" 16 [email protected]
17 [email protected] "attr1"
17 [email protected] "]" 18 [email protected] "]"
18 [email protected] " " 19 [email protected] " "
19 [email protected] 20 [email protected]
20 [email protected] "#" 21 [email protected] "#"
21 [email protected] "[" 22 [email protected] "["
22 [email protected] 23 [email protected]
23 [email protected] 24 [email protected]
24 [email protected] 25 [email protected]
25 [email protected] "attr2" 26 [email protected]
27 [email protected] "attr2"
26 [email protected] "]" 28 [email protected] "]"
27 [email protected] " " 29 [email protected] " "
28 [email protected] 30 [email protected]
@@ -52,10 +54,11 @@ [email protected]
52 [email protected] 54 [email protected]
53 [email protected] "#" 55 [email protected] "#"
54 [email protected] "[" 56 [email protected] "["
55 [email protected] 57 [email protected]
56 [email protected] 58 [email protected]
57 [email protected] 59 [email protected]
58 [email protected] "attr1" 60 [email protected]
61 [email protected] "attr1"
59 [email protected] "]" 62 [email protected] "]"
60 [email protected] " " 63 [email protected] " "
61 [email protected] 64 [email protected]
@@ -111,10 +114,11 @@ [email protected]
111 [email protected] 114 [email protected]
112 [email protected] "#" 115 [email protected] "#"
113 [email protected] "[" 116 [email protected] "["
114 [email protected] 117 [email protected]
115 [email protected] 118 [email protected]
116 [email protected] 119 [email protected]
117 [email protected] "attr" 120 [email protected]
121 [email protected] "attr"
118 [email protected] "]" 122 [email protected] "]"
119 [email protected] " " 123 [email protected] " "
120 [email protected] "..." 124 [email protected] "..."
@@ -157,10 +161,11 @@ [email protected]
157 [email protected] 161 [email protected]
158 [email protected] "#" 162 [email protected] "#"
159 [email protected] "[" 163 [email protected] "["
160 [email protected] 164 [email protected]
161 [email protected] 165 [email protected]
162 [email protected] 166 [email protected]
163 [email protected] "attr" 167 [email protected]
168 [email protected] "attr"
164 [email protected] "]" 169 [email protected] "]"
165 [email protected] " " 170 [email protected] " "
166 [email protected] 171 [email protected]
@@ -207,10 +212,11 @@ [email protected]
207 [email protected] 212 [email protected]
208 [email protected] "#" 213 [email protected] "#"
209 [email protected] "[" 214 [email protected] "["
210 [email protected] 215 [email protected]
211 [email protected] 216 [email protected]
212 [email protected] 217 [email protected]
213 [email protected] "attr" 218 [email protected]
219 [email protected] "attr"
214 [email protected] "]" 220 [email protected] "]"
215 [email protected] " " 221 [email protected] " "
216 [email protected] 222 [email protected]
@@ -229,10 +235,11 @@ [email protected]
229 [email protected] "#" 235 [email protected] "#"
230 [email protected] " " 236 [email protected] " "
231 [email protected] "[" 237 [email protected] "["
232 [email protected] 238 [email protected]
233 [email protected] 239 [email protected]
234 [email protected] 240 [email protected]
235 [email protected] "attr" 241 [email protected]
242 [email protected] "attr"
236 [email protected] "]" 243 [email protected] "]"
237 [email protected] " " 244 [email protected] " "
238 [email protected] 245 [email protected]
@@ -275,10 +282,11 @@ [email protected]
275 [email protected] 282 [email protected]
276 [email protected] "#" 283 [email protected] "#"
277 [email protected] "[" 284 [email protected] "["
278 [email protected] 285 [email protected]
279 [email protected] 286 [email protected]
280 [email protected] 287 [email protected]
281 [email protected] "must_use" 288 [email protected]
289 [email protected] "must_use"
282 [email protected] "]" 290 [email protected] "]"
283 [email protected] " " 291 [email protected] " "
284 [email protected] 292 [email protected]
@@ -300,10 +308,11 @@ [email protected]
300 [email protected] 308 [email protected]
301 [email protected] "#" 309 [email protected] "#"
302 [email protected] "[" 310 [email protected] "["
303 [email protected] 311 [email protected]
304 [email protected] 312 [email protected]
305 [email protected] 313 [email protected]
306 [email protected] "attr" 314 [email protected]
315 [email protected] "attr"
307 [email protected] "]" 316 [email protected] "]"
308 [email protected] " " 317 [email protected] " "
309 [email protected] 318 [email protected]
@@ -325,10 +334,11 @@ [email protected]
325 [email protected] 334 [email protected]
326 [email protected] "#" 335 [email protected] "#"
327 [email protected] "[" 336 [email protected] "["
328 [email protected] 337 [email protected]
329 [email protected] 338 [email protected]
330 [email protected] 339 [email protected]
331 [email protected] "attr" 340 [email protected]
341 [email protected] "attr"
332 [email protected] "]" 342 [email protected] "]"
333 [email protected] " " 343 [email protected] " "
334 [email protected] "&" 344 [email protected] "&"
@@ -357,10 +367,11 @@ [email protected]
357 [email protected] 367 [email protected]
358 [email protected] "#" 368 [email protected] "#"
359 [email protected] "[" 369 [email protected] "["
360 [email protected] 370 [email protected]
361 [email protected] 371 [email protected]
362 [email protected] 372 [email protected]
363 [email protected] "attr" 373 [email protected]
374 [email protected] "attr"
364 [email protected] "]" 375 [email protected] "]"
365 [email protected] " " 376 [email protected] " "
366 [email protected] "&" 377 [email protected] "&"
@@ -391,10 +402,11 @@ [email protected]
391 [email protected] 402 [email protected]
392 [email protected] "#" 403 [email protected] "#"
393 [email protected] "[" 404 [email protected] "["
394 [email protected] 405 [email protected]
395 [email protected] 406 [email protected]
396 [email protected] 407 [email protected]
397 [email protected] "attr" 408 [email protected]
409 [email protected] "attr"
398 [email protected] "]" 410 [email protected] "]"
399 [email protected] " " 411 [email protected] " "
400 [email protected] "&" 412 [email protected] "&"
@@ -426,10 +438,11 @@ [email protected]
426 [email protected] 438 [email protected]
427 [email protected] "#" 439 [email protected] "#"
428 [email protected] "[" 440 [email protected] "["
429 [email protected] 441 [email protected]
430 [email protected] 442 [email protected]
431 [email protected] 443 [email protected]
432 [email protected] "attr" 444 [email protected]
445 [email protected] "attr"
433 [email protected] "]" 446 [email protected] "]"
434 [email protected] " " 447 [email protected] " "
435 [email protected] "&" 448 [email protected] "&"
@@ -457,10 +470,11 @@ [email protected]
457 [email protected] 470 [email protected]
458 [email protected] "#" 471 [email protected] "#"
459 [email protected] "[" 472 [email protected] "["
460 [email protected] 473 [email protected]
461 [email protected] 474 [email protected]
462 [email protected] 475 [email protected]
463 [email protected] "attr" 476 [email protected]
477 [email protected] "attr"
464 [email protected] "]" 478 [email protected] "]"
465 [email protected] " " 479 [email protected] " "
466 [email protected] 480 [email protected]
@@ -489,10 +503,11 @@ [email protected]
489 [email protected] 503 [email protected]
490 [email protected] "#" 504 [email protected] "#"
491 [email protected] "[" 505 [email protected] "["
492 [email protected] 506 [email protected]
493 [email protected] 507 [email protected]
494 [email protected] 508 [email protected]
495 [email protected] "attr" 509 [email protected]
510 [email protected] "attr"
496 [email protected] "]" 511 [email protected] "]"
497 [email protected] " " 512 [email protected] " "
498 [email protected] 513 [email protected]
diff --git a/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast b/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
index 87d8ebcba..97416f16a 100644
--- a/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
+++ b/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
@@ -5,10 +5,11 @@ [email protected]
5 [email protected] 5 [email protected]
6 [email protected] "#" 6 [email protected] "#"
7 [email protected] "[" 7 [email protected] "["
8 [email protected] 8 [email protected]
9 [email protected] 9 [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "macro_export" 11 [email protected]
12 [email protected] "macro_export"
12 [email protected] "]" 13 [email protected] "]"
13 [email protected] "\n" 14 [email protected] "\n"
14 [email protected] "macro_rules" 15 [email protected] "macro_rules"
diff --git a/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast b/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast
index 0c22c31a4..e75848f0f 100644
--- a/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast
+++ b/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast
@@ -55,10 +55,11 @@ [email protected]
55 [email protected] 55 [email protected]
56 [email protected] "#" 56 [email protected] "#"
57 [email protected] "[" 57 [email protected] "["
58 [email protected] 58 [email protected]
59 [email protected] 59 [email protected]
60 [email protected] 60 [email protected]
61 [email protected] "test" 61 [email protected]
62 [email protected] "test"
62 [email protected] "]" 63 [email protected] "]"
63 [email protected] "\n" 64 [email protected] "\n"
64 [email protected] "fn" 65 [email protected] "fn"
diff --git a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
index f7c094898..dcc4105c9 100644
--- a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
+++ b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
@@ -96,14 +96,15 @@ [email protected]
96 [email protected] 96 [email protected]
97 [email protected] "#" 97 [email protected] "#"
98 [email protected] "[" 98 [email protected] "["
99 [email protected] 99 [email protected]
100 [email protected] 100 [email protected]
101 [email protected] 101 [email protected]
102 [email protected] "cfg" 102 [email protected]
103 [email protected] 103 [email protected] "cfg"
104 [email protected] "(" 104 [email protected]
105 [email protected] "never" 105 [email protected] "("
106 [email protected] ")" 106 [email protected] "never"
107 [email protected] ")"
107 [email protected] "]" 108 [email protected] "]"
108 [email protected] " " 109 [email protected] " "
109 [email protected] 110 [email protected]
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index bd017567c..b2fe25f82 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -190,10 +190,21 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
190 res 190 res
191} 191}
192 192
193/// Extracts `//^ some text` annotations 193/// Extracts `//^^^ some text` annotations.
194///
195/// A run of `^^^` can be arbitrary long and points to the corresponding range
196/// in the line above.
197///
198/// The `// ^file text` syntax can be used to attach `text` to the entirety of
199/// the file.
200///
201/// Multiline string values are supported:
202///
203/// // ^^^ first line
204/// // | second line
194pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { 205pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
195 let mut res = Vec::new(); 206 let mut res = Vec::new();
196 let mut prev_line_start: Option<TextSize> = None; 207 let mut prev_line_start: Option<TextSize> = Some(0.into());
197 let mut line_start: TextSize = 0.into(); 208 let mut line_start: TextSize = 0.into();
198 let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); 209 let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new();
199 for line in text.split_inclusive('\n') { 210 for line in text.split_inclusive('\n') {
@@ -202,10 +213,15 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
202 let annotation_offset = TextSize::of(&line[..idx + "//".len()]); 213 let annotation_offset = TextSize::of(&line[..idx + "//".len()]);
203 for annotation in extract_line_annotations(&line[idx + "//".len()..]) { 214 for annotation in extract_line_annotations(&line[idx + "//".len()..]) {
204 match annotation { 215 match annotation {
205 LineAnnotation::Annotation { mut range, content } => { 216 LineAnnotation::Annotation { mut range, content, file } => {
206 range += annotation_offset; 217 range += annotation_offset;
207 this_line_annotations.push((range.end(), res.len())); 218 this_line_annotations.push((range.end(), res.len()));
208 res.push((range + prev_line_start.unwrap(), content)) 219 let range = if file {
220 TextRange::up_to(TextSize::of(text))
221 } else {
222 range + prev_line_start.unwrap()
223 };
224 res.push((range, content))
209 } 225 }
210 LineAnnotation::Continuation { mut offset, content } => { 226 LineAnnotation::Continuation { mut offset, content } => {
211 offset += annotation_offset; 227 offset += annotation_offset;
@@ -226,11 +242,12 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
226 242
227 prev_line_annotations = this_line_annotations; 243 prev_line_annotations = this_line_annotations;
228 } 244 }
245
229 res 246 res
230} 247}
231 248
232enum LineAnnotation { 249enum LineAnnotation {
233 Annotation { range: TextRange, content: String }, 250 Annotation { range: TextRange, content: String, file: bool },
234 Continuation { offset: TextSize, content: String }, 251 Continuation { offset: TextSize, content: String },
235} 252}
236 253
@@ -238,14 +255,9 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
238 let mut res = Vec::new(); 255 let mut res = Vec::new();
239 let mut offset: TextSize = 0.into(); 256 let mut offset: TextSize = 0.into();
240 let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' }; 257 let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' };
241 loop { 258 while let Some(idx) = line.find(marker) {
242 match line.find(marker) { 259 offset += TextSize::try_from(idx).unwrap();
243 Some(idx) => { 260 line = &line[idx..];
244 offset += TextSize::try_from(idx).unwrap();
245 line = &line[idx..];
246 }
247 None => break,
248 };
249 261
250 let mut len = line.chars().take_while(|&it| it == '^').count(); 262 let mut len = line.chars().take_while(|&it| it == '^').count();
251 let mut continuation = false; 263 let mut continuation = false;
@@ -256,12 +268,20 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
256 } 268 }
257 let range = TextRange::at(offset, len.try_into().unwrap()); 269 let range = TextRange::at(offset, len.try_into().unwrap());
258 let next = line[len..].find(marker).map_or(line.len(), |it| it + len); 270 let next = line[len..].find(marker).map_or(line.len(), |it| it + len);
259 let content = line[len..][..next - len].trim().to_string(); 271 let mut content = &line[len..][..next - len];
272
273 let mut file = false;
274 if !continuation && content.starts_with("file") {
275 file = true;
276 content = &content["file".len()..]
277 }
278
279 let content = content.trim().to_string();
260 280
261 let annotation = if continuation { 281 let annotation = if continuation {
262 LineAnnotation::Continuation { offset: range.end(), content } 282 LineAnnotation::Continuation { offset: range.end(), content }
263 } else { 283 } else {
264 LineAnnotation::Annotation { range, content } 284 LineAnnotation::Annotation { range, content, file }
265 }; 285 };
266 res.push(annotation); 286 res.push(annotation);
267 287
@@ -282,16 +302,20 @@ fn main() {
282 zoo + 1 302 zoo + 1
283} //^^^ type: 303} //^^^ type:
284 // | i32 304 // | i32
305
306// ^file
285 "#, 307 "#,
286 ); 308 );
287 let res = extract_annotations(&text) 309 let res = extract_annotations(&text)
288 .into_iter() 310 .into_iter()
289 .map(|(range, ann)| (&text[range], ann)) 311 .map(|(range, ann)| (&text[range], ann))
290 .collect::<Vec<_>>(); 312 .collect::<Vec<_>>();
313
291 assert_eq!( 314 assert_eq!(
292 res, 315 res[..3],
293 vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),] 316 [("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into())]
294 ); 317 );
318 assert_eq!(res[3].0.len(), 115);
295} 319}
296 320
297/// Returns `false` if slow tests should not run, otherwise returns `true` and 321/// Returns `false` if slow tests should not run, otherwise returns `true` and
diff --git a/crates/tt/src/buffer.rs b/crates/tt/src/buffer.rs
index e0021039a..dc577700f 100644
--- a/crates/tt/src/buffer.rs
+++ b/crates/tt/src/buffer.rs
@@ -133,7 +133,7 @@ impl<'a> TokenTreeRef<'a> {
133 } 133 }
134} 134}
135 135
136/// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125 136/// A safe version of `Cursor` from `syn` crate <https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125>
137#[derive(Copy, Clone, Debug)] 137#[derive(Copy, Clone, Debug)]
138pub struct Cursor<'a> { 138pub struct Cursor<'a> {
139 buffer: &'a TokenBuffer<'a>, 139 buffer: &'a TokenBuffer<'a>,
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index bed44d600..b51ad5d0f 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -88,7 +88,7 @@ pub struct Ident {
88} 88}
89 89
90fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result { 90fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result {
91 let align = std::iter::repeat(" ").take(level).collect::<String>(); 91 let align = " ".repeat(level);
92 92
93 let aux = match subtree.delimiter.map(|it| (it.kind, it.id.0)) { 93 let aux = match subtree.delimiter.map(|it| (it.kind, it.id.0)) {
94 None => "$".to_string(), 94 None => "$".to_string(),
@@ -113,7 +113,7 @@ fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usi
113} 113}
114 114
115fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) -> fmt::Result { 115fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) -> fmt::Result {
116 let align = std::iter::repeat(" ").take(level).collect::<String>(); 116 let align = " ".repeat(level);
117 117
118 match tkn { 118 match tkn {
119 TokenTree::Leaf(leaf) => match leaf { 119 TokenTree::Leaf(leaf) => match leaf {
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 0a4590c8d..0011f73c9 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -111,7 +111,7 @@ impl FileSetConfig {
111 let mut scratch_space = Vec::new(); 111 let mut scratch_space = Vec::new();
112 let mut res = vec![FileSet::default(); self.len()]; 112 let mut res = vec![FileSet::default(); self.len()];
113 for (file_id, path) in vfs.iter() { 113 for (file_id, path) in vfs.iter() {
114 let root = self.classify(&path, &mut scratch_space); 114 let root = self.classify(path, &mut scratch_space);
115 res[root].insert(file_id, path.clone()) 115 res[root].insert(file_id, path.clone())
116 } 116 }
117 res 117 res
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 16b23adc6..e81f1e74c 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -1,6 +1,6 @@
1# Contributing Quick Start 1# Contributing Quick Start
2 2
3Rust Analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries. 3rust-analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries.
4So, just 4So, just
5 5
6``` 6```
@@ -9,18 +9,18 @@ $ cargo test
9 9
10should be enough to get you started! 10should be enough to get you started!
11 11
12To learn more about how rust-analyzer works, see [./architecture.md](./architecture.md) document. 12To learn more about how rust-analyzer works, see [./architecture.md](./architecture.md).
13It also explains the high-level layout of the source code. 13It also explains the high-level layout of the source code.
14Do skim through that document. 14Do skim through that document.
15 15
16We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/. 16We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/.
17Note though, that internal documentation is very incomplete. 17Note though, that the internal documentation is very incomplete.
18 18
19Various organizational and process issues are discussed in this document. 19Various organizational and process issues are discussed in this document.
20 20
21# Getting in Touch 21# Getting in Touch
22 22
23Rust Analyzer is a part of [RLS-2.0 working 23rust-analyzer is a part of the [RLS-2.0 working
24group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0). 24group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0).
25Discussion happens in this Zulip stream: 25Discussion happens in this Zulip stream:
26 26
@@ -33,7 +33,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) 33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
34 issues have links to the code in question and tests. 34 issues have links to the code in question and tests.
35* [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22) 35* [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22)
36 are issues which are not critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt. 36 are issues which are not necessarily critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt.
37* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), 37* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy),
38 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), 38 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium),
39 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard), 39 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard),
@@ -42,7 +42,9 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
42* [S-actionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-actionable) and 42* [S-actionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-actionable) and
43 [S-unactionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-unactionable) 43 [S-unactionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-unactionable)
44 specify if there are concrete steps to resolve or advance an issue. Roughly, actionable issues need only work to be fixed, 44 specify if there are concrete steps to resolve or advance an issue. Roughly, actionable issues need only work to be fixed,
45 while unactionable ones are effectively wont-fix. Each triaged issue should have one of these labels. 45 while unactionable ones are blocked either on user feedback (providing a reproducible example), or on larger architectural
46 work or decisions. This classification is descriptive, not prescriptive, and might be wrong: Any unactionable issue might have a simple fix that we missed.
47 Each triaged issue should have one of these labels.
46* [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun) 48* [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun)
47 is for cool, but probably hard stuff. 49 is for cool, but probably hard stuff.
48* [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design) 50* [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design)
@@ -206,7 +208,7 @@ Release process is handled by `release`, `dist` and `promote` xtasks, `release`
206./rust-rust-analyzer # Note the name! 208./rust-rust-analyzer # Note the name!
207``` 209```
208 210
209Additionally, it assumes that remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). 211Additionally, it assumes that the remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork).
210 212
211`release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog. 213`release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog.
212This step uses the `curl` and `jq` applications, which need to be available in `PATH`. 214This step uses the `curl` and `jq` applications, which need to be available in `PATH`.
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index 3de1b99a5..721c555ab 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -428,7 +428,7 @@ Rather than spawning futures or scheduling callbacks (open), the event loop acce
428It's easy to see all the things that trigger rust-analyzer processing, together with their performance 428It's easy to see all the things that trigger rust-analyzer processing, together with their performance
429 429
430rust-analyzer includes a simple hierarchical profiler (`hprof`). 430rust-analyzer includes a simple hierarchical profiler (`hprof`).
431It is enabled with `RA_PROFILE='*>50` env var (log all (`*`) actions which take more than `50` ms) and produces output like: 431It is enabled with `RA_PROFILE='*>50'` env var (log all (`*`) actions which take more than `50` ms) and produces output like:
432 432
433``` 433```
43485ms - handle_completion 43485ms - handle_completion
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index dbd9a3503..34a91486b 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -119,6 +119,7 @@ similar option.
119+ 119+
120-- 120--
121Whether to add argument snippets when completing functions. 121Whether to add argument snippets when completing functions.
122Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set.
122-- 123--
123[[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: 124[[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`)::
124+ 125+
@@ -180,6 +181,11 @@ List of warnings that should be displayed with info severity.
180The warnings will be indicated by a blue squiggly underline in code 181The warnings will be indicated by a blue squiggly underline in code
181and a blue icon in the `Problems Panel`. 182and a blue icon in the `Problems Panel`.
182-- 183--
184[[rust-analyzer.experimental.procAttrMacros]]rust-analyzer.experimental.procAttrMacros (default: `false`)::
185+
186--
187Expand attribute macros.
188--
183[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: 189[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`)::
184+ 190+
185-- 191--
@@ -222,6 +228,12 @@ Whether to show `Go to Type Definition` action. Only applies when
222Whether to show `Implementations` action. Only applies when 228Whether to show `Implementations` action. Only applies when
223`#rust-analyzer.hoverActions.enable#` is set. 229`#rust-analyzer.hoverActions.enable#` is set.
224-- 230--
231[[rust-analyzer.hoverActions.references]]rust-analyzer.hoverActions.references (default: `false`)::
232+
233--
234Whether to show `References` action. Only applies when
235`#rust-analyzer.hoverActions.enable#` is set.
236--
225[[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: 237[[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`)::
226+ 238+
227-- 239--
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 1f95df56e..9a8d76700 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -187,6 +187,20 @@ Install it with pacman, for example:
187$ pacman -S rust-analyzer 187$ pacman -S rust-analyzer
188---- 188----
189 189
190==== Gentoo Linux
191
192`rust-analyzer` is available in the GURU repository:
193
194- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer-bin/rust-analyzer-bin-9999.ebuild[`dev-util/rust-analyzer-bin-9999`] (the https://github.com/rust-analyzer/rust-analyzer/releases/latest[latest release] as a live binary ebuild)
195
196If not already, GURU must be enabled (e.g. using `app-eselect/eselect-repository`) and sync'd before running `emerge`:
197
198[source,bash]
199----
200$ eselect repository enable guru && emaint sync -r guru
201$ emerge rust-analyzer-bin
202----
203
190=== Emacs 204=== Emacs
191 205
192Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm]. 206Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm].
diff --git a/editors/code/package.json b/editors/code/package.json
index 42a06e137..0f3ed48a0 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -553,7 +553,7 @@
553 } 553 }
554 }, 554 },
555 "rust-analyzer.completion.addCallArgumentSnippets": { 555 "rust-analyzer.completion.addCallArgumentSnippets": {
556 "markdownDescription": "Whether to add argument snippets when completing functions.", 556 "markdownDescription": "Whether to add argument snippets when completing functions.\nOnly applies when `#rust-analyzer.completion.addCallParenthesis#` is set.",
557 "default": true, 557 "default": true,
558 "type": "boolean" 558 "type": "boolean"
559 }, 559 },
@@ -617,6 +617,11 @@
617 "type": "string" 617 "type": "string"
618 } 618 }
619 }, 619 },
620 "rust-analyzer.experimental.procAttrMacros": {
621 "markdownDescription": "Expand attribute macros.",
622 "default": false,
623 "type": "boolean"
624 },
620 "rust-analyzer.files.watcher": { 625 "rust-analyzer.files.watcher": {
621 "markdownDescription": "Controls file watching implementation.", 626 "markdownDescription": "Controls file watching implementation.",
622 "default": "client", 627 "default": "client",
@@ -655,6 +660,11 @@
655 "default": true, 660 "default": true,
656 "type": "boolean" 661 "type": "boolean"
657 }, 662 },
663 "rust-analyzer.hoverActions.references": {
664 "markdownDescription": "Whether to show `References` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
665 "default": false,
666 "type": "boolean"
667 },
658 "rust-analyzer.hoverActions.run": { 668 "rust-analyzer.hoverActions.run": {
659 "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", 669 "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
660 "default": true, 670 "default": true,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 7200a26f7..2277eeb7e 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -165,6 +165,7 @@ export class Config {
165 return { 165 return {
166 enable: this.get<boolean>("hoverActions.enable"), 166 enable: this.get<boolean>("hoverActions.enable"),
167 implementations: this.get<boolean>("hoverActions.implementations"), 167 implementations: this.get<boolean>("hoverActions.implementations"),
168 references: this.get<boolean>("hoverActions.references"),
168 run: this.get<boolean>("hoverActions.run"), 169 run: this.get<boolean>("hoverActions.run"),
169 debug: this.get<boolean>("hoverActions.debug"), 170 debug: this.get<boolean>("hoverActions.debug"),
170 gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"), 171 gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"),
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index cf67dd8cf..2ffd3be6f 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -39,6 +39,7 @@ export class Ctx {
39 extCtx.subscriptions.push(statusBar); 39 extCtx.subscriptions.push(statusBar);
40 statusBar.text = "rust-analyzer"; 40 statusBar.text = "rust-analyzer";
41 statusBar.tooltip = "ready"; 41 statusBar.tooltip = "ready";
42 statusBar.command = "rust-analyzer.analyzerStatus";
42 statusBar.show(); 43 statusBar.show();
43 44
44 const res = new Ctx(config, extCtx, client, serverPath, statusBar); 45 const res = new Ctx(config, extCtx, client, serverPath, statusBar);
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 997770958..c4fc91386 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -11,7 +11,7 @@ anyhow = "1.0.26"
11flate2 = "1.0" 11flate2 = "1.0"
12proc-macro2 = "1.0.8" 12proc-macro2 = "1.0.8"
13quote = "1.0.2" 13quote = "1.0.2"
14ungrammar = "=1.13" 14ungrammar = "=1.14"
15walkdir = "2.3.1" 15walkdir = "2.3.1"
16write-json = "0.1.0" 16write-json = "0.1.0"
17xshell = "0.1" 17xshell = "0.1"
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 0fd1d13e6..fe37d0245 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -184,7 +184,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
184 "ITEM_LIST", 184 "ITEM_LIST",
185 "ASSOC_ITEM_LIST", 185 "ASSOC_ITEM_LIST",
186 "ATTR", 186 "ATTR",
187 "META_ITEM", // not an item actually 187 "META",
188 "USE_TREE", 188 "USE_TREE",
189 "USE_TREE_LIST", 189 "USE_TREE_LIST",
190 "PATH", 190 "PATH",
diff --git a/xtask/src/codegen/gen_lint_completions.rs b/xtask/src/codegen/gen_lint_completions.rs
index 24dbc6a39..54fcaa0e6 100644
--- a/xtask/src/codegen/gen_lint_completions.rs
+++ b/xtask/src/codegen/gen_lint_completions.rs
@@ -1,4 +1,5 @@
1//! Generates descriptors structure for unstable feature from Unstable Book 1//! Generates descriptors structure for unstable feature from Unstable Book
2use std::borrow::Cow;
2use std::fmt::Write; 3use std::fmt::Write;
3use std::path::{Path, PathBuf}; 4use std::path::{Path, PathBuf};
4 5
@@ -12,25 +13,75 @@ pub(crate) fn generate_lint_completions() -> Result<()> {
12 cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; 13 cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?;
13 } 14 }
14 15
15 let mut contents = String::from("use crate::completions::attribute::LintCompletion;\n\n"); 16 let mut contents = String::from(
16 generate_descriptor(&mut contents, "./target/rust/src/doc/unstable-book/src".into())?; 17 r#"pub struct Lint {
18 pub label: &'static str,
19 pub description: &'static str,
20}
21
22"#,
23 );
24 generate_lint_descriptor(&mut contents)?;
25 contents.push('\n');
26
27 generate_feature_descriptor(&mut contents, "./target/rust/src/doc/unstable-book/src".into())?;
17 contents.push('\n'); 28 contents.push('\n');
18 29
19 cmd!("curl http://rust-lang.github.io/rust-clippy/master/lints.json --output ./target/clippy_lints.json").run()?; 30 cmd!("curl https://rust-lang.github.io/rust-clippy/master/lints.json --output ./target/clippy_lints.json").run()?;
20 generate_descriptor_clippy(&mut contents, &Path::new("./target/clippy_lints.json"))?; 31 generate_descriptor_clippy(&mut contents, Path::new("./target/clippy_lints.json"))?;
21 let contents = reformat(&contents)?; 32 let contents = reformat(&contents)?;
22 33
23 let destination = 34 let destination = project_root().join("crates/ide_db/src/helpers/generated_lints.rs");
24 project_root().join("crates/ide_completion/src/generated_lint_completions.rs");
25 ensure_file_contents(destination.as_path(), &contents)?; 35 ensure_file_contents(destination.as_path(), &contents)?;
26 36
27 Ok(()) 37 Ok(())
28} 38}
29 39
30fn generate_descriptor(buf: &mut String, src_dir: PathBuf) -> Result<()> { 40fn generate_lint_descriptor(buf: &mut String) -> Result<()> {
31 buf.push_str(r#"pub(super) const FEATURES: &[LintCompletion] = &["#); 41 let stdout = cmd!("rustc -W help").read()?;
42 let start_lints =
43 stdout.find("---- ------- -------").ok_or_else(|| anyhow::format_err!(""))?;
44 let start_lint_groups =
45 stdout.find("---- ---------").ok_or_else(|| anyhow::format_err!(""))?;
46 let end_lints =
47 stdout.find("Lint groups provided by rustc:").ok_or_else(|| anyhow::format_err!(""))?;
48 let end_lint_groups = stdout
49 .find("Lint tools like Clippy can provide additional lints and lint groups.")
50 .ok_or_else(|| anyhow::format_err!(""))?;
51 buf.push_str(r#"pub const DEFAULT_LINTS: &[Lint] = &["#);
52 buf.push('\n');
53 let mut lints = stdout[start_lints..end_lints]
54 .lines()
55 .skip(1)
56 .filter(|l| !l.is_empty())
57 .map(|line| {
58 let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap();
59 let (_default_level, description) =
60 rest.trim().split_once(char::is_whitespace).unwrap();
61 (name.trim(), Cow::Borrowed(description.trim()))
62 })
63 .collect::<Vec<_>>();
64 lints.extend(
65 stdout[start_lint_groups..end_lint_groups].lines().skip(1).filter(|l| !l.is_empty()).map(
66 |line| {
67 let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap();
68 (name.trim(), format!("lint group for: {}", lints.trim()).into())
69 },
70 ),
71 );
72
73 lints.sort_by(|(ident, _), (ident2, _)| ident.cmp(ident2));
74 lints.into_iter().for_each(|(name, description)| {
75 push_lint_completion(buf, &name.replace("-", "_"), &description)
76 });
77 buf.push_str("];\n");
78 Ok(())
79}
80
81fn generate_feature_descriptor(buf: &mut String, src_dir: PathBuf) -> Result<()> {
82 buf.push_str(r#"pub const FEATURES: &[Lint] = &["#);
32 buf.push('\n'); 83 buf.push('\n');
33 ["language-features", "library-features"] 84 let mut vec = ["language-features", "library-features"]
34 .iter() 85 .iter()
35 .flat_map(|it| WalkDir::new(src_dir.join(it))) 86 .flat_map(|it| WalkDir::new(src_dir.join(it)))
36 .filter_map(|e| e.ok()) 87 .filter_map(|e| e.ok())
@@ -38,13 +89,16 @@ fn generate_descriptor(buf: &mut String, src_dir: PathBuf) -> Result<()> {
38 // Get all `.md ` files 89 // Get all `.md ` files
39 entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md" 90 entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md"
40 }) 91 })
41 .for_each(|entry| { 92 .map(|entry| {
42 let path = entry.path(); 93 let path = entry.path();
43 let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_"); 94 let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_");
44 let doc = read_file(path).unwrap(); 95 let doc = read_file(path).unwrap();
45 96 (feature_ident, doc)
46 push_lint_completion(buf, &feature_ident, &doc); 97 })
47 }); 98 .collect::<Vec<_>>();
99 vec.sort_by(|(feature_ident, _), (feature_ident2, _)| feature_ident.cmp(feature_ident2));
100 vec.into_iter()
101 .for_each(|(feature_ident, doc)| push_lint_completion(buf, &feature_ident, &doc));
48 buf.push_str("];\n"); 102 buf.push_str("];\n");
49 Ok(()) 103 Ok(())
50} 104}
@@ -55,6 +109,10 @@ struct ClippyLint {
55 id: String, 109 id: String,
56} 110}
57 111
112fn unescape(s: &str) -> String {
113 s.replace(r#"\""#, "").replace(r#"\n"#, "\n").replace(r#"\r"#, "")
114}
115
58fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> { 116fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> {
59 let file_content = read_file(path)?; 117 let file_content = read_file(path)?;
60 let mut clippy_lints: Vec<ClippyLint> = vec![]; 118 let mut clippy_lints: Vec<ClippyLint> = vec![];
@@ -81,12 +139,12 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> {
81 .strip_prefix(prefix_to_strip) 139 .strip_prefix(prefix_to_strip)
82 .expect("should be prefixed by what it does") 140 .expect("should be prefixed by what it does")
83 .strip_suffix(suffix_to_strip) 141 .strip_suffix(suffix_to_strip)
84 .expect("should be suffixed by comma") 142 .map(unescape)
85 .into(); 143 .expect("should be suffixed by comma");
86 } 144 }
87 } 145 }
88 146 clippy_lints.sort_by(|lint, lint2| lint.id.cmp(&lint2.id));
89 buf.push_str(r#"pub(super) const CLIPPY_LINTS: &[LintCompletion] = &["#); 147 buf.push_str(r#"pub const CLIPPY_LINTS: &[Lint] = &["#);
90 buf.push('\n'); 148 buf.push('\n');
91 clippy_lints.into_iter().for_each(|clippy_lint| { 149 clippy_lints.into_iter().for_each(|clippy_lint| {
92 let lint_ident = format!("clippy::{}", clippy_lint.id); 150 let lint_ident = format!("clippy::{}", clippy_lint.id);
@@ -102,7 +160,7 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> {
102fn push_lint_completion(buf: &mut String, label: &str, description: &str) { 160fn push_lint_completion(buf: &mut String, label: &str, description: &str) {
103 writeln!( 161 writeln!(
104 buf, 162 buf,
105 r###" LintCompletion {{ 163 r###" Lint {{
106 label: "{}", 164 label: "{}",
107 description: r##"{}"## 165 description: r##"{}"##
108 }},"###, 166 }},"###,
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index ba4b24848..5435da76e 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -94,18 +94,16 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> {
94 support::children(&self.syntax) 94 support::children(&self.syntax)
95 } 95 }
96 } 96 }
97 } else { 97 } else if let Some(token_kind) = field.token_kind() {
98 if let Some(token_kind) = field.token_kind() { 98 quote! {
99 quote! { 99 pub fn #method_name(&self) -> Option<#ty> {
100 pub fn #method_name(&self) -> Option<#ty> { 100 support::token(&self.syntax, #token_kind)
101 support::token(&self.syntax, #token_kind)
102 }
103 } 101 }
104 } else { 102 }
105 quote! { 103 } else {
106 pub fn #method_name(&self) -> Option<#ty> { 104 quote! {
107 support::child(&self.syntax) 105 pub fn #method_name(&self) -> Option<#ty> {
108 } 106 support::child(&self.syntax)
109 } 107 }
110 } 108 }
111 } 109 }
@@ -260,7 +258,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> {
260 for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") { 258 for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") {
261 res.push_str(chunk); 259 res.push_str(chunk);
262 if let Some(doc) = docs.next() { 260 if let Some(doc) = docs.next() {
263 write_doc_comment(&doc, &mut res); 261 write_doc_comment(doc, &mut res);
264 } 262 }
265 } 263 }
266 264
@@ -296,14 +294,14 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> {
296 294
297 let full_keywords_values = &grammar.keywords; 295 let full_keywords_values = &grammar.keywords;
298 let full_keywords = 296 let full_keywords =
299 full_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(&kw))); 297 full_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(kw)));
300 298
301 let all_keywords_values = 299 let all_keywords_values =
302 grammar.keywords.iter().chain(grammar.contextual_keywords.iter()).collect::<Vec<_>>(); 300 grammar.keywords.iter().chain(grammar.contextual_keywords.iter()).collect::<Vec<_>>();
303 let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw)); 301 let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw));
304 let all_keywords = all_keywords_values 302 let all_keywords = all_keywords_values
305 .iter() 303 .iter()
306 .map(|name| format_ident!("{}_KW", to_upper_snake_case(&name))) 304 .map(|name| format_ident!("{}_KW", to_upper_snake_case(name)))
307 .collect::<Vec<_>>(); 305 .collect::<Vec<_>>();
308 306
309 let literals = 307 let literals =
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index d0bef7b7a..063e11a5a 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -1,4 +1,4 @@
1//! See https://github.com/matklad/cargo-xtask/. 1//! See <https://github.com/matklad/cargo-xtask/>.
2//! 2//!
3//! This binary defines various auxiliary build commands, which are not 3//! This binary defines various auxiliary build commands, which are not
4//! expressible with just `cargo`. Notably, it provides tests via `cargo test -p xtask` 4//! expressible with just `cargo`. Notably, it provides tests via `cargo test -p xtask`
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index b0b76b8aa..7b190d425 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -71,7 +71,7 @@ impl Metrics {
71 Ok(()) 71 Ok(())
72 } 72 }
73 fn measure_analysis_stats_self(&mut self) -> Result<()> { 73 fn measure_analysis_stats_self(&mut self) -> Result<()> {
74 self.measure_analysis_stats_path("self", &".") 74 self.measure_analysis_stats_path("self", ".")
75 } 75 }
76 fn measure_analysis_stats(&mut self, bench: &str) -> Result<()> { 76 fn measure_analysis_stats(&mut self, bench: &str) -> Result<()> {
77 self.measure_analysis_stats_path( 77 self.measure_analysis_stats_path(
@@ -81,9 +81,8 @@ impl Metrics {
81 } 81 }
82 fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> { 82 fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> {
83 eprintln!("\nMeasuring analysis-stats/{}", name); 83 eprintln!("\nMeasuring analysis-stats/{}", name);
84 let output = 84 let output = cmd!("./target/release/rust-analyzer -q analysis-stats --memory-usage {path}")
85 cmd!("./target/release/rust-analyzer --quiet analysis-stats --memory-usage {path}") 85 .read()?;
86 .read()?;
87 for (metric, value, unit) in parse_metrics(&output) { 86 for (metric, value, unit) in parse_metrics(&output) {
88 self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into()); 87 self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into());
89 } 88 }
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
index ffcae2cf7..2384a746f 100644
--- a/xtask/src/release/changelog.rs
+++ b/xtask/src/release/changelog.rs
@@ -132,7 +132,7 @@ fn parse_changelog_line(s: &str) -> Option<PrInfo> {
132 return Some(PrInfo { kind, message: Some(message) }); 132 return Some(PrInfo { kind, message: Some(message) });
133 } 133 }
134 }; 134 };
135 let res = PrInfo { kind, message }; 135 let res = PrInfo { message, kind };
136 Some(res) 136 Some(res)
137} 137}
138 138
@@ -152,7 +152,7 @@ fn parse_title_line(s: &str) -> PrInfo {
152 PrKind::Skip => None, 152 PrKind::Skip => None,
153 _ => Some(s[prefix.len()..].to_string()), 153 _ => Some(s[prefix.len()..].to_string()),
154 }; 154 };
155 return PrInfo { kind, message }; 155 return PrInfo { message, kind };
156 } 156 }
157 } 157 }
158 PrInfo { kind: PrKind::Other, message: Some(s.to_string()) } 158 PrInfo { kind: PrKind::Other, message: Some(s.to_string()) }
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 6f687a788..f2ba8efef 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -1,4 +1,7 @@
1use std::path::{Path, PathBuf}; 1use std::{
2 collections::HashSet,
3 path::{Path, PathBuf},
4};
2 5
3use xshell::{cmd, pushd, pushenv, read_file}; 6use xshell::{cmd, pushd, pushenv, read_file};
4 7
@@ -33,7 +36,7 @@ fn check_code_formatting() {
33 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); 36 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
34 crate::ensure_rustfmt().unwrap(); 37 crate::ensure_rustfmt().unwrap();
35 let res = cmd!("cargo fmt -- --check").run(); 38 let res = cmd!("cargo fmt -- --check").run();
36 if !res.is_ok() { 39 if res.is_err() {
37 let _ = cmd!("cargo fmt").run(); 40 let _ = cmd!("cargo fmt").run();
38 } 41 }
39 res.unwrap() 42 res.unwrap()
@@ -81,6 +84,7 @@ Please adjust docs/dev/lsp-extensions.md.
81#[test] 84#[test]
82fn rust_files_are_tidy() { 85fn rust_files_are_tidy() {
83 let mut tidy_docs = TidyDocs::default(); 86 let mut tidy_docs = TidyDocs::default();
87 let mut tidy_marks = TidyMarks::default();
84 for path in rust_files() { 88 for path in rust_files() {
85 let text = read_file(&path).unwrap(); 89 let text = read_file(&path).unwrap();
86 check_todo(&path, &text); 90 check_todo(&path, &text);
@@ -88,8 +92,10 @@ fn rust_files_are_tidy() {
88 check_trailing_ws(&path, &text); 92 check_trailing_ws(&path, &text);
89 deny_clippy(&path, &text); 93 deny_clippy(&path, &text);
90 tidy_docs.visit(&path, &text); 94 tidy_docs.visit(&path, &text);
95 tidy_marks.visit(&path, &text);
91 } 96 }
92 tidy_docs.finish(); 97 tidy_docs.finish();
98 tidy_marks.finish();
93} 99}
94 100
95#[test] 101#[test]
@@ -193,7 +199,9 @@ https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after-
193fn deny_clippy(path: &Path, text: &str) { 199fn deny_clippy(path: &Path, text: &str) {
194 let ignore = &[ 200 let ignore = &[
195 // The documentation in string literals may contain anything for its own purposes 201 // The documentation in string literals may contain anything for its own purposes
196 "ide_completion/src/generated_lint_completions.rs", 202 "ide_db/src/helpers/generated_lints.rs",
203 // The tests test clippy lint hovers
204 "ide/src/hover.rs",
197 ]; 205 ];
198 if ignore.iter().any(|p| path.ends_with(p)) { 206 if ignore.iter().any(|p| path.ends_with(p)) {
199 return; 207 return;
@@ -244,19 +252,19 @@ Zlib OR Apache-2.0 OR MIT
244 .map(|it| it.trim()) 252 .map(|it| it.trim())
245 .map(|it| it[r#""license":"#.len()..].trim_matches('"')) 253 .map(|it| it[r#""license":"#.len()..].trim_matches('"'))
246 .collect::<Vec<_>>(); 254 .collect::<Vec<_>>();
247 licenses.sort(); 255 licenses.sort_unstable();
248 licenses.dedup(); 256 licenses.dedup();
249 if licenses != expected { 257 if licenses != expected {
250 let mut diff = String::new(); 258 let mut diff = String::new();
251 259
252 diff += &format!("New Licenses:\n"); 260 diff.push_str("New Licenses:\n");
253 for &l in licenses.iter() { 261 for &l in licenses.iter() {
254 if !expected.contains(&l) { 262 if !expected.contains(&l) {
255 diff += &format!(" {}\n", l) 263 diff += &format!(" {}\n", l)
256 } 264 }
257 } 265 }
258 266
259 diff += &format!("\nMissing Licenses:\n"); 267 diff.push_str("\nMissing Licenses:\n");
260 for &l in expected.iter() { 268 for &l in expected.iter() {
261 if !licenses.contains(&l) { 269 if !licenses.contains(&l) {
262 diff += &format!(" {}\n", l) 270 diff += &format!(" {}\n", l)
@@ -280,7 +288,7 @@ fn check_todo(path: &Path, text: &str) {
280 // `ast::make`. 288 // `ast::make`.
281 "ast/make.rs", 289 "ast/make.rs",
282 // The documentation in string literals may contain anything for its own purposes 290 // The documentation in string literals may contain anything for its own purposes
283 "ide_completion/src/generated_lint_completions.rs", 291 "ide_db/src/helpers/generated_lints.rs",
284 ]; 292 ];
285 if need_todo.iter().any(|p| path.ends_with(p)) { 293 if need_todo.iter().any(|p| path.ends_with(p)) {
286 return; 294 return;
@@ -310,7 +318,7 @@ fn check_dbg(path: &Path, text: &str) {
310 "ide_completion/src/completions/postfix.rs", 318 "ide_completion/src/completions/postfix.rs",
311 // The documentation in string literals may contain anything for its own purposes 319 // The documentation in string literals may contain anything for its own purposes
312 "ide_completion/src/lib.rs", 320 "ide_completion/src/lib.rs",
313 "ide_completion/src/generated_lint_completions.rs", 321 "ide_db/src/helpers/generated_lints.rs",
314 // test for doc test for remove_dbg 322 // test for doc test for remove_dbg
315 "src/tests/generated.rs", 323 "src/tests/generated.rs",
316 ]; 324 ];
@@ -364,7 +372,10 @@ impl TidyDocs {
364 self.contains_fixme.push(path.to_path_buf()); 372 self.contains_fixme.push(path.to_path_buf());
365 } 373 }
366 } else { 374 } else {
367 if text.contains("// Feature:") || text.contains("// Assist:") { 375 if text.contains("// Feature:")
376 || text.contains("// Assist:")
377 || text.contains("// Diagnostic:")
378 {
368 return; 379 return;
369 } 380 }
370 self.missing_docs.push(path.display().to_string()); 381 self.missing_docs.push(path.display().to_string());
@@ -406,6 +417,39 @@ fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
406 .any(|it| dirs_to_exclude.contains(&it)) 417 .any(|it| dirs_to_exclude.contains(&it))
407} 418}
408 419
420#[derive(Default)]
421struct TidyMarks {
422 hits: HashSet<String>,
423 checks: HashSet<String>,
424}
425
426impl TidyMarks {
427 fn visit(&mut self, _path: &Path, text: &str) {
428 for line in text.lines() {
429 if let Some(mark) = find_mark(line, "hit") {
430 self.hits.insert(mark.to_string());
431 }
432 if let Some(mark) = find_mark(line, "check") {
433 self.checks.insert(mark.to_string());
434 }
435 if let Some(mark) = find_mark(line, "check_count") {
436 self.checks.insert(mark.to_string());
437 }
438 }
439 }
440
441 fn finish(self) {
442 assert!(!self.hits.is_empty());
443
444 let diff: Vec<_> =
445 self.hits.symmetric_difference(&self.checks).map(|it| it.as_str()).collect();
446
447 if !diff.is_empty() {
448 panic!("unpaired marks: {:?}", diff)
449 }
450 }
451}
452
409#[allow(deprecated)] 453#[allow(deprecated)]
410fn stable_hash(text: &str) -> u64 { 454fn stable_hash(text: &str) -> u64 {
411 use std::hash::{Hash, Hasher, SipHasher}; 455 use std::hash::{Hash, Hasher, SipHasher};
@@ -415,3 +459,11 @@ fn stable_hash(text: &str) -> u64 {
415 text.hash(&mut hasher); 459 text.hash(&mut hasher);
416 hasher.finish() 460 hasher.finish()
417} 461}
462
463fn find_mark<'a>(text: &'a str, mark: &'static str) -> Option<&'a str> {
464 let idx = text.find(mark)?;
465 let text = text[idx + mark.len()..].strip_prefix("!(")?;
466 let idx = text.find(|c: char| !(c.is_alphanumeric() || c == '_'))?;
467 let text = &text[..idx];
468 Some(text)
469}