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)]
#[derive(thiserror::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 {
module: String,
first: PathBuf,
@ -353,9 +353,9 @@ impl Diagnostic for Error {
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match self {
Error::DuplicateModule { first, second, .. } => Some(Box::new(format!(
"Rename either {} or {}",
first.display(),
second.display()
"Rename either of them:\n- {}\n- {}",
first.display().if_supports_color(Stderr, |s| s.yellow()),
second.display().if_supports_color(Stderr, |s| s.yellow()),
))),
Error::FileIo { error, .. } => Some(Box::new(format!("{error}"))),
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_primitives::conway::PolicyId;
use std::{
collections::{BTreeSet, HashMap},
collections::{BTreeSet, HashMap, HashSet},
fs::{self, File},
io::BufReader,
path::{Path, PathBuf},
@ -698,12 +698,12 @@ where
fn parse_sources(&mut self, package_name: PackageName) -> Result<ParsedModules, Vec<Error>> {
use rayon::prelude::*;
let (parsed_modules, errors) = self
let (parsed_modules, parse_errors, duplicates) = self
.sources
.par_drain(0..)
.fold(
|| (ParsedModules::new(), Vec::new()),
|(mut parsed_modules, mut errors), elem| {
|| (ParsedModules::new(), Vec::new(), Vec::new()),
|(mut parsed_modules, mut parse_errors, mut duplicates), elem| {
let Source {
path,
name,
@ -720,19 +720,24 @@ where
kind,
ast,
code,
name,
name: name.clone(),
path,
extra,
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) => {
for error in errs {
errors.push((
parse_errors.push((
path.clone(),
code.clone(),
NamedSource::new(path.display().to_string(), code.clone()),
@ -740,31 +745,62 @@ where
))
}
(parsed_modules, errors)
(parsed_modules, parse_errors, duplicates)
}
}
},
)
.reduce(
|| (ParsedModules::new(), Vec::new()),
|(mut parsed_modules, mut errors), (mut parsed, mut errs)| {
|| (ParsedModules::new(), Vec::new(), Vec::new()),
|(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());
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
.into_iter()
.map(|(path, src, named, error)| Error::Parse {
path,
src,
named: named.into(),
error,
})
.collect();
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()
.map(|(path, src, named, error)| Error::Parse {
path,
src,
named: named.into(),
error,
})
.collect::<Vec<_>>(),
);
for parsed_module in parsed_modules.values() {
if let Some(first) = self