fixed error off-by-one once and for all, and more restructuring+a load more to go until Im happy with it (aka never)

This commit is contained in:
Noah Santschi-Cooney 2022-11-27 18:08:53 +00:00
parent 786e09bdcf
commit e7221304da
No known key found for this signature in database
GPG key ID: 3B22282472C8AE48
29 changed files with 1319 additions and 1128 deletions

View file

@ -8,12 +8,12 @@ edition = "2021"
doctest = false
[dependencies]
glutin = "0.28"
glutin = "0.29"
gl = "0.14"
url = "2.2"
filesystem = { path = "../filesystem" }
graph = { path = "../graph" }
tower-lsp = "0.17.0"
tower-lsp = "0.17"
regex = "1.4"
mockall = "0.11"
logging = { path = "../logging" }

View file

@ -1,63 +1,64 @@
use std::collections::HashMap;
use core::cell::OnceCell;
use filesystem::NormalizedPathBuf;
use logging::debug;
use regex::Regex;
use std::collections::HashMap;
use tower_lsp::lsp_types::*;
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
use url::Url;
use crate::ShaderValidator;
use sourcefile::{SourceMapper, SourceNum};
use crate::GPUVendor;
use sourcefile::{SourceMapper, SourceNum, Sourcefile, Version, ROOT_SOURCE_NUM};
pub struct DiagnosticsParser<'a, T: ShaderValidator + ?Sized> {
// line_offset: OnceCell<u32>,
line_regex: OnceCell<Regex>,
vendor_querier: &'a T,
pub struct DiagnosticsParser {
line_regex: Regex,
line_offset: u32,
}
impl<'a, T: ShaderValidator + ?Sized> DiagnosticsParser<'a, T> {
pub fn new(vendor_querier: &'a T) -> Self {
impl DiagnosticsParser {
pub fn new(gpu_vendor: GPUVendor, doc_glsl_version: Version) -> Self {
DiagnosticsParser {
// line_offset: OnceCell::new(),
line_regex: OnceCell::new(),
vendor_querier,
line_regex: DiagnosticsParser::get_line_regex(gpu_vendor),
line_offset: DiagnosticsParser::get_line_offset(gpu_vendor, doc_glsl_version),
}
}
fn get_line_regex(&self) -> &Regex {
self.line_regex.get_or_init(|| match self.vendor_querier.vendor().as_str() {
"NVIDIA Corporation" => {
Regex::new(r#"^(?P<filepath>\d+)\((?P<linenum>\d+)\) : (?P<severity>error|warning) [A-C]\d+: (?P<output>.+)"#).unwrap()
fn get_line_regex(gpu_vendor: GPUVendor) -> Regex {
match gpu_vendor {
GPUVendor::NVIDIA => {
Regex::new(r#"^(?P<filepath>\d+)\((?P<linenum>\d+)\) : (?P<severity>error|warning) [A-C]\d+: (?P<output>.+)"#)
}
_ => Regex::new(
r#"^(?P<severity>ERROR|WARNING): (?P<filepath>[^?<>*|"\n]+):(?P<linenum>\d+): (?:'.*' :|[a-z]+\(#\d+\)) +(?P<output>.+)$"#,
)
.unwrap(),
})
),
}
.unwrap()
}
// fn get_line_offset(&self) -> u32 {
// *self.line_offset.get_or_init(|| match self.vendor_querier.vendor().as_str() {
// "ATI Technologies" | "ATI Technologies Inc." | "AMD" => 0,
// _ => 1,
// })
// }
/// for certain NVIDIA GLSL versions, we need to offset the diagnostic number by -1 as those versions (incorrectly/inconsistently) state that:
/// "After processing this directive (including its new-line), the implementation will behave as if it is compiling at line number line+1".
/// So to get the correct behaviour (first line), with source strings being 0-based, we need to -1.
fn get_line_offset(gpu_vendor: GPUVendor, doc_glsl_version: Version) -> u32 {
match (gpu_vendor, doc_glsl_version) {
(GPUVendor::NVIDIA, Version::Glsl110)
| (GPUVendor::NVIDIA, Version::Glsl120)
| (GPUVendor::NVIDIA, Version::Glsl130)
| (GPUVendor::NVIDIA, Version::Glsl140)
| (GPUVendor::NVIDIA, Version::Glsl150) => 1,
_ => 0,
}
}
pub fn parse_diagnostics_output(
&self,
output: String,
uri: &NormalizedPathBuf,
source_mapper: &SourceMapper<NormalizedPathBuf>,
// graph: &CachedStableGraph<NormalizedPathBuf, IncludeLine>,
&self, output: String, uri: &NormalizedPathBuf, source_mapper: &SourceMapper<NormalizedPathBuf>,
sources: &HashMap<&NormalizedPathBuf, &Sourcefile>,
) -> HashMap<Url, Vec<Diagnostic>> {
let output_lines = output.split('\n').collect::<Vec<&str>>();
let mut diagnostics: HashMap<Url, Vec<Diagnostic>> = HashMap::with_capacity(output_lines.len());
debug!("diagnostics regex selected"; "regex" => self.get_line_regex() .as_str());
debug!("diagnostics regex selected"; "regex" => self.line_regex .as_str());
for line in output_lines {
let diagnostic_capture = match self.get_line_regex().captures(line) {
let diagnostic_capture = match self.line_regex.captures(line) {
Some(d) => d,
None => continue,
};
@ -66,14 +67,22 @@ impl<'a, T: ShaderValidator + ?Sized> DiagnosticsParser<'a, T> {
let msg = diagnostic_capture.name("output").unwrap().as_str();
let line = match diagnostic_capture.name("linenum") {
Some(c) => c.as_str().parse::<u32>().unwrap_or(0),
None => 0,
let source_num: SourceNum = match diagnostic_capture.name("filepath") {
Some(o) => o.as_str().parse::<usize>().unwrap().into(),
None => 0.into(),
};
// TODO: line matching maybe
/* let line_text = source_lines[line as usize];
let leading_whitespace = line_text.len() - line_text.trim_start().len(); */
let origin = match source_num {
ROOT_SOURCE_NUM => uri,
_ => source_mapper.get_node(source_num),
};
let line = match diagnostic_capture.name("linenum") {
Some(c) => {
c.as_str().parse::<u32>().unwrap_or(0) - self.line_offset
}
None => 0,
};
let severity = match diagnostic_capture.name("severity") {
Some(c) => match c.as_str().to_lowercase().as_str() {
@ -84,20 +93,14 @@ impl<'a, T: ShaderValidator + ?Sized> DiagnosticsParser<'a, T> {
_ => DiagnosticSeverity::INFORMATION,
};
let origin = match diagnostic_capture.name("filepath") {
Some(o) => {
let source_num: SourceNum = o.as_str().parse::<usize>().unwrap().into();
source_mapper.get_node(source_num)
}
None => uri,
};
let source = sources[origin];
let (start, end) = source.linemap().line_range_for_position(Position::new(line, 0));
let line_text = &source.source[start..end.unwrap_or(source.source.len() - 1)];
let diagnostic = Diagnostic {
range: Range::new(
/* Position::new(line, leading_whitespace as u64),
Position::new(line, line_text.len() as u64) */
Position::new(line-1, 0),
Position::new(line-1, 1000),
Position::new(line, (line_text.len() - line_text.trim_start().len()) as u32),
Position::new(line, line_text.len() as u32),
),
severity: Some(severity),
source: Some("mcglsl".to_string()),
@ -119,21 +122,20 @@ impl<'a, T: ShaderValidator + ?Sized> DiagnosticsParser<'a, T> {
#[cfg(test)]
mod diagnostics_test {
use std::collections::HashMap;
use filesystem::NormalizedPathBuf;
use sourcefile::SourceMapper;
use sourcefile::{SourceMapper, Sourcefile};
use trim_margin::MarginTrimmable;
use url::Url;
use crate::{diagnostics_parser::DiagnosticsParser, MockShaderValidator};
use crate::diagnostics_parser::DiagnosticsParser;
#[test]
#[logging_macro::scope]
fn test_nvidia_diagnostics() {
fn test_nvidia_diagnostics_glsl150() {
logging::scope(&logging::logger().new(slog_o!("driver" => "nvidia")), || {
let mut mockgl = MockShaderValidator::new();
mockgl.expect_vendor().returning(|| "NVIDIA Corporation".into());
let output = "0(9) : error C0000: syntax error, unexpected '}', expecting ',' or ';' at token \"}\"";
let output = "0(1) : error C0000: syntax error, unexpected '}', expecting ',' or ';' at token \"}\"";
#[cfg(target_family = "unix")]
let path: NormalizedPathBuf = "/home/noah/.minecraft/shaderpacks/test/shaders/final.fsh".into();
@ -143,9 +145,47 @@ mod diagnostics_test {
let mut source_mapper = SourceMapper::new(0);
source_mapper.get_num(&path);
let parser = DiagnosticsParser::new(&mockgl);
let parser = DiagnosticsParser::new(crate::GPUVendor::NVIDIA, sourcefile::Version::Glsl150);
let results = parser.parse_diagnostics_output(output.to_string(), &path.parent().unwrap(), &source_mapper);
let source = Sourcefile::new(
"sample text".to_string(),
path.clone(),
path.parent().and_then(|p| p.parent()).unwrap(),
);
let sources = HashMap::from_iter(vec![(&path, &source)]);
let results = parser.parse_diagnostics_output(output.to_string(), &path, &source_mapper, &sources);
assert_eq!(results.len(), 1);
let first = results.into_iter().next().unwrap();
assert_eq!(first.0, Url::from_file_path(path).unwrap());
});
}
#[test]
#[logging_macro::scope]
fn test_nvidia_diagnostics_glsl330() {
logging::scope(&logging::logger().new(slog_o!("driver" => "nvidia")), || {
let output = "0(0) : error C0000: syntax error, unexpected '}', expecting ',' or ';' at token \"}\"";
#[cfg(target_family = "unix")]
let path: NormalizedPathBuf = "/home/noah/.minecraft/shaderpacks/test/shaders/final.fsh".into();
#[cfg(target_family = "windows")]
let path: NormalizedPathBuf = "c:\\home\\noah\\.minecraft\\shaderpacks\\test\\shaders\\final.fsh".into();
let mut source_mapper = SourceMapper::new(0);
source_mapper.get_num(&path);
let parser = DiagnosticsParser::new(crate::GPUVendor::NVIDIA, sourcefile::Version::Glsl330);
let source = Sourcefile::new(
"sample text".to_string(),
path.clone(),
path.parent().and_then(|p| p.parent()).unwrap(),
);
let sources = HashMap::from_iter(vec![(&path, &source)]);
let results = parser.parse_diagnostics_output(output.to_string(), &path, &source_mapper, &sources);
assert_eq!(results.len(), 1);
let first = results.into_iter().next().unwrap();
@ -157,14 +197,13 @@ mod diagnostics_test {
#[logging_macro::scope]
fn test_amd_diagnostics() {
logging::scope(&logging::logger().new(slog_o!("driver" => "amd")), || {
let mut mockgl = MockShaderValidator::new();
mockgl.expect_vendor().returning(|| "ATI Technologies".into());
let output = r#"
|ERROR: 0:0: '' : syntax error: #line
|ERROR: 0:1: '' : syntax error: #line
|ERROR: 0:10: '' : syntax error: #line
|ERROR: 0:15: 'varying' : syntax error: syntax error
"#.trim_margin().unwrap();
|ERROR: 0:2: 'varying' : syntax error: syntax error
"#
.trim_margin()
.unwrap();
#[cfg(target_family = "unix")]
let path: NormalizedPathBuf = "/home/noah/.minecraft/shaderpacks/test/shaders/final.fsh".into();
@ -174,9 +213,21 @@ mod diagnostics_test {
let mut source_mapper = SourceMapper::new(0);
source_mapper.get_num(&path);
let parser = DiagnosticsParser::new(&mockgl);
let parser = DiagnosticsParser::new(crate::GPUVendor::AMD, sourcefile::Version::Glsl150);
let results = parser.parse_diagnostics_output(output, &path.parent().unwrap(), &source_mapper);
let source = Sourcefile::new(
"|int main() {
| hello_world();
|}"
.to_string()
.trim_margin()
.unwrap(),
path.clone(),
path.parent().and_then(|p| p.parent()).unwrap(),
);
let sources = HashMap::from_iter(vec![(&path, &source)]);
let results = parser.parse_diagnostics_output(output, &path, &source_mapper, &sources);
assert_eq!(results.len(), 1);
let first = results.into_iter().next().unwrap();

View file

@ -1,7 +1,8 @@
use std::ffi::{CStr, CString};
use std::ptr;
use glutin::platform::unix::EventLoopExtUnix;
use glutin::event_loop::EventLoopBuilder;
use glutin::platform::unix::EventLoopBuilderExtUnix;
use logging::info;
use crate::ShaderValidator;
@ -12,7 +13,8 @@ pub(crate) struct Context {
impl Context {
pub fn default() -> Context {
let events_loop = glutin::event_loop::EventLoop::<()>::new_any_thread();
let events_loop = EventLoopBuilder::new().with_any_thread(true).build();
// let events_loop = glutin::event_loop::EventLoop::<()>::new_any_thread();
let gl_window = glutin::ContextBuilder::new()
.build_headless(&*events_loop, glutin::dpi::PhysicalSize::new(1, 1))
.unwrap();