{-
    Copyright 2022 Vidar Holen

    This file is part of ShellCheck.
    https://www.shellcheck.net

    ShellCheck is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ShellCheck is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
-}
{-# LANGUAGE TemplateHaskell #-}

-- Checks that run on the Control Flow Graph (as opposed to the AST)
-- This is scaffolding for a work in progress.

module ShellCheck.Checks.ControlFlow (checker, optionalChecks, ShellCheck.Checks.ControlFlow.runTests) where

import ShellCheck.AST
import ShellCheck.ASTLib
import ShellCheck.CFG hiding (cfgAnalysis)
import ShellCheck.CFGAnalysis
import ShellCheck.AnalyzerLib
import ShellCheck.Data
import ShellCheck.Interface

import Control.Monad
import Control.Monad.Reader
import Data.Graph.Inductive.Graph
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List
import Data.Maybe

import Test.QuickCheck.All (forAllProperties)
import Test.QuickCheck.Test (quickCheckWithResult, stdArgs, maxSuccess)


optionalChecks :: [CheckDescription]
optionalChecks :: [CheckDescription]
optionalChecks = []

-- A check that runs on the entire graph
type ControlFlowCheck = Analysis
-- A check invoked once per node, with its (pre,post) data
type ControlFlowNodeCheck = LNode CFNode -> (ProgramState, ProgramState) -> Analysis
-- A check invoked once per effect, with its node's (pre,post) data
type ControlFlowEffectCheck = IdTagged CFEffect -> Node -> (ProgramState, ProgramState) -> Analysis


checker :: AnalysisSpec -> Parameters -> Checker
checker :: AnalysisSpec -> Parameters -> Checker
checker AnalysisSpec
spec Parameters
params = Checker {
    perScript :: Root -> Analysis
perScript = Analysis -> Root -> Analysis
forall a b. a -> b -> a
const (Analysis -> Root -> Analysis) -> Analysis -> Root -> Analysis
forall a b. (a -> b) -> a -> b
$ [Analysis] -> Analysis
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ [Analysis]
controlFlowChecks,
    perToken :: Token -> Analysis
perToken = Analysis -> Token -> Analysis
forall a b. a -> b -> a
const (Analysis -> Token -> Analysis) -> Analysis -> Token -> Analysis
forall a b. (a -> b) -> a -> b
$ () -> Analysis
forall a. a -> RWST Parameters [TokenComment] Cache Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
}

controlFlowChecks :: [ControlFlowCheck]
controlFlowChecks :: [Analysis]
controlFlowChecks = [
        [ControlFlowNodeCheck] -> Analysis
runNodeChecks [ControlFlowNodeCheck]
controlFlowNodeChecks
    ]

controlFlowNodeChecks :: [ControlFlowNodeCheck]
controlFlowNodeChecks :: [ControlFlowNodeCheck]
controlFlowNodeChecks = [
    [ControlFlowEffectCheck] -> ControlFlowNodeCheck
runEffectChecks [ControlFlowEffectCheck]
controlFlowEffectChecks
    ]

controlFlowEffectChecks :: [ControlFlowEffectCheck]
controlFlowEffectChecks :: [ControlFlowEffectCheck]
controlFlowEffectChecks = [
    ]

runNodeChecks :: [ControlFlowNodeCheck] -> ControlFlowCheck
runNodeChecks :: [ControlFlowNodeCheck] -> Analysis
runNodeChecks [ControlFlowNodeCheck]
perNode = do
    Maybe CFGAnalysis
cfg <- (Parameters -> Maybe CFGAnalysis)
-> RWST
     Parameters [TokenComment] Cache Identity (Maybe CFGAnalysis)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Parameters -> Maybe CFGAnalysis
cfgAnalysis
    Maybe Analysis -> Analysis
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe Analysis -> Analysis) -> Maybe Analysis -> Analysis
forall a b. (a -> b) -> a -> b
$ CFGAnalysis -> Analysis
runOnAll (CFGAnalysis -> Analysis) -> Maybe CFGAnalysis -> Maybe Analysis
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe CFGAnalysis
cfg
  where
    getData :: Map k (a, b) -> (k, b) -> Maybe ((k, b), (a, b))
