Skip to content

Commit 48e6b43

Browse files
authored
Merge pull request #44 from rhaiscript:hir/more-errors
feat: more HIR errors
2 parents 2987cef + c13ab1b commit 48e6b43

File tree

11 files changed

+299
-484
lines changed

11 files changed

+299
-484
lines changed

crates/hir/src/error.rs

+8-13
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,36 @@
11
use crate::Symbol;
2-
use rhai_rowan::TextRange;
32
use thiserror::Error;
43

54
#[derive(Debug, Clone, Error)]
65
#[error("{kind}")]
76
pub struct Error {
8-
pub text_range: Option<TextRange>,
97
pub kind: ErrorKind,
108
}
119

1210
#[derive(Debug, Clone, Error)]
1311
pub enum ErrorKind {
14-
#[error("duplicate parameter `{name}` for function `{fn_name}`")]
12+
#[error("duplicate function parameter")]
1513
DuplicateFnParameter {
16-
name: String,
17-
fn_name: String,
1814
duplicate_symbol: Symbol,
19-
duplicate_range: Option<TextRange>,
2015
existing_symbol: Symbol,
21-
existing_range: Option<TextRange>,
2216
},
23-
2417
#[error(
25-
"cannot resolve reference `{reference_name}`{}",
18+
"cannot resolve reference{}",
2619
match &similar_name {
2720
Some(n) => {
28-
format!(", maybe you meant `{}`?", n)
21+
format!(", did you mean `{}`?", n)
2922
}
3023
None => {
3124
String::from("")
3225
}
3326
}
3427
)]
3528
UnresolvedReference {
36-
reference_name: String,
37-
similar_name: Option<String>,
3829
reference_symbol: Symbol,
39-
reference_range: Option<TextRange>,
30+
similar_name: Option<String>,
4031
},
32+
#[error("unresolved import")]
33+
UnresolvedImport { import: Symbol },
34+
#[error("nested functions are not allowed")]
35+
NestedFunction { function: Symbol },
4136
}

crates/hir/src/hir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod add;
22
mod query;
33
mod remove;
44
mod resolve;
5+
mod errors;
56

67
use core::ops;
78

