From b9f4bfe19815b3741cf455c2aa9238718da432ab Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Fri, 5 Jul 2024 12:09:00 -0700 Subject: [PATCH] Add unused binding detection to LSP --- unison-cli/src/Unison/LSP/FileAnalysis.hs | 5 ++- .../Unison/LSP/FileAnalysis/UnusedBindings.hs | 32 +++++++++++++++++++ unison-cli/unison-cli.cabal | 1 + 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 unison-cli/src/Unison/LSP/FileAnalysis/UnusedBindings.hs diff --git a/unison-cli/src/Unison/LSP/FileAnalysis.hs b/unison-cli/src/Unison/LSP/FileAnalysis.hs index f5f29b5e2..32a51af4a 100644 --- a/unison-cli/src/Unison/LSP/FileAnalysis.hs +++ b/unison-cli/src/Unison/LSP/FileAnalysis.hs @@ -35,6 +35,7 @@ import Unison.KindInference.Error qualified as KindInference import Unison.LSP.Conversions import Unison.LSP.Conversions qualified as Cv import Unison.LSP.Diagnostics (DiagnosticSeverity (..), mkDiagnostic, reportDiagnostics) +import Unison.LSP.FileAnalysis.UnusedBindings qualified as UnusedBindings import Unison.LSP.Orphans () import Unison.LSP.Types import Unison.LSP.VFS qualified as VFS @@ -111,12 +112,14 @@ checkFile doc = runMaybeT do & toRangeMap let typeSignatureHints = fromMaybe mempty (mkTypeSignatureHints <$> parsedFile <*> typecheckedFile) let fileSummary = FileSummary.mkFileSummary parsedFile typecheckedFile + let unusedBindingDiagnostics = fileSummary ^.. _Just . to termsBySymbol . folded . _3 . folding (UnusedBindings.analyseTerm fileUri) + Debug.debugM Debug.Temp "Unused binding diagnostics" unusedBindingDiagnostics let tokenMap = getTokenMap tokens conflictWarningDiagnostics <- fold <$> for fileSummary \fs -> lift $ computeConflictWarningDiagnostics fileUri fs let diagnosticRanges = - (errDiagnostics <> conflictWarningDiagnostics) + (errDiagnostics <> conflictWarningDiagnostics <> unusedBindingDiagnostics) & fmap (\d -> (d ^. range, d)) & toRangeMap let fileAnalysis = FileAnalysis {diagnostics = diagnosticRanges, codeActions = codeActionRanges, fileSummary, typeSignatureHints, ..} diff --git a/unison-cli/src/Unison/LSP/FileAnalysis/UnusedBindings.hs b/unison-cli/src/Unison/LSP/FileAnalysis/UnusedBindings.hs new file mode 100644 index 000000000..0a9e231c5 --- /dev/null +++ b/unison-cli/src/Unison/LSP/FileAnalysis/UnusedBindings.hs @@ -0,0 +1,32 @@ +module Unison.LSP.FileAnalysis.UnusedBindings (analyseTerm) where + +import Data.Foldable qualified as Foldable +import Data.Map qualified as Map +import Data.Set qualified as Set +import Language.LSP.Protocol.Types (Diagnostic) +import Language.LSP.Protocol.Types qualified as Lsp +import U.Core.ABT (ABT (..)) +import U.Core.ABT qualified as ABT +import Unison.LSP.Conversions qualified as Cv +import Unison.LSP.Diagnostics qualified as Diagnostic +import Unison.Parser.Ann (Ann) +import Unison.Prelude +import Unison.Symbol (Symbol) +import Unison.Term (Term) + +analyseTerm :: Lsp.Uri -> Term Symbol Ann -> [Diagnostic] +analyseTerm fileUri tm = + let (unusedVars, _) = ABT.cata alg tm + in Map.toList unusedVars & mapMaybe \(v, ann) -> do + range <- Cv.annToRange ann + pure $ Diagnostic.mkDiagnostic fileUri range Diagnostic.DiagnosticSeverity_Warning ("Unused binding " <> tShow v) [] + where + alg :: (Foldable f, Ord v) => Ann -> ABT f v (Map v Ann, Set v) -> (Map v Ann, Set v) + alg ann abt = case abt of + Var v -> (mempty, Set.singleton v) + Cycle x -> x + Abs v (unusedBindings, usedVars) -> + if v `Set.member` usedVars + then (unusedBindings, Set.delete v usedVars) + else (Map.insert v ann unusedBindings, usedVars) + Tm fx -> Foldable.fold fx diff --git a/unison-cli/unison-cli.cabal b/unison-cli/unison-cli.cabal index a8b820276..94a05b1ea 100644 --- a/unison-cli/unison-cli.cabal +++ b/unison-cli/unison-cli.cabal @@ -128,6 +128,7 @@ library Unison.LSP.Conversions Unison.LSP.Diagnostics Unison.LSP.FileAnalysis + Unison.LSP.FileAnalysis.UnusedBindings Unison.LSP.FoldingRange Unison.LSP.Formatting Unison.LSP.HandlerUtils