1
1
use alloc:: borrow:: Cow ;
2
2
3
- use hashql_core:: span:: SpanId ;
3
+ use hashql_core:: {
4
+ pretty:: { PrettyOptions , PrettyPrint as _} ,
5
+ span:: SpanId ,
6
+ r#type:: environment:: Environment ,
7
+ } ;
4
8
use hashql_diagnostics:: {
5
9
Diagnostic ,
6
10
category:: { DiagnosticCategory , TerminalDiagnosticCategory } ,
@@ -10,13 +14,9 @@ use hashql_diagnostics::{
10
14
severity:: Severity ,
11
15
} ;
12
16
13
- pub type SpecializationDiagnostic = Diagnostic < SpecializationDiagnosticCategory , SpanId > ;
17
+ use crate :: node :: Node ;
14
18
15
- #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
16
- pub enum SpecializationDiagnosticCategory {
17
- UnsupportedIntrinsic ,
18
- UnknownIntrinsic ,
19
- }
19
+ pub type SpecializationDiagnostic = Diagnostic < SpecializationDiagnosticCategory , SpanId > ;
20
20
21
21
const UNSUPPORTED_INTRINSIC : TerminalDiagnosticCategory = TerminalDiagnosticCategory {
22
22
id : "unsupported-intrinsic" ,
@@ -28,6 +28,30 @@ const UNKNOWN_INTRINSIC: TerminalDiagnosticCategory = TerminalDiagnosticCategory
28
28
name : "Unknown intrinsic operation" ,
29
29
} ;
30
30
31
+ const INVALID_GRAPH_CHAIN : TerminalDiagnosticCategory = TerminalDiagnosticCategory {
32
+ id : "invalid-graph-chain" ,
33
+ name : "Invalid graph operation chain" ,
34
+ } ;
35
+
36
+ const NON_INTRINSIC_GRAPH_OPERATION : TerminalDiagnosticCategory = TerminalDiagnosticCategory {
37
+ id : "non-intrinsic-graph-operation" ,
38
+ name : "Non-intrinsic function in graph operation" ,
39
+ } ;
40
+
41
+ const NON_GRAPH_INTRINSIC : TerminalDiagnosticCategory = TerminalDiagnosticCategory {
42
+ id : "non-graph-intrinsic" ,
43
+ name : "Non-graph intrinsic in graph operation" ,
44
+ } ;
45
+
46
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , Hash ) ]
47
+ pub enum SpecializationDiagnosticCategory {
48
+ UnsupportedIntrinsic ,
49
+ UnknownIntrinsic ,
50
+ InvalidGraphChain ,
51
+ NonIntrinsicGraphOperation ,
52
+ NonGraphIntrinsic ,
53
+ }
54
+
31
55
impl DiagnosticCategory for SpecializationDiagnosticCategory {
32
56
fn id ( & self ) -> Cow < ' _ , str > {
33
57
Cow :: Borrowed ( "specialization" )
@@ -41,6 +65,9 @@ impl DiagnosticCategory for SpecializationDiagnosticCategory {
41
65
match * self {
42
66
Self :: UnsupportedIntrinsic => Some ( & UNSUPPORTED_INTRINSIC ) ,
43
67
Self :: UnknownIntrinsic => Some ( & UNKNOWN_INTRINSIC ) ,
68
+ Self :: InvalidGraphChain => Some ( & INVALID_GRAPH_CHAIN ) ,
69
+ Self :: NonIntrinsicGraphOperation => Some ( & NON_INTRINSIC_GRAPH_OPERATION ) ,
70
+ Self :: NonGraphIntrinsic => Some ( & NON_GRAPH_INTRINSIC ) ,
44
71
}
45
72
}
46
73
}
@@ -113,3 +140,105 @@ pub(crate) fn unknown_intrinsic(span: SpanId, intrinsic_name: &str) -> Specializ
113
140
114
141
diagnostic
115
142
}
143
+
144
+ /// Creates a diagnostic for an invalid graph operation chain.
145
+ ///
146
+ /// This occurs when following a graph chain but encountering a specialized operation
147
+ /// that is not a graph operation (e.g., a math operation that was already processed).
148
+ pub ( crate ) fn invalid_graph_chain < ' heap > (
149
+ env : & Environment < ' heap > ,
150
+ span : SpanId ,
151
+ node : Node < ' heap > ,
152
+ ) -> SpecializationDiagnostic {
153
+ let mut diagnostic = Diagnostic :: new (
154
+ SpecializationDiagnosticCategory :: InvalidGraphChain ,
155
+ Severity :: Error ,
156
+ ) ;
157
+
158
+ diagnostic
159
+ . labels
160
+ . push ( Label :: new ( span, "Expected a graph operation here" ) . with_order ( 0 ) ) ;
161
+
162
+ diagnostic. add_help ( Help :: new ( format ! (
163
+ "{} is not a graph operation. Graph chains can only contain operations that work with \
164
+ graph data, such as filtering, entity selection, or other graph transformations. \
165
+ Operations like math, comparisons, or other non-graph functions cannot be part of a \
166
+ graph chain.",
167
+ node. pretty_print( env, PrettyOptions :: default ( ) . with_max_width( 60 ) )
168
+ ) ) ) ;
169
+
170
+ diagnostic. add_note ( Note :: new (
171
+ "Graph operation chains work by passing graph objects through a sequence of \
172
+ graph-specific operations. Each operation in the chain must accept a graph and return a \
173
+ modified graph. Non-graph operations like math, comparisons, or boolean logic should be \
174
+ used within closures or separate expressions, not as part of the main graph chain.",
175
+ ) ) ;
176
+
177
+ diagnostic
178
+ }
179
+
180
+ /// Creates a diagnostic for using a non-intrinsic function in graph operations.
181
+ ///
182
+ /// This occurs when a graph operation chain contains a function call that is not
183
+ /// mapped to an intrinsic operation.
184
+ pub ( crate ) fn non_intrinsic_graph_operation < ' heap > (
185
+ env : & Environment < ' heap > ,
186
+ span : SpanId ,
187
+ function : Node < ' heap > ,
188
+ ) -> SpecializationDiagnostic {
189
+ let mut diagnostic = Diagnostic :: new (
190
+ SpecializationDiagnosticCategory :: NonIntrinsicGraphOperation ,
191
+ Severity :: Error ,
192
+ ) ;
193
+
194
+ let label_text = "This is not a graph intrinsic operation" ;
195
+
196
+ diagnostic
197
+ . labels
198
+ . push ( Label :: new ( span, label_text) . with_order ( 0 ) ) ;
199
+
200
+ diagnostic. add_help ( Help :: new ( format ! (
201
+ "{} is not a valid graph operation. Graph operation chains can only contain intrinsic \
202
+ functions that are part of the HashQL graph API. Higher-order functions (HOFs) and \
203
+ user-defined functions are not supported yet. To track support for user-defined \
204
+ functions see https://linear.app/hash/issue/H-4776/hashql-allow-user-defined-functions-in-graph-pipelines",
205
+ function. pretty_print( env, PrettyOptions :: default ( ) . with_max_width( 60 ) ) ) ) ) ;
206
+
207
+ diagnostic. add_note ( Note :: new (
208
+ "Graph intrinsics are built-in operations like `::core::graph::head::entities`, \
209
+ `::core::graph::body::filter`, and `::core::graph::tail::collect` that can be optimized \
210
+ during compilation. Only these predefined operations can be used to build graph query \
211
+ chains.",
212
+ ) ) ;
213
+
214
+ diagnostic
215
+ }
216
+
217
+ /// Creates a diagnostic for an unknown graph intrinsic operation.
218
+ ///
219
+ /// This indicates a compiler bug where a graph intrinsic that should be mapped is missing.
220
+ #[ coverage( off) ] // compiler bugs should never be hit
221
+ pub ( crate ) fn non_graph_intrinsic ( span : SpanId , intrinsic_name : & str ) -> SpecializationDiagnostic {
222
+ let mut diagnostic = Diagnostic :: new (
223
+ SpecializationDiagnosticCategory :: NonGraphIntrinsic ,
224
+ Severity :: Bug ,
225
+ ) ;
226
+
227
+ diagnostic. labels . push (
228
+ Label :: new ( span, format ! ( "unknown graph intrinsic `{intrinsic_name}`" ) ) . with_order ( 0 ) ,
229
+ ) ;
230
+
231
+ diagnostic. add_help ( Help :: new ( format ! (
232
+ "The graph intrinsic `{intrinsic_name}` is missing from the graph specialization phase \
233
+ mapping. Add this intrinsic to the match statement in the `fold_graph_read` method to \
234
+ resolve this compiler bug."
235
+ ) ) ) ;
236
+
237
+ diagnostic. add_note ( Note :: new (
238
+ "This error indicates that a new graph intrinsic was added to the standard library but \
239
+ the graph specialization phase wasn't updated to handle it. The compiler should be kept \
240
+ in sync with stdlib changes.",
241
+ ) ) ;
242
+
243
+ diagnostic
244
+ }
0 commit comments