packages feed

SourceGraph-0.6.0.0: Analyse/Module.hs

{-
Copyright (C) 2009 Ivan Lazar Miljenovic <Ivan.Miljenovic@gmail.com>

This file is part of SourceGraph.

SourceGraph 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.

This program 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

{- |
   Module      : Analyse.Module
   Description : Analyse modules.
   Copyright   : (c) Ivan Lazar Miljenovic 2009
   License     : GPL-3 or later.
   Maintainer  : Ivan.Miljenovic@gmail.com

   Analysis of Haskell modules.
 -}
module Analyse.Module(analyseModules) where

import Parsing.Types
import Analyse.Utils
import Analyse.GraphRepr
import Analyse.Visualise

import Data.Graph.Analysis

import Data.Maybe(mapMaybe)
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.MultiSet as MS
import Text.Printf(printf)

-- -----------------------------------------------------------------------------

-- Helper types

-- | Shorthand type
type ModuleData = (String, ModName, HData')

-- -----------------------------------------------------------------------------

-- Analysing.

-- | Performs analysis of all modules present in the 'ParsedModules' provided.
analyseModules :: ParsedModules -> DocElement
analyseModules = Section (Text "Analysis of each module")
                 . mapMaybe analyseModule . M.elems

-- | Performs analysis of the given 'ParsedModule'.
analyseModule    :: ParsedModule -> Maybe DocElement
analyseModule hm = if n > 1
                   then Just $ Section sec elems
                   else Nothing
    where
      (n,fd@(m,_,_)) = moduleToGraph hm
      elems = mapMaybe ($fd) [ graphOf
                             -- , collapseAnal
                             , coreAnal
                             , levelAnal
                             , cycleCompAnal
                             , rootAnal
                             , componentAnal
                             , cliqueAnal
                             , cycleAnal
                             , chainAnal
                             ]
      sec = Grouping [ Text "Analysis of"
                     , Emphasis (Text m)]

-- | Convert the module to the /Graphalyze/ format.
moduleToGraph    :: ParsedModule -> (Int,ModuleData)
moduleToGraph hm = (n,(nameOfModule mn, mn, mkHData' vs fd))
    where
      mn = moduleName hm
      n = applyAlg noNodes fd
      fd = importData params
      vs = virtualEnts hm
      params = Params { dataPoints    = S.toList $ internalEnts hm
                      , relationships = MS.toList . MS.map callToRel
                                        $ funcCalls hm
                      , roots         = S.toList $ exports hm
                      , directed      = True
                      }

graphOf          :: ModuleData -> Maybe DocElement
graphOf (n,m,fd) = Just $ Section sec [gi]
    where
      sec = Grouping [ Text "Visualisation of"
                     , Emphasis $ Text n]
      gi = GraphImage $ DG n (Text lbl) (drawGraph lbl (Just m) fd)
      lbl = unwords ["Diagram of:", n]

componentAnal :: ModuleData -> Maybe DocElement
componentAnal (n,_,fd)
    | single comp = Nothing
    | otherwise   = Just el
    where
      -- Use compactData as the number of edges don't matter, just
      -- whether or not an edge exists.
      comp = applyAlg componentsOf . compactData $ collapsedHData fd
      len = length comp
      el = Section sec [Paragraph [Text text]]
      sec = Grouping [ Text "Component analysis of"
                     , Emphasis (Text n)]
      text = printf "The module %s has %d components.  \
                     \You may wish to consider splitting it up." n len

cliqueAnal :: ModuleData -> Maybe DocElement
cliqueAnal (n,_,fd)
    | null clqs = Nothing
    | otherwise = Just el
    where
      clqs = applyAlg cliquesIn . onlyNormalCalls' . compactData
             $ collapsedHData fd
      clqs' = return . Itemized
              $ map (Paragraph . return . Text . showNodes' (name . snd)) clqs
      text = Text $ printf "The module %s has the following cliques:" n
      el = Section sec $ Paragraph [text] : clqs'
      sec = Grouping [ Text "Clique analysis of"
                     , Emphasis (Text n)]

cycleAnal :: ModuleData -> Maybe DocElement
cycleAnal (n,_,fd)
    | null cycs = Nothing
    | otherwise = Just el
    where
      cycs = applyAlg uniqueCycles . onlyNormalCalls' . compactData
             $ collapsedHData fd
      cycs' = return . Itemized
              $ map (Paragraph . return . Text . showCycle' (name . snd)) cycs
      text = Text $ printf "The module %s has the following non-clique \
                            \cycles:" n
      el = Section sec $ Paragraph [text] : cycs'
      sec = Grouping [ Text "Cycle analysis of"
                     , Emphasis (Text n)]

chainAnal :: ModuleData -> Maybe DocElement
chainAnal (n,_,fd)
    | null chns = Nothing
    | otherwise = Just el
    where
      chns = interiorChains . onlyNormalCalls' . compactData
             $ collapsedHData fd
      chns' = return . Itemized
              $ map (Paragraph . return . Text . showPath' (name . snd)) chns
      text = Text $ printf "The module %s has the following chains:" n
      textAfter = Text "These chains can all be compressed down to \
                       \a single function."
      el = Section sec
           $ [Paragraph [text]] ++ chns' ++ [Paragraph [textAfter]]
      sec = Grouping [ Text "Chain analysis of"
                     , Emphasis (Text n)]

rootAnal :: ModuleData -> Maybe DocElement
rootAnal (n,_,fd)
    | asExpected = Nothing
    | otherwise  = Just $ Section sec inaccessible
    where
      fd' = compactData $ origHData fd
      ntWd = S.toList . inaccessibleNodes $ addImplicit (origVirts fd) fd'
      ntWd' = applyAlg getLabels fd' ntWd
      asExpected = null ntWd
      inaccessible = [ Paragraph [Text "These functions are those that are inaccessible:"]
                    , Paragraph [Emphasis . Text $ showNodes' name ntWd']
                    ]
      sec = Grouping [ Text "Root analysis of"
                     , Emphasis (Text n)]

coreAnal          :: ModuleData -> Maybe DocElement
coreAnal (n,m,fd) = fmap mkDE $ makeCore fd
    where
      mkDE fd' = Section sec [ hdr
                             , GraphImage $ DG p
                                               (Text lbl)
                                               (drawGraph lbl (Just m) fd')
                             ]
      p = n ++ "_core"
      lbl = unwords ["Core of", n]
      hdr = coreDesc "a module"
      sec = Grouping [ Text "Core analysis of"
                     , Emphasis (Text n)]

levelAnal          :: ModuleData -> Maybe DocElement
levelAnal (n,m,fd) = Just $ Section sec [hdr, lvls]
  where
    lvls = GraphImage $ DG p (Text lbl) (drawLevels lbl (Just m) fd)
    p = n ++ "_levels"
    lbl = unwords ["Levels within", n]
    sec = Grouping [ Text "Visualisation of levels in"
                   , Emphasis (Text n)
                   ]
    hdr = Paragraph [Text "Visualises how far away from the exported root\
                           \ entities an entity is."]

-- Comment out until can work out a way of dealing with [Entity] for
-- the node-label type.
{-
collapseAnal          :: ModuleData -> Maybe DocElement
collapseAnal (n,m,fd) = if (trivialCollapse gc)
                        then Nothing
                        else Just el
    where
      fd' = updateGraph collapseGraph fd
      gc = graph fd'
      p = n ++ "_collapsed"
      lbl = unwords ["Collapsed view of", n]
      hdr = Paragraph [Text "The collapsed view of a module collapses \
                            \down all cliques, cycles, chains, etc. to \
                            \make the graph tree-like." ]
      gr = GraphImage (p,Text lbl,drawGraph lbl (Just m) fd')
      el = Section sec [hdr, gr]
      sec = Grouping [ Text "Collapsed view of"
                     , Emphasis (Text n)]
-}

cycleCompAnal          :: ModuleData -> Maybe DocElement
cycleCompAnal (n,_,fd) = Just $ Section sec pars
    where
      cc = cyclomaticComplexity . graphData $ collapsedHData fd
      sec = Grouping [ Text "Cyclomatic Complexity of"
                       , Emphasis (Text n)]
      pars = [Paragraph [text], Paragraph [textAfter, link]]
      text = Text
             $ printf "The cyclomatic complexity of %s is: %d." n cc
      textAfter = Text "For more information on cyclomatic complexity, \
                       \please see: "
      link = DocLink (Text "Wikipedia: Cyclomatic Complexity")
                     (Web "http://en.wikipedia.org/wiki/Cyclomatic_complexity")