@@ -10,6 +10,7 @@ module Unison.LSP.FileAnalysis
1010 fileAnalysisWorker ,
1111 getFileDefLocations ,
1212 getFileNames ,
13+ analyseNotes ,
1314 )
1415where
1516
@@ -74,6 +75,7 @@ import Unison.Syntax.Name qualified as Name
7475import Unison.Syntax.Parser qualified as Parser
7576import Unison.Syntax.TypePrinter qualified as TypePrinter
7677import Unison.Term qualified as Term
78+ import Unison.Typechecker qualified as Typechecker
7779import Unison.Typechecker.Context qualified as Context
7880import Unison.Typechecker.TypeError qualified as TypeError
7981import Unison.UnisonFile qualified as UF
@@ -224,7 +226,8 @@ fileAnalysisWorker = forever do
224226analyseFile :: (Lspish m ) => (Foldable f ) => Uri -> Text -> PPED. PrettyPrintEnvDecl -> f (Note Symbol Ann ) -> m ([Diagnostic ], [RangedCodeAction ])
225227analyseFile fileUri srcText pped notes = do
226228 let ppe = PPED. suffixifiedPPE pped
227- (noteDiags, noteActions) <- analyseNotes fileUri ppe (Text. unpack srcText) notes
229+ Env {codebase} <- ask
230+ (noteDiags, noteActions) <- analyseNotes codebase fileUri ppe (Text. unpack srcText) notes
228231 pure (noteDiags, noteActions)
229232
230233-- | Returns diagnostics which show a warning diagnostic when editing a term that's conflicted in the
@@ -272,18 +275,31 @@ getTokenMap tokens =
272275 )
273276 & fold
274277
275- analyseNotes :: forall m f . (Lspish m , Foldable f ) => Uri -> PrettyPrintEnv -> String -> f (Note Symbol Ann ) -> m ([Diagnostic ], [RangedCodeAction ])
276- analyseNotes fileUri ppe src notes = do
278+ analyseNotes ::
279+ forall f m .
280+ (Foldable f , MonadIO m ) =>
281+ (Codebase. Codebase IO Symbol Ann ) ->
282+ Uri ->
283+ PrettyPrintEnv ->
284+ String ->
285+ f (Note Symbol Ann ) ->
286+ m ([Diagnostic ], [RangedCodeAction ])
287+ analyseNotes codebase fileUri ppe src notes = do
277288 foldMapM go notes
278289 where
279290 go :: Note Symbol Ann -> m ([Diagnostic ], [RangedCodeAction ])
280291 go note = case note of
281292 Result. TypeError errNote@ (Context. ErrorNote {cause}) -> do
282293 let typeErr = TypeError. typeErrorFromNote errNote
283294 ranges = case typeErr of
284- TypeError. Mismatch {mismatchSite} -> singleRange $ ABT. annotation mismatchSite
285- TypeError. BooleanMismatch {mismatchSite} -> singleRange $ ABT. annotation mismatchSite
286- TypeError. ExistentialMismatch {mismatchSite} -> singleRange $ ABT. annotation mismatchSite
295+ TypeError. Mismatch {mismatchSite, foundType, expectedType}
296+ | -- If it's a delay mismatch, the error is likely with the block definition (e.g. missing 'do') so we highlight the whole block.
297+ Just _ <- Typechecker. isMismatchMissingDelay foundType expectedType ->
298+ singleRange $ ABT. annotation mismatchSite
299+ -- Otherwise we highlight the leafe nodes of the block
300+ | otherwise -> leafNodeRanges " mismatch" mismatchSite
301+ TypeError. BooleanMismatch {mismatchSite} -> leafNodeRanges " mismatch" mismatchSite
302+ TypeError. ExistentialMismatch {mismatchSite} -> leafNodeRanges " mismatch" mismatchSite
287303 TypeError. FunctionUnderApplied {mismatchSite} -> singleRange $ ABT. annotation mismatchSite
288304 TypeError. FunctionApplication {f} -> singleRange $ ABT. annotation f
289305 TypeError. NotFunctionApplication {f} -> singleRange $ ABT. annotation f
@@ -381,6 +397,10 @@ analyseNotes fileUri ppe src notes = do
381397 Context. OtherBug _s -> todoAnnotation
382398 pure (noteDiagnostic note ranges, [] )
383399
400+ leafNodeRanges label mismatchSite = do
401+ let locs = ABT. annotation <$> expressionLeafNodes mismatchSite
402+ (r, rs) <- withNeighbours (locs >>= aToR)
403+ pure (r, (label,) <$> rs)
384404 -- Diagnostics with this return value haven't been properly configured yet.
385405 todoAnnotation = []
386406 singleRange :: Ann -> [(Range , [a ])]
@@ -432,7 +452,6 @@ analyseNotes fileUri ppe src notes = do
432452 typeHoleReplacementCodeActions diags v typ
433453 | not (isUserBlank v) = pure []
434454 | otherwise = do
435- Env {codebase} <- ask
436455 let cleanedTyp = Context. generalizeAndUnTypeVar typ -- TODO: is this right?
437456 refs <- liftIO . Codebase. runTransaction codebase $ Codebase. termsOfType codebase cleanedTyp
438457 forMaybe (toList refs) $ \ ref -> runMaybeT $ do
@@ -586,3 +605,38 @@ mkDocumentSymbols parsedFile typecheckedFile =
586605 name <- maybeToList $ Name. parseText (Var. name sym)
587606 range <- maybeToList $ annToRange ann
588607 pure $ UDocumentSymbol name (Just typ) TermSymbol range []
608+
609+ -- | Crawl a term and find the nodes which actually influence its return type. This is useful for narrowing down a giant
610+ -- "This let/do block has the wrong type" into "This specific line returns the wrong type"
611+ -- This is just a heuristic.
612+ expressionLeafNodes :: Term. Term2 vt at ap v a -> [Term. Term2 vt at ap v a ]
613+ expressionLeafNodes abt =
614+ case ABT. out abt of
615+ ABT. Var {} -> [abt]
616+ ABT. Cycle r -> expressionLeafNodes r
617+ ABT. Abs _ r -> expressionLeafNodes r
618+ ABT. Tm f -> case f of
619+ Term. Int {} -> [abt]
620+ Term. Nat {} -> [abt]
621+ Term. Float {} -> [abt]
622+ Term. Boolean {} -> [abt]
623+ Term. Text {} -> [abt]
624+ Term. Char {} -> [abt]
625+ Term. Blank {} -> [abt]
626+ Term. Ref {} -> [abt]
627+ Term. Constructor {} -> [abt]
628+ Term. Request {} -> [abt]
629+ -- Not 100% sure whether the error should appear on the handler or action, maybe both?
630+ Term. Handle handler _action -> expressionLeafNodes handler
631+ Term. App _a _b -> [abt]
632+ Term. Ann a _ -> expressionLeafNodes a
633+ Term. List {} -> [abt]
634+ Term. If _cond a b -> expressionLeafNodes a <> expressionLeafNodes b
635+ Term. And {} -> [abt]
636+ Term. Or {} -> [abt]
637+ Term. Lam a -> expressionLeafNodes a
638+ Term. LetRec _isTop _bindings body -> expressionLeafNodes body
639+ Term. Let _isTop _bindings body -> expressionLeafNodes body
640+ Term. Match _a cases -> cases & foldMap \ (Term. MatchCase {matchBody}) -> expressionLeafNodes matchBody
641+ Term. TermLink {} -> [abt]
642+ Term. TypeLink {} -> [abt]
0 commit comments