{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Strict            #-}
module Tokstyle.Linter.GlobalFuncs (descr) where

import           Control.Monad.State.Strict  (State)
import qualified Control.Monad.State.Strict  as State
import           Data.Fix                    (Fix (..))
import           Data.Text                   (Text)
import qualified Data.Text                   as Text
import           Language.Cimple             (Lexeme (..), Node, NodeF (..),
                                              Scope (..), lexemeText)
import           Language.Cimple.Diagnostics (CimplePos, Diagnostic)
import           Prettyprinter               (pretty, (<+>))
import           System.FilePath             (takeExtension)
import           Tokstyle.Common             (backticks, warn, warnDoc)


analyse :: (FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos]
analyse :: (String, [Node (Lexeme Text)]) -> [Diagnostic CimplePos]
analyse (String
file, [Node (Lexeme Text)]
_) | String -> String
takeExtension String
file String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String
".c" = []
analyse (String
file, [Node (Lexeme Text)]
ast) = [Diagnostic CimplePos] -> [Diagnostic CimplePos]
forall a. [a] -> [a]
reverse ([Diagnostic CimplePos] -> [Diagnostic CimplePos])
-> [Diagnostic CimplePos] -> [Diagnostic CimplePos]
forall a b. (a -> b) -> a -> b
$ State [Diagnostic CimplePos] [()]
-> [Diagnostic CimplePos] -> [Diagnostic CimplePos]
forall s a. State s a -> s -> s
State.execState ((Node (Lexeme Text) -> StateT [Diagnostic CimplePos] Identity ())
-> [Node (Lexeme Text)] -> State [Diagnostic CimplePos] [()]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM Node (Lexeme Text) -> StateT [Diagnostic CimplePos] Identity ()
forall {diags} {a}.
(HasDiagnosticsRich diags CimplePos,
 HasDiagnosticInfo (Lexeme a) CimplePos, Pretty a) =>
Fix (NodeF (Lexeme a)) -> DiagnosticsT diags ()
go [Node (Lexeme Text)]
ast) []
  where
    go :: Fix (NodeF (Lexeme a)) -> DiagnosticsT diags ()
go (Fix (FunctionDecl Scope
Global (Fix (FunctionPrototype Fix (NodeF (Lexeme a))
_ Lexeme a
name [Fix (NodeF (Lexeme a))]
_)))) =
        String -> Lexeme a -> Doc AnsiStyle -> DiagnosticsT diags ()
forall diags at.
(HasDiagnosticsRich diags CimplePos,
 HasDiagnosticInfo at CimplePos) =>
String -> at -> Doc AnsiStyle -> DiagnosticsT diags ()
warnDoc String
file Lexeme a
name (Doc AnsiStyle -> DiagnosticsT diags ())
-> Doc AnsiStyle -> DiagnosticsT diags ()
forall a b. (a -> b) -> a -> b
$
            Doc AnsiStyle
"global function" Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc AnsiStyle -> Doc AnsiStyle
forall ann. Doc ann -> Doc ann
backticks (a -> Doc AnsiStyle
forall ann. a -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty (Lexeme a -> a
forall text. Lexeme text -> text
lexemeText Lexeme a
name)) Doc AnsiStyle -> Doc AnsiStyle -> Doc AnsiStyle
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc AnsiStyle
"declared in .c file"
    go Fix (NodeF (Lexeme a))
_ = () -> DiagnosticsT diags ()
forall a. a -> StateT diags Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

descr :: ((FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos], (Text, Text))
descr :: ((String, [Node (Lexeme Text)]) -> [Diagnostic CimplePos],
 (Text, Text))
descr = ((String, [Node (Lexeme Text)]) -> [Diagnostic CimplePos]
analyse, (Text
"global-funcs", [Text] -> Text
Text.unlines
    [ Text
"Checks that no extern functions are declared in .c files."
    , Text
""
    , Text
"Extern functions must only be declared in .h files. In .c files all declarations"
    , Text
"must be static."
    , Text
""
    , Text
"**Reason:** extern declarations in .c files mean that we depend on a function"
    , Text
"not declared in a .h file we can include. This means we're depending on an"
    , Text
"unexported implementation detail, and there is no compiler that can check"
    , Text
"whether our declaration matches the implementation's definition."
    ]))