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

Analysing functions by 'InspectionAnalysis' for the corresponding
'Inspection'.
-}

module Stan.Analysis.Analyser
    ( analyseAst
    ) where

import Extensions (ExtensionsResult)
import GHC.LanguageExtensions.Type (Extension (Strict, StrictData))
import Slist (Slist)

import Stan.Analysis.Visitor (Visitor (..), VisitorState (..), addFixity, addObservation,
                              addObservations, addOpDecl, getFinalObservations)
import Stan.Core.Id (Id)
import Stan.Core.List (nonRepeatingPairs)
import Stan.FileInfo (isExtensionDisabled)
import Stan.Ghc.Compat (RealSrcSpan, isSymOcc, nameOccName, occNameString)
import Stan.Hie (eqAst)
import Stan.Hie.Compat (HieAST (..), HieFile (..), Identifier, NodeInfo (..), TypeIndex, nodeInfo)
import Stan.Hie.MatchAst (hieMatchPatternAst)
import Stan.Inspection (Inspection (..), InspectionAnalysis (..))
import Stan.NameMeta (NameMeta, ghcPrimNameFrom)
import Stan.Observation (Observations, mkObservation)
import Stan.Pattern.Ast (Literal (..), PatternAst (..), anyNamesToPatternAst, case', constructor,
                         constructorNameIdentifier, dataDecl, fixity, fun, guardBranch, lambdaCase,
                         lazyField, literalPat, opApp, patternMatchArrow, patternMatchBranch,
                         patternMatch_, rhs, tuple, typeSig)
import Stan.Pattern.Edsl (PatternBool (..))

import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Slist as S

{- | Analyses the whole AST starting from the very top.
-}
analyseAst
    :: HieFile
    -> ExtensionsResult
    -> [Inspection]
    -> Observations
analyseAst :: HieFile -> ExtensionsResult -> [Inspection] -> Observations
analyseAst HieFile
hie ExtensionsResult
exts = HieFile -> Visitor -> Observations
getFinalObservations HieFile
hie (Visitor -> Observations)
-> ([Inspection] -> Visitor) -> [Inspection] -> Observations
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HieFile -> ExtensionsResult -> [Inspection] -> Visitor
createVisitor HieFile
hie ExtensionsResult
exts

{- | Create a sinble 'Visitor' value from a list of 'Inspection's and
additional read-only context. This 'Visitor' can be used to traverse
HIE AST in a single pass.
-}
createVisitor
    :: HieFile
    -> ExtensionsResult
    -> [Inspection]
    -> Visitor
createVisitor :: HieFile -> ExtensionsResult -> [Inspection] -> Visitor
createVisitor HieFile
hie ExtensionsResult
exts [Inspection]
inspections = (HieAST TypeIndex -> State VisitorState ()) -> Visitor
Visitor ((HieAST TypeIndex -> State VisitorState ()) -> Visitor)
-> (HieAST TypeIndex -> State VisitorState ()) -> Visitor
forall a b. (a -> b) -> a -> b
$ \HieAST TypeIndex
node ->
    [Inspection]
-> (Inspection -> State VisitorState ()) -> State VisitorState ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Inspection]
inspections ((Inspection -> State VisitorState ()) -> State VisitorState ())
-> (Inspection -> State VisitorState ()) -> State VisitorState ()
forall a b. (a -> b) -> a -> b
$ \Inspection{[Text]
NonEmpty Category
Text
Id Inspection
Severity
InspectionAnalysis
inspectionId :: Id Inspection
inspectionName :: Text
inspectionDescription :: Text
inspectionSolution :: [Text]
inspectionCategory :: NonEmpty Category
inspectionSeverity :: Severity
inspectionAnalysis :: InspectionAnalysis
inspectionId :: Inspection -> Id Inspection
inspectionName :: Inspection -> Text
inspectionDescription :: Inspection -> Text
inspectionSolution :: Inspection -> [Text]
inspectionCategory :: Inspection -> NonEmpty Category
inspectionSeverity :: Inspection -> Severity
inspectionAnalysis :: Inspection -> InspectionAnalysis
..} -> case InspectionAnalysis
inspectionAnalysis of
        FindAst PatternAst
patAst -> Id Inspection
-> PatternAst
-> HieFile
-> HieAST TypeIndex
-> State VisitorState ()
matchAst Id Inspection
inspectionId PatternAst
patAst HieFile
hie HieAST TypeIndex
node
        InspectionAnalysis
Infix -> HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseInfix HieFile
hie HieAST TypeIndex
node
        InspectionAnalysis
LazyField -> Bool -> State VisitorState () -> State VisitorState ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when
            (Extension -> ExtensionsResult -> Bool
isExtensionDisabled Extension
StrictData ExtensionsResult
exts Bool -> Bool -> Bool
&& Extension -> ExtensionsResult -> Bool
isExtensionDisabled Extension
Strict ExtensionsResult
exts)
            (Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseLazyFields Id Inspection
inspectionId HieFile
hie HieAST TypeIndex
node)
        InspectionAnalysis
BigTuples -> Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseBigTuples Id Inspection
inspectionId HieFile
hie HieAST TypeIndex
node
        InspectionAnalysis
PatternMatchOn_ -> Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analysePatternMatch_ Id Inspection
inspectionId HieFile
hie HieAST TypeIndex
node
        InspectionAnalysis
UseCompare -> Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseCompare Id Inspection
inspectionId HieFile
hie HieAST TypeIndex
node

