Skip to content

Commit 3d83e55

Browse files
authored
Refactor vhdl lang main (#306)
1 parent 5d2038d commit 3d83e55

File tree

8 files changed

+309
-151
lines changed

8 files changed

+309
-151
lines changed

Cargo.lock

Lines changed: 182 additions & 66 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vhdl_lang/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ tempfile = "3"
3535
pretty_assertions = "1"
3636
assert_matches = "1"
3737
brunch = "0"
38+
assert_cmd = "2.0.14"
39+
predicates = "3.1.0"
3840

3941
[[bench]]
4042
name = "benchmark"

vhdl_lang/src/config.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,15 @@ impl Config {
253253
}
254254

255255
/// Load configuration file from installation folder
256-
fn load_installed_config(&mut self, messages: &mut dyn MessageHandler) {
256+
fn load_installed_config(
257+
&mut self,
258+
messages: &mut dyn MessageHandler,
259+
location: Option<String>,
260+
) {
261+
if let Some(location) = location {
262+
self.load_config(&PathBuf::from(location), "Installation", messages);
263+
return;
264+
}
257265
let search_paths = [
258266
"../vhdl_libraries",
259267
"../../vhdl_libraries",
@@ -325,8 +333,17 @@ impl Config {
325333
}
326334

327335
/// Load all external configuration
328-
pub fn load_external_config(&mut self, messages: &mut dyn MessageHandler) {
329-
self.load_installed_config(messages);
336+
/// If the `standard_libraries_path` is given, it must point to a valid
337+
/// `vhdl_ls.toml` file, which will be used as source for the standard libraries
338+
/// i.e., `std` or `ieee`.
339+
/// If this path is `None`, a set of standard search paths will be queried for the location
340+
/// of this file.
341+
pub fn load_external_config(
342+
&mut self,
343+
messages: &mut dyn MessageHandler,
344+
standard_libraries_path: Option<String>,
345+
) {
346+
self.load_installed_config(messages, standard_libraries_path);
330347
self.load_home_config(messages);
331348
self.load_env_config("VHDL_LS_CONFIG", messages);
332349
}

vhdl_lang/src/main.rs

Lines changed: 23 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,26 @@
55
// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com
66

77
use clap::Parser;
8+
use itertools::Itertools;
89
use std::path::Path;
9-
use std::time::SystemTime;
10-
use vhdl_lang::{Config, Diagnostic, MessagePrinter, NullMessages, Project, Severity, SeverityMap};
10+
use vhdl_lang::{Config, Diagnostic, MessagePrinter, Project, Severity, SeverityMap};
1111

1212
/// Run vhdl analysis
1313
#[derive(Parser, Debug)]
1414
#[command(author, version, about, long_about = None)]
1515
struct Args {
16-
/// The number of threads to use. By default the maximum is selected based on process cores
16+
/// The number of threads to use. By default, the maximum is selected based on process cores
1717
#[arg(short = 'p', long)]
1818
num_threads: Option<usize>,
1919

20-
/// Prints the number of files processed and the execution time
21-
#[arg(long, default_value_t = false)]
22-
perf: bool,
23-
24-
/// Run repeatedly to get a reliable benchmark result
25-
#[arg(long, default_value_t = false)]
26-
bench: bool,
27-
28-
/// Hide hint diagnostics
29-
#[arg(long, default_value_t = false)]
30-
no_hint: bool,
20+
/// Path to the config file for the VHDL standard libraries (i.e., IEEE std_logic_1164).
21+
/// If omitted, will search for these libraries in a set of standard paths
22+
#[arg(short = 'l', long)]
23+
libraries: Option<String>,
3124

3225
/// Config file in TOML format containing libraries and settings
3326
#[arg(short, long)]
3427
config: String,
35-
36-
/// Dump items that are not resolved into an unique reference
37-
/// This is used for development to test where the language server is blind
38-
#[arg(long)]
39-
dump_unresolved: bool,
40-
41-
/// Count items that are not resolved into an unique reference
42-
/// This is used for development to test where the language server is blind
43-
#[arg(long)]
44-
count_unresolved: bool,
4528
}
4629

4730
fn main() {
@@ -53,77 +36,36 @@ fn main() {
5336

5437
let mut config = Config::default();
5538
let mut msg_printer = MessagePrinter::default();
56-
config.load_external_config(&mut msg_printer);
39+
config.load_external_config(&mut msg_printer, args.libraries.clone());
5740
config.append(
5841
&Config::read_file_path(Path::new(&args.config)).expect("Failed to read config file"),
5942
&mut msg_printer,
6043
);
6144

62-
let start = SystemTime::now();
63-
64-
let iterations = if args.bench {
65-
let iterations = 10;
66-
println!("Running {iterations} iterations for benchmarking");
67-
for _ in 0..(iterations - 1) {
68-
let mut project = Project::from_config(config.clone(), &mut NullMessages);
69-
project.analyse();
70-
}
71-
iterations
72-
} else {
73-
1
74-
};
75-
7645
let severity_map = *config.severities();
7746
let mut project = Project::from_config(config, &mut msg_printer);
78-
let mut diagnostics = project.analyse();
79-
let duration = start.elapsed().unwrap() / iterations;
80-
81-
if args.no_hint {
82-
diagnostics.retain(|diag| severity_map[diag.code] != Some(Severity::Hint));
83-
}
47+
project.enable_unused_declaration_detection();
48+
let diagnostics = project.analyse();
8449

8550
show_diagnostics(&diagnostics, &severity_map);
8651

87-
if args.perf || args.bench {
88-
let mut num_files = 0;
89-
let mut num_lines = 0;
90-
for source_file in project.files() {
91-
num_files += 1;
92-
num_lines += source_file.num_lines();
93-
}
94-
let duration_per_line = duration.checked_div(num_lines as u32).unwrap();
95-
96-
println!("Analyzed {num_files} files with {num_lines} lines of code");
97-
println!(
98-
"Total time to run was {} ms with an average of {} ns per line",
99-
duration.as_millis(),
100-
duration_per_line.as_nanos()
101-
);
102-
}
103-
104-
if args.dump_unresolved || args.count_unresolved {
105-
let (total, unresolved) = project.find_all_unresolved();
106-
107-
if args.dump_unresolved {
108-
for pos in unresolved.iter() {
109-
println!("{}", pos.show("Unresolved"));
110-
}
111-
}
112-
113-
if args.count_unresolved {
114-
println!("{} out of {} positions unresolved", unresolved.len(), total);
115-
}
52+
if diagnostics
53+
.iter()
54+
.any(|diag| severity_map[diag.code].is_some_and(|severity| severity == Severity::Error))
55+
{
56+
std::process::exit(1);
57+
} else {
58+
std::process::exit(0);
11659
}
117-
118-
// Exit without running Drop on entire allocated AST
119-
std::process::exit(0);
12060
}
12161

12262
fn show_diagnostics(diagnostics: &[Diagnostic], severity_map: &SeverityMap) {
123-
for diagnostic in diagnostics {
124-
if let Some(str) = diagnostic.show(severity_map) {
125-
println!("{str}");
126-
}
63+
let diagnostics = diagnostics
64+
.iter()
65+
.filter_map(|diag| diag.show(severity_map))
66+
.collect_vec();
67+
for str in &diagnostics {
68+
println!("{str}");
12769
}
12870

12971
if !diagnostics.is_empty() {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use assert_cmd::prelude::*;
2+
use itertools::Itertools;
3+
use predicates::prelude::*;
4+
use std::error::Error;
5+
use std::path::PathBuf;
6+
use std::process::Command;
7+
use vhdl_lang::{Config, MessagePrinter, Project, Severity};
8+
9+
#[test]
10+
pub fn parses_example_project_without_errors() {
11+
let mut config = Config::default();
12+
let mut msg_printer = MessagePrinter::default();
13+
14+
let mut vhdl_libraries_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
15+
// Load the VHDL standard libraries
16+
vhdl_libraries_path.push("../vhdl_libraries/vhdl_ls.toml");
17+
config.append(
18+
&Config::read_file_path(&vhdl_libraries_path).expect("Failed to read config file"),
19+
&mut msg_printer,
20+
);
21+
22+
// Load the configuration from the example project
23+
let mut config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
24+
config_path.push("../example_project/vhdl_ls.toml");
25+
config.append(
26+
&Config::read_file_path(&config_path).expect("Failed to read config file"),
27+
&mut msg_printer,
28+
);
29+
30+
let severity_map = *config.severities();
31+
let mut project = Project::from_config(config, &mut msg_printer);
32+
project.enable_unused_declaration_detection();
33+
34+
let diagnostics = project.analyse();
35+
let diagnostics_with_errors = diagnostics
36+
.iter()
37+
.filter(|diag| severity_map[diag.code] == Some(Severity::Error))
38+
.collect_vec();
39+
if !diagnostics_with_errors.is_empty() {
40+
for diagnostic in diagnostics_with_errors {
41+
println!("{}", diagnostic.show(&severity_map).unwrap())
42+
}
43+
panic!("Found diagnostics with severity error in the example project");
44+
}
45+
}
46+
47+
#[test]
48+
fn unused_function_gets_detected() -> Result<(), Box<dyn Error>> {
49+
let mut cmd = Command::cargo_bin("vhdl_lang")?;
50+
51+
cmd.arg("--config")
52+
.arg("tests/unused_declarations/vhdl_ls.toml")
53+
.arg("--libraries")
54+
.arg("../vhdl_libraries/vhdl_ls.toml");
55+
cmd.assert().failure().stdout(predicate::str::contains(
56+
"error: Unused declaration of port 'baz' : inout",
57+
));
58+
59+
Ok(())
60+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
library ieee;
2+
use ieee.std_logic_1164.all;
3+
4+
entity my_ent is
5+
port (
6+
foo : in std_logic;
7+
bar : out std_logic;
8+
baz : inout std_logic
9+
);
10+
end my_ent;
11+
12+
architecture arch of my_ent is
13+
begin
14+
bar <= foo;
15+
end architecture arch;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[libraries]
2+
3+
my_library.files = ["my_entity.vhd"]
4+
5+
[lint]
6+
unused = "error"

vhdl_ls/src/vhdl_server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl VHDLServer {
116116
let mut config = Config::default();
117117

118118
if self.use_external_config {
119-
config.load_external_config(&mut self.message_filter());
119+
config.load_external_config(&mut self.message_filter(), None);
120120
}
121121

122122
match self.load_root_uri_config() {

0 commit comments

Comments
 (0)