module Dhall.LSP.Backend.Linting ( Suggestion(..) , suggest , Dhall.lint ) where import Dhall.Parser (Src) import Dhall.Core (Expr(..), Binding(..), Var(..), subExpressions, freeIn, Import) import qualified Dhall.Lint as Dhall import Dhall.LSP.Backend.Diagnostics import Data.Monoid ((<>)) import Data.Text (Text) import Data.List.NonEmpty (NonEmpty(..), tails, toList) import Control.Lens (universeOf) data Suggestion = Suggestion { range :: Range, suggestion :: Text } -- Diagnose nested let blocks. diagLetInLet :: Expr Src a -> [Suggestion] diagLetInLet (Note _ (Let _ (Note src (Let _ _)))) = [Suggestion (rangeFromDhall src) "Superfluous 'in' before nested let binding"] diagLetInLet _ = [] -- Given a (noted) let block compute all unused variables in the block. unusedBindings :: Eq a => Expr s a -> [Text] unusedBindings (Note _ (Let bindings d)) = concatMap (\case Binding var _ _ : [] | not (V var 0 `freeIn` d) -> [var] Binding var _ _ : (b : bs) | not (V var 0 `freeIn` Let (b :| bs) d) -> [var] _ -> []) (toList $ tails bindings) unusedBindings _ = [] -- Diagnose unused let bindings. diagUnusedBinding :: Eq a => Expr Src a -> [Suggestion] diagUnusedBinding e@(Note src (Let _ _)) = map (\var -> Suggestion (rangeFromDhall src) ("Unused let binding '" <> var <> "'")) (unusedBindings e) diagUnusedBinding _ = [] -- | Given an dhall expression suggest all the possible improvements that would -- be made by the linter. suggest :: Expr Src Import -> [Suggestion] suggest expr = concat [ diagLetInLet e ++ diagUnusedBinding e | e <- universeOf subExpressions expr ]