{- | Check for big tuples (size >= 4) in the following places:

* Type signatures: foo :: (Int, Int, Int, Int)
* Literals: (True, 0, [], Nothing)
-}
analyseBigTuples
    :: Id Inspection
    -> HieFile
    -> HieAST TypeIndex
    -> State VisitorState ()
analyseBigTuples :: Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseBigTuples Id Inspection
insId = (HieAST TypeIndex -> Bool)
-> Id Inspection
-> PatternAst
-> HieFile
-> HieAST TypeIndex
-> State VisitorState ()
matchAstWith HieAST TypeIndex -> Bool
isBigTuple Id Inspection
insId PatternAst
tuple
  where
    isBigTuple :: HieAST TypeIndex -> Bool
    isBigTuple :: HieAST TypeIndex -> Bool
isBigTuple Node{[HieAST TypeIndex]
RealSrcSpan
SourcedNodeInfo TypeIndex
sourcedNodeInfo :: SourcedNodeInfo TypeIndex
nodeSpan :: RealSrcSpan
nodeChildren :: [HieAST TypeIndex]
sourcedNodeInfo :: forall a. HieAST a -> SourcedNodeInfo a
nodeSpan :: forall a. HieAST a -> RealSrcSpan
nodeChildren :: forall a. HieAST a -> [HieAST a]
..} = case [HieAST TypeIndex]
nodeChildren of
        HieAST TypeIndex
_:HieAST TypeIndex
_:HieAST TypeIndex
_:HieAST TypeIndex
_:[HieAST TypeIndex]
_  -> Bool
True
        [HieAST TypeIndex]
_lessThan4 -> Bool
False

{- | Find usages of multiple comparison operators and suggest using
'compare'. Currently, handles the following cases:

* Guards

The algorithm is to find all guards, filter them by usage of
comparison operators and find matches.
-}
analyseCompare
    :: Id Inspection
    -> HieFile
    -> HieAST TypeIndex
    -> State VisitorState ()
analyseCompare :: Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseCompare Id Inspection
insId HieFile
hie HieAST TypeIndex
curNode =
    Observations -> State VisitorState ()
addObservations (Observations -> State VisitorState ())
-> Observations -> State VisitorState ()
forall a b. (a -> b) -> a -> b
$ Id Inspection -> HieFile -> RealSrcSpan -> Observation
mkObservation Id Inspection
insId HieFile
hie (RealSrcSpan -> Observation) -> Slist RealSrcSpan -> Observations
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HieAST TypeIndex -> Slist RealSrcSpan
matchComparisonGuards HieAST TypeIndex
curNode
  where
    matchComparisonGuards :: HieAST TypeIndex -> Slist RealSrcSpan
    matchComparisonGuards :: HieAST TypeIndex -> Slist RealSrcSpan
matchComparisonGuards HieAST TypeIndex
node = Bool -> Slist RealSrcSpan -> Slist RealSrcSpan
forall m. Monoid m => Bool -> m -> m
memptyIfFalse
        (HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node PatternAst
fun)
        (Slist RealSrcSpan -> Slist RealSrcSpan)
