rust/compiler
Ralf Jung 78fecaaec8
Rollup merge of #133655 - dtolnay:maybeparen, r=lcnr
Eliminate print_expr_maybe_paren function from pretty printers

This PR is part of backporting Syn's expression precedence design into rustc. (See #133603 for other work on this.)

In Syn, our version of `print_expr_cond_paren` is called `print_subexpression` and it is called from 19 places. Of those calls, 12 of them need a "custom" behavior for the `needs_paren` argument, whereas only 7 use a "standard" behavior resembling `print_subexpression($e, $e.precedence() < Precedence::$Variant, ...)`. In other words the behavior that rustc_ast_pretty's `print_expr_maybe_paren` implements is actually not what you want most of the time. The current usage you see in rustc is overuse.

<details>
<summary>Aside: am I confident about the correctness of Syn's parenthesization? Yes. Click for details.</summary>

---

The behavior is constrained by the following pair of tests which both run over every Rust source file of rustc and the standard library and tools and test suites:

- To rule out **false positives**: for every expression in every source file, print the expression, parse it back, and verify that not a single new parenthesis got added. Since these are expressions parsed from source code, not macro-generated syntax trees, we know they must never need automatic parenthesis insertion. Rustc's pretty printer does not pass this.

    Pseudocode: `assert(expr == parse(print(expr)))`

- To rule out **false negatives**: for every expression in every source file, replace every Expr::Paren node in the syntax tree with just its contents, i.e. stripping the parentheses but otherwise preserving the syntax tree structure. Then print the stripped expression performing parenthesis insertion wherever needed, and reparse it. Verify that the reparsed expression has identical structure to the original, despite there being no parentheses in the original prior to printing, i.e. all the right parentheses got re-inserted by the printer to preserve the expression's structure. Rustc's pretty printer does not pass this. See https://github.com/dtolnay/syn/pull/1788 which reveals multiple rustc_ast_pretty bugs.

    Pseudocode: `assert(unparenthesize(expr) == unparenthesize(parse(print(unparenthesize(expr)))))`

---
</details>

If `print_expr_maybe_paren` is usually not correct, is there harm in keeping it for the minority of cases where it is correct? I think the answer is yes and Syn doesn't use any equivalent of this helper function. The problems with it are:

