#![deny(clippy::arithmetic_side_effects)]
use core::cmp::Ordering;
use std::borrow::Cow;
use crate::diag::{Diagnostic, DiagnosticSink};
use crate::symbols::{Definition, DefinitionKind, DuplicateSymbol, Resolver};
use super::inputs::MachInput;
#[derive(Clone, Copy, Debug, Default)]
pub struct MachRules;
fn rank(kind: DefinitionKind) -> u8 {
match kind {
DefinitionKind::Undefined => 0,
DefinitionKind::Lazy => 1,
DefinitionKind::Shared => 2,
DefinitionKind::Common => 3,
DefinitionKind::Weak => 4,
DefinitionKind::Regular => 5,
}
}
impl Resolver for MachRules {
fn compare(&self, a: &Definition, b: &Definition) -> Ordering {
match rank(a.kind).cmp(&rank(b.kind)) {
Ordering::Equal if a.kind == DefinitionKind::Common => a.aux.cmp(&b.aux),
other => other,
}
}
fn is_duplicate(&self, winner: &Definition, other: &Definition) -> bool {
winner.kind == DefinitionKind::Regular
&& other.kind == DefinitionKind::Regular
&& !winner.same_origin(other)
}
}
#[must_use]
pub fn display_name(name: &[u8], demangle: bool) -> Cow<'_, str> {
match name.strip_prefix(b"_") {
Some(rest) if demangle && rest.starts_with(b"_Z") => {
crate::hints::display_symbol(rest, true)
}
_ => String::from_utf8_lossy(name),
}
}
#[must_use]
pub fn report_duplicates(
duplicates: &[DuplicateSymbol<'_>],
files: &[MachInput<'_>],
demangle: bool,
diagnostics: &dyn DiagnosticSink,
) -> usize {
let mut errors = 0usize;
for duplicate in duplicates {
let name = display_name(duplicate.name.bytes(), demangle);
let mut diagnostic = Diagnostic::error(format!("duplicate symbol: {name}"));
let winner = files
.get(duplicate.winner.file.index())
.map_or_else(|| "<unknown>".to_string(), MachInput::display);
diagnostic = diagnostic.detail(format!("defined in {winner}"));
for other in &duplicate.others {
let file = files
.get(other.file.index())
.map_or_else(|| "<unknown>".to_string(), MachInput::display);
diagnostic = diagnostic.detail(format!("defined in {file}"));
}
diagnostics.emit(diagnostic);
errors = errors.saturating_add(1);
}
errors
}