From 77a2b54e019d16ddc8ddaca704040b3889f96ac6 Mon Sep 17 00:00:00 2001 From: Olof Kraigher Date: Mon, 13 Nov 2023 17:37:25 +0100 Subject: [PATCH] Ensure paths are consistently canonicalized. Closes #115 --- vhdl_lang/src/config.rs | 33 ++++----------------------- vhdl_lang/src/data/source.rs | 43 ++++++++++++++++++++++++++++++++++-- vhdl_lang/src/project.rs | 24 +++++++++++--------- 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/vhdl_lang/src/config.rs b/vhdl_lang/src/config.rs index d9901d4c..396cc1d0 100644 --- a/vhdl_lang/src/config.rs +++ b/vhdl_lang/src/config.rs @@ -33,17 +33,6 @@ impl LibraryConfig { /// Only include files that exists /// Files that do not exist produce a warning message pub fn file_names(&self, messages: &mut dyn MessageHandler) -> Vec { - fn as_abspath(file_path: &Path) -> Result { - match dunce::canonicalize(file_path) { - Ok(file_path) => Ok(file_path), - Err(err) => Err(Message::error(format!( - "Could not create absolute path {}: {:?}", - file_path.to_string_lossy(), - err - ))), - } - } - let mut result = Vec::new(); for pattern in self.patterns.iter() { let stripped_pattern = if cfg!(windows) { @@ -53,17 +42,10 @@ impl LibraryConfig { }; if is_literal(stripped_pattern) { - let file_path = Path::new(pattern); + let file_path = Path::new(pattern).to_owned(); if file_path.exists() { - match as_abspath(file_path) { - Ok(abs_path) => { - result.push(abs_path); - } - Err(msg) => { - messages.push(msg); - } - }; + result.push(file_path); } else { messages.push(Message::warning(format! {"File {pattern} does not exist"})); } @@ -76,14 +58,7 @@ impl LibraryConfig { empty_pattern = false; match file_path_or_error { Ok(file_path) => { - match as_abspath(&file_path) { - Ok(abs_path) => { - result.push(abs_path); - } - Err(msg) => { - messages.push(msg); - } - }; + result.push(file_path); } Err(err) => { messages.push(Message::error(err.to_string())); @@ -329,7 +304,7 @@ mod tests { } fn assert_files_eq(got: &[PathBuf], expected: &[PathBuf]) { - assert_eq!(got, abspaths(expected).as_slice()); + assert_eq!(abspaths(got), abspaths(expected)); } #[test] diff --git a/vhdl_lang/src/data/source.rs b/vhdl_lang/src/data/source.rs index 7ad3db15..febad1fd 100644 --- a/vhdl_lang/src/data/source.rs +++ b/vhdl_lang/src/data/source.rs @@ -17,7 +17,7 @@ pub use std::path::{Path, PathBuf}; use std::sync::Arc; struct FileId { - name: PathBuf, + name: FilePath, /// Hash value of `self.name`. hash: u64, } @@ -26,7 +26,7 @@ impl FileId { fn new(name: &Path) -> FileId { let hash = hash(name); Self { - name: name.to_owned(), + name: FilePath::new(name), hash, } } @@ -93,6 +93,10 @@ impl UniqueSource { fn file_name(&self) -> &Path { self.file_id.name.as_ref() } + + fn file_path(&self) -> &FilePath { + &self.file_id.name + } } /// A thread-safe reference to a source file. @@ -160,6 +164,10 @@ impl Source { self.source.file_name() } + pub(crate) fn file_path(&self) -> &FilePath { + self.source.file_path() + } + pub fn pos(&self, start: Position, end: Position) -> SrcPos { SrcPos { source: self.clone(), @@ -578,6 +586,37 @@ impl HasSource for T { } } +/// A wrapper arround a PathBuf that ensures the path is canoninicalized +#[derive(PartialEq, Eq, Hash, Clone)] +pub(crate) struct FilePath { + path: PathBuf, +} + +impl std::ops::Deref for FilePath { + type Target = Path; + fn deref(&self) -> &Self::Target { + &self.path + } +} + +impl FilePath { + pub fn new(path: &Path) -> Self { + let path = match dunce::canonicalize(path) { + Ok(path) => path, + Err(err) => { + eprintln!( + "Could not create absolute path {}: {:?}", + path.to_string_lossy(), + err + ); + path.to_owned() + } + }; + + Self { path } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/vhdl_lang/src/project.rs b/vhdl_lang/src/project.rs index cac17bd9..633507df 100644 --- a/vhdl_lang/src/project.rs +++ b/vhdl_lang/src/project.rs @@ -14,13 +14,13 @@ use crate::syntax::VHDLParser; use crate::{data::*, EntHierarchy, EntityId}; use fnv::{FnvHashMap, FnvHashSet}; use std::collections::hash_map::Entry; -use std::path::{Path, PathBuf}; +use std::path::Path; pub struct Project { parser: VHDLParser, config: Config, root: DesignRoot, - files: FnvHashMap, + files: FnvHashMap, empty_libraries: FnvHashSet, lint: Option, } @@ -90,8 +90,8 @@ impl Project { &mut self, config: &Config, messages: &mut dyn MessageHandler, - ) -> FnvHashMap> { - let mut files: FnvHashMap> = FnvHashMap::default(); + ) -> FnvHashMap> { + let mut files: FnvHashMap> = FnvHashMap::default(); self.empty_libraries.clear(); for library in config.iter_libraries() { @@ -103,7 +103,7 @@ impl Project { for file_name in library.file_names(messages) { empty_library = false; - match files.entry(file_name.clone()) { + match files.entry(FilePath::new(&file_name)) { Entry::Occupied(mut entry) => { entry.get_mut().insert(library_name.clone()); } @@ -124,7 +124,7 @@ impl Project { fn parse_and_add_files( &mut self, - files_to_parse: FnvHashMap>, + files_to_parse: FnvHashMap>, messages: &mut dyn MessageHandler, ) { use rayon::prelude::*; @@ -151,7 +151,7 @@ impl Project { }; self.files.insert( - source.file_name().to_owned(), + FilePath::new(source.file_name()), SourceFile { source, library_names, @@ -163,7 +163,7 @@ impl Project { } pub fn library_mapping_of(&self, source: &Source) -> Vec { - let file = if let Some(file) = self.files.get(source.file_name()) { + let file = if let Some(file) = self.files.get(source.file_path()) { file } else { return Vec::new(); @@ -174,12 +174,14 @@ impl Project { } pub fn get_source(&self, file_name: &Path) -> Option { - self.files.get(file_name).map(|file| file.source.clone()) + self.files + .get(&FilePath::new(file_name)) + .map(|file| file.source.clone()) } pub fn update_source(&mut self, source: &Source) { let mut source_file = { - if let Some(mut source_file) = self.files.remove(source.file_name()) { + if let Some(mut source_file) = self.files.remove(source.file_path()) { // File is already part of the project for library_name in source_file.library_names.iter() { self.root.remove_source(library_name.clone(), source); @@ -208,7 +210,7 @@ impl Project { .parser .parse_design_source(source, &mut source_file.parser_diagnostics); self.files - .insert(source.file_name().to_owned(), source_file); + .insert(source.file_path().to_owned(), source_file); } pub fn analyse(&mut self) -> Vec {