Giter Club home page Giter Club logo

stan's Introduction

Stan

Stan Logo

GitHub CI Hackage MPL-2.0 license

Stan is a Haskell STatic ANalysis tool.

โš ๏ธ Note: Stan is in the beta phase. The API is the subject to be changed if required by our needs โš ๏ธ

Table of Contents

What this tool is about

[Back to the Table of Contents] โ†‘

Stan is a command-line tool for analysing Haskell projects. It discovers which parts of the code can potentially be improved, and offers suggestions on how to do so. Stan is searching for not only performance or error-prone code pieces, but it also can help with establishing and applying best-practices from the whole Haskell ecosystem.

Although Haskell is a statically typed language, not all properties can be encoded in types. Even though GHC is quite a powerful compiler, it tries to be library-agnostic and provide only language-specific suggestions, while Stan uses the knowledge about the current state of the ecosystem and commonly used libraries.

You will find Stan helpful if you enjoy writing in Haskell, but want more guarantees from your code, not provided by the Haskell type system or GHC.

For a crash course to Stan, watch the talk about Stan, presented by Veronika Romashkina and Dmitrii Kovanikov at the Haskell Love conference.

Stan โ€“ Haskell Static Analyser

Goals

[Back to the Table of Contents] โ†‘

Stan design and implementation is driven by the following goals:

  • Catch common errors, anti-patterns, performance issues
  • Provide meaningful insights on the projects generally
  • Point out potential bugs and weak points in the programs flow for users, so they can carefully evaluate each problem with the code
  • Help beginners to learn best practices in an easy and informative way
  • Generate the report that can be used as a proof of code quality
  • Create best in the class and flexible enough interface for usage (including e.g. opt-in and opt-out inspections)

Features

[Back to the Table of Contents] โ†‘

Stan is a configurable CLI tool. Besides the main feature of analysing Haskell projects statically, Stan has a list of features that make it unique, easy to use and flexible to configure:

  • Pretty analysis results, including both HTML and terminal reports
  • Suggestions and possible solutions for fixing the existing problems
  • Analysing not only Haskell source code, but also information from the .cabal files
  • Flexible runtime configuration via TOML and CLI

You can see an example of Stan HTML report hosted online here:

The below example of the terminal output gives you the understanding of what sorts of analysis you can expect from Stan:

Stan terminal example

How it works

[Back to the Table of Contents] โ†‘

Stan analysis is based on the HIE files โ€” compile-time information about Haskell source code gathered and recorded by GHC. The HIE files contain the Haskell AST, detailed information about each identifier and types of all expressions and sub-expressions. GHC does a huge amount of work when compiling the Haskell projects, and Stan takes advantage of this feature to avoid duplicating the work and focus more on the unique features.

To analyse HIE files easily, we developed an eDSL for defining AST and Type patterns based on the final tagless approach. Stan algorithm traverses HIE AST for each HIE file in the project, and matches every AST node with the given pattern to find potential improvement areas in the code.

Each Stan analysis check is represented by the inspection with the unique ID. Each inspection has a name, description, severity, list of categories, pattern for matching relevant parts of source code and possible solutions to the problem.

When an inspection is casted on the project, it produces zero or more observations โ€”. You can think of an observation as a pair of an inspection and a piece of source code where this inspection was triggered. Each observation is assigned an unique stable ID depending on the source location, so you can refer to them later or ignore.

You can disable inspections or enable them only in particular modules using check โ€” rules for controlling which inspections to run and where. Each check has a type (include or exclude), filter (by inspection id, category, severity, etc.) and scope (file, directory, everything). Checks can be specified using either TOML of CLI interfaces. By default, Stan analyses all source files using all implemented inspections.

If you want to understand Stan terminology better, refer to the glossary:

Installation instructions

[Back to the Table of Contents] โ†‘