crates/hir/src/hir/add/def.rs

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ impl Hir {
9797
selection_text_range: None,
9898
},
9999
kind: SymbolKind::Import(ImportSymbol {
100+
target: None,
100101
scope: import_scope,
101102
alias: import_def.alias().map(|alias| {
102103
let alias_symbol = self.add_symbol(SymbolData {

crates/hir/src/hir/add/script.rs

+1
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,7 @@ impl Hir {
942942
selection_text_range: None,
943943
},
944944
kind: SymbolKind::Import(ImportSymbol {
945+
target: None,
945946
scope: import_scope,
946947
alias: expr.alias().map(|alias| {
947948
let alias_symbol = self.add_symbol(SymbolData {

crates/hir/src/hir/errors.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use crate::{
2+
error::{Error, ErrorKind},
3+
source::Source,
4+
symbol::SymbolKind,
5+
HashMap, Hir, Symbol,
6+
};
7+
8+
impl Hir {
9+
#[must_use]
10+
pub fn errors(&self) -> Vec<Error> {
11+
let mut errors = Vec::new();
12+
13+
for (symbol, _) in self.symbols() {
14+
self.collect_errors_from_symbol(symbol, &mut errors);
15+
}
16+
17+
errors
18+
}
19+
20+
#[must_use]
21+
pub fn errors_for_source(&self, source: Source) -> Vec<Error> {
22+
let mut errors = Vec::new();
23+
24+
for (symbol, _) in self
25+
.symbols()
26+
.filter(|(_, symbol_data)| symbol_data.source.source == Some(source))
27+
{
28+
self.collect_errors_from_symbol(symbol, &mut errors);
29+
}
30+
31+
errors
32+
}
33+
34+
fn collect_errors_from_symbol(&self, symbol: Symbol, errors: &mut Vec<Error>) {
35+
if let Some(symbol_data) = self.symbol(symbol) {
36+
match &symbol_data.kind {
37+
SymbolKind::Reference(r) => {
38+
if !r.field_access && r.target.is_none() && r.name != "this" {
39+
errors.push(Error {
40+
kind: ErrorKind::UnresolvedReference {
41+
reference_symbol: symbol,
42+
similar_name: self.find_similar_name(symbol, &r.name),
43+
},
44+
});
45+
}
46+
}
47+
SymbolKind::Fn(f) => {
48+
let top_level = self
49+
.modules
50+
.iter()
51+
.any(|(_, m)| m.scope == symbol_data.parent_scope);
52+
53+
if !top_level {
54+
errors.push(Error {
55+
kind: ErrorKind::NestedFunction { function: symbol },
56+
});
57+
}
58+
59+
let mut param_names: HashMap<&str, Symbol> = HashMap::new();
60+
61+
for &param in &self[f.scope].symbols {
62+
let param_data = &self[param];
63+
64+
if !param_data.is_param() {
65+
break;
66+
}
67+
68+
if let Some(name) = param_data.name() {
69+
if let Some(existing_param) = param_names.insert(name, param) {
70+
errors.push(Error {
71+
kind: ErrorKind::DuplicateFnParameter {
72+
duplicate_symbol: param,
73+
existing_symbol: existing_param,
74+
},
75+
});
76+
}
77+
}
78+
}
79+
}
80+
SymbolKind::Import(import) => {
81+
if import.target.is_none() {
82+
errors.push(Error {
83+
kind: ErrorKind::UnresolvedImport { import: symbol },
84+
});
85+
}
86+
}
87+
_ => {}
88+
}
89+
}
90+
}
91+
}

crates/hir/src/hir/query.rs

+2-50
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
use core::iter;
22
use std::cmp::Ordering;
3-
43
use itertools::Either;
54
use rhai_rowan::{TextRange, TextSize};
6-
7-
use crate::{
8-
error::{Error, ErrorKind},
9-
scope::ScopeParent,
10-
};
5+
use crate::scope::ScopeParent;
116

127
use super::*;
138

@@ -270,31 +265,6 @@ impl Hir {
270265
None
271266
}
272267

273-
#[must_use]
274-
pub fn errors(&self) -> Vec<Error> {
275-
let mut errors = Vec::new();
276-
277-
for (symbol, _) in self.symbols() {
278-
self.collect_errors_from_symbol(symbol, &mut errors);
279-
}
280-
281-
errors
282-
}
283-
284-
#[must_use]
285-
pub fn errors_for_source(&self, source: Source) -> Vec<Error> {
286-
let mut errors = Vec::new();
287-
288-
for (symbol, _) in self
289-
.symbols()
290-
.filter(|(_, symbol_data)| symbol_data.source.source == Some(source))
291-
{
292-
self.collect_errors_from_symbol(symbol, &mut errors);
293-
}
294-
295-
errors
296-
}
297-
298268
/// All the missing modules that appear in imports.
299269
#[must_use]
300270
pub fn missing_modules(&self) -> impl ExactSizeIterator<Item = Url> {
@@ -351,25 +321,7 @@ impl Hir {
351321
None
352322
}
353323

354-
fn collect_errors_from_symbol(&self, symbol: Symbol, errors: &mut Vec<Error>) {
355-
if let Some(symbol_data) = self.symbol(symbol) {
356-
if let SymbolKind::Reference(r) = &symbol_data.kind {
357-
if !r.field_access && r.target.is_none() && r.name != "this" {
358-
errors.push(Error {
359-
text_range: symbol_data.selection_or_text_range(),
360-
kind: ErrorKind::UnresolvedReference {
361-
reference_name: r.name.clone(),
362-
reference_range: symbol_data.selection_or_text_range(),
363-
reference_symbol: symbol,
364-
similar_name: self.find_similar_name(symbol, &r.name),
365-
},
366-
});
367-
}
368-
}
369-
}
370-
}
371-
372-
fn find_similar_name(&self, symbol: Symbol, name: &str) -> Option<String> {
324+
pub(super) fn find_similar_name(&self, symbol: Symbol, name: &str) -> Option<String> {
373325
const MIN_DISTANCE: f64 = 0.5;
374326

375327
self.visible_symbols_from_symbol(symbol)

crates/hir/src/hir/resolve.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ impl Hir {
111111
}
112112
};
113113

114-
if let Some(import_symbol) = self[import_symbol].kind.as_import() {
115-
if let Some(import_path) = import_symbol.import_path(self) {
114+
if let Some(import_symbol_data) = self[import_symbol].kind.as_import() {
115+
if let Some(import_path) = import_symbol_data.import_path(self) {
116116
let import_url = match self.resolve_import_url(self[module].url(), import_path)
117117
{
118118
Some(u) => u,
@@ -124,11 +124,17 @@ impl Hir {
124124
None => continue,
125125
};
126126

127-
if let Some(alias) = import_symbol.alias {
127+
if let Some(alias) = import_symbol_data.alias {
128128
if let Some(alias_decl) = self.symbol_mut(alias).kind.as_decl_mut() {
129129
alias_decl.target = Some(ReferenceTarget::Module(target_module));
130130
}
131131
}
132+
133+
self.symbol_mut(import_symbol)
134+
.kind
135+
.as_import_mut()
136+
.unwrap()
137+
.target = Some(target_module);
132138
}
133139
}
134140
}

crates/hir/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub(crate) mod util;
2424
pub(crate) type IndexMap<K, V> = indexmap::IndexMap<K, V, ahash::RandomState>;
2525
pub(crate) type IndexSet<V> = indexmap::IndexSet<V, ahash::RandomState>;
2626
pub(crate) type HashSet<V> = ahash::AHashSet<V>;
27+
pub(crate) type HashMap<K, V> = ahash::AHashMap<K, V>;
2728

2829
pub use hir::Hir;
2930
pub use module::Module;

crates/hir/src/symbol.rs

+20
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,22 @@ impl SymbolData {
6868
}
6969
}
7070

71+
#[inline]
72+
#[must_use]
73+
pub fn is_param(&self) -> bool {
74+
match &self.kind {
75+
SymbolKind::Decl(d) => d.is_param,
76+
_ => false,
77+
}
78+
}
79+
7180
#[inline]
7281
#[must_use]
7382
pub fn target(&self) -> Option<ReferenceTarget> {
7483
match &self.kind {
7584
SymbolKind::Reference(r) => r.target,
7685
SymbolKind::Decl(d) => d.target,
86+
SymbolKind::Import(i) => i.target.map(ReferenceTarget::Module),
7787
_ => None,
7888
}
7989
}
@@ -562,6 +572,15 @@ impl SymbolKind {
562572
}
563573
}
564574

575+
#[must_use]
576+
pub fn as_import_mut(&mut self) -> Option<&mut ImportSymbol> {
577+
if let Self::Import(v) = self {
578+
Some(v)
579+
} else {
580+
None
581+
}
582+
}
583+
565584
/// Returns `true` if the symbol kind is [`Discard`].
566585
///
567586
/// [`Discard`]: SymbolKind::Discard
@@ -763,6 +782,7 @@ pub struct ImportSymbol {
763782
pub scope: Scope,
764783
pub expr: Option<Symbol>,
765784
pub alias: Option<Symbol>,
785+
pub target: Option<Module>,
766786
}
767787

768788
impl ImportSymbol {

0 commit comments

Comments
 (0)