charon_lib/
options.rs

1//! The options that control charon behavior.
2use annotate_snippets::Level;
3use clap::ValueEnum;
4use indoc::indoc;
5use itertools::Itertools;
6use macros::EnumAsGetters;
7use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10use crate::{
11    ast::*,
12    errors::{ErrorCtx, display_unspanned_error},
13    name_matcher::NamePattern,
14    raise_error, register_error,
15};
16
17/// The name of the environment variable we use to save the serialized Cli options
18/// when calling charon-driver from cargo-charon.
19pub const CHARON_ARGS: &str = "CHARON_ARGS";
20
21// This structure is used to store the command-line instructions.
22// We automatically derive a command-line parser based on this structure.
23// Note that the doc comments are used to generate the help message when using
24// `--help`.
25//
26// Note that because we need to transmit the options to the charon driver,
27// we store them in a file before calling this driver (hence the `Serialize`,
28// `Deserialize` options).
29#[derive(Debug, Default, Clone, clap::Args, PartialEq, Eq, Serialize, Deserialize)]
30#[clap(name = "Charon")]
31#[charon::rename("cli_options")]
32pub struct CliOpts {
33    /// Extract the unstructured LLBC (i.e., don't reconstruct the control-flow)
34    #[clap(long)]
35    #[serde(default)]
36    pub ullbc: bool,
37    /// Whether to precisely translate drops and drop-related code. For this, we add explicit
38    /// `Destruct` bounds to all generic parameters, set the MIR level to at least `elaborated`,
39    /// and attempt to retrieve drop glue for all types.
40    ///
41    /// This option is known to cause panics inside rustc, because their drop handling is not
42    /// design to work on polymorphic types. To silence the warning, pass appropriate `--opaque
43    /// '{impl core::marker::Destruct for some::Type}'` options.
44    ///
45    /// Without this option, drops may be "conditional" and we may lack information about what code
46    /// is run on drop in a given polymorphic function body.
47    #[clap(long)]
48    #[serde(default)]
49    pub precise_drops: bool,
50    /// If activated, this skips borrow-checking of the crate.
51    #[clap(long)]
52    #[serde(default)]
53    pub skip_borrowck: bool,
54    /// The MIR stage to extract. This is only relevant for the current crate; for dpendencies only
55    /// MIR optimized is available.
56    #[arg(long)]
57    pub mir: Option<MirLevel>,
58    /// Extra flags to pass to rustc.
59    #[clap(long = "rustc-arg")]
60    #[serde(default)]
61    pub rustc_args: Vec<String>,
62
63    /// Monomorphize the items encountered when possible. Generic items found in the crate are
64    /// skipped. To only translate a particular call graph, use `--start-from`. Note: this doesn't
65    /// currently support `dyn Trait`.
66    #[clap(long, visible_alias = "mono")]
67    #[serde(default)]
68    pub monomorphize: bool,
69    /// Partially monomorphize items to make it so that no item is ever monomorphized with a
70    /// mutable reference (or type containing one); said differently, so that the presence of
71    /// mutable references in a type is independent of its generics. This is used by Aeneas.
72    #[clap(
73        long,
74        value_name("INCLUDE_TYPES"),
75        num_args(0..=1),
76        require_equals(true),
77        default_missing_value("all"),
78    )]
79    #[serde(default)]
80    pub monomorphize_mut: Option<MonomorphizeMut>,
81
82    /// A list of item paths to use as starting points for the translation. We will translate these
83    /// items and any items they refer to, according to the opacity rules. When absent, we start
84    /// from the path `crate` (which translates the whole crate).
85    #[clap(long, value_delimiter = ',')]
86    #[serde(default)]
87    pub start_from: Vec<String>,
88    /// Use all the items annotated with the given attribute as starting points for translation
89    /// (except modules).
90    /// If an attribute name is not specified, `verify::start_from` is used.
91    #[clap(
92        long,
93        value_name("ATTRIBUTE"),
94        num_args(0..=1),
95        require_equals(true),
96        default_missing_value("verify::start_from"),
97    )]
98    #[serde(default)]
99    pub start_from_attribute: Option<String>,
100    /// Use all the `pub` items as starting points for translation (except modules).
101    #[clap(long)]
102    #[serde(default)]
103    pub start_from_pub: bool,
104
105    /// Whitelist of items to translate. These use the name-matcher syntax.
106    #[clap(
107        long,
108        help = indoc!("
109            Whitelist of items to translate. These use the name-matcher syntax (note: this differs
110            a bit from the ocaml NameMatcher).
111
112            Note: This is very rough at the moment. E.g. this parses `u64` as a path instead of the
113            built-in type. It is also not possible to filter a trait impl (this will only filter
114            its methods). Please report bugs or missing features.
115
116            Examples:
117              - `crate::module1::module2::item`: refers to this item and all its subitems (e.g.
118                  submodules or trait methods);
119              - `crate::module1::module2::item::_`: refers only to the subitems of this item;
120              - `core::convert::{impl core::convert::Into<_> for _}`: retrieve the body of this
121                  very useful impl;
122
123            When multiple patterns in the `--include` and `--opaque` options match the same item,
124            the most precise pattern wins. E.g.: `charon --opaque crate::module --include
125            crate::module::_` makes the `module` opaque (we won't explore its contents), but the
126            items in it transparent (we will translate them if we encounter them.)
127    "))]
128    #[serde(default)]
129    #[charon::rename("included")]
130    pub include: Vec<String>,
131    /// Blacklist of items to keep opaque. Works just like `--include`, see the doc there.
132    #[clap(long)]
133    #[serde(default)]
134    pub opaque: Vec<String>,
135    /// Blacklist of items to not translate at all. Works just like `--include`, see the doc there.
136    #[clap(long)]
137    #[serde(default)]
138    pub exclude: Vec<String>,
139    /// Usually we skip the bodies of foreign methods and structs with private fields. When this
140    /// flag is on, we don't.
141    #[clap(long)]
142    #[serde(default)]
143    pub extract_opaque_bodies: bool,
144    /// Usually we skip the provided methods that aren't used. When this flag is on, we translate
145    /// them all.
146    #[clap(long)]
147    #[serde(default)]
148    pub translate_all_methods: bool,
149
150    /// Transforma the associate types of traits to be type parameters instead. This takes a list
151    /// of name patterns of the traits to transform, using the same syntax as `--include`.
152    #[clap(long)]
153    #[serde(default)]
154    pub remove_associated_types: Vec<String>,
155    /// Whether to hide various marker traits such as `Sized`, `Sync`, `Send` and `Destruct`
156    /// anywhere they show up. This can considerably speed up translation.
157    #[clap(long)]
158    #[serde(default)]
159    pub hide_marker_traits: bool,
160    /// Remove trait clauses from type declarations. Must be combined with
161    /// `--remove-associated-types` for type declarations that use trait associated types in their
162    /// fields, otherwise this will result in errors.
163    #[clap(long)]
164    #[serde(default)]
165    pub remove_adt_clauses: bool,
166    /// Hide the `A` type parameter on standard library containers (`Box`, `Vec`, etc).
167    #[clap(long)]
168    #[serde(default)]
169    pub hide_allocator: bool,
170    /// Trait method declarations take a `Self: Trait` clause as parameter, so that they can be
171    /// reused by multiple trait impls. This however causes trait definitions to be mutually
172    /// recursive with their method declarations. This flag removes `Self` clauses that aren't used
173    /// to break this mutual recursion when possible.
174    #[clap(long)]
175    #[serde(default)]
176    pub remove_unused_self_clauses: bool,
177
178    /// Transform precise drops to the equivalent `drop_in_place(&raw mut p)` call.
179    #[clap(long)]
180    #[serde(default)]
181    pub desugar_drops: bool,
182    /// Transform array-to-slice unsizing, repeat expressions, and raw pointer construction into
183    /// builtin functions in ULLBC.
184    #[clap(long)]
185    #[serde(default)]
186    pub ops_to_function_calls: bool,
187    /// Transform array/slice indexing into builtin functions in ULLBC. Note that this may
188    /// introduce UB since it creates references that were not normally created, including when
189    /// indexing behind a raw pointer.
190    #[clap(long)]
191    #[serde(default)]
192    pub index_to_function_calls: bool,
193    /// Treat `Box<T>` as if it was a built-in type.
194    #[clap(long)]
195    #[serde(default)]
196    pub treat_box_as_builtin: bool,
197    /// Do not inline or evaluate constants.
198    #[clap(long)]
199    #[serde(default)]
200    pub raw_consts: bool,
201    /// Replace string literal constants with a constant u8 array that gets unsized,
202    /// expliciting the fact a string constant has a hidden reference.
203    #[clap(long)]
204    #[serde(default)]
205    pub unsized_strings: bool,
206    /// Replace "bound checks followed by UB-on-overflow operation" with the corresponding
207    /// panic-on-overflow operation. This loses unwinding information.
208    #[clap(long)]
209    #[serde(default)]
210    pub reconstruct_fallible_operations: bool,
211    /// Replace "if x { panic() }" with "assert(x)".
212    #[clap(long)]
213    #[serde(default)]
214    pub reconstruct_asserts: bool,
215    /// Use `DeBruijnVar::Free` for the variables bound in item signatures, instead of
216    /// `DeBruijnVar::Bound` everywhere. This simplifies the management of generics for projects
217    /// that don't intend to manipulate them too much.
218    #[clap(long)]
219    #[serde(default)]
220    pub unbind_item_vars: bool,
221
222    /// Pretty-print the ULLBC immediately after extraction from MIR.
223    #[clap(long)]
224    #[serde(default)]
225    pub print_original_ullbc: bool,
226    /// Pretty-print the ULLBC after applying the micro-passes (before serialization/control-flow reconstruction).
227    #[clap(long)]
228    #[serde(default)]
229    pub print_ullbc: bool,
230    /// Pretty-print the LLBC just after we built it (i.e., immediately after loop reconstruction).
231    #[clap(long)]
232    #[serde(default)]
233    pub print_built_llbc: bool,
234    /// Pretty-print the final LLBC (after all the cleaning micro-passes).
235    #[clap(long)]
236    #[serde(default)]
237    pub print_llbc: bool,
238
239    /// The destination directory. Files will be generated as `<dest_dir>/<crate_name>.{u}llbc`,
240    /// unless `dest_file` is set. `dest_dir` defaults to the current directory.
241    #[clap(long = "dest", value_parser)]
242    #[serde(default)]
243    pub dest_dir: Option<PathBuf>,
244    /// The destination file. By default `<dest_dir>/<crate_name>.llbc`. If this is set we ignore
245    /// `dest_dir`.
246    #[clap(long, value_parser)]
247    #[serde(default)]
248    pub dest_file: Option<PathBuf>,
249    /// Don't deduplicate values (types, trait refs) in the .(u)llbc file. This makes the file easier to inspect.
250    #[clap(long)]
251    #[serde(default)]
252    pub no_dedup_serialized_ast: bool,
253    /// Don't serialize the final (U)LLBC to a file.
254    #[clap(long)]
255    #[serde(default)]
256    pub no_serialize: bool,
257    /// Panic on the first error. This is useful for debugging.
258    #[clap(long)]
259    #[serde(default)]
260    pub abort_on_error: bool,
261    /// Consider any warnings to be errors.
262    #[clap(long)]
263    #[serde(default)]
264    pub error_on_warnings: bool,
265
266    /// Named builtin sets of options.
267    #[clap(long)]
268    #[arg(value_enum)]
269    pub preset: Option<Preset>,
270}
271
272/// The MIR stage to use. This is only relevant for the current crate: for dependencies, only mir
273/// optimized is available (or mir elaborated for consts).
274#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
275pub enum MirLevel {
276    /// The MIR just after MIR lowering.
277    Built,
278    /// The MIR after const promotion. This is the MIR used by the borrow-checker.
279    Promoted,
280    /// The MIR after drop elaboration. This is the first MIR to include all the runtime
281    /// information.
282    Elaborated,
283    /// The MIR after optimizations. Charon disables all the optimizations it can, so this is
284    /// sensibly the same MIR as the elaborated MIR.
285    Optimized,
286}
287
288/// Presets to make it easier to tweak options without breaking dependent projects. Eventually we
289/// should define semantically-meaningful presets instead of project-specific ones.
290#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
291#[non_exhaustive]
292pub enum Preset {
293    /// The default translation used before May 2025. After that, many passes were made optional
294    /// and disabled by default.
295    OldDefaults,
296    /// Emit the MIR as unmodified as possible. This is very imperfect for now, we should make more
297    /// passes optional.
298    RawMir,
299    Aeneas,
300    Eurydice,
301    Soteria,
302    Tests,
303}
304
305#[derive(
306    Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize,
307)]
308pub enum MonomorphizeMut {
309    /// Monomorphize any item instantiated with `&mut`.
310    #[default]
311    All,
312    /// Monomorphize all non-typedecl items instantiated with `&mut`.
313    ExceptTypes,
314}
315
316impl CliOpts {
317    pub fn apply_preset(&mut self) {
318        if let Some(preset) = self.preset {
319            match preset {
320                Preset::OldDefaults => {
321                    self.treat_box_as_builtin = true;
322                    self.hide_allocator = true;
323                    self.ops_to_function_calls = true;
324                    self.index_to_function_calls = true;
325                    self.reconstruct_fallible_operations = true;
326                    self.reconstruct_asserts = true;
327                    self.unbind_item_vars = true;
328                }
329                Preset::RawMir => {
330                    self.extract_opaque_bodies = true;
331                    self.raw_consts = true;
332                    self.ullbc = true;
333                }
334                Preset::Aeneas => {
335                    self.remove_associated_types.push("*".to_owned());
336                    self.treat_box_as_builtin = true;
337                    self.ops_to_function_calls = true;
338                    self.index_to_function_calls = true;
339                    self.reconstruct_fallible_operations = true;
340                    self.reconstruct_asserts = true;
341                    self.hide_marker_traits = true;
342                    self.hide_allocator = true;
343                    self.remove_unused_self_clauses = true;
344                    self.unbind_item_vars = true;
345                    // Hide drop impls because they often involve nested borrows. which aeneas
346                    // doesn't handle yet.
347                    self.exclude.push("core::ops::drop::Drop".to_owned());
348                    self.exclude
349                        .push("{impl core::ops::drop::Drop for _}".to_owned());
350                }
351                Preset::Eurydice => {
352                    self.hide_allocator = true;
353                    self.treat_box_as_builtin = true;
354                    self.ops_to_function_calls = true;
355                    self.index_to_function_calls = true;
356                    self.reconstruct_fallible_operations = true;
357                    self.reconstruct_asserts = true;
358                    self.remove_associated_types.push("*".to_owned());
359                    self.unbind_item_vars = true;
360                    // Eurydice doesn't support opaque vtables it seems?
361                    self.include.push("core::marker::MetaSized".to_owned());
362                }
363                Preset::Soteria => {
364                    self.extract_opaque_bodies = true;
365                    self.monomorphize = true;
366                    self.mir = Some(MirLevel::Elaborated);
367                    self.ullbc = true;
368                }
369                Preset::Tests => {
370                    self.no_dedup_serialized_ast = true; // Helps debug
371                    self.treat_box_as_builtin = true;
372                    self.hide_allocator = true;
373                    self.reconstruct_fallible_operations = true;
374                    self.reconstruct_asserts = true;
375                    self.ops_to_function_calls = true;
376                    self.index_to_function_calls = true;
377                    self.rustc_args.push("--edition=2021".to_owned());
378                    self.rustc_args
379                        .push("-Zcrate-attr=feature(register_tool)".to_owned());
380                    self.rustc_args
381                        .push("-Zcrate-attr=register_tool(charon)".to_owned());
382                    self.exclude.push("core::fmt::Formatter".to_owned());
383                }
384            }
385        }
386    }
387
388    /// Check that the options are meaningful
389    pub fn validate(&self) -> anyhow::Result<()> {
390        if self.dest_dir.is_some() {
391            display_unspanned_error(
392                Level::WARNING,
393                "`--dest` is deprecated, use `--dest-file` instead",
394            )
395        }
396
397        if self.remove_adt_clauses && self.remove_associated_types.is_empty() {
398            anyhow::bail!(
399                "`--remove-adt-clauses` should be used with `--remove-associated-types='*'` \
400                to avoid missing clause errors",
401            )
402        }
403        if matches!(self.monomorphize_mut, Some(MonomorphizeMut::ExceptTypes))
404            && !self.remove_adt_clauses
405        {
406            anyhow::bail!(
407                "`--monomorphize-mut=except-types` should be used with `--remove-adt-clauses` \
408                to avoid generics mismatches"
409            )
410        }
411        Ok(())
412    }
413}
414
415/// Predicates that determine wihch items to use as starting point for translation.
416#[derive(Debug, Clone, EnumAsGetters)]
417pub enum StartFrom {
418    /// Item identified by a pattern/path.
419    Pattern(NamePattern),
420    /// Item annotated with the given attribute.
421    Attribute(String),
422    /// Item marked `pub`. Note that this does not take accessibility into account; a
423    /// non-reexported `pub` item will be included here.
424    Pub,
425}
426
427impl StartFrom {
428    pub fn matches(&self, ctx: &TranslatedCrate, item_meta: &ItemMeta) -> bool {
429        match self {
430            StartFrom::Pattern(pattern) => pattern.matches(ctx, &item_meta.name),
431            StartFrom::Attribute(attr) => item_meta
432                .attr_info
433                .attributes
434                .iter()
435                .filter_map(|a| a.as_unknown())
436                .any(|raw_attr| raw_attr.path == *attr),
437            StartFrom::Pub => item_meta.attr_info.public,
438        }
439    }
440}
441
442/// The options that control translation and transformation.
443pub struct TranslateOptions {
444    /// Items from which to start translation.
445    pub start_from: Vec<StartFrom>,
446    /// The level at which to extract the MIR
447    pub mir_level: MirLevel,
448    /// Usually we skip the provided methods that aren't used. When this flag is on, we translate
449    /// them all.
450    pub translate_all_methods: bool,
451    /// If `Some(_)`, run the partial mutability monomorphization pass. The contained enum
452    /// indicates whether to partially monomorphize types.
453    pub monomorphize_mut: Option<MonomorphizeMut>,
454    /// Whether to hide various marker traits such as `Sized`, `Sync`, `Send` and `Destruct`
455    /// anywhere they show up.
456    pub hide_marker_traits: bool,
457    /// Remove trait clauses attached to type declarations.
458    pub remove_adt_clauses: bool,
459    /// Hide the `A` type parameter on standard library containers (`Box`, `Vec`, etc).
460    pub hide_allocator: bool,
461    /// Remove unused `Self: Trait` clauses on method declarations.
462    pub remove_unused_self_clauses: bool,
463    /// Monomorphize code using hax's instantiation mechanism.
464    pub monomorphize_with_hax: bool,
465    /// Transform array-to-slice unsizing, repeat expressions, and raw pointer construction into
466    /// builtin functions in ULLBC.
467    pub ops_to_function_calls: bool,
468    /// Transform array/slice indexing into builtin functions in ULLBC.
469    pub index_to_function_calls: bool,
470    /// Print the llbc just after control-flow reconstruction.
471    pub print_built_llbc: bool,
472    /// Treat `Box<T>` as if it was a built-in type.
473    pub treat_box_as_builtin: bool,
474    /// Don't inline or evaluate constants.
475    pub raw_consts: bool,
476    /// Replace string literal constants with a constant u8 array that gets unsized,
477    /// expliciting the fact a string constant has a hidden reference.
478    pub unsized_strings: bool,
479    /// Replace "bound checks followed by UB-on-overflow operation" with the corresponding
480    /// panic-on-overflow operation. This loses unwinding information.
481    pub reconstruct_fallible_operations: bool,
482    /// Replace "if x { panic() }" with "assert(x)".
483    pub reconstruct_asserts: bool,
484    // Use `DeBruijnVar::Free` for the variables bound in item signatures.
485    pub unbind_item_vars: bool,
486    /// List of patterns to assign a given opacity to. Same as the corresponding `TranslateOptions`
487    /// field.
488    pub item_opacities: Vec<(NamePattern, ItemOpacity)>,
489    /// List of traits for which we transform associated types to type parameters.
490    pub remove_associated_types: Vec<NamePattern>,
491    /// Transform Drop to Call drop_in_place
492    pub desugar_drops: bool,
493    /// Add `Destruct` bounds to all generic params.
494    pub add_destruct_bounds: bool,
495    /// Translate drop glue for poly types, knowing that this may cause ICEs.
496    pub translate_poly_drop_glue: bool,
497}
498
499impl TranslateOptions {
500    pub fn new(error_ctx: &mut ErrorCtx, options: &CliOpts) -> Self {
501        let mut parse_pattern = |s: &str| match NamePattern::parse(s) {
502            Ok(p) => Ok(p),
503            Err(e) => {
504                raise_error!(
505                    error_ctx,
506                    crate(&TranslatedCrate::default()),
507                    Span::dummy(),
508                    "failed to parse pattern `{s}` ({e})"
509                )
510            }
511        };
512
513        let mut mir_level = options.mir.unwrap_or(MirLevel::Promoted);
514        if options.precise_drops {
515            mir_level = std::cmp::max(mir_level, MirLevel::Elaborated);
516        }
517
518        let mut start_from = options
519            .start_from
520            .iter()
521            .filter_map(|path| parse_pattern(&path).ok())
522            .map(StartFrom::Pattern)
523            .collect_vec();
524        if let Some(attr) = options.start_from_attribute.clone() {
525            start_from.push(StartFrom::Attribute(attr));
526        }
527        if options.start_from_pub {
528            start_from.push(StartFrom::Pub);
529        }
530        if start_from.is_empty() {
531            start_from.push(StartFrom::Pattern(parse_pattern("crate").unwrap()))
532        }
533
534        let item_opacities = {
535            use ItemOpacity::*;
536            let mut opacities = vec![];
537
538            // This is how to treat items that don't match any other pattern.
539            if options.extract_opaque_bodies {
540                opacities.push(("_".to_string(), Transparent));
541            } else {
542                opacities.push(("_".to_string(), Foreign));
543            }
544
545            // We always include the items from the crate.
546            opacities.push(("crate".to_owned(), Transparent));
547
548            for pat in options.include.iter() {
549                opacities.push((pat.to_string(), Transparent));
550            }
551            for pat in options.opaque.iter() {
552                opacities.push((pat.to_string(), Opaque));
553            }
554            for pat in options.exclude.iter() {
555                opacities.push((pat.to_string(), Invisible));
556            }
557
558            if options.hide_allocator {
559                opacities.push((format!("core::alloc::Allocator"), Invisible));
560                opacities.push((
561                    format!("alloc::alloc::{{impl core::alloc::Allocator for _}}"),
562                    Invisible,
563                ));
564            }
565
566            opacities
567                .into_iter()
568                .filter_map(|(s, opacity)| parse_pattern(&s).ok().map(|pat| (pat, opacity)))
569                .collect()
570        };
571
572        let remove_associated_types = options
573            .remove_associated_types
574            .iter()
575            .filter_map(|s| parse_pattern(&s).ok())
576            .collect();
577
578        TranslateOptions {
579            start_from,
580            mir_level,
581            monomorphize_mut: options.monomorphize_mut,
582            hide_marker_traits: options.hide_marker_traits,
583            remove_adt_clauses: options.remove_adt_clauses,
584            hide_allocator: options.hide_allocator,
585            remove_unused_self_clauses: options.remove_unused_self_clauses,
586            monomorphize_with_hax: options.monomorphize,
587            ops_to_function_calls: options.ops_to_function_calls,
588            index_to_function_calls: options.index_to_function_calls,
589            print_built_llbc: options.print_built_llbc,
590            item_opacities,
591            treat_box_as_builtin: options.treat_box_as_builtin,
592            raw_consts: options.raw_consts,
593            unsized_strings: options.unsized_strings,
594            reconstruct_fallible_operations: options.reconstruct_fallible_operations,
595            reconstruct_asserts: options.reconstruct_asserts,
596            remove_associated_types,
597            unbind_item_vars: options.unbind_item_vars,
598            translate_all_methods: options.translate_all_methods,
599            desugar_drops: options.desugar_drops,
600            add_destruct_bounds: options.precise_drops,
601            translate_poly_drop_glue: options.precise_drops,
602        }
603    }
604
605    /// Find the opacity requested for the given name. This does not take into account
606    /// `#[charon::opaque]` annotations, only cli parameters.
607    #[tracing::instrument(skip(self, krate), ret)]
608    pub fn opacity_for_name(&self, krate: &TranslatedCrate, name: &Name) -> ItemOpacity {
609        // Find the most precise pattern that matches this name. There is always one since
610        // the list contains the `_` pattern. If there are conflicting settings for this item, we
611        // err on the side of being more opaque.
612        let (_, opacity) = self
613            .item_opacities
614            .iter()
615            .filter(|(pat, _)| pat.matches(krate, name))
616            .max()
617            .unwrap();
618        *opacity
619    }
620}