{-# 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 :: (FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos]
analyse (FilePath
file, [Node (Lexeme Text)]
_) | FilePath -> FilePath
takeExtension FilePath
file FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
/= FilePath
".c" = []
analyse (FilePath
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)
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))]
_)))) =
        FilePath -> Lexeme a -> Doc AnsiStyle -> DiagnosticsT diags ()
forall diags at.
(HasDiagnosticsRich diags CimplePos,
 HasDiagnosticInfo at CimplePos) =>
FilePath -> at -> Doc AnsiStyle -> DiagnosticsT diags ()
warnDoc FilePath
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 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 (m :: * -> *) a. Monad m => a -> m a
return ()

descr :: ((FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos], (Text, Text))
descr :: ((FilePath, [Node (Lexeme Text)]) -> [Diagnostic CimplePos],
 (Text, Text))
descr = ((FilePath, [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."
    ]))