getData Map k (a, b)
datas n :: (k, b)
n@(k
node, b
label) = do
        (a
pre, b
post) <- k -> Map k (a, b) -> Maybe (a, b)
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup k
node Map k (a, b)
datas
        ((k, b), (a, b)) -> Maybe ((k, b), (a, b))
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return ((k, b)
n, (a
pre, b
post))

    runOn :: (LNode CFNode, (ProgramState, ProgramState)) -> Analysis
    runOn :: (LNode CFNode, (ProgramState, ProgramState)) -> Analysis
runOn (LNode CFNode
node, (ProgramState, ProgramState)
prepost) = (ControlFlowNodeCheck -> Analysis)
-> [ControlFlowNodeCheck] -> Analysis
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\ControlFlowNodeCheck
c -> ControlFlowNodeCheck
c LNode CFNode
node (ProgramState, ProgramState)
prepost) [ControlFlowNodeCheck]
perNode
    runOnAll :: CFGAnalysis -> Analysis
runOnAll CFGAnalysis
cfg = ((LNode CFNode, (ProgramState, ProgramState)) -> Analysis)
-> [(LNode CFNode, (ProgramState, ProgramState))] -> Analysis
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (LNode CFNode, (ProgramState, ProgramState)) -> Analysis
runOn ([(LNode CFNode, (ProgramState, ProgramState))] -> Analysis)
-> [(LNode CFNode, (ProgramState, ProgramState))] -> Analysis
forall a b. (a -> b) -> a -> b
$ (LNode CFNode
 -> Maybe (LNode CFNode, (ProgramState, ProgramState)))
-> [LNode CFNode] -> [(LNode CFNode, (ProgramState, ProgramState))]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (Map Node (ProgramState, ProgramState)
-> LNode CFNode
-> Maybe (LNode CFNode, (ProgramState, ProgramState))
forall {k} {a} {b} {b}.
Ord k =>
Map k (a, b) -> (k, b) -> Maybe ((k, b), (a, b))
getData (Map Node (ProgramState, ProgramState)
 -> LNode CFNode
 -> Maybe (LNode CFNode, (ProgramState, ProgramState)))
-> Map Node (ProgramState, ProgramState)
-> LNode CFNode
-> Maybe (LNode CFNode, (ProgramState, ProgramState))
forall a b. (a -> b) -> a -> b
$ CFGAnalysis -> Map Node (ProgramState, ProgramState)
nodeToData CFGAnalysis
cfg) ([LNode CFNode] -> [(LNode CFNode, (ProgramState, ProgramState))])
-> [LNode CFNode] -> [(LNode CFNode, (ProgramState, ProgramState))]
forall a b. (a -> b) -> a -> b
$ Gr CFNode CFEdge -> [LNode CFNode]
forall a b. Gr a b -> [LNode a]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LNode a]
labNodes (CFGAnalysis -> Gr CFNode CFEdge
graph CFGAnalysis
cfg)

runEffectChecks :: [ControlFlowEffectCheck] -> ControlFlowNodeCheck
runEffectChecks :: [ControlFlowEffectCheck] -> ControlFlowNodeCheck
runEffectChecks [ControlFlowEffectCheck]
list = ControlFlowNodeCheck
checkNode
  where
    checkNode :: ControlFlowNodeCheck
checkNode (Node
node, CFNode
label) (ProgramState, ProgramState)
prepost =
        case CFNode
label of
            CFApplyEffects [IdTagged CFEffect]
effects -> (IdTagged CFEffect -> Analysis) -> [IdTagged CFEffect] -> Analysis
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\IdTagged CFEffect
effect -> (ControlFlowEffectCheck -> Analysis)
-> [ControlFlowEffectCheck] -> Analysis
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\ControlFlowEffectCheck
c -> ControlFlowEffectCheck
c IdTagged CFEffect
effect Node
node (ProgramState, ProgramState)
prepost) [ControlFlowEffectCheck]
list) [IdTagged CFEffect]
effects
            CFNode
_ -> () -> Analysis
forall a. a -> RWST Parameters [TokenComment] Cache Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return ()


return []
runTests :: IO Bool
runTests =  $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])