-> Slist RealSrcSpan -> Slist RealSrcSpan
forall a b. (a -> b) -> a -> b
$ let guards :: [(HieAST TypeIndex, HieAST TypeIndex)]
guards = (HieAST TypeIndex -> Maybe (HieAST TypeIndex, HieAST TypeIndex))
-> [HieAST TypeIndex] -> [(HieAST TypeIndex, HieAST TypeIndex)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe HieAST TypeIndex -> Maybe (HieAST TypeIndex, HieAST TypeIndex)
extractComparisonGuard (HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
node)
          in Bool -> Slist RealSrcSpan -> Slist RealSrcSpan
forall m. Monoid m => Bool -> m -> m
memptyIfFalse ([(HieAST TypeIndex, HieAST TypeIndex)] -> Bool
hasManyCompares [(HieAST TypeIndex, HieAST TypeIndex)]
guards) (RealSrcSpan -> Slist RealSrcSpan
forall a. a -> Slist a
S.one (RealSrcSpan -> Slist RealSrcSpan)
-> RealSrcSpan -> Slist RealSrcSpan
forall a b. (a -> b) -> a -> b
$ HieAST TypeIndex -> RealSrcSpan
forall a. HieAST a -> RealSrcSpan
nodeSpan HieAST TypeIndex
node)

    {- Extract left argument, name of a comparison operator and right
    argument from a guard.
    -}
    extractComparisonGuard
        :: HieAST TypeIndex
        -> Maybe (HieAST TypeIndex, HieAST TypeIndex)
    extractComparisonGuard :: HieAST TypeIndex -> Maybe (HieAST TypeIndex, HieAST TypeIndex)
extractComparisonGuard HieAST TypeIndex
node = do
        -- guard starts with GRHS annotation
        Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node PatternAst
rhs
        -- guard predicate is a first son
        HieAST TypeIndex
stmt:[HieAST TypeIndex]
_ <- [HieAST TypeIndex] -> Maybe [HieAST TypeIndex]
forall a. a -> Maybe a
Just ([HieAST TypeIndex] -> Maybe [HieAST TypeIndex])
-> [HieAST TypeIndex] -> Maybe [HieAST TypeIndex]
forall a b. (a -> b) -> a -> b
$ HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
node
        -- check if it's a guard
        Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
stmt PatternAst
guardBranch
        -- check if it's an operator
        Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
stmt (PatternAst -> Bool) -> PatternAst -> Bool
forall a b. (a -> b) -> a -> b
$ PatternAst -> PatternAst -> PatternAst -> PatternAst
opApp PatternAst
forall a. PatternBool a => a
(?) PatternAst
opsPat PatternAst
forall a. PatternBool a => a
(?)
        -- extract comparison
        HieAST TypeIndex
x:HieAST TypeIndex
_opAst:HieAST TypeIndex
y:[HieAST TypeIndex]
_ <- [HieAST TypeIndex] -> Maybe [HieAST TypeIndex]
forall a. a -> Maybe a
Just ([HieAST TypeIndex] -> Maybe [HieAST TypeIndex])
-> [HieAST TypeIndex] -> Maybe [HieAST TypeIndex]
forall a b. (a -> b) -> a -> b
$ HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
stmt
        (HieAST TypeIndex, HieAST TypeIndex)
-> Maybe (HieAST TypeIndex, HieAST TypeIndex)
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (HieAST TypeIndex
x, HieAST TypeIndex
y)

    -- pattern for any comparison operator
    opsPat :: PatternAst
    opsPat :: PatternAst
opsPat = NonEmpty NameMeta -> PatternAst
anyNamesToPatternAst (NonEmpty NameMeta -> PatternAst)
-> NonEmpty NameMeta -> PatternAst
forall a b. (a -> b) -> a -> b
$ NameMeta
le NameMeta -> [NameMeta] -> NonEmpty NameMeta
forall a. a -> [a] -> NonEmpty a
:| [NameMeta
leq, NameMeta
eq, NameMeta
ge, NameMeta
geq]

    le, leq, eq, ge, geq :: NameMeta
    le :: NameMeta
le  = Text -> NameMeta
opName Text
"<"
    leq :: NameMeta
leq = Text -> NameMeta
opName Text
"<="
    eq :: NameMeta
eq  = Text -> NameMeta
opName Text
"=="
    ge :: NameMeta
ge  = Text -> NameMeta
opName Text
">"
    geq :: NameMeta
geq = Text -> NameMeta
opName Text
">="

    opName :: Text -> NameMeta
    opName :: Text -> NameMeta
opName = (Text -> ModuleName -> NameMeta
`ghcPrimNameFrom` ModuleName
"GHC.Classes")

    -- return True if any two pairs perform comparison of similar things
    hasManyCompares :: [(HieAST TypeIndex, HieAST TypeIndex)] -> Bool
    hasManyCompares :: [(HieAST TypeIndex, HieAST TypeIndex)] -> Bool
hasManyCompares = (((HieAST TypeIndex, HieAST TypeIndex),
  (HieAST TypeIndex, HieAST TypeIndex))
 -> Bool)
-> [((HieAST TypeIndex, HieAST TypeIndex),
     (HieAST TypeIndex, HieAST TypeIndex))]
-> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (((HieAST TypeIndex, HieAST TypeIndex)
 -> (HieAST TypeIndex, HieAST TypeIndex) -> Bool)
-> ((HieAST TypeIndex, HieAST TypeIndex),
    (HieAST TypeIndex, HieAST TypeIndex))
-> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (HieAST TypeIndex, HieAST TypeIndex)
-> (HieAST TypeIndex, HieAST TypeIndex) -> Bool
matchingComparions) ([((HieAST TypeIndex, HieAST TypeIndex),
   (HieAST TypeIndex, HieAST TypeIndex))]
 -> Bool)
-> ([(HieAST TypeIndex, HieAST TypeIndex)]
    -> [((HieAST TypeIndex, HieAST TypeIndex),
         (HieAST TypeIndex, HieAST TypeIndex))])
-> [(HieAST TypeIndex, HieAST TypeIndex)]
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(HieAST TypeIndex, HieAST TypeIndex)]
-> [((HieAST TypeIndex, HieAST TypeIndex),
     (HieAST TypeIndex, HieAST TypeIndex))]
forall a. [a] -> [(a, a)]
nonRepeatingPairs

    matchingComparions
        :: (HieAST TypeIndex, HieAST TypeIndex)
        -> (HieAST TypeIndex, HieAST TypeIndex)
        -> Bool
    matchingComparions :: (HieAST TypeIndex, HieAST TypeIndex)
-> (HieAST TypeIndex, HieAST TypeIndex) -> Bool
matchingComparions (HieAST TypeIndex
a, HieAST TypeIndex
b) (HieAST TypeIndex
x, HieAST TypeIndex
y) =
        (HieFile -> HieAST TypeIndex -> HieAST TypeIndex -> Bool
forall a. Ord a => HieFile -> HieAST a -> HieAST a -> Bool
eqAst HieFile
hie HieAST TypeIndex
a HieAST TypeIndex
x Bool -> Bool -> Bool
&& HieFile -> HieAST TypeIndex -> HieAST TypeIndex -> Bool
forall a. Ord a => HieFile -> HieAST a -> HieAST a -> Bool
eqAst HieFile
hie HieAST TypeIndex
b HieAST TypeIndex
y) Bool -> Bool -> Bool
|| (HieFile -> HieAST TypeIndex -> HieAST TypeIndex -> Bool
forall a. Ord a => HieFile -> HieAST a -> HieAST a -> Bool
eqAst HieFile
hie HieAST TypeIndex
a HieAST TypeIndex
y Bool -> Bool -> Bool
&& HieFile -> HieAST TypeIndex -> HieAST TypeIndex -> Bool
forall a. Ord a => HieFile -> HieAST a -> HieAST a -> Bool
eqAst HieFile
hie HieAST TypeIndex
b HieAST TypeIndex
x)


