Detect and report duplicate module names during parsing

Funny enough, we thought about that but only across packages. Now, the
  situation gets a little tricky because of folder structure, it's easy
  to define a module "foo" in `env`, `lib` and/or `validators`. From the
  compiler's perspective, they all have the same name.
This commit is contained in:
KtorZ 2024-09-03 15:24:01 +02:00
parent f8be81baa5
commit 038f6ecbfd
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 62 additions and 26 deletions

View File

@ -24,7 +24,7 @@ use zip::result::ZipError;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(thiserror::Error)] #[derive(thiserror::Error)]
pub enum Error { pub enum Error {
#[error("I just found two modules with the same name: '{module}'")] #[error("I just found two modules with the same name: '{}'", module.if_supports_color(Stderr, |s| s.yellow()))]
DuplicateModule { DuplicateModule {
module: String, module: String,
first: PathBuf, first: PathBuf,
@ -353,9 +353,9 @@ impl Diagnostic for Error {
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match self { match self {
Error::DuplicateModule { first, second, .. } => Some(Box::new(format!( Error::DuplicateModule { first, second, .. } => Some(Box::new(format!(
"Rename either {} or {}", "Rename either of them:\n- {}\n- {}",
first.display(), first.display().if_supports_color(Stderr, |s| s.yellow()),
second.display() second.display().if_supports_color(Stderr, |s| s.yellow()),
))), ))),
Error::FileIo { error, .. } => Some(Box::new(format!("{error}"))), Error::FileIo { error, .. } => Some(Box::new(format!("{error}"))),
Error::Blueprint(e) => e.help(), Error::Blueprint(e) => e.help(),

View File

@ -52,7 +52,7 @@ use package_name::PackageName;
use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyDelegationPart, StakePayload}; use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyDelegationPart, StakePayload};
use pallas_primitives::conway::PolicyId; use pallas_primitives::conway::PolicyId;
use std::{ use std::{
collections::{BTreeSet, HashMap}, collections::{BTreeSet, HashMap, HashSet},
fs::{self, File}, fs::{self, File},
io::BufReader, io::BufReader,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -698,12 +698,12 @@ where
fn parse_sources(&mut self, package_name: PackageName) -> Result<ParsedModules, Vec<Error>> { fn parse_sources(&mut self, package_name: PackageName) -> Result<ParsedModules, Vec<Error>> {
use rayon::prelude::*; use rayon::prelude::*;
let (parsed_modules, errors) = self let (parsed_modules, parse_errors, duplicates) = self
.sources .sources
.par_drain(0..) .par_drain(0..)
.fold( .fold(
|| (ParsedModules::new(), Vec::new()), || (ParsedModules::new(), Vec::new(), Vec::new()),
|(mut parsed_modules, mut errors), elem| { |(mut parsed_modules, mut parse_errors, mut duplicates), elem| {
let Source { let Source {
path, path,
name, name,
@ -720,19 +720,24 @@ where
kind, kind,
ast, ast,
code, code,
name, name: name.clone(),
path, path,
extra, extra,
package: package_name.to_string(), package: package_name.to_string(),
}; };
parsed_modules.insert(module.name.clone(), module); let path = module.path.clone();
(parsed_modules, errors) if let Some(first) = parsed_modules.insert(module.name.clone(), module)
{
duplicates.push((name, first.path.clone(), path))
}
(parsed_modules, parse_errors, duplicates)
} }
Err(errs) => { Err(errs) => {
for error in errs { for error in errs {
errors.push(( parse_errors.push((
path.clone(), path.clone(),
code.clone(), code.clone(),
NamedSource::new(path.display().to_string(), code.clone()), NamedSource::new(path.display().to_string(), code.clone()),
@ -740,23 +745,53 @@ where
)) ))
} }
(parsed_modules, errors) (parsed_modules, parse_errors, duplicates)
} }
} }
}, },
) )
.reduce( .reduce(
|| (ParsedModules::new(), Vec::new()), || (ParsedModules::new(), Vec::new(), Vec::new()),
|(mut parsed_modules, mut errors), (mut parsed, mut errs)| { |(mut parsed_modules, mut parse_errors, mut duplicates),
(mut parsed, mut errs, mut dups)| {
let keys_left = parsed_modules.keys().collect::<HashSet<_>>();
let keys_right = parsed.keys().collect::<HashSet<_>>();
for module in keys_left.intersection(&keys_right) {
duplicates.push((
module.to_string(),
parsed_modules
.get(module.as_str())
.map(|m| m.path.clone())
.unwrap(),
parsed.get(module.as_str()).map(|m| m.path.clone()).unwrap(),
));
}
parsed_modules.extend(parsed.drain()); parsed_modules.extend(parsed.drain());
errors.append(&mut errs); parse_errors.append(&mut errs);
duplicates.append(&mut dups);
(parsed_modules, errors) (parsed_modules, parse_errors, duplicates)
}, },
); );
let mut errors: Vec<Error> = errors let mut errors: Vec<Error> = Vec::new();
errors.extend(
duplicates
.into_iter()
.map(|(module, first, second)| Error::DuplicateModule {
module,
first,
second,
})
.collect::<Vec<_>>(),
);
errors.extend(
parse_errors
.into_iter() .into_iter()
.map(|(path, src, named, error)| Error::Parse { .map(|(path, src, named, error)| Error::Parse {
path, path,
@ -764,7 +799,8 @@ where
named: named.into(), named: named.into(),
error, error,
}) })
.collect(); .collect::<Vec<_>>(),
);
for parsed_module in parsed_modules.values() { for parsed_module in parsed_modules.values() {
if let Some(first) = self if let Some(first) = self