Improve error locations

This commit is contained in:
2026-02-07 16:55:33 -08:00
parent d1729afea7
commit bca61f95a0
9 changed files with 78 additions and 43 deletions

View File

@@ -7,8 +7,8 @@
- [ ] maybe allow "Main" module name for any file - [ ] maybe allow "Main" module name for any file
- [ ] Improve handling of names: - [ ] Improve handling of names:
- We need FC on names in a lot of places - We need FC on names in a lot of places
- [ ] FC for duplicate data constructor name is wrong (points to `data`) - [x] FC for duplicate `data`, `record`, `class` name is wrong (points to `data`)
- [ ] FC on bad import should point to the name - [x] FC on bad import should point to the name
- [x] Current module overrides imports - [x] Current module overrides imports
- [ ] Allow Qualified names in surface syntax - [ ] Allow Qualified names in surface syntax
- Don't disambiguate on type for now - Don't disambiguate on type for now

View File

@@ -533,10 +533,11 @@ parseImport : Parser Import
parseImport = do parseImport = do
fc <- getPos fc <- getPos
keyword "import" keyword "import"
ident <- uident -- TODO revisit when we have parser for qualified names in source
rest <- many $ token Projection (nameFC, ident) <- withFC uident
(restFC,rest) <- withFC $ many $ token Projection
let name = joinBy "" (ident :: rest) let name = joinBy "" (ident :: rest)
pure $ MkImport fc name pure $ MkImport fc (nameFC + restFC, name)
-- Do we do pattern stuff now? or just name = lambda? -- Do we do pattern stuff now? or just name = lambda?
-- TODO multiple names -- TODO multiple names
@@ -613,7 +614,7 @@ parseData : Parser Decl
parseData = do parseData = do
fc <- getPos fc <- getPos
-- commit when we hit ":" -- commit when we hit ":"
name <- try $ (keyword "data" *> (uident <|> ident <|> token MixFix) <* keyword ":") name <- try $ (keyword "data" *> withFC (uident <|> token MixFix) <* keyword ":")
ty <- typeExpr ty <- typeExpr
Just _ <- optional (keyword "where") Just _ <- optional (keyword "where")
| _ => pure $ Data fc name ty Nothing | _ => pure $ Data fc name ty Nothing
@@ -632,10 +633,10 @@ parseRecord : Parser Decl
parseRecord = do parseRecord = do
fc <- getPos fc <- getPos
keyword "record" keyword "record"
name <- uident name <- withFC uident
teles <- many $ ebind <|> nakedBind teles <- many $ ebind <|> nakedBind
keyword "where" keyword "where"
cname <- optional $ keyword "constructor" *> (uident <|> token MixFix) cname <- optional $ keyword "constructor" *> withFC (uident <|> token MixFix)
decls <- startBlock $ manySame $ parseSig decls <- startBlock $ manySame $ parseSig
pure $ Record fc name (join teles) cname decls pure $ Record fc name (join teles) cname decls
@@ -645,7 +646,7 @@ parseClass : Parser Decl
parseClass = do parseClass = do
fc <- getPos fc <- getPos
keyword "class" keyword "class"
name <- uident name <- withFC uident
teles <- many $ ebind <|> nakedBind teles <- many $ ebind <|> nakedBind
keyword "where" keyword "where"
decls <- startBlock $ manySame $ parseSig decls <- startBlock $ manySame $ parseSig

View File

