Skip to content

Commit

Permalink
Merge pull request #5186 from unisonweb/lsp/unused-binding-detection
Browse files Browse the repository at this point in the history
  • Loading branch information
aryairani committed Jul 5, 2024
2 parents 222bd48 + a6b8af1 commit 0331d22
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 1 deletion.
7 changes: 7 additions & 0 deletions unison-cli/src/Unison/LSP/Conversions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,10 @@ annToRange = \case
Ann.External -> Nothing
Ann.GeneratedFrom a -> annToRange a
Ann.Ann start end -> Just $ Range (uToLspPos start) (uToLspPos end)

annToURange :: Ann.Ann -> Maybe Range.Range
annToURange = \case
Ann.Intrinsic -> Nothing
Ann.External -> Nothing
Ann.GeneratedFrom a -> annToURange a
Ann.Ann start end -> Just $ Range.Range start end
4 changes: 3 additions & 1 deletion unison-cli/src/Unison/LSP/FileAnalysis.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -111,12 +112,13 @@ 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 . folding (\(topLevelAnn, _refId, trm, _type) -> UnusedBindings.analyseTerm fileUri topLevelAnn trm)
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, ..}
Expand Down
47 changes: 47 additions & 0 deletions unison-cli/src/Unison/LSP/FileAnalysis/UnusedBindings.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Unison.LSP.FileAnalysis.UnusedBindings where

import Data.Foldable qualified as Foldable
import Data.Map qualified as Map
import Data.Set qualified as Set
import Data.Text qualified as Text
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.Lexer.Pos qualified as Pos
import Unison.Parser.Ann (Ann)
import Unison.Prelude
import Unison.Symbol (Symbol (..))
import Unison.Term (Term)
import Unison.Util.Range qualified as Range
import Unison.Var qualified as Var

analyseTerm :: Lsp.Uri -> Ann -> Term Symbol Ann -> [Diagnostic]
analyseTerm fileUri topLevelTermAnn tm =
let (unusedVars, _) = ABT.cata alg tm
in Map.toList unusedVars & mapMaybe \(v, _ann) -> do
name <- getRelevantVarName v
-- Unfortunately we don't capture the annotation of the actual binding when parsing :'(, for now the least
-- annoying thing to do is just highlight the top of the binding.
urange <- Cv.annToURange topLevelTermAnn <&> (\(Range.Range start@(Pos.Pos line _col) _end) -> Range.Range start (Pos.Pos line 9999))
let lspRange = Cv.uToLspRange urange
pure $ Diagnostic.mkDiagnostic fileUri lspRange Diagnostic.DiagnosticSeverity_Warning ("Unused binding " <> tShow name <> " inside this term.\nUse the binding, or prefix it with an _ to dismiss this warning.") []
where
getRelevantVarName :: Symbol -> Maybe Text
getRelevantVarName = \case
-- We only care about user bindings which don't start with an underscore
Symbol _ (Var.User n) -> do
guard (not (Text.isPrefixOf "_" n))
pure n
_ -> Nothing
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
1 change: 1 addition & 0 deletions unison-cli/unison-cli.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ library
Unison.LSP.Conversions
Unison.LSP.Diagnostics
Unison.LSP.FileAnalysis
Unison.LSP.FileAnalysis.UnusedBindings
Unison.LSP.FoldingRange
Unison.LSP.Formatting
Unison.LSP.HandlerUtils
Expand Down

0 comments on commit 0331d22

Please sign in to comment.