packages feed

stan-0.1.3.0: src/Stan/Inspection/Partial.hs

{-# LANGUAGE CPP #-}

{- |
Copyright: (c) 2020 Kowainik
SPDX-License-Identifier: MPL-2.0
Maintainer: Kowainik <xrom.xkov@gmail.com>

Contains all 'Inspection's for partial functions.

The __partial__ inspections are in ranges:

* @STAN-0001 .. STAN-0100@
-}

module Stan.Inspection.Partial
    ( -- * Partial 'Inspection's
      -- *** Partial 'GHC.List.head'
      stan0001
      -- *** Partial 'GHC.List.tail'
    , stan0002
      -- *** Partial 'GHC.List.init'
    , stan0003
      -- *** Partial 'GHC.List.last'
    , stan0004
      -- *** Partial 'GHC.List.!!'
    , stan0005
      -- *** Partial 'GHC.List.cycle'
    , stan0006
      -- *** Partial 'Data.OldList.genericIndex'
    , stan0007
      -- *** Partial 'Data.Maybe.fromJust'
    , stan0008
      -- *** Partial 'Text.Read.read'
    , stan0009
      -- *** Partial 'GHC.Enum.succ'
    , stan0010
      -- *** Partial 'GHC.Enum.pred'
    , stan0011
      -- *** Partial 'GHC.Enum.toEnum'
    , stan0012
      -- *** Partial 'Data.Foldable.maximum'
    , stan0013
      -- *** Partial 'Data.Foldable.minimum'
    , stan0014
      -- *** Partial 'Data.Foldable.maximumBy'
    , stan0015
      -- *** Partial 'Data.Foldable.minimumBy'
    , stan0016
      -- *** Partial 'Data.Foldable.foldl1'
    , stan0017
      -- *** Partial 'Data.Foldable.foldl1\''
    , stan0018
      -- *** Partial 'Data.Foldable.foldr1'
    , stan0019
      -- *** Partial instance for 'NonEmpty' method 'GHC.Exts.fromList'
    , stan0020
      -- *** Partial instance for 'Natural' method 'GHC.Num.fromInteger'
    , stan0021

      -- * List of all partial 'Inspection's
    , partialInspectionsMap
    ) where

import Prelude hiding ((&&&))
import Relude.Extra.Lens ((%~), (.~))
import Relude.Extra.Tuple (fmapToFst)

import Stan.Core.Id (Id (..))
import Stan.Inspection (Inspection (..), InspectionAnalysis (..), InspectionsMap, analysisL,
                        categoryL, descriptionL, solutionL)
import Stan.NameMeta (NameMeta (..), baseNameFrom, mkBaseFoldableMeta, mkBaseListMeta,
                      mkBaseOldListMeta, _nameFrom)
import Stan.Pattern.Ast (PatternAst (PatternAstName), namesToPatternAst)
import Stan.Pattern.Edsl (PatternBool (..))
import Stan.Pattern.Type (PatternType (..), integerPattern, listFunPattern, listPattern,
                          naturalPattern, nonEmptyPattern, (|->))
import Stan.Severity (Severity (..))

import qualified Stan.Category as Category


-- | All partial 'Inspection's.
partialInspectionsMap :: InspectionsMap
partialInspectionsMap = fromList $ fmapToFst inspectionId
    [ stan0001
    , stan0002
    , stan0003
    , stan0004
    , stan0005
    , stan0006
    , stan0007
    , stan0008
    , stan0009
    , stan0010
    , stan0011
    , stan0012
    , stan0013
    , stan0014
    , stan0015
    , stan0016
    , stan0017
    , stan0018
    , stan0019
    , stan0020
    , stan0021
    ]

-- | Smart constructor to create generic partial 'Inspection' with a given 'Pattern'.
mkPartialInspectionPattern
    :: Id Inspection
    -> NameMeta
    -> PatternType
    -> Text  -- ^ Type name
    -> Inspection
mkPartialInspectionPattern insId nameMeta@NameMeta{..} pat typeName = Inspection
    { inspectionId = insId
    , inspectionName = "Partial: " <> nameMetaPackage <> "/" <> nameMetaName
    , inspectionDescription = usage nameMetaName typeName
    , inspectionSolution = []
    , inspectionCategory = one Category.partial
    , inspectionSeverity = Warning
    , inspectionAnalysis = FindAst $ PatternAstName nameMeta pat
    }

-- | Smart constructor to create generic partial 'Inspection' with 'PatternAnything'.
mkPartialInspection
    :: Id Inspection
    -> NameMeta
    -> Text  -- ^ Type name
    -> Inspection
mkPartialInspection insId nameMeta =
    mkPartialInspectionPattern insId nameMeta (?)

usage :: Text -> Text -> Text
usage funName forWhat =
    "Usage of partial function '" <> funName <> "' for " <> forWhat

{- | Smart constructor to create partial 'Inspection' for functions
that work with lists.
-}
mkPartialInspectionList :: Id Inspection -> NameMeta -> Inspection
mkPartialInspectionList insId nameMeta = mkPartialInspection insId nameMeta "lists"
    & categoryL %~ (<> one Category.list)
    & solutionL .~
        [ "Replace list with 'NonEmpty' from 'Data.List.NonEmpty'"
        , "Use explicit pattern-matching over lists"
        ]

{- | Smart constructor to create partial 'Inspection' for functions
that work with enumerable types.
-}
mkPartialInspectionEnum :: Id Inspection -> Text -> PatternType -> [Text] -> Inspection
mkPartialInspectionEnum insId funName pat solution =
    mkPartialInspectionPattern insId enumMeta pat ""
    & descriptionL .~ usage funName "enumerable types"
    & solutionL .~ solution
  where
    enumMeta :: NameMeta
    enumMeta =
#if __GLASGOW_HASKELL__ < 910
      funName `_nameFrom` "GHC.Enum"
#else
      funName `_nameFrom` "GHC.Internal.Enum"
#endif

-- | 'Inspection' — partial 'GHC.List.head' @STAN-0001@.
stan0001 :: Inspection
stan0001 = mkPartialInspectionList (Id "STAN-0001") (mkBaseListMeta "head")

-- | 'Inspection' — partial 'GHC.List.tail' @STAN-0002@.
stan0002 :: Inspection
stan0002 = mkPartialInspectionList (Id "STAN-0002") (mkBaseListMeta "tail")

-- | 'Inspection' — partial 'GHC.List.init' @STAN-0003@.
stan0003 :: Inspection
stan0003 = mkPartialInspectionList (Id "STAN-0003") (mkBaseListMeta "init")

-- | 'Inspection' — partial 'GHC.List.last' @STAN-0004@.
stan0004 :: Inspection
stan0004 = mkPartialInspectionList (Id "STAN-0004") (mkBaseListMeta "last")

-- | 'Inspection' — partial 'GHC.List.!!' @STAN-0005@.
stan0005 :: Inspection
stan0005 = mkPartialInspectionList (Id "STAN-0005") (mkBaseListMeta "!!")
    & solutionL .~ []

-- | 'Inspection' — partial 'GHC.List.cycle' @STAN-0006@.
stan0006 :: Inspection
stan0006 = mkPartialInspectionList (Id "STAN-0006") (mkBaseListMeta "cycle")

-- | 'Inspection' — partial 'Data.OldList.genericIndex' @STAN-0007@.
stan0007 :: Inspection
stan0007 = mkPartialInspectionList (Id "STAN-0007") (mkBaseOldListMeta "genericIndex")
    & solutionL .~ []

-- | 'Inspection' — partial 'Data.Maybe.fromJust' @STAN-0008@.
stan0008 :: Inspection
stan0008 = mkPartialInspection (Id "STAN-0008") fromJustNameMeta "'Maybe'"
    & solutionL .~
        [ "Use explicit pattern-matching over Maybe"
        , "Use one of the standard functions: 'maybe', 'fromMaybe'"
        ]
  where
    fromJustNameMeta :: NameMeta
    fromJustNameMeta =
#if __GLASGOW_HASKELL__ < 910
      "fromJust" `_nameFrom` "Data.Maybe"
#else
      "fromJust" `_nameFrom` "GHC.Internal.Data.Maybe"
#endif

-- | 'Inspection' — partial 'Text.Read.read' @STAN-0009@.
stan0009 :: Inspection
stan0009 = mkPartialInspection (Id "STAN-0009") readNameMeta ""
    & descriptionL .~ usage "read" "parsing 'String'"
    & solutionL .~
        [ "Use 'readMaybe' or 'readEither' to handle failed parsing"
        ]
  where
    readNameMeta :: NameMeta
    readNameMeta =
#if __GLASGOW_HASKELL__ < 910
      "read" `_nameFrom` "Text.Read"
#else
      "read" `_nameFrom` "GHC.Internal.Text.Read"
#endif

-- | 'Inspection' — partial 'GHC.Enum.succ' @STAN-0010@.
stan0010 :: Inspection
stan0010 = mkPartialInspectionEnum
    (Id "STAN-0010")
    "succ"
    (neg (integerPattern |-> (?)) &&& neg (naturalPattern |-> (?)))
    [ "Use '(+ 1)' for integral types (but be aware of arithmetic overflow)"
    , "{Extra dependency} Use 'next' from 'Relude.Extra.Enum' in 'relude'"
    ]

-- | 'Inspection' — partial 'GHC.Enum.pred' @STAN-0011@.
stan0011 :: Inspection
stan0011 = mkPartialInspectionEnum
    (Id "STAN-0011")
    "pred"
    (neg (integerPattern |-> (?)))
    [ "Use '(- 1)' for integral types (but be aware of arithmetic overflow)"
    , "{Extra dependency} Use 'prev' from 'Relude.Extra.Enum' in 'relude'"
    ]

-- | 'Inspection' — partial 'GHC.Enum.toEnum' @STAN-0012@.
stan0012 :: Inspection
stan0012 = mkPartialInspectionEnum (Id "STAN-0012") "toEnum" (?)
    [ "{Extra dependency} Use 'safeToEnum' from 'Relude.Extra.Enum' in 'relude'"
    ]

-- | 'Inspection' — partial 'Data.Foldable.maximum' @STAN-0013@.
stan0013 :: Inspection
stan0013 = mkPartialInspectionPattern
    (Id "STAN-0013") (mkBaseFoldableMeta "maximum") listFunPattern ""

-- | 'Inspection' — partial 'Data.Foldable.minimum' @STAN-0014@.
stan0014 :: Inspection
stan0014 = mkPartialInspectionPattern
    (Id "STAN-0014") (mkBaseFoldableMeta "minimum") listFunPattern ""

orderingFunPattern :: PatternType
orderingFunPattern = (?) |-> listFunPattern

-- | 'Inspection' — partial 'Data.Foldable.maximumBy' @STAN-0015@.
stan0015 :: Inspection
stan0015 = mkPartialInspectionPattern
    (Id "STAN-0015") (mkBaseFoldableMeta "maximumBy") orderingFunPattern ""

-- | 'Inspection' — partial 'Data.Foldable.minimumBy' @STAN-0016@.
stan0016 :: Inspection
stan0016 = mkPartialInspectionPattern
    (Id "STAN-0016") (mkBaseFoldableMeta "minimumBy") orderingFunPattern ""

-- | 'Inspection' — partial 'Data.Foldable.foldl1' @STAN-0017@.
stan0017 :: Inspection
stan0017 = mkPartialInspectionPattern
    (Id "STAN-0017") (mkBaseFoldableMeta "foldl1") orderingFunPattern ""

-- | 'Inspection' — partial 'Data.Foldable.foldl1\'' @STAN-0018@.
stan0018 :: Inspection
stan0018 = mkPartialInspectionList (Id "STAN-0018") (mkBaseListMeta "foldl1'")

-- | 'Inspection' — partial 'Data.Foldable.foldr1' @STAN-0019@.
stan0019 :: Inspection
stan0019 = mkPartialInspectionPattern
    (Id "STAN-0019") (mkBaseFoldableMeta "foldr1") orderingFunPattern ""

-- | 'Inspection' — partial 'GHC.Exts.fromList' @STAN-0020@.
stan0020 :: Inspection
stan0020 = mkPartialInspectionPattern (Id "STAN-0020") exts pat ""
    & analysisL .~ FindAst (namesToPatternAst $ (exts, pat) :| [(ne, pat)])
  where
    pat = listPattern |-> nonEmptyPattern
#if __GLASGOW_HASKELL__ < 904
    exts = "fromList" `baseNameFrom` "GHC.Exts"
#elif __GLASGOW_HASKELL__ < 910
    exts = "fromList" `baseNameFrom` "GHC.IsList"
#else
    exts = "fromList" `_nameFrom` "GHC.Internal.IsList"
#endif
    ne = "fromList" `baseNameFrom` "Data.List.NonEmpty"

-- | 'Inspection' — partial 'GHC.Num.fromInteger' @STAN-0021@.
stan0021 :: Inspection
stan0021 = mkPartialInspectionPattern
    (Id "STAN-0021")
#if __GLASGOW_HASKELL__ < 910
    ("fromInteger" `_nameFrom` "GHC.Num")
#else
    ("fromInteger" `_nameFrom` "GHC.Internal.Num")
#endif
    (integerPattern |-> naturalPattern)
    ""