{- | Check for occurrences lazy fields in all constructors. Ignores
@newtype@s. Currently HIE Ast doesn't have information whether the
data type is @newtype@ or not. So the algorithm ignores all data types
with a single constructor and single field inside that constructor.
-}
analyseLazyFields
    :: Id Inspection
    -> HieFile
    -> HieAST TypeIndex
    -> State VisitorState ()
analyseLazyFields :: Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseLazyFields Id Inspection
insId HieFile
hie HieAST TypeIndex
curNode =
    Observations -> State VisitorState ()
addObservations (Observations -> State VisitorState ())
-> Observations -> State VisitorState ()
forall a b. (a -> b) -> a -> b
$ Id Inspection -> HieFile -> RealSrcSpan -> Observation
mkObservation Id Inspection
insId HieFile
hie (RealSrcSpan -> Observation) -> Slist RealSrcSpan -> Observations
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HieAST TypeIndex -> Slist RealSrcSpan
matchLazyField HieAST TypeIndex
curNode
  where
    matchLazyField :: HieAST TypeIndex -> Slist RealSrcSpan
    matchLazyField :: HieAST TypeIndex -> Slist RealSrcSpan
matchLazyField HieAST TypeIndex
node = Bool -> Slist RealSrcSpan -> Slist RealSrcSpan
forall m. Monoid m => Bool -> m -> m
memptyIfFalse
        -- return empty list if it's not a data type
        (HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node PatternAst
dataDecl)
        -- get list of all constructors
        (Slist RealSrcSpan -> Slist RealSrcSpan)
-> Slist RealSrcSpan -> Slist RealSrcSpan
forall a b. (a -> b) -> a -> b
$ let constructors :: [HieAST TypeIndex]
constructors = (HieAST TypeIndex -> Bool)
-> [HieAST TypeIndex] -> [HieAST TypeIndex]
forall a. (a -> Bool) -> [a] -> [a]
filter
                (\HieAST TypeIndex
n -> HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
n PatternAst
constructor)
                (HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
node)
          in case [HieAST TypeIndex]
constructors of
              -- no constructors = not observations
              []  -> Slist RealSrcSpan
forall a. Monoid a => a
mempty
              -- single constructor
              [HieAST TypeIndex
c] -> (HieAST TypeIndex -> Slist RealSrcSpan)
-> [HieAST TypeIndex] -> Slist RealSrcSpan
forall (t :: * -> *) a b.
Foldable t =>
(a -> Slist b) -> t a -> Slist b
S.concatMap HieAST TypeIndex -> Slist RealSrcSpan
matchField ([HieAST TypeIndex] -> Slist RealSrcSpan)
-> [HieAST TypeIndex] -> Slist RealSrcSpan
forall a b. (a -> b) -> a -> b
$ Bool -> HieAST TypeIndex -> [HieAST TypeIndex]
extractFields Bool
False HieAST TypeIndex
c
              -- multiple constructors = analyse everything
              [HieAST TypeIndex]
cs  -> (HieAST TypeIndex -> Slist RealSrcSpan)
-> [HieAST TypeIndex] -> Slist RealSrcSpan
forall (t :: * -> *) a b.
Foldable t =>
(a -> Slist b) -> t a -> Slist b
S.concatMap ((HieAST TypeIndex -> Slist RealSrcSpan)
-> [HieAST TypeIndex] -> Slist RealSrcSpan
forall (t :: * -> *) a b.
Foldable t =>
(a -> Slist b) -> t a -> Slist b
S.concatMap HieAST TypeIndex -> Slist RealSrcSpan
matchField ([HieAST TypeIndex] -> Slist RealSrcSpan)
-> (HieAST TypeIndex -> [HieAST TypeIndex])
-> HieAST TypeIndex
-> Slist RealSrcSpan
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> HieAST TypeIndex -> [HieAST TypeIndex]
extractFields Bool
True) [HieAST TypeIndex]
cs

    -- Extract fields as AST nodes. Return empty list if only one field
    -- (as a workaround for the @newtype@ problem)
    --
    -- record constructors have the following children:
    --   1. One or many constraints (e.g. forall a . Num a =>)
    --   2. Constructor name.
    --   3. Dummy child with all fields as childrens
    -- plain constructors have constructor name and children in the same list
    extractFields :: Bool -> HieAST TypeIndex -> [HieAST TypeIndex]
    extractFields :: Bool -> HieAST TypeIndex -> [HieAST TypeIndex]
extractFields Bool
hasManyCtors HieAST TypeIndex
ctor = case TypeIndex -> [HieAST TypeIndex] -> [HieAST TypeIndex]
forall a. TypeIndex -> [a] -> [a]
drop TypeIndex
1 ([HieAST TypeIndex] -> [HieAST TypeIndex])
-> [HieAST TypeIndex] -> [HieAST TypeIndex]
forall a b. (a -> b) -> a -> b
$ (HieAST TypeIndex -> Bool)
-> [HieAST TypeIndex] -> [HieAST TypeIndex]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile HieAST TypeIndex -> Bool
isConstraint ([HieAST TypeIndex] -> [HieAST TypeIndex])
-> [HieAST TypeIndex] -> [HieAST TypeIndex]
forall a b. (a -> b) -> a -> b
$ HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
ctor of
        [] -> []  -- no fields
        [HieAST TypeIndex
n] ->  -- single field, maybe dummy record node
            if HieAST TypeIndex -> Bool
