argon 0.3.0.0 → 0.3.1.0
raw patch · 12 files changed
+172/−142 lines, 12 filesdep +ghc-syb-utilsdep ~filepathdep ~ghcdep ~pathwalkPVP ok
version bump matches the API change (PVP)
Dependencies added: ghc-syb-utils
Dependency ranges changed: filepath, ghc, pathwalk, syb
API changes (from Hackage documentation)
+ Argon: type LModule = Located (HsModule RdrName)
Files
- CHANGELOG.md +4/−0
- README.md +86/−81
- USAGE.txt +1/−0
- app/Main.hs +1/−0
- argon.cabal +15/−10
- src/Argon.hs +2/−1
- src/Argon/Loc.hs +2/−2
- src/Argon/Parser.hs +3/−4
- src/Argon/Preprocess.hs +4/−0
- src/Argon/Types.hs +4/−0
- src/Argon/Visitor.hs +5/−6
- test/ArgonSpec.hs +45/−38
CHANGELOG.md view
@@ -2,6 +2,10 @@ This package uses [Semantic Versioning][1]. +## v0.3.1.0++- Add compatibility with GHC 7.8: #6+ ## v0.3.0.0 - Major: replace Haskell-Src-Exts with GHC API: #5
README.md view
@@ -33,43 +33,50 @@ ### Installing Simple as ``stack install argon`` or ``cabal install argon``.+Note: if you are using Stack and your resolver if too old, you might have to+add some packages to your `stack.yaml` file. +#### GHC compatibility++Argon is compatible with GHC 7.8 and 7.10, it's tested against those versions+only.+ ### Running -The Argon executable expects a list of file paths:+The Argon executable expects a list of file paths (files or directories): - $ argon --no-color src/**/*.hs- src/Argon/Formatters.hs- 47:1 formatResult - 3- 33:1 coloredFunc - 2- 27:1 fore - 1- 37:1 coloredRank - 1+ $ argon --no-color --min 2 src+ src/Argon/Types.hs+ 61:5 toJSON - 2+ src/Argon/Visitor.hs+ 55:1 visitExp - 5+ 62:1 visitOp - 4+ 28:11 visit - 2+ 35:1 getFuncName - 2 src/Argon/Parser.hs- 51:1 parseCode - 2- 44:1 handleExc - 1+ 55:1 parseModuleWithCpp - 3+ 88:1 customLogAction - 3+ 35:1 analyze - 2+ 39:9 analysis - 2+ src/Argon/Formatters.hs+ 61:1 formatResult - 3+ 42:1 coloredFunc - 2+ 43:11 color - 2 src/Argon/Results.hs- 17:1 filterResults - 2- 22:1 export - 2- src/Argon/Visitor.hs- 35:1 visitExp - 5- 42:1 visitOp - 4- 14:1 funCC - 3- 16:11 name - 2- 11:1 funcsCC - 1- 20:1 sumWith - 1- 23:1 complexity - 1- 28:11 descend - 1- 32:11 inspect - 1+ 35:1 export - 3+ 28:1 filterResults - 2+ src/Argon/Loc.hs+ 18:11 toRealSrcLoc - 2 For every file, Argon sorts results with the following criteria (and in this order): - * complexity (descending)- * line number (ascending)- * alphabetically+1. complexity (descending)+2. line number (ascending)+3. alphabetically When colors are enabled (default), Argon computes a rank associated with the-coomplexity score:+complexity score: | Complexity | Rank | |:----------:|:----:|@@ -81,59 +88,57 @@ #### JSON Results can also be exported to JSON:-- $ argon --json src/**/*.hs- [- {- "blocks": [- { "complexity": 3, "name": "formatResult", "lineno": 58, "col": 1 },- { "complexity": 2, "name": "coloredFunc", "lineno": 39, "col": 1 },- { "complexity": 1, "name": "fore", "lineno": 33, "col": 1 },- { "complexity": 1, "name": "coloredRank", "lineno": 43, "col": 1 },- { "complexity": 1, "name": "formatSingle", "lineno": 52, "col": 1 }- ],- "path": "src/Argon/Formatters.hs",- "type": "result"- },- { "blocks": [ ], "path": "src/Argon.hs", "type": "result" },- {- "blocks": [- { "complexity": 2, "name": "parseCode", "lineno": 55, "col": 1 },- { "complexity": 1, "name": "handleExc", "lineno": 48, "col": 1 }- ],- "path": "src/Argon/Parser.hs",- "type": "result"- },- {- "blocks": [- { "complexity": 3, "name": "export", "lineno": 35, "col": 1 },- { "complexity": 2, "name": "filterResults", "lineno": 28, "col": 1 },- { "complexity": 1, "name": "sortOn", "lineno": 12, "col": 1 }- ],- "path": "src/Argon/Results.hs",- "type": "result"- },- {- "blocks": [- { "complexity": 2, "name": "toJSON", "lineno": 30, "col": 5 },- { "complexity": 1, "name": "toJSON", "lineno": 18, "col": 5 }- ],- "path": "src/Argon/Types.hs",- "type": "result"- },- {- "blocks": [- { "complexity": 5, "name": "visitExp", "lineno": 36, "col": 1 },- { "complexity": 4, "name": "visitOp", "lineno": 43, "col": 1 },- { "complexity": 3, "name": "funCC", "lineno": 15, "col": 1 },- { "complexity": 2, "name": "name", "lineno": 17, "col": 11 },- { "complexity": 1, "name": "funcsCC", "lineno": 12, "col": 1 },- { "complexity": 1, "name": "sumWith", "lineno": 21, "col": 1 },- { "complexity": 1, "name": "complexity", "lineno": 24, "col": 1 },- { "complexity": 1, "name": "descend", "lineno": 29, "col": 11 },- { "complexity": 1, "name": "inspect", "lineno": 33, "col": 11 }- ],- "path": "src/Argon/Visitor.hs",- "type": "result"- }- ]+```json+$ argon --json --min 2 src+[+ { "blocks": [ ], "path": "src/Argon.hs", "type": "result" },+ {+ "blocks": [{ "complexity": 2, "name": "toJSON", "lineno": 61, "col": 5 }],+ "path": "src/Argon/Types.hs",+ "type": "result"+ },+ {+ "blocks": [+ { "complexity": 5, "name": "visitExp", "lineno": 55, "col": 1 },+ { "complexity": 4, "name": "visitOp", "lineno": 62, "col": 1 },+ { "complexity": 2, "name": "visit", "lineno": 28, "col": 11 },+ { "complexity": 2, "name": "getFuncName", "lineno": 35, "col": 1 }+ ],+ "path": "src/Argon/Visitor.hs",+ "type": "result"+ },+ {+ "blocks": [+ { "complexity": 3, "name": "parseModuleWithCpp", "lineno": 55, "col": 1 },+ { "complexity": 3, "name": "customLogAction", "lineno": 88, "col": 1 },+ { "complexity": 2, "name": "analyze", "lineno": 35, "col": 1 },+ { "complexity": 2, "name": "analysis", "lineno": 39, "col": 9 }+ ],+ "path": "src/Argon/Parser.hs",+ "type": "result"+ },+ {+ "blocks": [+ { "complexity": 3, "name": "formatResult", "lineno": 61, "col": 1 },+ { "complexity": 2, "name": "coloredFunc", "lineno": 42, "col": 1 },+ { "complexity": 2, "name": "color", "lineno": 43, "col": 11 }+ ],+ "path": "src/Argon/Formatters.hs",+ "type": "result"+ },+ {+ "blocks": [+ { "complexity": 3, "name": "export", "lineno": 35, "col": 1 },+ { "complexity": 2, "name": "filterResults", "lineno": 28, "col": 1 }+ ],+ "path": "src/Argon/Results.hs",+ "type": "result"+ },+ {+ "blocks": [{ "complexity": 2, "name": "toRealSrcLoc", "lineno": 18, "col": 11 }],+ "path": "src/Argon/Loc.hs",+ "type": "result"+ },+ { "blocks": [ ], "path": "src/Argon/Preprocess.hs", "type": "result" }+]+```
USAGE.txt view
@@ -1,5 +1,6 @@ Usage: argon [-h] [--no-color] [--json] [-m=<min>] <paths>...+ argon [-h] [--no-color] [--m=<min>] [--json] <paths>... Options: -h --help show this help
app/Main.hs view
@@ -7,6 +7,7 @@ import qualified Data.Sequence as S import Data.Foldable (toList) #if __GLASGOW_HASKELL__ < 710+import Data.Monoid (mappend) import Control.Applicative ((<$>)) #endif import System.Environment (getArgs)
argon.cabal view
@@ -1,5 +1,5 @@ name: argon-version: 0.3.0.0+version: 0.3.1.0 synopsis: Measure your code's complexity homepage: http://github.com/rubik/argon bug-reports: http://github.com/rubik/argon/issues@@ -23,6 +23,7 @@ README.md CHANGELOG.md USAGE.txt+tested-with: GHC >= 7.8 library hs-source-dirs: src@@ -38,12 +39,13 @@ , ansi-terminal >=0.6 , aeson >=0.8 , bytestring >=0.10- , ghc >=7.10.2 && <8+ , ghc >=7.8 && <8 , ghc-paths >=0.1- , syb >=0.5+ , ghc-syb-utils >=0.2+ , syb >=0.4 default-language: Haskell2010 ghc-options: -Wall- if impl(ghc < 7.10.2)+ if impl(ghc < 7.8) buildable: False executable argon@@ -53,12 +55,14 @@ build-depends: base >=4.7 && <5 , docopt >=0.7 , pathwalk >=0.3- , filepath >=1.4+ , filepath >=1.3 , directory >=1.2 , containers >=0.5 , argon -any+ if impl(ghc < 7.10)+ build-depends: pathwalk >=0.3.1.2 default-language: Haskell2010- if impl(ghc < 7.10.2)+ if impl(ghc < 7.8) buildable: False test-suite argon-test@@ -69,15 +73,16 @@ , ansi-terminal >=0.6 , aeson >=0.8 , bytestring >=0.10- , ghc >=7.10.2 && <8+ , ghc >=7.8 && <8 , ghc-paths >=0.1- , syb >=0.5+ , ghc-syb-utils >=0.2+ , syb >=0.4 , hspec >=2.1 , QuickCheck -any- , filepath >=1.4+ , filepath >=1.3 , argon -any ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N- if impl(ghc < 7.10.2)+ if impl(ghc < 7.8) buildable: False default-language: Haskell2010 other-modules: Argon
src/Argon.hs view
@@ -15,6 +15,7 @@ , OutputMode(..) , Config(..) , Loc+ , LModule -- * Parsing , analyze , parseModule@@ -24,7 +25,7 @@ , export ) where -import Argon.Parser (analyze, parseModule)+import Argon.Parser (LModule, analyze, parseModule) import Argon.Results (order, export, filterResults) import Argon.Types import Argon.Loc
src/Argon/Loc.hs view
@@ -7,8 +7,8 @@ import qualified SrcLoc as GHC import qualified FastString as GHC --- | Type synonym representing a portion of the source code. The tuple--- represents the following: @(start line, start col, end line, end col)@.+-- | Type synonym representing a location in the source code. The tuple+-- represents the following: @(start line, start col)@. type Loc = (Int, Int)
src/Argon/Parser.hs view
@@ -1,12 +1,9 @@ {-# LANGUAGE CPP #-}-module Argon.Parser (analyze, parseModule)+module Argon.Parser (LModule, analyze, parseModule) where import Control.Monad (void) import qualified Control.Exception as E-#if __GLASGOW_HASKELL__ < 710-import Control.Applicative ((<$>))-#endif import qualified GHC hiding (parseModule) import qualified SrcLoc as GHC@@ -25,6 +22,8 @@ import Argon.Types import Argon.Loc +-- | Type synonym for a syntax node representing a module tagged with a+-- 'SrcSpan' type LModule = GHC.Located (GHC.HsModule GHC.RdrName)
src/Argon/Preprocess.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards #-} -- | This module provides support for CPP and interpreter directives. module Argon.Preprocess@@ -7,6 +8,9 @@ , getPreprocessedSrcDirect ) where +#if __GLASGOW_HASKELL__ < 710+import Control.Applicative ((<$>))+#endif import qualified GHC import qualified DynFlags as GHC import qualified MonadUtils as GHC
src/Argon/Types.hs view
@@ -1,6 +1,10 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}+#if __GLASGOW_HASKELL__ < 710+{-# LANGUAGE DeriveDataTypeable #-}+#endif module Argon.Types (ComplexityBlock(CC), AnalysisResult, Config(..) , OutputMode(..), GhcParseError(..))
src/Argon/Visitor.hs view
@@ -1,7 +1,8 @@ module Argon.Visitor (funcsCC) where -import Data.Generics (Data, Typeable, everything, mkQ)+import Data.Generics (Data, Typeable, mkQ)+import GHC.SYB.Utils (Stage(..), everythingStaged) import Control.Arrow ((&&&)) import qualified GHC@@ -24,7 +25,7 @@ funCC f = CC (getLocation $ GHC.fun_id f, getFuncName f, complexity f) getBinds :: (Data from, Typeable from) => from -> [Function]-getBinds = everything (++) $ mkQ [] visit+getBinds = everythingStaged Parser (++) [] $ mkQ [] visit where visit fun@(GHC.FunBind {}) = [fun] visit _ = [] @@ -32,13 +33,11 @@ getLocation = srcSpanToLoc . GHC.getLoc getFuncName :: Function -> String-getFuncName f = case GHC.m_fun_id_infix . GHC.unLoc . head $ getMatches f of- Just name -> getName . GHC.unLoc $ fst name- Nothing -> "<unknown>"+getFuncName = getName . GHC.unLoc . GHC.fun_id complexity :: Function -> Int complexity f = let matches = getMatches f- query = everything (+) $ 0 `mkQ` visit+ query = everythingStaged Parser (+) 0 $ 0 `mkQ` visit visit = uncurry (+) . (visitExp &&& visitOp) in length matches + sumWith query matches
test/ArgonSpec.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE CPP #-} {-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE DeriveAnyClass #-}+ module ArgonSpec (spec) where @@ -25,7 +25,7 @@ <*> arbitrary shrink (CC t) = map CC $ shrink t -deriving instance Arbitrary OutputMode+instance Arbitrary OutputMode where ones :: Loc@@ -47,42 +47,49 @@ spec :: Spec spec = do- describe "loc module" $ do- it "can convert a real src span to loc" $- property $ \a b -> srcSpanToLoc (realSpan a b) == (a, b)- it "can convert a bad src span to loc" $- srcSpanToLoc GHC.noSrcSpan `shouldBe` (0, 0)- it "can convert a loc to string" $- locToString (1, 30) `shouldBe` "1:30"- it "can tag messages" $- tagMsg (2, 3) "my custom msg" `shouldBe` "2:3 my custom msg"- describe "order" $ do- it "does not error on empty list" $- order [] `shouldBe` []- it "orders by complexity (descending)" $- order [CC (ones, "f", 1), CC (lo 2, "f", 2)] `shouldBe`- [CC (lo 2, "f", 2), CC (ones, "f", 1)]- it "orders by lines (ascending)" $- order [CC (lo 11, "f", 3), CC (ones, "f", 3)] `shouldBe`- [CC (ones, "f", 3), CC (lo 11, "f", 3)]- it "orders by function name (ascending)" $- order [CC (lo 11, "g", 3), CC (lo 11, "f", 3)] `shouldBe`- [CC (lo 11, "f", 3), CC (lo 11, "g", 3)]- it "does not add or remove elements" $- property $ \xs -> sort xs == sort (order xs)- it "is idempotent" $- property $ \xs -> order xs == order (order xs)- describe "filterResults" $ do- it "discards results with too low complexity" $- filterResults (Config { minCC = 3, outputMode = BareText })- ("p", Right [ CC (ones, "f", 3), CC (lo 2, "g", 2)- , CC (lo 4, "h", 10), CC (lo 3, "l", 1)])- `shouldBe`- ("p", Right [CC (lo 4, "h", 10), CC (ones, "f", 3)])- it "does nothing on Left" $- property $ \m o p err -> filterResults (Config m o)- (p, Left err) ==- (p, Left err)+ describe "Argon.Loc" $ do+ describe "srcSpanToLoc" $ do+ it "can convert a real src span to loc" $+ property $ \a b -> srcSpanToLoc (realSpan a b) == (a, b)+ it "can convert a bad src span to loc" $+ srcSpanToLoc GHC.noSrcSpan `shouldBe` (0, 0)+ describe "locToString" $+ it "can convert a loc to string" $+ locToString (1, 30) `shouldBe` "1:30"+ describe "tagMsg" $+ it "can tag messages" $+ tagMsg (2, 3) "my custom msg" `shouldBe` "2:3 my custom msg"+ describe "Argon.Results" $ do+ describe "order" $ do+ it "does not error on empty list" $+ order [] `shouldBe` []+ it "orders by complexity (descending)" $+ order [CC (ones, "f", 1), CC (lo 2, "f", 2)] `shouldBe`+ [CC (lo 2, "f", 2), CC (ones, "f", 1)]+ it "orders by lines (ascending)" $+ order [CC (lo 11, "f", 3), CC (ones, "f", 3)] `shouldBe`+ [CC (ones, "f", 3), CC (lo 11, "f", 3)]+ it "orders by function name (ascending)" $+ order [CC (lo 11, "g", 3), CC (lo 11, "f", 3)] `shouldBe`+ [CC (lo 11, "f", 3), CC (lo 11, "g", 3)]+ it "does not add or remove elements" $+ property $ \xs -> sort xs == sort (order xs)+ it "is idempotent" $+ property $ \xs -> order xs == order (order xs)+ describe "filterResults" $ do+ it "discards results with too low complexity" $+ filterResults (Config { minCC = 3, outputMode = BareText })+ ("p", Right [ CC (ones, "f", 3)+ , CC (lo 2, "g", 2)+ , CC (lo 4, "h", 10)+ , CC (lo 3, "l", 1)])+ `shouldBe`+ ("p", Right [ CC (lo 4, "h", 10)+ , CC (ones, "f", 3)])+ it "does nothing on Left" $+ property $ \m o p err -> filterResults (Config m o)+ (p, Left err) ==+ (p, Left err) describe "analyze" $ do it "accounts for case" $ "case.hs" `shouldAnalyze` Right [CC (ones, "func", 3)]