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}