{-# LANGUAGE NamedFieldPuns #-} module Dhall.LSP.Backend.Linting ( Suggestion(..) , suggest , Lint.lint ) where import Control.Lens (universeOf) import Data.Maybe (maybeToList) import Data.Text (Text) import Dhall.Core ( Binding (..) , Expr (..) , Import , MultiLet (..) ) import Dhall.LSP.Backend.Diagnostics import Dhall.Parser (Src) import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Maybe as Maybe import qualified Dhall.Core as Core import qualified Dhall.Lint as Lint data Suggestion = Suggestion { range :: Range, suggestion :: Text } -- Diagnose nested let-blocks. -- -- Pattern matching on a 'Let' wrapped in a 'Note' prevents us from repeating -- the search beginning at different @let@s in the same let-block – only -- the outermost 'Let' of a let-block is wrapped in a 'Note'. diagLetInLet :: Expr Src a -> Maybe Suggestion diagLetInLet (Note _ (Let b e)) = case Core.multiLet b e of MultiLet _ (Note src (Let {})) -> Just (Suggestion (rangeFromDhall src) "Superfluous 'in' before nested let binding") _ -> Nothing diagLetInLet _ = Nothing -- Given a let-block compute all unused variables in the block. unusedBindings :: Eq a => MultiLet s a -> [ (Text, Maybe s) ] unusedBindings (MultiLet bindings d) = let go bs@(Binding { variable = var, value } : _) | Just _ <- Lint.removeUnusedBindings (Core.wrapInLets bs d) = [ (var, maybeSrc) ] where maybeSrc = case value of Note src _ -> Just src _ -> Nothing go _ = [] in foldMap go (NonEmpty.tails bindings) -- Diagnose unused let bindings. diagUnusedBindings :: Eq a => Expr Src a -> [Suggestion] diagUnusedBindings (Note src (Let b e)) = map adapt (unusedBindings (Core.multiLet b e)) where adapt (var, maybeSrc) = Suggestion (rangeFromDhall finalSrc) ("Unused let binding '" <> var <> "'") where finalSrc = Maybe.fromMaybe src maybeSrc diagUnusedBindings _ = [] -- | Given an dhall expression suggest all the possible improvements that would -- be made by the linter. suggest :: Expr Src Import -> [Suggestion] suggest expr = concat [ maybeToList (diagLetInLet e) ++ diagUnusedBindings e | e <- universeOf Core.subExpressions expr ]