- Having both `print_expr_maybe_paren` and `print_expr_cond_paren` applies counterproductive inertia against moving from the first to the second. When looking at a call site like `print_expr_maybe_paren(e, Precedence::$Variant, ...)` with parentheses not being inserted where they should be, anyone's first inclination would be to solve the bug by tweaking $Variant because that is the only knob that visibly appears in the function call. For example to pass "prec + 1", like tweaking the code to conditionally pass `Precedence::Prefix` instead of `Precedence::Cast`.

    Experience in Syn shows this is (almost?) never what you want the person to do. In a call `print_expr_cond_paren(e, e.precedence() < ExprPrecedence::$Variant, ...)` almost always the best fix involves one of:

    - Changing `e.precedence()`, e.g. to `fixup.leading_precedence(e)` and `fixup.trailing_precedence(e)` in cases of asymmetrical precedence (`(return 1) + 1` vs `1 + return 1`).

    - Changing `<` to `<=`, to handle associativity and other grammar restrictions like chained comparisons (which rustc gets wrong today).

    - Adding `||` and/or `&&` clauses to the condition.

    By using these 3 better knobs instead of $Variant, it upholds the property that any time we talk about precedence, it is always the precedence of some actual expression that our code is actively manipulating, instead of a value standing in for some imaginary precedence level that would exist between two consecutive [real levels](https://doc.rust-lang.org/1.83.0/reference/expressions.html#expression-precedence). For example consider that "`Cast` + 1" might be `Prefix` today, but only until some new Rust syntax ends up adding a level between those.

- The `print_expr_maybe_paren` call sites look shorter, but they are not clearer. For myself, a function argument that says "does this subexpression need parenthesization" is a concrete thing that is easy to think about, while a function argument that is "what is the effective precedence level associated with this subexpression's placement inside its parent expression" is abstract and tricky to even state a precise meaning for. I expect that for someone less familiar with the pretty printer working on adding a new expression kind (like postfix match, recently), having every subexpression consistently printed using `print_expr_cond_paren` will be more beneficial, for the same reason, than having `print_expr_maybe_paren` available.

r? ``@lcnr``
2024-11-30 19:24:42 +01:00
..
rustc Auto merge of #132282 - Noratrieb:it-is-the-end-of-serial, r=cjgillot 2024-11-12 15:14:56 +00:00
rustc_abi Auto merge of #130867 - michirakara:steps_between, r=dtolnay 2024-11-22 10:54:22 +00:00
rustc_arena move strict provenance lints to new feature gate, remove old feature gates 2024-10-21 15:22:17 +01:00
rustc_ast always create DefIds when lowering anon-consts 2024-11-28 12:22:02 +00:00
rustc_ast_ir Add sugar for &pin (const|mut) types 2024-10-07 11:15:04 -07:00
rustc_ast_lowering Auto merge of #133468 - lcnr:uwu4, r=BoxyUwU 2024-11-28 15:58:17 +00:00
rustc_ast_passes Refactor where predicates, and reserve for attributes support 2024-11-25 16:38:35 +08:00
rustc_ast_pretty Eliminate rustc_ast_pretty's print_expr_maybe_paren 2024-11-29 16:46:31 -08:00
rustc_attr ensure that all publicly reachable const fn have const stability info 2024-11-10 10:16:26 +01:00
rustc_baked_icu_data Delete the cfg(not(parallel)) serial compiler 2024-11-12 13:38:58 +00:00
rustc_borrowck uplift fold_regions to rustc_type_ir 2024-11-28 10:40:58 +01:00
rustc_builtin_macros update cfgs 2024-11-27 15:14:54 +00:00
rustc_codegen_cranelift Rollup merge of #133422 - taiki-e:riscv-e-clobber-abi, r=Amanieu 2024-11-28 12:06:01 +01:00
rustc_codegen_gcc Support predicate registers (clobber-only) in Hexagon inline assembly 2024-11-25 23:11:17 +09:00
rustc_codegen_llvm Revert "Rollup merge of #133418 - Zalathar:spans, r=jieyouxu" 2024-11-29 14:57:01 +11:00
rustc_codegen_ssa Rollup merge of #131698 - the8472:remove-set-discriminant-hack, r=RalfJung 2024-11-30 19:24:40 +01:00
rustc_const_eval Move always_storage_live_locals. 2024-11-26 12:05:57 +11:00
rustc_data_structures Add UnordMap::clear method 2024-11-20 18:11:37 +01:00
rustc_driver
rustc_driver_impl Rollup merge of #133590 - nnethercote:rename-parse-only, r=estebank 2024-11-29 10:19:00 +01:00
rustc_error_codes remove support for rustc_safe_intrinsic attribute; use rustc_intrinsic functions instead 2024-11-08 09:16:00 +01:00
rustc_error_messages Delete the cfg(not(parallel)) serial compiler 2024-11-12 13:38:58 +00:00
rustc_errors Auto merge of #132954 - matthiaskrgr:rollup-x3rww9h, r=matthiaskrgr 2024-11-12 18:04:27 +00:00
rustc_expand Implement the unsafe-fields RFC. 2024-11-21 19:32:07 +01:00
rustc_feature Rollup merge of #116161 - Soveu:varargs2, r=cjgillot 2024-11-30 12:56:50 +08:00
rustc_fluent_macro use tracked_path in rustc_fluent_macro 2024-10-19 22:32:38 +08:00
rustc_fs_util Couple of changes to make it easier to compile rustc for wasm 2024-09-26 19:51:14 +00:00
rustc_graphviz Reformat using the new identifier sorting from rustfmt 2024-09-22 19:11:29 -04:00
rustc_hir update comment 2024-11-28 12:22:02 +00:00
rustc_hir_analysis Rollup merge of #116161 - Soveu:varargs2, r=cjgillot 2024-11-30 12:56:50 +08:00
rustc_hir_pretty Eliminate rustc_hir_pretty's print_expr_maybe_paren 2024-11-29 16:46:33 -08:00
rustc_hir_typeck simplify how the hir_typeck_pass_to_variadic_function diagnostic is created 2024-11-29 20:49:06 +01:00
rustc_incremental Move some code from Compiler::enter to GlobalCtxt::finish 2024-11-09 17:55:39 +00:00
rustc_index Remove HybridBitSet. 2024-11-29 17:23:34 +11:00
rustc_index_macros Auto merge of #130867 - michirakara:steps_between, r=dtolnay 2024-11-22 10:54:22 +00:00
rustc_infer support revealing defined opaque post borrowck 2024-11-28 10:40:58 +01:00
rustc_interface Rollup merge of #133590 - nnethercote:rename-parse-only, r=estebank 2024-11-29 10:19:00 +01:00
rustc_lexer Clean up c_or_byte_string. 2024-11-25 16:10:55 +11:00
rustc_lint Rollup merge of #133487 - pitaj:reserve-guarded-strings, r=fee1-dead 2024-11-28 12:06:04 +01:00
rustc_lint_defs fix confusing diagnostic for reserved ## 2024-11-25 22:29:14 -07:00
rustc_llvm Rollup merge of #127483 - BertalanD:no_sanitize-global-var, r=rcvalle 2024-11-23 20:19:51 +08:00
rustc_log Reformat using the new identifier sorting from rustfmt 2024-09-22 19:11:29 -04:00
rustc_macros give a better error for tuple structs in derive(Diagnostic) 2024-10-27 21:23:28 -04:00
rustc_metadata Rollup merge of #132750 - daltenty:daltenty/libs, r=jieyouxu 2024-11-30 12:56:50 +08:00
rustc_middle Rollup merge of #133501 - lcnr:post-borrowck-analysis, r=compiler-errors 2024-11-29 10:18:57 +01:00
rustc_mir_build fix a comment with uneven number of backticks in rustc_mir_build 2024-11-28 21:54:27 +01:00
rustc_mir_dataflow Stop using HybridBitSet in dataflow diffs. 2024-11-29 17:23:34 +11:00
rustc_mir_transform Revert "Rollup merge of #133418 - Zalathar:spans, r=jieyouxu" 2024-11-29 14:57:01 +11:00
rustc_monomorphize Share inline(never) generics across crates 2024-11-28 13:43:05 -05:00
rustc_next_trait_solver support revealing defined opaque post borrowck 2024-11-28 10:40:58 +01:00
rustc_parse Rollup merge of #133623 - nnethercote:parse_expr_bottom-spans, r=compiler-errors 2024-11-30 12:56:54 +08:00
rustc_parse_format Remove 'apostrophes' from rustc_parse_format 2024-10-14 23:22:51 +02:00
rustc_passes Refactor where predicates, and reserve for attributes support 2024-11-25 16:38:35 +08:00
rustc_pattern_analysis no more Reveal :( 2024-11-23 13:52:54 +01:00
rustc_privacy Simplify some places that deal with generic parameter defaults 2024-11-11 21:29:18 +01:00
rustc_query_impl Rollup merge of #132410 - bjorn3:yet_another_driver_refactor_round, r=cjgillot 2024-11-27 22:23:24 +01:00
rustc_query_system Auto merge of #124780 - Mark-Simulacrum:lockless-cache, r=lcnr 2024-11-19 02:07:48 +00:00
rustc_resolve always create DefIds when lowering anon-consts 2024-11-28 12:22:02 +00:00
rustc_sanitizers use TypingEnv when no infcx is available 2024-11-18 10:38:56 +01:00
rustc_serialize Fix explicit_iter_loop in rustc_serialize 2024-10-16 15:44:16 +02:00
rustc_session Update -Zshow-span help message. 2024-11-29 06:10:16 +11:00
rustc_smir Rollup merge of #132410 - bjorn3:yet_another_driver_refactor_round, r=cjgillot 2024-11-27 22:23:24 +01:00
rustc_span Rollup merge of #133463 - taiki-e:aarch64-asm-x18, r=Amanieu 2024-11-28 12:06:02 +01:00
rustc_symbol_mangling additional TypingEnv cleanups 2024-11-19 21:36:23 +01:00
rustc_target Rollup merge of #133571 - madsmtm:visionos-support-std, r=Noratrieb 2024-11-30 19:24:41 +01:00
rustc_trait_selection Rollup merge of #133585 - estebank:issue-133563, r=jieyouxu 2024-11-30 12:56:52 +08:00
rustc_traits Delay a bug when encountering an impl with unconstrained generics in codegen_select 2024-11-23 05:27:45 +00:00
rustc_transmute use TypingEnv when no infcx is available 2024-11-18 10:38:56 +01:00
rustc_ty_utils support revealing defined opaque post borrowck 2024-11-28 10:40:58 +01:00
rustc_type_ir support revealing defined opaque post borrowck 2024-11-28 10:40:58 +01:00
rustc_type_ir_macros do not relate Abi and Safety 2024-10-22 23:13:04 +02:00
stable_mir Rollup merge of #132161 - celinval:smir-fix-indent, r=compiler-errors 2024-11-08 18:51:28 +11:00