isDummyRecordNode HieAST TypeIndex
n
            then case HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
n of
                []      -> []
                [HieAST TypeIndex
field] -> [HieAST TypeIndex
field | Bool
hasManyCtors]
                [HieAST TypeIndex]
fields  -> [HieAST TypeIndex]
fields
            else [HieAST TypeIndex
n | Bool
hasManyCtors]
        [HieAST TypeIndex]
fields -> [HieAST TypeIndex]
fields  -- plain constructor
      where
        -- simple check for the dummy AST node
        isDummyRecordNode :: HieAST TypeIndex -> Bool
        isDummyRecordNode :: HieAST TypeIndex -> Bool
isDummyRecordNode = Set NodeAnnotation -> Bool
forall a. Set a -> Bool
Set.null (Set NodeAnnotation -> Bool)
-> (HieAST TypeIndex -> Set NodeAnnotation)
-> HieAST TypeIndex
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NodeInfo TypeIndex -> Set NodeAnnotation
forall a. NodeInfo a -> Set NodeAnnotation
nodeAnnotations (NodeInfo TypeIndex -> Set NodeAnnotation)
-> (HieAST TypeIndex -> NodeInfo TypeIndex)
-> HieAST TypeIndex
-> Set NodeAnnotation
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HieAST TypeIndex -> NodeInfo TypeIndex
forall a. Ord a => HieAST a -> NodeInfo a
nodeInfo

        -- Not the constructor identifier
        isConstraint :: HieAST TypeIndex -> Bool
        isConstraint :: HieAST TypeIndex -> Bool
isConstraint HieAST TypeIndex
n = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
n PatternAst
constructorNameIdentifier

    -- matches record fields non-recursively
    matchField :: HieAST TypeIndex -> Slist RealSrcSpan
    matchField :: HieAST TypeIndex -> Slist RealSrcSpan
matchField = PatternAst -> HieFile -> HieAST TypeIndex -> Slist RealSrcSpan
createMatch PatternAst
lazyField HieFile
hie

{- | Check for occurrences of pattern matching on @_@ for sum types (except
literals).
-}
analysePatternMatch_
    :: Id Inspection
    -> HieFile
    -> HieAST TypeIndex
    -> State VisitorState ()
analysePatternMatch_ :: Id Inspection
-> HieFile -> HieAST TypeIndex -> State VisitorState ()
analysePatternMatch_ Id Inspection
insId HieFile
hie HieAST TypeIndex
curNode =
    Observations -> State VisitorState ()
addObservations (Observations -> State VisitorState ())
-> Observations -> State VisitorState ()
forall a b. (a -> b) -> a -> b
$ Id Inspection -> HieFile -> RealSrcSpan -> Observation
mkObservation Id Inspection
insId HieFile
hie (RealSrcSpan -> Observation) -> Slist RealSrcSpan -> Observations
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HieAST TypeIndex -> Slist RealSrcSpan
matchPatternMatch HieAST TypeIndex
curNode
  where
    matchPatternMatch :: HieAST TypeIndex -> Slist RealSrcSpan
    matchPatternMatch :: HieAST TypeIndex -> Slist RealSrcSpan
matchPatternMatch HieAST TypeIndex
node = Bool -> Slist RealSrcSpan -> Slist RealSrcSpan
forall m. Monoid m => Bool -> m -> m
memptyIfFalse
        -- return empty list if it's not a case or lambda case
        (HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node (PatternAst -> Bool) -> PatternAst -> Bool
forall a b. (a -> b) -> a -> b
$ PatternAst
lambdaCase PatternAst -> PatternAst -> PatternAst
forall a. PatternBool a => a -> a -> a
||| PatternAst
case')
        -- get list of all case branches
        (Slist RealSrcSpan -> Slist RealSrcSpan)
-> Slist RealSrcSpan -> Slist RealSrcSpan
forall a b. (a -> b) -> a -> b
$ case HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
node of
              -- no branches = not observations
              []     -> Slist RealSrcSpan
forall a. Monoid a => a
mempty
              -- lambda case, first kid is pattern matching
              [HieAST TypeIndex
pm]   -> HieAST TypeIndex -> Slist RealSrcSpan
analyseBranches HieAST TypeIndex
pm
              -- case, first kid is @case exp of@, the second is pattern matching
              HieAST TypeIndex
_:HieAST TypeIndex
pm:[HieAST TypeIndex]
_ -> HieAST TypeIndex -> Slist RealSrcSpan
analyseBranches HieAST TypeIndex
pm

    {- Check the pattern matching child on some particular expressions.

    -}
    analyseBranches :: HieAST TypeIndex -> Slist RealSrcSpan
    analyseBranches :: HieAST TypeIndex -> Slist RealSrcSpan
analyseBranches HieAST TypeIndex
pm = case HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
pm of
        -- if there is no children = no observations
        [] -> Slist RealSrcSpan
forall a. Monoid a => a
mempty
        -- we need to check first and all other children separately
        -- see 'isFirstPatternMatchBranchOk' comment to understand the first
        -- child's rules.
        HieAST TypeIndex
c:[HieAST TypeIndex]
cs -> Bool -> Slist RealSrcSpan -> Slist RealSrcSpan
forall m. Monoid m => Bool -> m -> m
memptyIfFalse (HieAST TypeIndex -> Bool
isFirstPatternMatchBranchOk HieAST TypeIndex
c) (Slist RealSrcSpan -> Slist RealSrcSpan)
-> Slist RealSrcSpan -> Slist RealSrcSpan
forall a b. (a -> b) -> a -> b
$
            {- if the first child satisfies rules of the first pattern matching
            branch, then we need to find the child with pattern matching on @_@.
            If there is no such expression = all is good.
            -}
            case (HieAST TypeIndex -> Bool)
-> [HieAST TypeIndex] -> Maybe (HieAST TypeIndex)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\HieAST TypeIndex
x -> HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
x (PatternAst -> PatternAst
patternMatch_ PatternAst
forall a. PatternBool a => a
(?))) [HieAST TypeIndex]
cs of
                Maybe (HieAST TypeIndex)