Stan takes advantage of the GHC API to provide its analysis. Because of this, Stan and the analysed project need to be built with the same GHC version (for more details see #178). That is why the easiest and most robust way to install Stan is to build it from sources on your machine.

Note: Stan is compatible with the GHC versions โฉพ 8.8

Using Cabal

[Back to the Table of Contents] โ†‘

Below are the steps to install Stan using the Cabal build tool.

You need to have Cabal โฉพ 2.4

First, you need to clone the repository:

$ git clone https://github.com/kowainik/stan.git
$ cd stan

Then, you need to build it using Cabal:

$ cabal v2-build exe:stan

Finally, you can copy the resulting executable under the desired location (that should be under the PATH environment variable), like so:

$ cp "$(cabal v2-exec --verbose=0 --offline sh -- -c 'command -v stan')" ~/.local/bin/stan

Using Stack

[Back to the Table of Contents] โ†‘

Below are the steps to install Stan from its repository using the Stack tool.

You need to have Stack โฉพ 2.1.3

First, you need to clone the repository and change to the stan directory:

$ git clone https://github.com/kowainik/stan.git
$ cd stan

Then, using Stack, you need to build the package and copy the executable to the desired location (typically one on your PATH). If Stack's --local-bin-path option is omitted, Stack will copy the built executable to a default location:

$ stack --local-bin-path=<path_to_desired_location> install

Stack's build, including the version of GHC used, will be configured by a stack.yaml file provided by the repository or the package from Hackage. If you wish to build Stan with a different version of GHC than that assumed, you will need to edit the configuration accordingly.

Hackage

[Back to the Table of Contents] โ†‘

Stan is available on Hackage.

Using the Cabal build tool, you can install the tool from there as well:

$ cabal v2-install stan --install-method=copy --overwrite-policy=always

You can also choose with which GHC version you want to have Stan installed, and optionally add some suffix to the executable name:

$ cabal v2-install stan \
    -w ghc-8.10.1 \
    --install-method=copy \
    --overwrite-policy=always \
    --program-suffix=-8.10.1

Using the Stack tool, you can also install a version of Stan from Hackage:

First, you need to unpack the package locally and change to the package's directory. The following example assumes stan-0.1.0.1:

$ stack unpack stan-0.1.0.1 # Specify 'stan' for the most recent version
$ cd stan-0.1.0.1 # The directory is named after the package version

Then, using Stack, you need to build the package and copy the executable to the desired location (as above, for the repository example):

$ stack --local-bin-path=<path_to_desired_location> install

Homebrew

[Back to the Table of Contents] โ†‘

If you are on MacOS, you can get Stan using Homebrew Kowainik's Tap.

You need to run the following command for that:

$ brew install kowainik/tap/stan

NOTE: Homebrew installs the Stan version build with the latest supported GHC version. This means that this version of Stan is working with the project with the same GHC version due to the GHC issues described above.

Ubuntu PPA

[Back to the Table of Contents] โ†‘

If you are on Ubuntu, you can get Stan using Kowainik's PPA.

You need to run the following commands for that:

$ sudo add-apt-repository ppa:kowainik/stan
$ sudo apt update
$ sudo apt install stan

NOTE: apt-get installs the Stan version build with the latest supported GHC version. This means that this version of Stan is working with the project with the same GHC version due to the GHC issues described above.

Download binary

[Back to the Table of Contents] โ†‘

You can download binary directly from GitHub releases.

After downloading binary, make it executable and copy it under convenient location, e.g.:

$ chmod +x stan-0.0.1.0-Linux-ghc-8.10.1
$ mv stan-0.0.1.0-Linux-ghc-8.10.1 ~/.local/bin/stan

NOTE: you need to download binary for your specific OS and specicific GHC version you use due to the GHC issues described above.

Usage instructions

[Back to the Table of Contents] โ†‘

Stan works with the HIE files to analyse Haskell projects. Therefore, Stan requires users to generate HIE files in advance. Fortunately, it is straightforward to satisfy this necessity. To produce HIE files, add the following GHC options in your project's .cabal file to each stanza you want to analyse:

    ghc-options:       -fwrite-ide-info
                       -hiedir=.hie

Recommendation: you can use the common stanzas feature to write the above options only once and enable them in each stanza easily.

Note: here we recommend generating the HIE files into .hie/ folder. As it is the recommendation only, you can specify your own folder as well. But then you will need to run stan using the --hiedir option with the specified path to your hie folder.

After creating HIE files, you can just run Stan on the project:

$ stan

to see all found suggestions in your terminal.

If you want to see a more detailed information in a more structured way, you can generate an HTML report (to the stan.html file) using the following command:

$ stan report

Stan strives to implement the convenient interface, so you can use the tool without configuring a lot in advance. However, the tool also provides various ways to set it up in the way to be the most efficient with your particular use case.

General configuration info

[Back to the Table of Contents] โ†‘

Stan's work can be configured from the multiple sources (in increasing order of priority):

  1. Default settings (hard-coded in the library โ€” includes no custom settings)
  2. Environment variables
  3. TOML file configuration
  4. CLI arguments

Stan runtime settings have many parts, and each of them can come from different configuration sources. If some option is specified through multiple sources, the most prioritized one will be used. In addition, Stan helps to understand its own configuration, so it outputs detailed information about each part of the config, what configuration settings were used and how they were set.

Configuration explanation

TOML configurations

[Back to the Table of Contents] โ†‘

Stan supports TOML runtime configuration in order to customize the work of the tool based on the user's individual requirements. You can use the TOML configuration to disable some inspections, enable them only in particular Haskell modules, ignore some observations or completely remove some files from the analysis.

Specifically, you can use the following variables to set up custom configurations with TOML:

Variable Description Examples
check Set up rules to control the set of inspections per scope. check = [{type = "Exclude", id = "STAN-0101", scope = "all"}]
remove Remove some files from the analysis completely. Stan won't be run in the specified scope at all. remove = [ {file = "src/File.hs"}, {directory = "folder/"} ]
ignore Ignore specific observation that was found in your project ignore = [{ id = "OBS-STAN-0001-YrzpQi-11:42" }]

See Haddock documentation for explanation of how the TOML configuration works and examples of the different use cases.

In case you have a number of TOML files locally, the following rules describe how Stan decides which TOML configuration file to use:

  • By default, Stan tries to read settings from the local .stan.toml file in the current directory. So, if you want to adjust the default Stan settings with some custom rules, create a .stan.toml file in the root of your Haskell project.
  • If the local .stan.toml file is not found, Stan tries to read the global ~/.stan.toml file. Having a global Stan configuration can be convenient, if you work on several projects and want to have the same custom settings by default for all of them.
  • If you don't have any of the default configuration files, it is still okay. Stan will use its own default hard-coded settings.
  • You can specify a path to a specific configuration file using the --config-file option. This custom file will be used in addition to the default TOML config.
  • If you don't want to use the default TOML configuration, pass the --no-default flag or use the STAN_USE_DEFAULT_CONFIG=False environment variable.

Command-line Interface

[Back to the Table of Contents] โ†‘

This section describes what is possible to achieve with the Stan CLI. If you have already installed the analyser, you can use

$ stan --help

to get the short information of all possible commands and options in your terminal.

Main command

[Back to the Table of Contents] โ†‘

The main command is the one that actually would analyse the Haskell codebase. There are plenty of configurations and options you can tune for each run (similarly to the TOML configurations):

  • Specify the HIE files folder (will use .hie/ otherwise)
  • Specify .cabal files of your project (will lookup automatically otherwise)
  • Turn on/off the usage of the default .stan.toml configuration file
  • Specify the TOML configuration file to use (will be used additionally to default TOML file if applicable)
  • Filter in or out specific files, directories, inspections, categories or severities
  • Generate the HTML report file
  • Set up the output verbosity
  • Choose to have machine readable JSON output instead

Here is the high-level explanation of the available sub-commands:

Sub-command Description Examples
check Set up rules to control the set of inspections per scope. stan check --exclude --category=Infinity --scope-all check --include --id "STAN-0101" --file=src/File.hs
remove Remove some files from the analysis completely. Stan won't be run in the specified scope at all. stan remove --file=src/File.hs remove --directory=folder/
ignore Ignore specific observation that was found in your project stan ignore --id "OBS-STAN-0001-YrzpQi-11:42"

More precisely the commands and options are described in here:

stan
    [REPORT]
    [   CHECKs {[TYPE option] [FILTER option] [SCOPE option]}
      | REMOVEs {SCOPE option}
      | IGNOREs {ID option}
    ]
    [--hiedir=DIR_PATH]
    [--cabal-file-path=FILE_PATHs]
    [--config-file=FILE_PATH]
    [--no-default]
    [-s|--short]
    [--hide-solution]
    [--json-output]
    [-h|--help]
    [-v|--version]

Description:
  CHECKs           Command to Specify the list of checks
  REMOVEs          Command to Specify scope to be removed
  IGNOREs          Command to Specify the list of what needs to be ignored
  REPORT           Command to generate an HTML Report
  --hiedir=DIR_PATH        Relative path to the directory with HIE
                           files (default: .hie)
  --cabal-file-path=FILE_PATHs
                           Relative path to the .cabal file (can specify many of this option)
  --config-file=FILE_PATH  Relative path to the .toml configurations file
  --no-default             Ignore local .stan.toml configuration file
  -s,--short               Hide verbose output information for observations
  --hide-solution          Hide verbose solution information for observations
  --json-output            Output the machine-readable output in JSON format instead
  -h,--help                Show this help text
  -v,--version             Show Stan's version

Sub-commands options:


  TYPE:
    --include                Include check
    --exclude                Exclude check
  FILTER:
    --id=INSPECTION_ID       Inspection ID to be used
    --severity=SEVERITY      Inspection Severity to exclude or include
    --category=CATEGORY      Inspection Category to exclude or include
    --filter-all             Exclude or include ALL inspections
  SCOPE:
    --file=FILE_PATH         File to exclude or include
    --directory=DIRECTORY_PATH
                           Directory to exclude or include
    --scope-all              Apply check to all files

Report options:

  -b,--browse              Open report in a browser

For example, if you want to run Stan analysis only on a single file, generate the HTML report and immediately open report in a browser, you can use the following command:

$ stan check --exclude --filter-all --scope-all \
       check --include --filter-all --file=src/Stan/Example.hs \
       report --browse

Inspections

[Back to the Table of Contents] โ†‘

You can find the list of all available inspections with description and additional information on our dedicated wiki page. However, with the tool you can get this information easily by using the inspection command. Optionally, you can see details of a particular inspection by typing the corresponding inspection ID alongside. You can see more robust description of the command here:

inspection โ€“ Show all Inspections

Usage:
  stan inspection [INSPECTION_ID]

Available options:
  INSPECTION_ID            Show specific Inspection information
  -h,--help                Show this help text

Converting between TOML and CLI configurations

[Back to the Table of Contents] โ†‘

It is usually convenient to have a proper configuration file that suits your project, which you can reuse each run of the Stan.

But sometimes you need to quickly run the tool with the same settings on another machine where having such files is not possible. Or you want to send the reproducible command, that anyone could execute and get the identical results. For these purposes, we have a special command that allows you to do so:

toml-to-cli โ€“ Convert TOML configuration file into stan CLI command

Usage:
    stan toml-to-cli [--config-file=FILE_PATH]

Available options:
  --config-file=FILE_PATH  Relative path to the .toml configurations file
  -h,--help                Show this help text

And for convenience you are able to use the reversed command โ€“โ€“ cli-to-toml.

cli-to-toml โ€“ Convert CLI arguments into stan TOML configuration

Usage:
    stan cli-to-toml
      [--config-file=FILE_PATH]
      [   CHECKs {[TYPE option] [FILTER option] [SCOPE option]}
        | REMOVEs {SCOPE option}
        | IGNOREs {ID option}
      ]

Other tools

[Back to the Table of Contents] โ†‘

  • GHC โ€” Glasgow Haskell Compiler

    GHC is the most popular Haskell compiler. As it has access to all steps of the code compilation, GHC can warn about different aspects of your code: non-exhaustive pattern matching, unused variables, etc.

    However, it is not supposed to be used as a static analysis tool. It provides errors and warnings as a part of the whole compilation pipeline.

  • Weeder โ€” Haskell dead-code analysis tool

    Weeder is a tool that analyses the code but in a very specific and limited case. It helps to eliminate unreachable code in your project. Similarly to Stan, the Weeder tool is also working with the HIE files to get this information.

  • HLint โ€” Haskell Linter Tool

    HLint is a linter tool that suggests code improvements to make code simpler.

    Unlike Stan, that uses the HIE files for analysis and accesses the complete compile-time info produced by GHC, HLint relies only on parsing, which has its own benefits but also limits its capabilities.

    Stan and HLint are complementary tools that have different scopes and goals. There is no intention to duplicate HLint in Stan.

To learn more about the implementation and goals of our project, please read the sections above that describe the Stan project in detail.

Roadmap

[Back to the Table of Contents] โ†‘

Our plan for the nearest future:

  • Opt-in inspections
  • Custom users' inspections
  • More inspections on potential bugs and performance
  • Single-pass traverse on AST

We have much more ideas to work on. See more detailed plan in the dedicated GitHub Project page.

Users

Stan is known to be adopted by the following companies:

Links to Wiki

[Back to the Table of Contents] โ†‘

stan's People

Contributors

0rphee avatar afnanenayet avatar carlosdagos avatar chshersh avatar connorbaker avatar dependabot[bot] avatar hasufell avatar marcosh avatar mpickering avatar mpilgrem avatar neilmayhew avatar norm2782 avatar philderbeast avatar shrykewindgrace avatar tfausak avatar tomjaguarpaw avatar tristancacqueray avatar vrom911 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stan's Issues

Implement type search in HIE files

When working on some inspections, it was discovered that we need to implement some sort of search in types or matching of types of some expressions. Fortunately, HIE files contain information about types. Unfortunately, they contain information in compact way which is not always easy to process. So if you have a type like this:

foo :: NonEmpty String -> Int

It will be stored as an Array like this (I write as a mapping, but index is implicit in arrays):

1 -> "Int"      []
2 -> "String"   []
3 -> "NonEmpty" [ 2 ]
4 -> FunType    3 1

We need to implement the following:

  1. Interface to specify search queries. We need to think about type-safe and convenient way to specify queries via some nice eDSL. Sometimes we need to search for exact types, like fromInteger :: Natural -> Integer and sometimes we don't care about all type parameters and want to have some sort of wildcards, like length :: [_] -> Int.
  2. Matching function that will search the array of types recursively.

When verifying the type of the expression, we know the index of that type (Int, which is called TypeIndex in the HieTypes module). So a matching function will have the following shape:

matchType
    :: TypeIndex   -- ^ Index of the current expression type
    -> TypePattern  -- ^ Our search query
    -> Array TypeIndex HieTypeFlat  -- ^ Array of all types in HIE file
    -> Maybe HieTypeFlat  -- ^ Matched type, if it was found

Simplify code structure for Inspection

Proposal to change the current code to the following scheme:

  1. Remove old stanXXXX :: Id Inspection variable.
  2. Rename stanXXXInspection :: Inspection to simply stanXXXX :: Inspection. We have the inspectionId :: Inspection -> Id Inspection function to get the id.
  3. Remove stanXXXXMeta variables.
  4. Remove stanXXXXAnalyser variables.
  5. Add to Inspection a field of some big sum type that represents different kinds of our inspections. I'm thinking about something like this:
data InspectionAnalysis
    = FindName NameMeta  -- ^ For searching for  a specific functions
    | Infix
    ...
  1. Create a function: analysisByInspection :: InspectionAnalysis -> HieFile -> [Observation]
  2. Refactor the analyse function to the following scheme: traverse list of all inspections run the corresponding analysis function for the InspectionAnalysis constructor.

With this code structure, we should be able to reduce the number of variables and documentation we need to maintain.

What do you think?

Stable ids for Observations

Currently, ids for Observations are in the form:

1-STAN-0001-HEAD

The number in the beginning is some incremented counter. It would be better to have stable names, so people can disable specific. The idea is to include inspection id, module name and location, something like:

STAN-0001-HEAD-Target.Example-line7-startsOn18

What do you think? Is it too verbose?

Store file map in Analysis

This will help to keep per file information that could be used for more detailed reports.
For example, it could contain LoC, extensions per file.

Create basic command line interface

Stan without any options should run on the current folder.
We should be able to configure .hie files location via the option (--hie-dir?).

Find occurrences of [0 .. length xs]

This expression is represented in the HIE file in the following way (shortened for a clearer picture):

Node
    { annotations = [ ("ArithSeq", "HsExpr") ]
    , children =
        [ Node
            { annotations = [ ( "HsOverLit", "HsExpr") ]
            , children = []
            }
        , Node
            { annotations = [ ( "HsApp", "HsExpr") ]
            , children = [ length, xs ]
            }
        ]
    }

So, the algorithm to find a piece of the AST is the following:

  1. Traverse AST until we find the ("ArithSeq", "HsExpr") pair in the node context. This means that this node represents a range of the shape [a .. b].
  2. Pattern-match on children of this AST and verify they have the desired shape:
    • First children is "HsOverLit" equal to 0 (equality check is a bit tricky, since the constant is not stored in AST, only it's position in the source file)
    • Second children is a function application of the "length" function to anything

Full AST

Node
  { nodeInfo = NodeInfo
      { nodeAnnotations = fromList
          [
              ( "ArithSeq"
              , "HsExpr"
              )
          ]
      , nodeType = []
      , nodeIdentifiers = fromList []
      }
  , nodeSpan = SrcSpanOneLine "target/Target/Infinite.hs" 10 12 28
  , nodeChildren =
      [ Node
          { nodeInfo = NodeInfo
              { nodeAnnotations = fromList
                  [
                      ( "HsOverLit"
                      , "HsExpr"
                      )
                  ]
              , nodeType = []
              , nodeIdentifiers = fromList []
              }
          , nodeSpan = SrcSpanOneLine "target/Target/Infinite.hs" 10 13 14
          , nodeChildren = []
          }
      , Node
          { nodeInfo = NodeInfo
              { nodeAnnotations = fromList
                  [
                      ( "HsApp"
                      , "HsExpr"
                      )
                  ]
              , nodeType = []
              , nodeIdentifiers = fromList []
              }
          , nodeSpan = SrcSpanOneLine "target/Target/Infinite.hs" 10 18 27
          , nodeChildren =
              [ Node
                  { nodeInfo = NodeInfo
                      { nodeAnnotations = fromList
                          [
                              ( "HsVar"
                              , "HsExpr"
                              )
                          ,
                              ( "HsWrap"
                              , "HsExpr"
                              )
                          ]
                      , nodeType =
                          [ 7
                          , 18
                          ]
                      , nodeIdentifiers = fromList
                          [
                              ( Right $base$Data.Foldable$length
                              , IdentifierDetails
                                  { identType = Just 18
                                  , identInfo = fromList [ Use ]
                                  }
                              )
                          ]
                      }
                  , nodeSpan = SrcSpanOneLine "target/Target/Infinite.hs" 10 18 24
                  , nodeChildren = []
                  }
              , Node
                  { nodeInfo = NodeInfo
                      { nodeAnnotations = fromList
                          [
                              ( "HsVar"
                              , "HsExpr"
                              )
                          ]
                      , nodeType = [ 4 ]
                      , nodeIdentifiers = fromList
                          [
                              ( Right $_in$xs
                              , IdentifierDetails
                                  { identType = Just 4
                                  , identInfo = fromList [ Use ]
                                  }
                              )
                          ]
                      }
                  , nodeSpan = SrcSpanOneLine "target/Target/Infinite.hs" 10 25 27
                  , nodeChildren = []
                  }

Add 'Stan.Core.ModuleName'

We use module names in multiple places, and it would be nice to introduce our own wrapper in a separate module. We also can provide helper functions to convert from GHC module names.

newtype ModuleName
fromGhcModule :: GHC.Module -> ModuleName
fromGhcModuleName :: GHC.ModuleName -> ModuleName

Group observations into 'Slist'

Currently, Observations are displayed in non-uniform order. For better consistency, I propose to sort them by source location.

Also, while we're on this, let's think about the data structure for all Observations. Currently, it's a list. But what we want is:

  1. Calculate length fastly.
  2. Append different pieces from different inspections.

Something like slist should work nice here, as @vrom911 suggested. But we probably will need to implement custom grouping function:

groupBy :: (a -> k) -> [a] -> Map k (Slist a)

Add golden tests for the textual output

@vrom911 Implemented superb report in #28. It would be nice to test the textual output to avoid having surprises. The only difficult challenge here is how to print output without colours, so we can compare only the string content ๐Ÿค”

Count the number of analysed source code lines

HieFiles store the source of the file, so this should also be easily calculated.
Open question: should we separate somehow between code lines, empty lines and documentation lines? Calculating blank lines is easy; documentation is trickier...

Partial functions from boot libraries

containers

  • Data.Map.(!)
  • Data.IntMap.(!)

text

All functions as for lists, and much more. Also, for both Text types: strict Text and lazy Text.

bytestring

All functions as for lists, and much more. Also, for both ByteString types: strict ByteString and lazy ByteString.

Inspections for Math partial functions

Should we create a new category for Math functions (so they should be easily turned off).

The doubt here is only because we can't suggest anything else instead.. ๐Ÿค”

  • div
  • mod
  • divMod
  • quot
  • rem
  • quotRem
  • (^)

Inspections for partial functions

Lists

  • head
  • tail
  • init
  • last
  • (!!)
  • fromJust
  • maximum
  • minimum
  • maximumBy
  • minimumBy
  • foldl1
  • foldl1'
  • foldr1
  • cycle
  • genericIndex

Other

  • read
  • succ
  • pred
  • toEnum

Handle multiple definitions for Inspection

Some functions can come from multiple places. For example, length can come from GHC.OldList as a monomorphic function, or as polymorphic function, specialised to lists from Data.Foldable. I guess in such cases we want to somehow support multiple definition points for a single function. A naive solution would be to store NonEmpty InspectionAnalysis instead of InspectionAnalysis inside Inspection but maybe there's a better solution.

  • length
  • fromList

Create automatic test

We can test stan on the project itself.
For this, we can create a separate stanza, where we can put Haskell modules with potentially problematic code.
The test should check the output with the expected one.

Inspection on usage particular extension

Blocked by #124

This then could be used in the config, where the user will be able to specify unwanted extensions, for example: disallow = [AllowAmbigousTypes, IncoherentInstances]

Create configuration

It's probably a good idea to create some configuration file, if people want to ignore some checks. I'm thinking about having .stan.toml file in repository with the syntax like:

ignore =
    [ { id = "HEAD" }
    , { id = "TAIL" }
    , { severity = "Warning" }
    ]

I'm using an array of tables of custom types. It can be written in a more verbose (but probably less noisy way):

[[ignore]]
id = "HEAD"

[[ignore]]
id = "TAIL"

[[ignore]]
severity = "Warning"

Basically, you can specify a list of conditions you want to ignore. In this case: all inspections with ids HEAD and TAIL and all inspections with severity Warning (and lower?). Let me know what do you think!

Or maybe you have more ideas about what we can put in the config? It would be also nice to be able to extend stan externally, so people can create their own checks (like forbid partial functions from external packages), but I feel like this deserves a separate issue and a separate discussion...

hie files for the target project

Should we assume that they are present? Or should we create them during the run?
In the first option, we should add instructions, but with the second option, we need to decide how to make it more efficiently.

Add --non-verbose option in CLI

For very short one-line output per observation:

$ stan --non-verbose
 * [1-STAN-001] WARNING <src/Example.hs(20:4-20:8)> Partial: head
 * [2-STAN-001] ERROR   <src/Example.hs(20:4-20:8)> Partial: head

^ the output format should be discussed better ๐Ÿ™‚

Various grouping options for Observations

Currently, we group Observations by filepath for the console output. However, it is not the only possible option. User can be interested in e.g. Severity grouping, or Category grouping as well.

We need to come up with a strategy to extend current settings.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.