@@ -209,8 +209,8 @@ processCheck ns fc tm ty = do
putStrLn " NF \{render 90 $ pprint Nil norm}" putStrLn " NF \{render 90 $ pprint Nil norm}"
processClass : List String FC String Telescope List Decl M Unit processClass : List String FC (FC × String) Telescope List Decl M Unit
processClass ns classFC nm tele decls = do processClass ns classFC (nameFC, nm) tele decls = do
-- REVIEW maybe we can leverage Record for this -- REVIEW maybe we can leverage Record for this
-- a couple of catches, we don't want the dotted accessors and -- a couple of catches, we don't want the dotted accessors and
-- the self argument should be an auto-implicit -- the self argument should be an auto-implicit
@@ -225,7 +225,7 @@ processClass ns classFC nm tele decls = do
log 1 $ \ _ => "tcon type \{render 90 $ pretty tcType}" log 1 $ \ _ => "tcon type \{render 90 $ pretty tcType}"
log 1 $ \ _ => "dcon type \{render 90 $ pretty dcType}" log 1 $ \ _ => "dcon type \{render 90 $ pretty dcType}"
let decl = Data classFC nm tcType (Just $ TypeSig classFC (dcName :: Nil) dcType :: Nil) let decl = Data classFC (nameFC, nm) tcType (Just $ TypeSig classFC (dcName :: Nil) dcType :: Nil)
log 1 $ \ _ => "Decl:" log 1 $ \ _ => "Decl:"
log 1 $ \ _ => render 90 $ pretty decl log 1 $ \ _ => render 90 $ pretty decl
processDecl ns decl processDecl ns decl
@@ -374,10 +374,10 @@ processInstance ns instfc ty decls = do
-- desugars to Data and processes it -- desugars to Data and processes it
processShortData : List String → FC → Raw → List Raw → M Unit processShortData : List String → FC → Raw → List Raw → M Unit
processShortData ns fc lhs sigs = do processShortData ns fc lhs sigs = do
(nm,args) <- getArgs lhs Nil (nameFC, nm,args) <- getArgs lhs Nil
let ty = foldr mkPi (RU fc) args let ty = foldr mkPi (RU fc) args
cons <- traverse (mkDecl args Nil) sigs cons <- traverse (mkDecl args Nil) sigs
let dataDecl = Data fc nm ty (Just cons) let dataDecl = Data fc (nameFC, nm) ty (Just cons)
log 1 $ \ _ => "SHORTDATA" log 1 $ \ _ => "SHORTDATA"
log 1 $ \ _ => "\{render 90 $ pretty dataDecl}" log 1 $ \ _ => "\{render 90 $ pretty dataDecl}"
processDecl ns dataDecl processDecl ns dataDecl
@@ -385,8 +385,8 @@ processShortData ns fc lhs sigs = do
mkPi : FC × Name Raw Raw mkPi : FC × Name Raw Raw
mkPi (fc,n) a = RPi fc (BI fc n Explicit Zero) (RU fc) a mkPi (fc,n) a = RPi fc (BI fc n Explicit Zero) (RU fc) a
getArgs : Raw -> List (FC × String) -> M (String × List (FC × String)) getArgs : Raw -> List (FC × String) -> M (FC × String × List (FC × String))
getArgs (RVar fc1 nm) acc = pure (nm, acc) getArgs (RVar fc1 nm) acc = pure (fc1, nm, acc)
getArgs (RApp _ t (RVar fc' nm) _) acc = getArgs t ((fc', nm) :: acc) getArgs (RApp _ t (RVar fc' nm) _) acc = getArgs t ((fc', nm) :: acc)
getArgs tm _ = error (getFC tm) "Expected contructor application, got: \{show tm}" getArgs tm _ = error (getFC tm) "Expected contructor application, got: \{show tm}"
@@ -431,8 +431,8 @@ populateConInfo entries =
isSucc (MkEntry fc nm dty@(Pi _ _ _ _ (Ref _ a) (Ref _ b)) (DCon _ _ (Many :: Nil) hn) _) = a == b isSucc (MkEntry fc nm dty@(Pi _ _ _ _ (Ref _ a) (Ref _ b)) (DCon _ _ (Many :: Nil) hn) _) = a == b
isSucc _ = False isSucc _ = False
processData : List String FC String Raw List Decl M Unit processData : List String FC FC × String Raw List Decl M Unit
processData ns fc nm ty cons = do processData ns fc (nameFC, nm) ty cons = do
log 1 $ \ _ => "-----" log 1 $ \ _ => "-----"
log 1 $ \ _ => "Data \{nm}" log 1 $ \ _ => "Data \{nm}"
top <- getTop top <- getTop
@@ -443,7 +443,7 @@ processData ns fc nm ty cons = do
tyty' <- eval Nil tyty tyty' <- eval Nil tyty
type' <- eval Nil type type' <- eval Nil type
unifyCatch fc (mkCtx fc) tyty' type' unifyCatch fc (mkCtx fc) tyty' type'
Just _ => error fc "\{show nm} already declared" Just _ => error nameFC "\{show nm} already declared"
Nothing => setDef (QN ns nm) fc tyty Axiom Nil Nothing => setDef (QN ns nm) fc tyty Axiom Nil
-- check cons, return list of type,con -- check cons, return list of type,con
allCons <- join <$> (for cons $ \x => case x of allCons <- join <$> (for cons $ \x => case x of
@@ -487,25 +487,25 @@ processData ns fc nm ty cons = do
checkDeclType _ = error fc "data type doesn't return U" checkDeclType _ = error fc "data type doesn't return U"
processRecord : List String FC String Telescope Maybe Name List Decl M Unit processRecord : List String FC FC × String Telescope Maybe (FC × Name) List Decl M Unit
processRecord ns recordFC nm tele cname decls = do processRecord ns recordFC (nameFC, nm) tele cname decls = do
log 1 $ \ _ => "-----" log 1 $ \ _ => "-----"
log 1 $ \ _ => "Record" log 1 $ \ _ => "Record"
let fields = getSigs decls let fields = getSigs decls
let dcName = fromMaybe "Mk\{show nm}" cname let (dcFC,dcName) = fromMaybe (nameFC,"Mk\{show nm}") cname
let tcType = teleToPi tele (RU recordFC) let tcType = teleToPi tele (RU recordFC)
let tail = foldl (\ acc bi => case bi : BindInfo × Raw of (BI fc nm icit _, _) => RApp fc acc (RVar fc nm) icit) (RVar recordFC nm) tele let tail = foldl (\ acc bi => case bi : BindInfo × Raw of (BI fc nm icit _, _) => RApp fc acc (RVar fc nm) icit) (RVar nameFC nm) tele
let dcType = teleToPi (impTele tele) $ let dcType = teleToPi (impTele tele) $
foldr (\ x acc => case x : FC × String × Raw of (fc, nm, ty) => RPi fc (BI fc nm Explicit Many) ty acc ) tail fields foldr (\ x acc => case x : FC × String × Raw of (fc, nm, ty) => RPi fc (BI fc nm Explicit Many) ty acc ) tail fields
log 1 $ \ _ => "tcon type \{render 90 $ pretty tcType}" log 1 $ \ _ => "tcon type \{render 90 $ pretty tcType}"
log 1 $ \ _ => "dcon type \{render 90 $ pretty dcType}" log 1 $ \ _ => "dcon type \{render 90 $ pretty dcType}"
let decl = Data recordFC nm tcType (Just $ TypeSig recordFC (dcName :: Nil) dcType :: Nil) let decl = Data recordFC (nameFC,nm) tcType (Just $ TypeSig recordFC (dcName :: Nil) dcType :: Nil)
log 1 $ \ _ => "Decl:" log 1 $ \ _ => "Decl:"
log 1 $ \ _ => render 90 $ pretty decl log 1 $ \ _ => render 90 $ pretty decl
processDecl ns decl processDecl ns decl
-- pattern to peel out fields on LHS -- pattern to peel out fields on LHS
let autoPat = foldl (\acc x => case x : FC × String × Raw of (fc,nm,ty) => RApp fc acc (RVar fc nm) Explicit) (RVar recordFC dcName) fields let autoPat = foldl (\acc x => case x : FC × String × Raw of (fc,nm,ty) => RApp fc acc (RVar nameFC nm) Explicit) (RVar nameFC dcName) fields
processFields autoPat tail Nil fields processFields autoPat tail Nil fields
where where
processFields : Raw Raw List (String × Raw) List (FC × String × Raw) M Unit processFields : Raw Raw List (String × Raw) List (FC × String × Raw) M Unit
@@ -542,5 +542,5 @@ processDecl ns (Instance instfc ty decls) = processInstance ns instfc ty decls
processDecl ns (ShortData fc lhs sigs) = processShortData ns fc lhs sigs processDecl ns (ShortData fc lhs sigs) = processShortData ns fc lhs sigs
processDecl ns (Data fc nm ty (Just cons)) = processData ns fc nm ty cons processDecl ns (Data fc nm ty (Just cons)) = processData ns fc nm ty cons
-- TODO distinguish from function signatures -- TODO distinguish from function signatures
processDecl ns (Data fc nm ty Nothing) = processTypeSig ns fc (nm :: Nil) ty processDecl ns (Data fc (_, nm) ty Nothing) = processTypeSig ns fc (nm :: Nil) ty
processDecl ns (Record recordFC nm tele cname decls) = processRecord ns recordFC nm tele cname decls processDecl ns (Record recordFC nm tele cname decls) = processRecord ns recordFC nm tele cname decls

View File

@@ -74,7 +74,7 @@ instance HasFC Raw where
getFC (RUpdateRec fc _ _) = fc getFC (RUpdateRec fc _ _) = fc
getFC (RImpossible fc) = fc getFC (RImpossible fc) = fc
data Import = MkImport FC Name data Import = MkImport FC (FC × Name)
Telescope : U Telescope : U
Telescope = List (BindInfo × Raw) Telescope = List (BindInfo × Raw)
@@ -84,14 +84,14 @@ data Decl
| FunDef FC Name (List (Raw × Maybe Raw)) | FunDef FC Name (List (Raw × Maybe Raw))
| DCheck FC Raw Raw | DCheck FC Raw Raw
-- TODO maybe add Telescope (before `:`) and auto-add to constructors... -- TODO maybe add Telescope (before `:`) and auto-add to constructors...
| Data FC Name Raw (Maybe $ List Decl) | Data FC (FC × Name) Raw (Maybe $ List Decl)
| ShortData FC Raw (List Raw) | ShortData FC Raw (List Raw)
| PType FC Name (Maybe Raw) | PType FC Name (Maybe Raw)
| PFunc FC Name (List String) Raw String | PFunc FC Name (List String) Raw String
| PMixFix FC (List Name) Int Fixity | PMixFix FC (List Name) Int Fixity
| Class FC Name Telescope (List Decl) | Class FC (FC × Name) Telescope (List Decl)
| Instance FC Raw (Maybe (List Decl)) | Instance FC Raw (Maybe (List Decl))
| Record FC Name Telescope (Maybe Name) (List Decl) | Record FC (FC × Name) Telescope (Maybe $ FC × Name) (List Decl)
instance HasFC Decl where instance HasFC Decl where
@@ -140,7 +140,7 @@ instance Show Decl where
show (ShortData _ lhs sigs) = foo ("ShortData" :: show lhs :: show sigs :: Nil) show (ShortData _ lhs sigs) = foo ("ShortData" :: show lhs :: show sigs :: Nil)
show (PFunc _ nm used ty src) = foo ("PFunc" :: nm :: show used :: show ty :: show src :: Nil) show (PFunc _ nm used ty src) = foo ("PFunc" :: nm :: show used :: show ty :: show src :: Nil)
show (PMixFix _ nms prec fix) = foo ("PMixFix" :: show nms :: show prec :: show fix :: Nil) show (PMixFix _ nms prec fix) = foo ("PMixFix" :: show nms :: show prec :: show fix :: Nil)
show (Class _ nm tele decls) = foo ("Class" :: nm :: "..." :: (show $ map show decls) :: Nil) show (Class _ (_,nm) tele decls) = foo ("Class" :: nm :: "..." :: (show $ map show decls) :: Nil)
show (Instance _ nm decls) = foo ("Instance" :: show nm :: (show $ map show decls) :: Nil) show (Instance _ nm decls) = foo ("Instance" :: show nm :: (show $ map show decls) :: Nil)
show (Record _ nm tele nm1 decls) = foo ("Record" :: show nm :: show tele :: show nm1 :: show decls :: Nil) show (Record _ nm tele nm1 decls) = foo ("Record" :: show nm :: show tele :: show nm1 :: show decls :: Nil)
@@ -245,16 +245,16 @@ instance Pretty Decl where
prettyPair : Raw × Maybe Raw Doc prettyPair : Raw × Maybe Raw Doc
prettyPair (a, Nothing) = pretty a prettyPair (a, Nothing) = pretty a
prettyPair (a, Just b) = pretty a <+> text "=" <+> pretty b prettyPair (a, Just b) = pretty a <+> text "=" <+> pretty b
pretty (Data _ nm x Nothing) = text "data" <+> text nm <+> text ":" <+> pretty x pretty (Data _ (_,nm) x Nothing) = text "data" <+> text nm <+> text ":" <+> pretty x
pretty (Data _ nm x (Just xs)) = text "data" <+> text nm <+> text ":" <+> pretty x <+> (nest 2 $ text "where" </> stack (map pretty xs)) pretty (Data _ (_,nm) x (Just xs)) = text "data" <+> text nm <+> text ":" <+> pretty x <+> (nest 2 $ text "where" </> stack (map pretty xs))
pretty (DCheck _ x y) = text "#check" <+> pretty x <+> text ":" <+> pretty y pretty (DCheck _ x y) = text "#check" <+> pretty x <+> text ":" <+> pretty y
pretty (PType _ nm ty) = text "ptype" <+> text nm <+> (maybe empty (\ty => text ":" <+> pretty ty) ty) pretty (PType _ nm ty) = text "ptype" <+> text nm <+> (maybe empty (\ty => text ":" <+> pretty ty) ty)
pretty (PFunc _ nm Nil ty src) = text "pfunc" <+> text nm <+> text ":" <+> nest 2 (pretty ty <+> text ":=" <+/> text (show src)) pretty (PFunc _ nm Nil ty src) = text "pfunc" <+> text nm <+> text ":" <+> nest 2 (pretty ty <+> text ":=" <+/> text (show src))
pretty (PFunc _ nm used ty src) = text "pfunc" <+> text nm <+> text "uses" <+> spread (map text used) <+> text ":" <+> nest 2 (pretty ty <+> text ":=" <+/> text (show src)) pretty (PFunc _ nm used ty src) = text "pfunc" <+> text nm <+> text "uses" <+> spread (map text used) <+> text ":" <+> nest 2 (pretty ty <+> text ":=" <+/> text (show src))
pretty (PMixFix _ names prec fix) = text (show fix) <+> text (show prec) <+> spread (map text names) pretty (PMixFix _ names prec fix) = text (show fix) <+> text (show prec) <+> spread (map text names)
pretty (Record _ nm tele cname decls) = text "record" <+> text nm <+> text ":" <+> spread (map prettyBind tele) pretty (Record _ (_,nm) tele (cname) decls) = text "record" <+> text nm <+> text ":" <+> spread (map prettyBind tele)
<+> (nest 2 $ text "where" </> stack (maybe empty (\ nm' => text "constructor" <+> text nm') cname :: map pretty decls)) <+> (nest 2 $ text "where" </> stack (maybe empty (\ nm' => text "constructor" <+> text (snd nm')) cname :: map pretty decls))
pretty (Class _ nm tele decls) = text "class" <+> text nm <+> text ":" <+> spread (map prettyBind tele) pretty (Class _ (_,nm) tele decls) = text "class" <+> text nm <+> text ":" <+> spread (map prettyBind tele)
<+> (nest 2 $ text "where" </> stack (map pretty decls)) <+> (nest 2 $ text "where" </> stack (map pretty decls))
pretty (Instance _ _ _) = text "TODO pretty Instance" pretty (Instance _ _ _) = text "TODO pretty Instance"
pretty (ShortData _ lhs sigs) = text "data" <+> pretty lhs <+> text "=" <+> pipeSep (map pretty sigs) pretty (ShortData _ lhs sigs) = text "data" <+> pretty lhs <+> text "=" <+> pipeSep (map pretty sigs)
@@ -328,5 +328,5 @@ instance Pretty Module where
</> stack (map pretty decls) </> stack (map pretty decls)
where where
doImport : Import -> Doc doImport : Import -> Doc
doImport (MkImport _ nm) = text "import" <+> text nm ++ line doImport (MkImport _ (_,nm)) = text "import" <+> text nm ++ line

View File

@@ -94,7 +94,7 @@ importHints (entry :: entries) = do
importHints entries importHints entries
importToQN : Import QName importToQN : Import QName
importToQN (MkImport fc name') = uncurry QN $ unsnoc $ split1 name' "." importToQN (MkImport fc (_,name)) = uncurry QN $ unsnoc $ split1 name "."
-- New style loader, one def at a time -- New style loader, one def at a time
processModule : FC -> String -> List String -> QName -> M String processModule : FC -> String -> List String -> QName -> M String
@@ -128,13 +128,11 @@ processModule importFC base stk qn@(QN ns nm) = do
let importNames = map importToQN imports let importNames = map importToQN imports
imported <- for imports $ \case imported <- for imports $ \case
MkImport fc name' => do MkImport fc (nameFC,name') => do
let (a,b) = unsnoc $ split1 name' "." let (a,b) = unsnoc $ split1 name' "."
let qname = QN a b let qname = QN a b
-- we could use `fc` if it had a filename in it when (elem name' stk) $ \ _ => error nameFC "import loop \{show name} -> \{show name'}"
when (elem name' stk) $ \ _ => error emptyFC "import loop \{show name} -> \{show name'}" processModule nameFC base (name :: stk) qname
processModule fc base (name :: stk) qname
pure $ split name' "." pure $ split name' "."
let imported = snoc imported primNS let imported = snoc imported primNS
srcSum <- liftIO $ checksum src srcSum <- liftIO $ checksum src

4
tests/BadImport.newt Normal file
View File

@@ -0,0 +1,4 @@
module BadImport
-- Error should point to name here
import Does.Not.Exist

View File

@@ -0,0 +1,2 @@
*** Process tests/BadImport.newt
ERROR at tests/BadImport.newt:4:8--4:22: error reading tests/Does/Not/Exist.newt: Error: ENOENT: no such file or directory, open 'tests/Does/Not/Exist.newt'

9
tests/ErrorDup.newt Normal file
View File

@@ -0,0 +1,9 @@
module ErrorDup
data Nat = Z | S Nat
data Nat = Z | S Nat
record Nat where
class Nat where

21
tests/ErrorDup.newt.fail Normal file
View File

@@ -0,0 +1,21 @@
*** Process tests/ErrorDup.newt
module ErrorDup
ERROR at tests/ErrorDup.newt:5:6--5:9: Nat already declared
data Nat = Z | S Nat
data Nat = Z | S Nat
^^^
ERROR at tests/ErrorDup.newt:7:8--7:11: Nat already declared
data Nat = Z | S Nat
record Nat where
^^^
ERROR at tests/ErrorDup.newt:9:7--9:10: Nat already declared
record Nat where
class Nat where
^^^
Compile failed