Skip to main content

bhc_parser/
lib.rs

1//! Parser for Haskell 2026 source code.
2//!
3//! This crate provides a recursive descent parser that produces an AST
4//! from a token stream.
5
6#![warn(missing_docs)]
7
8use bhc_ast::{Expr, Module};
9use bhc_diagnostics::{Diagnostic, DiagnosticHandler, FullSpan};
10use bhc_lexer::{Lexer, Token, TokenKind};
11use bhc_span::{FileId, Span, Spanned};
12use thiserror::Error;
13
14mod decl;
15mod expr;
16mod pattern;
17mod types;
18
19/// Parser error type.
20#[derive(Debug, Error)]
21pub enum ParseError {
22    /// Unexpected token.
23    #[error("unexpected {found}, expected {expected}")]
24    Unexpected {
25        /// What was found.
26        found: String,
27        /// What was expected.
28        expected: String,
29        /// Location.
30        span: Span,
31    },
32
33    /// Unexpected end of file.
34    #[error("unexpected end of file")]
35    UnexpectedEof {
36        /// What was expected.
37        expected: String,
38    },
39
40    /// Invalid literal.
41    #[error("invalid literal: {message}")]
42    InvalidLiteral {
43        /// Error message.
44        message: String,
45        /// Location.
46        span: Span,
47    },
48
49    /// Nesting too deep for the parser.
50    #[error("expression nesting exceeds the maximum depth of {limit}")]
51    RecursionLimit {
52        /// The configured limit.
53        limit: usize,
54        /// Location.
55        span: Span,
56    },
57}
58
59impl ParseError {
60    /// Convert to a diagnostic.
61    #[must_use]
62    pub fn to_diagnostic(&self, file: FileId) -> Diagnostic {
63        match self {
64            Self::Unexpected {
65                found,
66                expected,
67                span,
68            } => Diagnostic::error(format!("unexpected {found}, expected {expected}"))
69                .with_label(FullSpan::new(file, *span), "unexpected token here"),
70            Self::UnexpectedEof { expected } => {
71                Diagnostic::error(format!("unexpected end of file, expected {expected}"))
72            }
73            Self::InvalidLiteral { message, span } => {
74                Diagnostic::error(format!("invalid literal: {message}"))
75                    .with_label(FullSpan::new(file, *span), "invalid literal")
76            }
77            Self::RecursionLimit { limit, span } => Diagnostic::error(format!(
78                "expression nesting exceeds the maximum depth of {limit}"
79            ))
80            .with_label(FullSpan::new(file, *span), "nesting too deep here"),
81        }
82    }
83}
84
85/// The result of parsing.
86pub type ParseResult<T> = Result<T, ParseError>;
87
88/// A parser for Haskell 2026 source code.
89pub struct Parser<'src> {
90    /// The token stream.
91    tokens: Vec<Spanned<Token>>,
92    /// Current position in the token stream.
93    pos: usize,
94    /// Diagnostic handler.
95    diagnostics: DiagnosticHandler,
96    /// Source file ID.
97    file_id: FileId,
98    /// The source code (for error messages).
99    #[allow(dead_code)]
100    src: &'src str,
101    /// Current recursion depth (expressions, types, patterns).
102    depth: usize,
103}
104
105/// Maximum nesting depth for expressions, types, and patterns.
106///
107/// Recursive-descent parsing uses the host stack; without a cap, deeply
108/// nested input (e.g. thousands of opening parentheses) overflows the
109/// stack and aborts the process instead of reporting a diagnostic.
110const MAX_PARSE_DEPTH: usize = 64;
111
112impl<'src> Parser<'src> {
113    /// Create a new parser for the given source code.
114    #[must_use]
115    pub fn new(src: &'src str, file_id: FileId) -> Self {
116        let tokens: Vec<_> = Lexer::new(src).collect();
117        Self {
118            tokens,
119            pos: 0,
120            diagnostics: DiagnosticHandler::new(),
121            file_id,
122            src,
123            depth: 0,
124        }
125    }
126
127    /// Enter one level of parse recursion, erroring at the depth limit.
128    pub(crate) fn enter_recursion(&mut self) -> ParseResult<()> {
129        self.depth += 1;
130        if self.depth > MAX_PARSE_DEPTH {
131            return Err(ParseError::RecursionLimit {
132                limit: MAX_PARSE_DEPTH,
133                span: self.current_span(),
134            });
135        }
136        Ok(())
137    }
138
139    /// Leave one level of parse recursion.
140    pub(crate) fn exit_recursion(&mut self) {
141        self.depth = self.depth.saturating_sub(1);
142    }
143
144    /// Get the current token.
145    fn current(&self) -> Option<&Spanned<Token>> {
146        self.tokens.get(self.pos)
147    }
148
149    /// Peek at the nth token from current position (0 = current).
150    fn peek_nth(&self, n: usize) -> Option<&Spanned<Token>> {
151        self.tokens.get(self.pos + n)
152    }
153
154    /// Get the current token kind.
155    fn current_kind(&self) -> Option<&TokenKind> {
156        self.current().map(|t| &t.node.kind)
157    }
158
159    /// Get the current span.
160    fn current_span(&self) -> Span {
161        self.current().map(|t| t.span).unwrap_or(Span::DUMMY)
162    }
163
164    /// Check if we're at the end of input.
165    fn at_eof(&self) -> bool {
166        self.pos >= self.tokens.len() || self.current_kind() == Some(&TokenKind::Eof)
167    }
168
169    /// Advance to the next token.
170    fn advance(&mut self) -> Option<Spanned<Token>> {
171        if self.at_eof() {
172            None
173        } else {
174            let tok = self.tokens[self.pos].clone();
175            self.pos += 1;
176            Some(tok)
177        }
178    }
179
180    /// Check if the current token matches the given kind.
181    fn check(&self, kind: &TokenKind) -> bool {
182        self.current_kind() == Some(kind)
183    }
184
185    /// Check if the current token is a constructor identifier.
186    #[allow(dead_code)]
187    fn check_con_id(&self) -> bool {
188        matches!(self.current_kind(), Some(TokenKind::ConId(_)))
189    }
190
191    /// Check if the current token is an identifier.
192    #[allow(dead_code)]
193    fn check_ident(&self) -> bool {
194        matches!(self.current_kind(), Some(TokenKind::Ident(_)))
195    }
196
197    /// Consume a token if it matches the given kind.
198    fn eat(&mut self, kind: &TokenKind) -> bool {
199        if self.check(kind) {
200            self.advance();
201            true
202        } else {
203            false
204        }
205    }
206
207    /// Consume an identifier token if it has the given string value.
208    /// Used for context-sensitive keywords like 'as', 'qualified', 'hiding'.
209    fn eat_ident_str(&mut self, s: &str) -> bool {
210        if let Some(TokenKind::Ident(sym)) = self.current_kind() {
211            if sym.as_str() == s {
212                self.advance();
213                return true;
214            }
215        }
216        false
217    }
218
219    /// Check if the current token is an identifier with the given string value.
220    /// Used for context-sensitive keywords like 'family', 'stock', 'pattern', etc.
221    fn check_ident_str(&self, s: &str) -> bool {
222        if let Some(TokenKind::Ident(sym)) = self.current_kind() {
223            sym.as_str() == s
224        } else {
225            false
226        }
227    }
228
229    /// Expect and consume an identifier with the given string value.
230    /// Used for context-sensitive keywords that must appear at a specific position.
231    fn expect_ident_str(&mut self, s: &str) -> ParseResult<Spanned<Token>> {
232        if self.check_ident_str(s) {
233            Ok(self.advance().unwrap())
234        } else if self.at_eof() {
235            Err(ParseError::UnexpectedEof {
236                expected: format!("`{}`", s),
237            })
238        } else {
239            let current = self.current().unwrap();
240            Err(ParseError::Unexpected {
241                found: current.node.kind.description().to_string(),
242                expected: format!("`{}`", s),
243                span: current.span,
244            })
245        }
246    }
247
248    /// Skip any virtual tokens (VirtualLBrace, VirtualRBrace, VirtualSemi).
249    /// These are inserted by the layout rule and need to be skipped in some contexts.
250    fn skip_virtual_tokens(&mut self) {
251        while let Some(kind) = self.current_kind() {
252            if kind.is_virtual() {
253                self.advance();
254            } else {
255                break;
256            }
257        }
258    }
259
260    /// Skip doc comments (Haddock comments like `-- |` or `{- | ... -}`).
261    /// These can appear before module declarations in real-world Haskell code.
262    fn skip_doc_comments(&mut self) {
263        while let Some(kind) = self.current_kind() {
264            match kind {
265                TokenKind::DocCommentLine(_) | TokenKind::DocCommentBlock(_) => {
266                    self.advance();
267                }
268                _ => break,
269            }
270        }
271    }
272
273    /// Collect doc comments, returning them as a `DocComment` if present.
274    ///
275    /// This collects all consecutive doc comments and merges them into a single
276    /// documentation string. Supports both line comments (`-- |`) and block
277    /// comments (`{- | ... -}`).
278    fn collect_doc_comments(&mut self) -> Option<bhc_ast::DocComment> {
279        let mut texts = Vec::new();
280        let mut first_span: Option<Span> = None;
281        let mut last_span: Option<Span> = None;
282        let mut kind = bhc_ast::DocKind::Preceding;
283
284        while let Some(tok) = self.current() {
285            match &tok.node.kind {
286                TokenKind::DocCommentLine(text) => {
287                    let span = tok.span;
288                    let text = text.clone();
289                    self.advance();
290
291                    // Check if it's a trailing comment (starts with ^)
292                    let trimmed = text.trim_start();
293                    let (actual_text, doc_kind) = if trimmed.starts_with('^') {
294                        (
295                            trimmed
296                                .strip_prefix('^')
297                                .unwrap_or(trimmed)
298                                .trim()
299                                .to_string(),
300                            bhc_ast::DocKind::Trailing,
301                        )
302                    } else if trimmed.starts_with('|') {
303                        (
304                            trimmed
305                                .strip_prefix('|')
306                                .unwrap_or(trimmed)
307                                .trim()
308                                .to_string(),
309                            bhc_ast::DocKind::Preceding,
310                        )
311                    } else {
312                        (trimmed.to_string(), bhc_ast::DocKind::Preceding)
313                    };
314
315                    if first_span.is_none() {
316                        first_span = Some(span);
317                        kind = doc_kind;
318                    }
319                    last_span = Some(span);
320                    texts.push(actual_text);
321                }
322                TokenKind::DocCommentBlock(text) => {
323                    let span = tok.span;
324                    let text = text.clone();
325                    self.advance();
326
327                    // Check if it's a trailing comment (starts with ^)
328                    let trimmed = text.trim();
329                    let (actual_text, doc_kind) = if trimmed.starts_with('^') {
330                        (
331                            trimmed
332                                .strip_prefix('^')
333                                .unwrap_or(trimmed)
334                                .trim()
335                                .to_string(),
336                            bhc_ast::DocKind::Trailing,
337                        )
338                    } else if trimmed.starts_with('|') {
339                        (
340                            trimmed
341                                .strip_prefix('|')
342                                .unwrap_or(trimmed)
343                                .trim()
344                                .to_string(),
345                            bhc_ast::DocKind::Preceding,
346                        )
347                    } else {
348                        (trimmed.to_string(), bhc_ast::DocKind::Preceding)
349                    };
350
351                    if first_span.is_none() {
352                        first_span = Some(span);
353                        kind = doc_kind;
354                    }
355                    last_span = Some(span);
356                    texts.push(actual_text);
357                }
358                // Two consecutive `-- |` doc lines at the same column produce a
359                // VirtualSemi between them (the layout rule treats them as same-
360                // column items). Skip it so the whole block is collected as one
361                // doc comment instead of orphaning the second line.
362                TokenKind::VirtualSemi if !texts.is_empty() => {
363                    let save = self.pos;
364                    self.advance();
365                    if !matches!(
366                        self.current().map(|t| &t.node.kind),
367                        Some(TokenKind::DocCommentLine(_) | TokenKind::DocCommentBlock(_))
368                    ) {
369                        self.pos = save;
370                        break;
371                    }
372                }
373                _ => break,
374            }
375        }
376
377        if texts.is_empty() {
378            return None;
379        }
380
381        let combined_text = texts.join("\n");
382        let span = first_span.unwrap().to(last_span.unwrap());
383
384        Some(bhc_ast::DocComment {
385            text: combined_text,
386            kind,
387            span,
388        })
389    }
390
391    /// Expect a token of the given kind.
392    fn expect(&mut self, kind: &TokenKind) -> ParseResult<Spanned<Token>> {
393        if self.check(kind) {
394            Ok(self.advance().unwrap())
395        } else if self.at_eof() {
396            Err(ParseError::UnexpectedEof {
397                expected: kind.description().to_string(),
398            })
399        } else {
400            let current = self.current().unwrap();
401            Err(ParseError::Unexpected {
402                found: current.node.kind.description().to_string(),
403                expected: kind.description().to_string(),
404                span: current.span,
405            })
406        }
407    }
408
409    /// Emit a diagnostic.
410    fn emit(&mut self, diagnostic: Diagnostic) {
411        self.diagnostics.emit(diagnostic);
412    }
413
414    /// Check if there are errors.
415    #[must_use]
416    pub fn has_errors(&self) -> bool {
417        self.diagnostics.has_errors()
418    }
419
420    /// Take the diagnostics.
421    pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
422        self.diagnostics.take_diagnostics()
423    }
424}
425
426/// Parse a module from source code.
427pub fn parse_module(src: &str, file_id: FileId) -> (Option<Module>, Vec<Diagnostic>) {
428    let mut parser = Parser::new(src, file_id);
429    let module = parser.parse_module();
430    let diagnostics = parser.take_diagnostics();
431
432    match module {
433        Ok(m) => (Some(m), diagnostics),
434        Err(e) => {
435            let mut diags = diagnostics;
436            diags.push(e.to_diagnostic(file_id));
437            (None, diags)
438        }
439    }
440}
441
442/// Parse an expression from source code.
443pub fn parse_expr(src: &str, file_id: FileId) -> (Option<Expr>, Vec<Diagnostic>) {
444    let mut parser = Parser::new(src, file_id);
445    let expr = parser.parse_expr();
446    let diagnostics = parser.take_diagnostics();
447
448    match expr {
449        Ok(e) => (Some(e), diagnostics),
450        Err(e) => {
451            let mut diags = diagnostics;
452            diags.push(e.to_diagnostic(file_id));
453            (None, diags)
454        }
455    }
456}
457
458/// Parse a single import declaration from source code.
459///
460/// This is used by the REPL to handle `import` statements entered interactively.
461pub fn parse_import_decl(
462    src: &str,
463    file_id: FileId,
464) -> (Option<bhc_ast::ImportDecl>, Vec<Diagnostic>) {
465    let mut parser = Parser::new(src, file_id);
466    let import = parser.parse_import();
467    let diagnostics = parser.take_diagnostics();
468
469    match import {
470        Ok(decl) => (Some(decl), diagnostics),
471        Err(e) => {
472            let mut diags = diagnostics;
473            let diag: Diagnostic = e.to_diagnostic(file_id);
474            diags.push(diag);
475            (None, diags)
476        }
477    }
478}
479
480#[cfg(test)]
481mod tests {
482    use super::*;
483    use bhc_ast::{Decl, ImportSpec, TypeFamilyKind};
484
485    fn parse_expr_ok(src: &str) -> Expr {
486        let (expr, diags) = parse_expr(src, FileId::new(0));
487        assert!(diags.is_empty(), "Parse errors: {:?}", diags);
488        expr.expect("Expected expression")
489    }
490
491    fn parse_module_ok(src: &str) -> Module {
492        let (module, diags) = parse_module(src, FileId::new(0));
493        assert!(diags.is_empty(), "Parse errors: {:?}", diags);
494        module.expect("Expected module")
495    }
496
497    #[test]
498    fn test_parser_creation() {
499        let parser = Parser::new("let x = 1 in x", FileId::new(0));
500        assert!(!parser.at_eof());
501    }
502
503    #[test]
504    fn test_simple_literals() {
505        let expr = parse_expr_ok("42");
506        assert!(matches!(expr, Expr::Lit(bhc_ast::Lit::Int(42), _)));
507
508        let expr = parse_expr_ok("3.14");
509        assert!(matches!(expr, Expr::Lit(bhc_ast::Lit::Float(_), _)));
510
511        let expr = parse_expr_ok("'a'");
512        assert!(matches!(expr, Expr::Lit(bhc_ast::Lit::Char('a'), _)));
513
514        let expr = parse_expr_ok("\"hello\"");
515        assert!(matches!(expr, Expr::Lit(bhc_ast::Lit::String(_), _)));
516    }
517
518    #[test]
519    fn test_variable_and_constructor() {
520        let expr = parse_expr_ok("foo");
521        assert!(matches!(expr, Expr::Var(_, _)));
522
523        let expr = parse_expr_ok("Foo");
524        assert!(matches!(expr, Expr::Con(_, _)));
525    }
526
527    #[test]
528    fn test_application() {
529        let expr = parse_expr_ok("f x");
530        assert!(matches!(expr, Expr::App(_, _, _)));
531
532        let expr = parse_expr_ok("f x y z");
533        assert!(matches!(expr, Expr::App(_, _, _)));
534    }
535
536    #[test]
537    fn test_infix_operators() {
538        let expr = parse_expr_ok("1 + 2");
539        assert!(matches!(expr, Expr::Infix(_, _, _, _)));
540
541        let expr = parse_expr_ok("a && b || c");
542        assert!(matches!(expr, Expr::Infix(_, _, _, _)));
543    }
544
545    #[test]
546    fn test_lambda() {
547        let expr = parse_expr_ok("\\x -> x");
548        assert!(matches!(expr, Expr::Lam(_, _, _)));
549
550        let expr = parse_expr_ok("\\x y -> x + y");
551        if let Expr::Lam(pats, _, _) = expr {
552            assert_eq!(pats.len(), 2);
553        } else {
554            panic!("Expected lambda");
555        }
556    }
557
558    #[test]
559    fn test_let_expression() {
560        let expr = parse_expr_ok("let { x = 1 } in x");
561        assert!(matches!(expr, Expr::Let(_, _, _)));
562    }
563
564    #[test]
565    fn test_if_expression() {
566        let expr = parse_expr_ok("if True then 1 else 2");
567        assert!(matches!(expr, Expr::If(_, _, _, _)));
568    }
569
570    #[test]
571    fn test_case_expression() {
572        let expr = parse_expr_ok("case x of { Just y -> y }");
573        assert!(matches!(expr, Expr::Case(_, _, _)));
574    }
575
576    #[test]
577    fn test_do_expression() {
578        let expr = parse_expr_ok("do { x <- getLine; putStrLn x }");
579        assert!(matches!(expr, Expr::Do(_, _)));
580    }
581
582    #[test]
583    fn test_tuple() {
584        let expr = parse_expr_ok("(1, 2, 3)");
585        if let Expr::Tuple(exprs, _) = expr {
586            assert_eq!(exprs.len(), 3);
587        } else {
588            panic!("Expected tuple");
589        }
590    }
591
592    #[test]
593    fn test_list() {
594        let expr = parse_expr_ok("[1, 2, 3]");
595        if let Expr::List(exprs, _) = expr {
596            assert_eq!(exprs.len(), 3);
597        } else {
598            panic!("Expected list");
599        }
600    }
601
602    #[test]
603    fn test_list_comprehension() {
604        let expr = parse_expr_ok("[x | x <- xs]");
605        assert!(matches!(expr, Expr::ListComp(_, _, _)));
606    }
607
608    #[test]
609    fn test_arithmetic_sequence() {
610        let expr = parse_expr_ok("[1..10]");
611        assert!(matches!(expr, Expr::ArithSeq(_, _)));
612
613        let expr = parse_expr_ok("[1..]");
614        assert!(matches!(expr, Expr::ArithSeq(_, _)));
615
616        let expr = parse_expr_ok("[1,3..10]");
617        assert!(matches!(expr, Expr::ArithSeq(_, _)));
618    }
619
620    #[test]
621    fn test_record_construction() {
622        let expr = parse_expr_ok("Foo { bar = 1, baz = 2 }");
623        assert!(matches!(expr, Expr::RecordCon(_, _, _, _)));
624    }
625
626    #[test]
627    fn test_qualified_record_construction() {
628        let expr = parse_expr_ok("M.Foo { bar = 1, baz = 2 }");
629        assert!(matches!(expr, Expr::QualRecordCon(_, _, _, _, _)));
630    }
631
632    #[test]
633    fn test_record_update() {
634        let expr = parse_expr_ok("foo { bar = 1 }");
635        assert!(matches!(expr, Expr::RecordUpd(_, _, _)));
636    }
637
638    #[test]
639    fn test_operator_section_right() {
640        // Right section: (+ 1) -> \y -> y + 1
641        let expr = parse_expr_ok("(+ 1)");
642        assert!(matches!(expr, Expr::Lam(_, _, _)));
643    }
644
645    #[test]
646    fn test_operator_section_left() {
647        // Left section: (1 +) -> \y -> 1 + y
648        let expr = parse_expr_ok("(1 +)");
649        assert!(matches!(expr, Expr::Lam(_, _, _)));
650    }
651
652    #[test]
653    fn test_operator_as_function() {
654        // (+) becomes a variable
655        let expr = parse_expr_ok("(+)");
656        assert!(matches!(expr, Expr::Var(_, _)));
657    }
658
659    #[test]
660    fn test_negation() {
661        // Negation only works after another expression in infix context
662        // `-x` at the start is ambiguous with operator prefix
663        let expr = parse_expr_ok("1 + -x");
664        // The result contains a Neg somewhere in the tree
665        assert!(matches!(expr, Expr::Infix(_, _, _, _)));
666    }
667
668    #[test]
669    fn test_lazy_expression() {
670        let expr = parse_expr_ok("lazy { expensive }");
671        assert!(matches!(expr, Expr::Lazy(_, _)));
672    }
673
674    // Pattern tests
675
676    #[test]
677    fn test_pattern_wildcard() {
678        let module = parse_module_ok("f _ = 1");
679        assert!(!module.decls.is_empty());
680    }
681
682    #[test]
683    fn test_pattern_constructor() {
684        let module = parse_module_ok("f (Just x) = x");
685        assert!(!module.decls.is_empty());
686    }
687
688    #[test]
689    fn test_pattern_infix() {
690        let module = parse_module_ok("f (x : xs) = xs");
691        assert!(!module.decls.is_empty());
692    }
693
694    #[test]
695    fn test_pattern_as() {
696        let module = parse_module_ok("f xs@(x : _) = xs");
697        assert!(!module.decls.is_empty());
698    }
699
700    #[test]
701    fn test_pattern_lazy() {
702        let module = parse_module_ok("f ~x = x");
703        assert!(!module.decls.is_empty());
704    }
705
706    #[test]
707    fn test_pattern_bang() {
708        let module = parse_module_ok("f !x = x");
709        assert!(!module.decls.is_empty());
710    }
711
712    #[test]
713    fn test_record_pattern() {
714        let module = parse_module_ok("f Foo { bar = x } = x");
715        assert!(!module.decls.is_empty());
716    }
717
718    // Type tests
719
720    #[test]
721    fn test_simple_type() {
722        let module = parse_module_ok("f :: Int");
723        assert!(!module.decls.is_empty());
724    }
725
726    #[test]
727    fn test_function_type() {
728        let module = parse_module_ok("f :: Int -> Bool");
729        assert!(!module.decls.is_empty());
730    }
731
732    #[test]
733    fn test_type_application() {
734        let module = parse_module_ok("f :: Maybe Int");
735        assert!(!module.decls.is_empty());
736    }
737
738    #[test]
739    fn test_tuple_type() {
740        let module = parse_module_ok("f :: (Int, Bool)");
741        assert!(!module.decls.is_empty());
742    }
743
744    #[test]
745    fn test_list_type() {
746        let module = parse_module_ok("f :: [Int]");
747        assert!(!module.decls.is_empty());
748    }
749
750    #[test]
751    fn test_constrained_type() {
752        let module = parse_module_ok("f :: Eq a => a -> Bool");
753        assert!(!module.decls.is_empty());
754    }
755
756    #[test]
757    fn test_multi_constrained_type() {
758        let module = parse_module_ok("f :: (Eq a, Ord a) => a -> a -> Bool");
759        assert!(!module.decls.is_empty());
760    }
761
762    #[test]
763    fn test_forall_type() {
764        let module = parse_module_ok("f :: forall a. a -> a");
765        assert!(!module.decls.is_empty());
766    }
767
768    // Module structure tests
769
770    #[test]
771    fn test_module_header() {
772        let module = parse_module_ok("module Foo where\nx = 1");
773        assert!(module.name.is_some());
774    }
775
776    #[test]
777    fn test_module_exports() {
778        let module = parse_module_ok("module Foo (bar, baz) where\nbar = 1\nbaz = 2");
779        assert!(module.exports.is_some());
780    }
781
782    #[test]
783    fn test_imports() {
784        let module = parse_module_ok("import Data.List\nx = 1");
785        assert!(!module.imports.is_empty());
786    }
787
788    #[test]
789    fn test_qualified_import() {
790        let module = parse_module_ok("import qualified Data.Map as M\nx = 1");
791        assert!(!module.imports.is_empty());
792        assert!(module.imports[0].qualified);
793    }
794
795    // Declaration tests
796
797    #[test]
798    fn test_data_declaration() {
799        let module = parse_module_ok("data Foo = Bar | Baz Int");
800        assert!(!module.decls.is_empty());
801    }
802
803    #[test]
804    fn test_newtype_declaration() {
805        let module = parse_module_ok("newtype Foo = Foo Int");
806        assert!(!module.decls.is_empty());
807    }
808
809    #[test]
810    fn test_type_alias() {
811        let module = parse_module_ok("type Foo = Int");
812        assert!(!module.decls.is_empty());
813    }
814
815    #[test]
816    fn test_class_declaration() {
817        let module = parse_module_ok("class Eq a where\n  eq :: a -> a -> Bool");
818        assert!(!module.decls.is_empty());
819    }
820
821    #[test]
822    fn test_instance_declaration() {
823        let module = parse_module_ok("instance Eq Int where\n  eq = primEqInt");
824        assert!(!module.decls.is_empty());
825    }
826
827    #[test]
828    fn test_fixity_declaration() {
829        let module = parse_module_ok("infixl 6 +");
830        assert!(!module.decls.is_empty());
831    }
832
833    // Pragma tests
834
835    #[test]
836    fn test_language_pragma() {
837        let module = parse_module_ok("{-# LANGUAGE GADTs #-}\nx = 1");
838        assert_eq!(module.pragmas.len(), 1);
839        match &module.pragmas[0].kind {
840            bhc_ast::PragmaKind::Language(exts) => {
841                assert_eq!(exts.len(), 1);
842                assert_eq!(exts[0].as_str(), "GADTs");
843            }
844            _ => panic!("Expected Language pragma"),
845        }
846    }
847
848    #[test]
849    fn test_multiple_pragmas() {
850        let module = parse_module_ok(
851            "{-# LANGUAGE GADTs #-}\n{-# LANGUAGE TypeFamilies, DataKinds #-}\nx = 1",
852        );
853        assert_eq!(module.pragmas.len(), 2);
854    }
855
856    #[test]
857    fn test_options_ghc_pragma() {
858        let module = parse_module_ok("{-# OPTIONS_GHC -Wall -Werror #-}\nx = 1");
859        assert_eq!(module.pragmas.len(), 1);
860        match &module.pragmas[0].kind {
861            bhc_ast::PragmaKind::OptionsGhc(opts) => {
862                assert!(opts.contains("-Wall"));
863                assert!(opts.contains("-Werror"));
864            }
865            _ => panic!("Expected OptionsGhc pragma"),
866        }
867    }
868
869    #[test]
870    fn test_inline_pragma() {
871        let module = parse_module_ok("{-# INLINE foo #-}\nfoo = 1");
872        assert_eq!(module.pragmas.len(), 1);
873        match &module.pragmas[0].kind {
874            bhc_ast::PragmaKind::Inline(name) => {
875                assert_eq!(name.name.as_str(), "foo");
876            }
877            _ => panic!("Expected Inline pragma"),
878        }
879    }
880
881    // ============================================================
882    // Phase 1: New parser features tests
883    // ============================================================
884
885    #[test]
886    fn test_guarded_function() {
887        let module = parse_module_ok("abs x | x >= 0 = x | otherwise = -x");
888        assert!(!module.decls.is_empty());
889        if let bhc_ast::Decl::FunBind(fun) = &module.decls[0] {
890            assert_eq!(fun.name.name.as_str(), "abs");
891            assert_eq!(fun.clauses.len(), 1);
892            if let bhc_ast::Rhs::Guarded(guards, _) = &fun.clauses[0].rhs {
893                assert_eq!(guards.len(), 2);
894            } else {
895                panic!("Expected guarded RHS");
896            }
897        } else {
898            panic!("Expected FunBind");
899        }
900    }
901
902    #[test]
903    fn test_multi_clause_function() {
904        // Simple test with explicit semicolons and parentheses around expression
905        let module = parse_module_ok("fac 0 = 1; fac n = (n * fac (n - 1))");
906        assert_eq!(module.decls.len(), 1); // Should be merged into one
907        if let bhc_ast::Decl::FunBind(fun) = &module.decls[0] {
908            assert_eq!(fun.name.name.as_str(), "fac");
909            assert_eq!(fun.clauses.len(), 2);
910        } else {
911            panic!("Expected FunBind");
912        }
913    }
914
915    #[test]
916    fn test_where_clause() {
917        // Simple where clause with single binding
918        let module = parse_module_ok("f x = y where { y = x }");
919        if let bhc_ast::Decl::FunBind(fun) = &module.decls[0] {
920            assert_eq!(fun.clauses[0].wheres.len(), 1);
921        } else {
922            panic!("Expected FunBind");
923        }
924    }
925
926    #[test]
927    fn test_where_clause_multiple() {
928        // Where clause with multiple bindings
929        let module = parse_module_ok("f x = y where { y = (x + 1); z = (x + 2) }");
930        if let bhc_ast::Decl::FunBind(fun) = &module.decls[0] {
931            assert_eq!(fun.clauses[0].wheres.len(), 2);
932        } else {
933            panic!("Expected FunBind");
934        }
935    }
936
937    #[test]
938    fn test_strict_field() {
939        let module = parse_module_ok("data Pair = Pair !Int !Int");
940        if let bhc_ast::Decl::DataDecl(data) = &module.decls[0] {
941            assert_eq!(data.constrs.len(), 1);
942            if let bhc_ast::ConFields::Positional(fields) = &data.constrs[0].fields {
943                assert_eq!(fields.len(), 2);
944                assert!(matches!(fields[0], bhc_ast::Type::Bang(_, _)));
945                assert!(matches!(fields[1], bhc_ast::Type::Bang(_, _)));
946            } else {
947                panic!("Expected Positional fields");
948            }
949        } else {
950            panic!("Expected DataDecl");
951        }
952    }
953
954    #[test]
955    fn test_lazy_field() {
956        let module = parse_module_ok("data Lazy a = Lazy ~a");
957        if let bhc_ast::Decl::DataDecl(data) = &module.decls[0] {
958            if let bhc_ast::ConFields::Positional(fields) = &data.constrs[0].fields {
959                assert_eq!(fields.len(), 1);
960                assert!(matches!(fields[0], bhc_ast::Type::Lazy(_, _)));
961            } else {
962                panic!("Expected Positional fields");
963            }
964        } else {
965            panic!("Expected DataDecl");
966        }
967    }
968
969    #[test]
970    fn test_mixed_strict_lazy_fields() {
971        let module = parse_module_ok("data Triple a b c = Triple !a b ~c");
972        if let bhc_ast::Decl::DataDecl(data) = &module.decls[0] {
973            if let bhc_ast::ConFields::Positional(fields) = &data.constrs[0].fields {
974                assert_eq!(fields.len(), 3);
975                assert!(matches!(fields[0], bhc_ast::Type::Bang(_, _)));
976                assert!(matches!(fields[1], bhc_ast::Type::Var(_, _)));
977                assert!(matches!(fields[2], bhc_ast::Type::Lazy(_, _)));
978            } else {
979                panic!("Expected Positional fields");
980            }
981        } else {
982            panic!("Expected DataDecl");
983        }
984    }
985
986    #[test]
987    fn test_guards_with_where() {
988        // Simplified: guards with a simple where clause
989        let module = parse_module_ok(
990            "signum x | x > 0 = positive | otherwise = zero where { positive = 1; zero = 0 }",
991        );
992        if let bhc_ast::Decl::FunBind(fun) = &module.decls[0] {
993            if let bhc_ast::Rhs::Guarded(guards, _) = &fun.clauses[0].rhs {
994                assert_eq!(guards.len(), 2);
995            } else {
996                panic!("Expected guarded RHS");
997            }
998            assert_eq!(fun.clauses[0].wheres.len(), 2);
999        } else {
1000            panic!("Expected FunBind");
1001        }
1002    }
1003
1004    #[test]
1005    fn test_backtick_operator_with_lambda() {
1006        // Backtick operators followed by lambda expressions
1007        let _ = parse_module_ok("test = f `catch` \\e -> handle e");
1008        let _ = parse_module_ok("test = x `fmap` (\\a -> a + 1)");
1009        // Qualified names in backticks
1010        let _ = parse_module_ok("test = action `E.catch` \\e -> case e of { Ex -> handler }");
1011    }
1012
1013    #[test]
1014    fn test_as_patterns() {
1015        // Simple as-pattern
1016        let _ = parse_module_ok("f x@(Just y) = y");
1017        // As-pattern with list
1018        let _ = parse_module_ok("g xs@(x:_) = x");
1019        // As-pattern with record (XMonad style) - using explicit braces
1020        let _ = parse_module_ok("h conf@(Config { field = v }) = v");
1021    }
1022
1023    #[test]
1024    fn test_list_type_annotation() {
1025        // List with type annotation (XMonad workspaces pattern)
1026        let _ = parse_module_ok("test = [1 .. 9 :: Int]");
1027        // List with explicit type inside
1028        let _ = parse_module_ok("test = map show [4..9]");
1029    }
1030
1031    #[test]
1032    fn test_multi_clause_explicit_layout() {
1033        // Multi-clause pattern matching function (uses explicit layout)
1034        let module = parse_module_ok("f 0 = 1; f n = n");
1035        // Check that both clauses are in the same FunBind
1036        if let bhc_ast::Decl::FunBind(fun) = &module.decls[0] {
1037            assert_eq!(fun.clauses.len(), 2, "Expected 2 clauses");
1038        } else {
1039            panic!("Expected FunBind");
1040        }
1041    }
1042
1043    #[test]
1044    fn test_multi_clause_with_type_sig_explicit() {
1045        // Type signature followed by multi-clause function
1046        let module = parse_module_ok("f :: Int -> Int; f 0 = 1; f n = n");
1047        // First decl is TypeSig, second is FunBind with 2 clauses
1048        assert!(matches!(module.decls[0], bhc_ast::Decl::TypeSig { .. }));
1049        if let bhc_ast::Decl::FunBind(fun) = &module.decls[1] {
1050            assert_eq!(fun.clauses.len(), 2, "Expected 2 clauses");
1051        } else {
1052            panic!("Expected FunBind");
1053        }
1054    }
1055
1056    #[test]
1057    fn test_multi_clause_with_layout() {
1058        // Multi-clause with layout-based syntax (no explicit semicolons)
1059        // Uses a module declaration to trigger proper layout handling
1060        let src = r#"module Test where
1061
1062f :: Int -> Int
1063f 0 = 1
1064f n = n
1065"#;
1066        let (module, diags) = parse_module(src, FileId::new(0));
1067        if !diags.is_empty() {
1068            // Print errors for debugging
1069            for d in &diags {
1070                eprintln!("Error: {:?}", d);
1071            }
1072        }
1073        let module = module.expect("Should parse");
1074        // Should have TypeSig and FunBind
1075        assert_eq!(
1076            module.decls.len(),
1077            2,
1078            "Expected 2 decls (TypeSig + FunBind)"
1079        );
1080        if let bhc_ast::Decl::FunBind(fun) = &module.decls[1] {
1081            assert_eq!(
1082                fun.clauses.len(),
1083                2,
1084                "Expected 2 clauses, got: {}",
1085                fun.clauses.len()
1086            );
1087        } else {
1088            panic!("Expected FunBind, got: {:?}", module.decls[1]);
1089        }
1090    }
1091
1092    #[test]
1093    fn test_record_with_layout_style() {
1094        // Record definition with XMonad-style layout (leading commas)
1095        let src = r#"module Test where
1096
1097data Foo = Foo { field1 :: Int
1098               , field2 :: String
1099               } deriving (Show)
1100"#;
1101        let (module, diags) = parse_module(src, FileId::new(0));
1102        for d in &diags {
1103            eprintln!("Error: {:?}", d);
1104        }
1105        assert!(diags.is_empty(), "Should parse without errors");
1106        let module = module.expect("Should parse");
1107        assert_eq!(module.decls.len(), 1);
1108    }
1109
1110    #[test]
1111    fn test_xmonad_stackset_style() {
1112        // XMonad StackSet-style records with type variables and strict fields
1113        let src = r#"module Test where
1114
1115data StackSet i l a sid sd =
1116    StackSet { current  :: !(Screen i l a sid sd)
1117             , visible  :: [Screen i l a sid sd]
1118             } deriving (Show, Read, Eq)
1119
1120data Screen i l a sid sd = Screen { workspace :: !(Workspace i l a) }
1121    deriving (Show, Read, Eq)
1122
1123data Workspace i l a = Workspace { tag :: !i }
1124    deriving (Show, Read, Eq)
1125"#;
1126        let (module, diags) = parse_module(src, FileId::new(0));
1127        for d in &diags {
1128            eprintln!("Error: {:?}", d);
1129        }
1130        assert!(diags.is_empty(), "Should parse without errors");
1131        let module = module.expect("Should parse");
1132        assert_eq!(module.decls.len(), 3, "Expected 3 data declarations");
1133    }
1134
1135    #[test]
1136    fn test_instance_with_operator_method() {
1137        // Instance declaration with operator method (XMonad Foldable style)
1138        let src = r#"module Test where
1139
1140instance Foldable Stack where
1141    toList = integrate
1142    foldr f z = foldr f z . toList
1143"#;
1144        let (module, diags) = parse_module(src, FileId::new(0));
1145        for d in &diags {
1146            eprintln!("Error: {:?}", d);
1147        }
1148        assert!(diags.is_empty(), "Should parse without errors");
1149        let module = module.expect("Should parse");
1150        assert_eq!(module.decls.len(), 1);
1151    }
1152
1153    #[test]
1154    fn test_do_let_without_in() {
1155        // In do-notation, 'let' doesn't need 'in'
1156        let src = r#"module Test where
1157
1158test = do
1159    let x = 1
1160    y <- getY
1161    pure (x + y)
1162"#;
1163        let (_module, diags) = parse_module(src, FileId::new(0));
1164        for d in &diags {
1165            eprintln!("Error: {:?}", d);
1166        }
1167        assert!(diags.is_empty(), "Do-notation let should work without 'in'");
1168    }
1169
1170    #[test]
1171    fn test_do_let_simple_binding() {
1172        // Simple case: let followed by another statement
1173        let src = r#"module Test where
1174
1175test = do
1176    sh <- io x
1177    let isFixedSize = isJust sh
1178    isTransient <- isJust sh
1179    pure isTransient
1180"#;
1181        let (_module, diags) = parse_module(src, FileId::new(0));
1182        for d in &diags {
1183            eprintln!("Error: {:?}", d);
1184        }
1185        assert!(
1186            diags.is_empty(),
1187            "Do-notation let followed by statement should parse"
1188        );
1189    }
1190
1191    #[test]
1192    fn test_do_let_complex_binding() {
1193        // XMonad Operations.hs style: let binding followed by more statements
1194        // Note: Uses <$> operator which is fmap infix
1195        let src = r#"module Test where
1196
1197isFixedSizeOrTransient d w = do
1198    sh <- io (getWMNormalHints d w)
1199    let isFixedSize = isJust (sh_min_size sh) && sh_min_size sh == sh_max_size sh
1200    isTransient <- isJust <$> io (getTransientForHint d w)
1201    pure (isFixedSize || isTransient)
1202"#;
1203        let (_module, diags) = parse_module(src, FileId::new(0));
1204        for d in &diags {
1205            eprintln!("Error: {:?}", d);
1206        }
1207        assert!(diags.is_empty(), "Complex do-notation should parse");
1208    }
1209
1210    #[test]
1211    fn test_import_then_function() {
1212        // Test imports followed by function definitions
1213        let src = r#"module Test where
1214
1215import Data.Maybe
1216
1217-- | Lift action
1218liftX :: X a -> Query a
1219liftX = Query . lift
1220"#;
1221        let (_module, diags) = parse_module(src, FileId::new(0));
1222        for d in &diags {
1223            eprintln!("Error: {:?}", d);
1224        }
1225        assert!(diags.is_empty(), "Import followed by function should parse");
1226    }
1227
1228    #[test]
1229    fn test_infix_function_definition() {
1230        // Test infix operator definitions like XMonad's (-->)
1231        let src = r#"module Test where
1232
1233(-->) :: Bool -> a -> a
1234p --> f = if p then f else undefined
1235
1236(<&&>) :: Bool -> Bool -> Bool
1237x <&&> y = x && y
1238"#;
1239        let (_module, diags) = parse_module(src, FileId::new(0));
1240        for d in &diags {
1241            eprintln!("Error: {:?}", d);
1242        }
1243        assert!(diags.is_empty(), "Infix function definitions should parse");
1244    }
1245
1246    #[test]
1247    fn test_primed_identifier_case_pattern() {
1248        // Test primed identifiers (f', xs') in case patterns
1249        let src = r#"module Test where
1250
1251test xs = case xs of
1252    f':rs' -> Just (f', rs')
1253    [] -> Nothing
1254"#;
1255        let (module, diags) = parse_module(src, FileId::new(0));
1256        for d in &diags {
1257            eprintln!("Error: {:?}", d);
1258        }
1259        assert!(
1260            diags.is_empty(),
1261            "Primed identifier case patterns should parse"
1262        );
1263        let module = module.expect("Should parse");
1264        assert_eq!(module.decls.len(), 1);
1265    }
1266
1267    #[test]
1268    fn test_as_pattern_with_record() {
1269        // Test as-patterns with record patterns like `conf'@XConfig { field = val }`
1270        let src = r#"module Test where
1271
1272test = do
1273    conf@Config { field = x } <- getConfig
1274    return x
1275"#;
1276        let (module, diags) = parse_module(src, FileId::new(0));
1277        for d in &diags {
1278            eprintln!("Error: {:?}", d);
1279        }
1280        assert!(diags.is_empty(), "As-pattern with record should parse");
1281        let module = module.expect("Should parse");
1282        assert_eq!(module.decls.len(), 1);
1283    }
1284
1285    #[test]
1286    fn test_deriving_with_type_applications() {
1287        // Test deriving clauses with type applications like `MonadState XState`
1288        let src = r#"module Test where
1289
1290newtype X a = X (ReaderT XConf (StateT XState IO) a)
1291    deriving (Functor, Applicative, Monad, MonadState XState, MonadReader XConf)
1292"#;
1293        let (module, diags) = parse_module(src, FileId::new(0));
1294        for d in &diags {
1295            eprintln!("Error: {:?}", d);
1296        }
1297        assert!(
1298            diags.is_empty(),
1299            "Deriving with type applications should parse"
1300        );
1301        let module = module.expect("Should parse");
1302        assert_eq!(module.decls.len(), 1);
1303    }
1304
1305    #[test]
1306    fn test_deriving_via() {
1307        // Test deriving via clause
1308        let src = r#"module Test where
1309
1310newtype X a = X (IO a) deriving (Semigroup, Monoid) via Ap X a
1311"#;
1312        let (module, diags) = parse_module(src, FileId::new(0));
1313        for d in &diags {
1314            eprintln!("Error: {:?}", d);
1315        }
1316        assert!(diags.is_empty(), "Deriving via should parse");
1317        let module = module.expect("Should parse");
1318        assert_eq!(module.decls.len(), 1);
1319    }
1320
1321    #[test]
1322    fn test_backtick_in_parentheses() {
1323        // Test backtick infix in parenthesized expression
1324        let src = r#"module Test where
1325
1326test x xs = guard (x `elem` xs)
1327"#;
1328        let (module, diags) = parse_module(src, FileId::new(0));
1329        for d in &diags {
1330            eprintln!("Error: {:?}", d);
1331        }
1332        assert!(diags.is_empty(), "Backtick in parentheses should parse");
1333        let module = module.expect("Should parse");
1334        assert_eq!(module.decls.len(), 1);
1335    }
1336
1337    #[test]
1338    fn test_backtick_right_section() {
1339        // Test backtick right section: (`op` x) means \y -> y `op` x
1340        let src = r#"module Test where
1341
1342test = filter (`M.notMember` floatingMap)
1343"#;
1344        let (module, diags) = parse_module(src, FileId::new(0));
1345        for d in &diags {
1346            eprintln!("Error: {:?}", d);
1347        }
1348        assert!(diags.is_empty(), "Backtick right section should parse");
1349        let module = module.expect("Should parse");
1350        assert_eq!(module.decls.len(), 1);
1351    }
1352
1353    #[test]
1354    fn test_backtick_left_section() {
1355        // Test backtick left section: (x `op`) means \y -> x `op` y
1356        let src = r#"module Test where
1357
1358test xs = filter (\x -> not $ any (x `containedIn`) xs) $ xs
1359"#;
1360        let (module, diags) = parse_module(src, FileId::new(0));
1361        for d in &diags {
1362            eprintln!("Error: {:?}", d);
1363        }
1364        assert!(diags.is_empty(), "Backtick left section should parse");
1365        let module = module.expect("Should parse");
1366        assert_eq!(module.decls.len(), 1);
1367    }
1368
1369    #[test]
1370    fn test_lambda_case_multi_alt() {
1371        // Test lambda-case with multiple alternatives
1372        let src = r#"module Test where
1373
1374rescreen = getInfo >>= \case
1375    [] -> trace "empty"
1376    x:xs -> process x xs
1377"#;
1378        let (module, diags) = parse_module(src, FileId::new(0));
1379        for d in &diags {
1380            eprintln!("Error: {:?}", d);
1381        }
1382        assert!(
1383            diags.is_empty(),
1384            "Lambda-case with multiple alternatives should parse"
1385        );
1386        let module = module.expect("Should parse");
1387        assert_eq!(module.decls.len(), 1);
1388    }
1389
1390    #[test]
1391    fn test_type_equality_constraint() {
1392        // Test type equality constraint: (a ~ Type) =>
1393        let src = r#"module Test where
1394
1395instance (a ~ Int) => Num a where
1396  fromInteger = undefined
1397"#;
1398        let (module, diags) = parse_module(src, FileId::new(0));
1399        for d in &diags {
1400            eprintln!("Error: {:?}", d);
1401        }
1402        assert!(diags.is_empty(), "Type equality constraint should parse");
1403        let module = module.expect("Should parse");
1404        assert_eq!(module.decls.len(), 1);
1405    }
1406
1407    #[test]
1408    fn test_multiline_type_signature_parsing() {
1409        // Test parsing multi-line type signature like XMonad Layout.hs
1410        let src = r#"module Foo where
1411tile
1412    :: Rational
1413    -> Rectangle
1414    -> Int
1415"#;
1416        let (module, diags) = parse_module(src, FileId::new(0));
1417        for d in &diags {
1418            eprintln!("Error: {:?}", d);
1419        }
1420        assert!(diags.is_empty(), "Multi-line type signature should parse");
1421        let module = module.expect("Should parse");
1422        // Should have one declaration: the type signature
1423        assert_eq!(
1424            module.decls.len(),
1425            1,
1426            "Should have 1 decl, got {:?}",
1427            module.decls
1428        );
1429    }
1430
1431    #[test]
1432    fn test_multiline_type_signature_with_function_parsing() {
1433        // Test parsing multi-line type signature followed by function definition
1434        let src = r#"module Foo where
1435tile
1436    :: Rational
1437    -> Rectangle
1438tile f r = r
1439"#;
1440        let (module, diags) = parse_module(src, FileId::new(0));
1441        for d in &diags {
1442            eprintln!("Error: {:?}", d);
1443        }
1444        assert!(
1445            diags.is_empty(),
1446            "Multi-line type signature with function should parse"
1447        );
1448        let module = module.expect("Should parse");
1449        // Should have two declarations: type signature and function binding
1450        assert_eq!(
1451            module.decls.len(),
1452            2,
1453            "Should have 2 decls, got {:?}",
1454            module.decls
1455        );
1456    }
1457
1458    #[test]
1459    fn test_multiline_type_signature_after_instance() {
1460        // Test Layout.hs pattern: instance body followed by top-level type signature
1461        let src = r#"module Foo where
1462instance Show Foo where
1463    show _ = "Foo"
1464
1465tile
1466    :: Int
1467    -> Bool
1468tile n = n > 0
1469"#;
1470        let (module, diags) = parse_module(src, FileId::new(0));
1471        for d in &diags {
1472            eprintln!("Error: {:?}", d);
1473        }
1474        assert!(
1475            diags.is_empty(),
1476            "Type signature after instance should parse"
1477        );
1478        let module = module.expect("Should parse");
1479        // Should have: instance, type signature, function binding
1480        assert_eq!(
1481            module.decls.len(),
1482            3,
1483            "Should have 3 decls, got {:?}",
1484            module.decls
1485        );
1486    }
1487
1488    #[test]
1489    fn test_multiline_type_signature_with_doc_comments() {
1490        // Test Layout.hs pattern with doc comments
1491        let src = r#"module Foo where
1492instance Show Foo where
1493    description _ = "Foo"
1494
1495-- | Doc comment
1496tile
1497    :: Int  -- ^ arg1
1498    -> Bool -- ^ result
1499tile n = n > 0
1500"#;
1501        let (module, diags) = parse_module(src, FileId::new(0));
1502        for d in &diags {
1503            eprintln!("Error: {:?}", d);
1504        }
1505        assert!(
1506            diags.is_empty(),
1507            "Type signature with doc comments should parse"
1508        );
1509        let module = module.expect("Should parse");
1510        // Should have: instance, type signature, function binding
1511        assert_eq!(
1512            module.decls.len(),
1513            3,
1514            "Should have 3 decls, got {:?}",
1515            module.decls
1516        );
1517    }
1518
1519    #[test]
1520    fn test_class_multiline_method_signature() {
1521        // Test Core.hs pattern: class with multi-line method signatures
1522        let src = r#"module Foo where
1523class Show a => Foo a b where
1524    -- | Method doc
1525    runMethod :: a
1526              -> b
1527              -> Int
1528    runMethod x y = 42
1529"#;
1530        let (module, diags) = parse_module(src, FileId::new(0));
1531        for d in &diags {
1532            eprintln!("Error: {:?}", d);
1533        }
1534        assert!(
1535            diags.is_empty(),
1536            "Class with multi-line method signature should parse"
1537        );
1538        let module = module.expect("Should parse");
1539        // Should have one class declaration
1540        assert_eq!(
1541            module.decls.len(),
1542            1,
1543            "Should have 1 decl, got {:?}",
1544            module.decls
1545        );
1546    }
1547
1548    #[test]
1549    fn test_class_default_method() {
1550        // Test class with type signature followed by default method implementation
1551        let src = r#"
1552class ExtensionClass a where
1553    initialValue :: a
1554    extensionType :: a -> String
1555    extensionType = show
1556"#;
1557        let (module, diags) = parse_module(src, FileId::new(0));
1558        for d in &diags {
1559            eprintln!("Error: {:?}", d);
1560        }
1561        assert!(diags.is_empty(), "Class with default method should parse");
1562        let module = module.expect("Should parse");
1563        assert_eq!(module.decls.len(), 1);
1564    }
1565
1566    #[test]
1567    fn test_class_default_method_with_docs() {
1568        // Test class with doc comments before type signature and default implementation
1569        let src = r#"
1570class ExtensionClass a where
1571    -- | Initial value
1572    initialValue :: a
1573    -- | The extension type.
1574    -- Multi-line doc.
1575    extensionType :: a -> String
1576    extensionType = show
1577"#;
1578        let (module, diags) = parse_module(src, FileId::new(0));
1579        for d in &diags {
1580            eprintln!("Error: {:?}", d);
1581        }
1582        assert!(diags.is_empty(), "Class with doc comments should parse");
1583        let module = module.expect("Should parse");
1584        assert_eq!(module.decls.len(), 1);
1585    }
1586
1587    #[test]
1588    fn test_class_xmonad_style() {
1589        // Test without MINIMAL pragma first
1590        let src = r#"
1591class ExtensionClass a where
1592    initialValue :: a
1593    extensionType :: a -> String
1594    extensionType = show
1595"#;
1596        let (module, diags) = parse_module(src, FileId::new(0));
1597        for d in &diags {
1598            eprintln!("Error (no pragma): {:?}", d);
1599        }
1600        assert!(diags.is_empty(), "Class without pragma should parse");
1601        let module = module.expect("Should parse");
1602        assert_eq!(module.decls.len(), 1);
1603
1604        // Now test with MINIMAL pragma
1605        let src_with_pragma = r#"
1606class ExtensionClass a where
1607    {-# MINIMAL initialValue #-}
1608    initialValue :: a
1609    extensionType :: a -> String
1610    extensionType = show
1611"#;
1612        let (module2, diags2) = parse_module(src_with_pragma, FileId::new(0));
1613        for d in &diags2 {
1614            eprintln!("Error (with pragma): {:?}", d);
1615        }
1616        assert!(diags2.is_empty(), "Class with MINIMAL pragma should parse");
1617        let module2 = module2.expect("Should parse");
1618        assert_eq!(module2.decls.len(), 1);
1619    }
1620
1621    #[test]
1622    fn test_class_with_associated_type() {
1623        // Test class with associated type declaration
1624        let src = r#"
1625class Collection c where
1626    type Elem c
1627    empty :: c
1628    insert :: Elem c -> c -> c
1629"#;
1630        let (module, diags) = parse_module(src, FileId::new(0));
1631        for d in &diags {
1632            eprintln!("Error: {:?}", d);
1633        }
1634        assert!(diags.is_empty(), "Class with associated type should parse");
1635        let module = module.expect("Should parse");
1636        assert_eq!(module.decls.len(), 1);
1637        if let bhc_ast::Decl::ClassDecl(class) = &module.decls[0] {
1638            assert_eq!(class.name.name.as_str(), "Collection");
1639            assert_eq!(class.assoc_types.len(), 1);
1640            assert_eq!(class.assoc_types[0].name.name.as_str(), "Elem");
1641        } else {
1642            panic!("Expected class declaration");
1643        }
1644    }
1645
1646    #[test]
1647    fn test_class_with_associated_type_default() {
1648        // Test class with associated type with default
1649        let src = r#"
1650class Container c where
1651    type Element c = Int
1652    size :: c -> Int
1653"#;
1654        let (module, diags) = parse_module(src, FileId::new(0));
1655        for d in &diags {
1656            eprintln!("Error: {:?}", d);
1657        }
1658        assert!(
1659            diags.is_empty(),
1660            "Class with associated type default should parse"
1661        );
1662        let module = module.expect("Should parse");
1663        if let bhc_ast::Decl::ClassDecl(class) = &module.decls[0] {
1664            assert_eq!(class.assoc_types.len(), 1);
1665            assert!(class.assoc_types[0].default.is_some());
1666        } else {
1667            panic!("Expected class declaration");
1668        }
1669    }
1670
1671    #[test]
1672    fn test_instance_with_associated_type_def() {
1673        // Test instance with associated type definition
1674        let src = r#"
1675instance Collection [a] where
1676    type Elem [a] = a
1677    empty = []
1678"#;
1679        let (module, diags) = parse_module(src, FileId::new(0));
1680        for d in &diags {
1681            eprintln!("Error: {:?}", d);
1682        }
1683        assert!(
1684            diags.is_empty(),
1685            "Instance with associated type should parse"
1686        );
1687        let module = module.expect("Should parse");
1688        if let bhc_ast::Decl::InstanceDecl(inst) = &module.decls[0] {
1689            assert_eq!(inst.assoc_type_defs.len(), 1);
1690            assert_eq!(inst.assoc_type_defs[0].name.name.as_str(), "Elem");
1691        } else {
1692            panic!("Expected instance declaration");
1693        }
1694    }
1695
1696    #[test]
1697    fn test_inline_let_expression() {
1698        // Test inline let...in expression
1699        let src = "foo = let x = 1 in x + 1";
1700        let (module, diags) = parse_module(src, FileId::new(0));
1701        for d in &diags {
1702            eprintln!("Error: {:?}", d);
1703        }
1704        assert!(diags.is_empty(), "Inline let expression should parse");
1705        let module = module.expect("Should parse");
1706        assert_eq!(module.decls.len(), 1);
1707    }
1708
1709    #[test]
1710    fn test_xmonad_parsing() {
1711        // Test parsing XMonad-style code
1712        use std::path::Path;
1713
1714        let xmonad_dir = Path::new("/tmp/xmonad/src/XMonad");
1715        if !xmonad_dir.exists() {
1716            println!("XMonad source not found at {:?}, skipping test", xmonad_dir);
1717            return;
1718        }
1719
1720        let mut total_errors = 0;
1721        for entry in std::fs::read_dir(xmonad_dir).unwrap() {
1722            let entry = entry.unwrap();
1723            let path = entry.path();
1724            if path.extension().is_some_and(|ext| ext == "hs") {
1725                let src = std::fs::read_to_string(&path).unwrap();
1726                let file_id = crate::FileId::new(0);
1727                let (_, diagnostics) = parse_module(&src, file_id);
1728                let error_count = diagnostics.iter().filter(|d| d.is_error()).count();
1729                total_errors += error_count;
1730                if error_count > 0 {
1731                    println!(
1732                        "{}: {} errors",
1733                        path.file_name().unwrap().to_str().unwrap(),
1734                        error_count
1735                    );
1736                    // Print first 25 errors for debugging
1737                    for (i, d) in diagnostics
1738                        .iter()
1739                        .filter(|d| d.is_error())
1740                        .take(25)
1741                        .enumerate()
1742                    {
1743                        println!("  {}: {:?}", i + 1, d);
1744                    }
1745                }
1746            }
1747        }
1748        println!("Total XMonad parse errors: {}", total_errors);
1749        // We're tracking progress, so allow errors but report them
1750        // assert_eq!(total_errors, 0, "XMonad files should parse without errors");
1751    }
1752
1753    #[test]
1754    fn test_cpp_if_else() {
1755        let src = r#"module Test where
1756
1757test = do
1758    x <- action
1759#if COND
1760    y <- branch1
1761#else
1762    y <- branch2
1763#endif
1764    return x
1765
1766-- | A doc comment
1767other = 42
1768"#;
1769        let (module, diags) = parse_module(src, FileId::new(0));
1770        for d in &diags {
1771            println!("Diagnostic: {:?}", d);
1772        }
1773        assert!(diags.is_empty(), "Parse errors: {:?}", diags);
1774        assert!(module.is_some(), "Failed to parse CPP if/else");
1775    }
1776
1777    #[test]
1778    fn test_cpp_in_where_clause() {
1779        // This mirrors the XMonad Core.hs structure more closely
1780        let src = r#"module Test where
1781
1782xfork x = io x
1783 where
1784    nullStdin = do
1785#if COND
1786        fd <- action1
1787#else
1788        fd <- action2
1789#endif
1790        dupTo fd
1791        closeFd fd
1792
1793-- | Doc comment for next function.
1794xmessage :: String -> IO ()
1795xmessage msg = print msg
1796"#;
1797        let (module, diags) = parse_module(src, FileId::new(0));
1798        for d in &diags {
1799            println!("Diagnostic: {:?}", d);
1800        }
1801        assert!(diags.is_empty(), "Parse errors: {:?}", diags);
1802        assert!(module.is_some(), "Failed to parse CPP in where clause");
1803    }
1804
1805    // Tests for operator exports and imports (fixes for #123)
1806
1807    #[test]
1808    fn test_export_dot_operator() {
1809        // Export the composition operator (.)
1810        let module = parse_module_ok("module Foo ((.), foo) where\nfoo = 1");
1811        assert!(module.exports.is_some());
1812        let exports = module.exports.unwrap();
1813        assert_eq!(exports.len(), 2);
1814    }
1815
1816    #[test]
1817    fn test_export_bang_operator() {
1818        // Export the indexing operator (!)
1819        let module = parse_module_ok("module Data.Map ((!), lookup) where\nlookup = undefined");
1820        assert!(module.exports.is_some());
1821        let exports = module.exports.unwrap();
1822        assert_eq!(exports.len(), 2);
1823    }
1824
1825    #[test]
1826    fn test_export_multiple_special_operators() {
1827        // Export multiple special operators
1828        let module = parse_module_ok("module Ops ((.), (!), (@), (~)) where\nx = 1");
1829        assert!(module.exports.is_some());
1830        let exports = module.exports.unwrap();
1831        assert_eq!(exports.len(), 4);
1832    }
1833
1834    #[test]
1835    fn test_import_dot_operator() {
1836        // Import the composition operator
1837        let module = parse_module_ok("import Data.Function ((.))\nx = 1");
1838        assert!(!module.imports.is_empty());
1839        let import = &module.imports[0];
1840        assert!(import.spec.is_some());
1841    }
1842
1843    #[test]
1844    fn test_import_bang_operator() {
1845        // Import the indexing operator
1846        let module = parse_module_ok("import Data.Map ((!), lookup)\nx = 1");
1847        assert!(!module.imports.is_empty());
1848    }
1849
1850    #[test]
1851    fn test_export_with_doc_comments() {
1852        // Export list with Haddock doc comments between items
1853        let src = r#"module Foo (
1854    -- * Section header
1855    foo,
1856    -- | Documentation for bar
1857    bar
1858) where
1859foo = 1
1860bar = 2"#;
1861        let module = parse_module_ok(src);
1862        assert!(module.exports.is_some());
1863        let exports = module.exports.unwrap();
1864        assert_eq!(exports.len(), 2);
1865    }
1866
1867    #[test]
1868    fn test_export_regular_operator() {
1869        // Export a regular operator like (++)
1870        let module = parse_module_ok("module Data.List ((++), map) where\nmap = undefined");
1871        assert!(module.exports.is_some());
1872    }
1873
1874    #[test]
1875    fn test_import_hiding_operator() {
1876        // Import hiding an operator
1877        let module = parse_module_ok("import Prelude hiding ((.))\nx = 1");
1878        assert!(!module.imports.is_empty());
1879        let import = &module.imports[0];
1880        // Check that the spec is a Hiding variant
1881        match &import.spec {
1882            Some(ImportSpec::Hiding(_)) => {}
1883            _ => panic!("Expected hiding import"),
1884        }
1885    }
1886
1887    // Type family tests
1888
1889    #[test]
1890    fn test_open_type_family() {
1891        let module = parse_module_ok("type family F a");
1892        assert_eq!(module.decls.len(), 1);
1893        match &module.decls[0] {
1894            Decl::TypeFamilyDecl(tf) => {
1895                assert_eq!(tf.name.name.as_str(), "F");
1896                assert_eq!(tf.params.len(), 1);
1897                assert_eq!(tf.family_kind, TypeFamilyKind::Open);
1898                assert!(tf.equations.is_empty());
1899            }
1900            _ => panic!("Expected TypeFamilyDecl"),
1901        }
1902    }
1903
1904    #[test]
1905    fn test_closed_type_family() {
1906        let src = "type family F a where\n  F Int = Bool\n  F a = ()";
1907        let module = parse_module_ok(src);
1908        assert_eq!(module.decls.len(), 1);
1909        match &module.decls[0] {
1910            Decl::TypeFamilyDecl(tf) => {
1911                assert_eq!(tf.name.name.as_str(), "F");
1912                assert_eq!(tf.family_kind, TypeFamilyKind::Closed);
1913                assert_eq!(tf.equations.len(), 2);
1914            }
1915            _ => panic!("Expected TypeFamilyDecl"),
1916        }
1917    }
1918
1919    #[test]
1920    fn test_type_instance() {
1921        let module = parse_module_ok("type instance F Int = Bool");
1922        assert_eq!(module.decls.len(), 1);
1923        match &module.decls[0] {
1924            Decl::TypeInstanceDecl(ti) => {
1925                assert_eq!(ti.name.name.as_str(), "F");
1926                assert_eq!(ti.args.len(), 1);
1927            }
1928            _ => panic!("Expected TypeInstanceDecl"),
1929        }
1930    }
1931
1932    #[test]
1933    fn test_type_family_with_kind_sig() {
1934        let module = parse_module_ok("type family F a :: * -> *");
1935        assert_eq!(module.decls.len(), 1);
1936        match &module.decls[0] {
1937            Decl::TypeFamilyDecl(tf) => {
1938                assert_eq!(tf.name.name.as_str(), "F");
1939                assert!(tf.kind.is_some());
1940                assert_eq!(tf.family_kind, TypeFamilyKind::Open);
1941            }
1942            _ => panic!("Expected TypeFamilyDecl"),
1943        }
1944    }
1945
1946    #[test]
1947    fn test_type_alias_still_works() {
1948        // Regression: ensure type aliases still parse correctly
1949        let module = parse_module_ok("type Foo = Int");
1950        assert!(!module.decls.is_empty());
1951        match &module.decls[0] {
1952            Decl::TypeAlias(ta) => {
1953                assert_eq!(ta.name.name.as_str(), "Foo");
1954            }
1955            _ => panic!("Expected TypeAlias"),
1956        }
1957    }
1958}