Nothing -> Slist RealSrcSpan
forall a. Monoid a => a
mempty
                Just HieAST TypeIndex
e  -> RealSrcSpan -> Slist RealSrcSpan
forall a. a -> Slist a
S.one (HieAST TypeIndex -> RealSrcSpan
forall a. HieAST a -> RealSrcSpan
nodeSpan HieAST TypeIndex
e)

    {- The first pattern matching branch should not:
    1. Be empty (makes no sense)
    2. Be a literal pattern matching (e.g. on 'Int's or 'String's)
    In all other cases we can continue our matching checks with other children.
    -}
    isFirstPatternMatchBranchOk :: HieAST TypeIndex -> Bool
    isFirstPatternMatchBranchOk :: HieAST TypeIndex -> Bool
isFirstPatternMatchBranchOk HieAST TypeIndex
c = HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
c PatternAst
patternMatchBranch Bool -> Bool -> Bool
&&
        case (HieAST TypeIndex -> Bool)
-> [HieAST TypeIndex] -> [HieAST TypeIndex]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile HieAST TypeIndex -> Bool
isNotMatchArrow ([HieAST TypeIndex] -> [HieAST TypeIndex])
-> [HieAST TypeIndex] -> [HieAST TypeIndex]
forall a b. (a -> b) -> a -> b
$ HieAST TypeIndex -> [HieAST TypeIndex]
forall a. HieAST a -> [HieAST a]
nodeChildren HieAST TypeIndex
c of
            []  -> Bool
False
            [HieAST TypeIndex
x] -> HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
x PatternAst
notLiteral
            HieAST TypeIndex
_:[HieAST TypeIndex]
_ -> Bool
True
      where
        isNotMatchArrow :: HieAST TypeIndex -> Bool
        isNotMatchArrow :: HieAST TypeIndex -> Bool
isNotMatchArrow HieAST TypeIndex
n = HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
n (PatternAst -> Bool) -> PatternAst -> Bool
forall a b. (a -> b) -> a -> b
$ PatternAst -> PatternAst
forall a. PatternBool a => a -> a
neg (PatternAst -> PatternAst) -> PatternAst -> PatternAst
forall a b. (a -> b) -> a -> b
$ PatternAst -> PatternAst
patternMatchArrow PatternAst
forall a. PatternBool a => a
(?)

    notLiteral :: PatternAst
    notLiteral :: PatternAst
notLiteral = PatternAst -> PatternAst
forall a. PatternBool a => a -> a
neg
        -- general literal expression
        ( Literal -> PatternAst
PatternAstConstant Literal
AnyLiteral
        -- since GHC-8.10 expression for literal in pattern matching
        PatternAst -> PatternAst -> PatternAst
forall a. PatternBool a => a -> a -> a
||| PatternAst
literalPat
        )

{- | Analyse HIE AST to find all operators which lack explicit fixity
declaration.

The algorithm is the following:

1. Traverse AST and discover all top-level operators and @infix@
declarations in a single pass.
2. Compare two resulting sets to find out operators without @infix@
declaration.
-}
analyseInfix
    :: HieFile
    -> HieAST TypeIndex
    -> State VisitorState ()
analyseInfix :: HieFile -> HieAST TypeIndex -> State VisitorState ()
analyseInfix HieFile
hie HieAST TypeIndex
curNode = do
    HieAST TypeIndex -> State VisitorState ()
matchInfix HieAST TypeIndex
curNode
    HieAST TypeIndex -> State VisitorState ()
matchOperator HieAST TypeIndex
curNode
  where
    -- adds to the state list of operator names defined in a single
    -- fixity declaration:
    -- infix 5 ***, +++, ???
    matchInfix :: HieAST TypeIndex -> State VisitorState ()
    matchInfix :: HieAST TypeIndex -> State VisitorState ()
matchInfix node :: HieAST TypeIndex
node@Node{[HieAST TypeIndex]
RealSrcSpan
SourcedNodeInfo TypeIndex
sourcedNodeInfo :: forall a. HieAST a -> SourcedNodeInfo a
nodeSpan :: forall a. HieAST a -> RealSrcSpan
nodeChildren :: forall a. HieAST a -> [HieAST a]
sourcedNodeInfo :: SourcedNodeInfo TypeIndex
nodeSpan :: RealSrcSpan
nodeChildren :: [HieAST TypeIndex]
..} = Bool -> State VisitorState () -> State VisitorState ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when
        (HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node PatternAst
fixity)
        ((Text -> State VisitorState ()) -> [Text] -> State VisitorState ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ Text -> State VisitorState ()
addFixity ([Text] -> State VisitorState ())
-> [Text] -> State VisitorState ()
forall a b. (a -> b) -> a -> b
$ (HieAST TypeIndex -> [Text]) -> [HieAST TypeIndex] -> [Text]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap HieAST TypeIndex -> [Text]
nodeIds [HieAST TypeIndex]
nodeChildren)

    -- add to state a singleton or empty list with the top-level
    -- operator definition:
    -- (+++) :: ...
    matchOperator :: HieAST TypeIndex -> State VisitorState ()
    matchOperator :: HieAST TypeIndex -> State VisitorState ()
matchOperator node :: HieAST TypeIndex
node@Node{[HieAST TypeIndex]
RealSrcSpan
SourcedNodeInfo TypeIndex
sourcedNodeInfo :: forall a. HieAST a -> SourcedNodeInfo a
nodeSpan :: forall a. HieAST a -> RealSrcSpan
nodeChildren :: forall a. HieAST a -> [HieAST a]
sourcedNodeInfo :: SourcedNodeInfo TypeIndex
nodeSpan :: RealSrcSpan
nodeChildren :: [HieAST TypeIndex]
..} = Bool -> State VisitorState () -> State VisitorState ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when
        (HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node PatternAst
typeSig)
        (Maybe [(Text, RealSrcSpan)]
-> ([(Text, RealSrcSpan)] -> State VisitorState ())
-> State VisitorState ()
forall (f :: * -> *) a.
Applicative f =>
Maybe a -> (a -> f ()) -> f ()
whenJust
            -- do nothing when cannot extract name
            -- first child of a parent is a name of a function/operator
            ((NonEmpty (HieAST TypeIndex) -> [(Text, RealSrcSpan)])
-> [HieAST TypeIndex] -> Maybe [(Text, RealSrcSpan)]
forall a b. (NonEmpty a -> b) -> [a] -> Maybe b
viaNonEmpty (HieAST TypeIndex -> [(Text, RealSrcSpan)]
extractOperatorName (HieAST TypeIndex -> [(Text, RealSrcSpan)])
-> (NonEmpty (HieAST TypeIndex) -> HieAST TypeIndex)
-> NonEmpty (HieAST TypeIndex)
-> [(Text, RealSrcSpan)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty (HieAST TypeIndex) -> HieAST TypeIndex
forall (f :: * -> *) a. IsNonEmpty f a a "head" => f a -> a
head) [HieAST TypeIndex]
nodeChildren)
            -- add each operator decl from a list (should be singleton list)
            (((Text, RealSrcSpan) -> State VisitorState ())
-> [(Text, RealSrcSpan)] -> State VisitorState ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ ((Text -> RealSrcSpan -> State VisitorState ())
-> (Text, RealSrcSpan) -> State VisitorState ()
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Text -> RealSrcSpan -> State VisitorState ()
addOpDecl))
        )

    -- return AST node identifier names as a sized list of texts
    nodeIds :: HieAST TypeIndex -> [Text]
    nodeIds :: HieAST TypeIndex -> [Text]
nodeIds =
        (Identifier -> [Text]) -> [Identifier] -> [Text]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Identifier -> [Text]
fixityName
        ([Identifier] -> [Text])
-> (HieAST TypeIndex -> [Identifier]) -> HieAST TypeIndex -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Identifier (IdentifierDetails TypeIndex) -> [Identifier]
forall k a. Map k a -> [k]
Map.keys
        (Map Identifier (IdentifierDetails TypeIndex) -> [Identifier])
-> (HieAST TypeIndex
    -> Map Identifier (IdentifierDetails TypeIndex))
-> HieAST TypeIndex
-> [Identifier]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NodeInfo TypeIndex -> Map Identifier (IdentifierDetails TypeIndex)
forall a. NodeInfo a -> NodeIdentifiers a
nodeIdentifiers
        (NodeInfo TypeIndex
 -> Map Identifier (IdentifierDetails TypeIndex))
-> (HieAST TypeIndex -> NodeInfo TypeIndex)
-> HieAST TypeIndex
-> Map Identifier (IdentifierDetails TypeIndex)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HieAST TypeIndex -> NodeInfo TypeIndex
forall a. Ord a => HieAST a -> NodeInfo a
nodeInfo

    fixityName :: Identifier -> [Text]
    fixityName :: Identifier -> [Text]
fixityName = \case
        Left ModuleName
_ -> []
        Right Name
name -> [String -> Text
forall a. ToText a => a -> Text
toText (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ OccName -> String
occNameString (OccName -> String) -> OccName -> String
forall a b. (a -> b) -> a -> b
$ Name -> OccName
nameOccName Name
name]

    extractOperatorName :: HieAST TypeIndex -> [(Text, RealSrcSpan)]
    extractOperatorName :: HieAST TypeIndex -> [(Text, RealSrcSpan)]
extractOperatorName n :: HieAST TypeIndex
n@Node{[HieAST TypeIndex]
RealSrcSpan
SourcedNodeInfo TypeIndex
sourcedNodeInfo :: forall a. HieAST a -> SourcedNodeInfo a
nodeSpan :: forall a. HieAST a -> RealSrcSpan
nodeChildren :: forall a. HieAST a -> [HieAST a]
sourcedNodeInfo :: SourcedNodeInfo TypeIndex
nodeSpan :: RealSrcSpan
nodeChildren :: [HieAST TypeIndex]
..} =
        (Identifier -> [(Text, RealSrcSpan)])
-> [Identifier] -> [(Text, RealSrcSpan)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (RealSrcSpan -> Identifier -> [(Text, RealSrcSpan)]
topLevelOperatorName RealSrcSpan
nodeSpan)
        ([Identifier] -> [(Text, RealSrcSpan)])
-> [Identifier] -> [(Text, RealSrcSpan)]
forall a b. (a -> b) -> a -> b
$ Map Identifier (IdentifierDetails TypeIndex) -> [Identifier]
forall k a. Map k a -> [k]
Map.keys
        (Map Identifier (IdentifierDetails TypeIndex) -> [Identifier])
-> Map Identifier (IdentifierDetails TypeIndex) -> [Identifier]
forall a b. (a -> b) -> a -> b
$ NodeInfo TypeIndex -> Map Identifier (IdentifierDetails TypeIndex)
forall a. NodeInfo a -> NodeIdentifiers a
nodeIdentifiers (HieAST TypeIndex -> NodeInfo TypeIndex
forall a. Ord a => HieAST a -> NodeInfo a
Stan.Hie.Compat.nodeInfo HieAST TypeIndex
n)

    topLevelOperatorName :: RealSrcSpan -> Identifier -> [(Text, RealSrcSpan)]
    topLevelOperatorName :: RealSrcSpan -> Identifier -> [(Text, RealSrcSpan)]
topLevelOperatorName RealSrcSpan
srcSpan = \case
        Left ModuleName
_ -> []
        Right Name
name ->
            let occName :: OccName
occName = Name -> OccName
nameOccName Name
name
            -- return empty list if identifier name is not operator name
            in [(String -> Text
forall a. ToText a => a -> Text
toText (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ OccName -> String
occNameString OccName
occName, RealSrcSpan
srcSpan) | OccName -> Bool
isSymOcc OccName
occName]

-- | Returns source spans of matched AST nodes.
createMatch
    :: PatternAst
    -> HieFile
    -> HieAST TypeIndex
    -> Slist RealSrcSpan
createMatch :: PatternAst -> HieFile -> HieAST TypeIndex -> Slist RealSrcSpan
createMatch PatternAst
patAst HieFile
hie HieAST TypeIndex
node =
    Bool -> Slist RealSrcSpan -> Slist RealSrcSpan
forall m. Monoid m => Bool -> m -> m
memptyIfFalse (HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node PatternAst
patAst) (RealSrcSpan -> Slist RealSrcSpan
forall a. a -> Slist a
S.one (RealSrcSpan -> Slist RealSrcSpan)
-> RealSrcSpan -> Slist RealSrcSpan
forall a b. (a -> b) -> a -> b
$ HieAST TypeIndex -> RealSrcSpan
forall a. HieAST a -> RealSrcSpan
nodeSpan HieAST TypeIndex
node)

{- | Specialized version of 'matchAstWith' where custom predicate
always returns 'True'.
-}
matchAst
    :: Id Inspection
    -> PatternAst
    -> HieFile
    -> HieAST TypeIndex  -- ^ Current node
    -> State VisitorState ()
matchAst :: Id Inspection
-> PatternAst
-> HieFile
-> HieAST TypeIndex
-> State VisitorState ()
matchAst = (HieAST TypeIndex -> Bool)
-> Id Inspection
-> PatternAst
-> HieFile
-> HieAST TypeIndex
-> State VisitorState ()
matchAstWith (Bool -> HieAST TypeIndex -> Bool
forall a b. a -> b -> a
const Bool
True)

{- | Add observation to the state if the given node matches the given
'PatternAst' exactly (non-recursively) and if the given custom
predicate returns 'True'..
-}
matchAstWith
    :: (HieAST TypeIndex -> Bool)  -- ^ Custom node check
    -> Id Inspection
    -> PatternAst
    -> HieFile
    -> HieAST TypeIndex  -- ^ Current node
    -> State VisitorState ()
matchAstWith :: (HieAST TypeIndex -> Bool)
-> Id Inspection
-> PatternAst
-> HieFile
-> HieAST TypeIndex
-> State VisitorState ()
matchAstWith HieAST TypeIndex -> Bool
check Id Inspection
insId PatternAst
patAst HieFile
hie node :: HieAST TypeIndex
node@Node{[HieAST TypeIndex]
RealSrcSpan
SourcedNodeInfo TypeIndex
sourcedNodeInfo :: forall a. HieAST a -> SourcedNodeInfo a
nodeSpan :: forall a. HieAST a -> RealSrcSpan
nodeChildren :: forall a. HieAST a -> [HieAST a]
sourcedNodeInfo :: SourcedNodeInfo TypeIndex
nodeSpan :: RealSrcSpan
nodeChildren :: [HieAST TypeIndex]
..} =
    Bool -> State VisitorState () -> State VisitorState ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (HieFile -> HieAST TypeIndex -> PatternAst -> Bool
hieMatchPatternAst HieFile
hie HieAST TypeIndex
node PatternAst
patAst Bool -> Bool -> Bool
&& HieAST TypeIndex -> Bool
check HieAST TypeIndex
node) (State VisitorState () -> State VisitorState ())
-> State VisitorState () -> State VisitorState ()
forall a b. (a -> b) -> a -> b
$
        Observation -> State VisitorState ()
addObservation (Observation -> State VisitorState ())
-> Observation -> State VisitorState ()
forall a b. (a -> b) -> a -> b
$ Id Inspection -> HieFile -> RealSrcSpan -> Observation
mkObservation Id Inspection
insId HieFile
hie RealSrcSpan
nodeSpan