Use stil4m/elm-syntax for parsing

This commit is contained in:
Jeroen Engels 2018-11-05 19:00:17 +01:00
parent a16c660048
commit 9a56823f20
5 changed files with 123 additions and 148 deletions

View File

@ -14,7 +14,8 @@
"elm/core": "1.0.0 <= v < 2.0.0",
"elm/html": "1.0.0 <= v < 2.0.0",
"elm/regex": "1.0.0 <= v < 2.0.0",
"elm-community/list-extra": "8.1.0 <= v < 9.0.0"
"elm-community/list-extra": "8.1.0 <= v < 9.0.0",
"stil4m/elm-syntax": "7.0.2 <= v < 8.0.0"
},
"test-dependencies": {}
}

View File

@ -1,7 +1,7 @@
module Lint exposing
( lintSource
, lint, doNothing, visitExpression
, countErrors, parseSource
, parseSource
)
{-| A linter for Elm.
@ -41,15 +41,17 @@ To run the rules on a source code and get a list of errors:
# Internal
@docs countErrors, parseSource
@docs parseSource
-}
import Ast
import Ast.Expression exposing (Expression)
import Ast.Statement exposing (Statement)
import Lint.Types exposing (Direction, File, LintError, LintImplementation, LintRule, LintRuleImplementation, Severity(..), Visitor)
import Lint.Visitor exposing (expressionToVisitors, transformStatementsIntoVisitors)
import Elm.Parser as Parser
import Elm.Processing exposing (addFile, init, process)
import Elm.Syntax.Expression exposing (Expression)
import Elm.Syntax.File exposing (File)
import Elm.Syntax.Node exposing (Node)
import Lint.Types exposing (Direction, LintError, LintImplementation, LintRule, LintRuleImplementation, Severity(..), Visitor)
import Lint.Visitor exposing (expressionToVisitors, transformDeclarationsIntoVisitors)
import Regex
@ -71,27 +73,22 @@ lintSource rules source =
)
lintSourceWithRule : List Statement -> ( Severity, LintRule ) -> List ( Severity, LintError )
lintSourceWithRule statements ( severity, rule ) =
rule statements
lintSourceWithRule : File -> ( Severity, LintRule ) -> List ( Severity, LintError )
lintSourceWithRule file ( severity, rule ) =
rule file
|> List.map (\b -> ( severity, b ))
{-| Parse source code into a AST
-}
parseSource : String -> Result (List String) (List Ast.Statement.Statement)
parseSource : String -> Result (List String) File
parseSource source =
source
|> removeComments
|> Ast.parse
|> Result.mapError (\( _, _, errors ) -> errors)
|> Result.map (\( _, _, statements ) -> statements)
removeComments : String -> String
removeComments =
Regex.replace Regex.All (Regex.regex "--.$") (always "")
>> Regex.replace Regex.All (Regex.regex "\n +\\w+ : .*") (always "")
|> Parser.parse
-- TODO Improve parsing error handling
|> Result.mapError (\error -> [ "Parsing error" ])
-- TODO Add all files to have more context https://package.elm-lang.org/packages/stil4m/elm-syntax/7.0.2/Elm-Processing
|> Result.map (process init)
{-| Lints source code using a given rule implementation, and gives back a list of errors that were found.
@ -102,18 +99,17 @@ removeComments =
implementation : LintRuleImplementation Context
implementation =
{ statementFn = doNothing
, typeFn = doNothing
{ typeFn = doNothing
, expressionFn = expressionFn
, moduleEndFn = \ctx -> ( [], ctx )
, initialContext = Context
}
-}
lint : List Statement -> LintRuleImplementation context -> List LintError
lint statements rule =
statements
|> transformStatementsIntoVisitors
lint : File -> LintRuleImplementation context -> List LintError
lint file rule =
file.declarations
|> transformDeclarationsIntoVisitors
|> lintWithVisitors rule
@ -139,7 +135,7 @@ part of the implementation of complex rules much easier. It gives back a list of
}
-}
visitExpression : LintRuleImplementation context -> Expression -> ( List LintError, context )
visitExpression : LintRuleImplementation context -> Node Expression -> ( List LintError, context )
visitExpression rule expression =
expressionToVisitors expression
|> List.foldl (visitAndAccumulate rule) ( [], rule.initialContext )
@ -174,19 +170,3 @@ context. This is used to avoid a bit of boilerplate for visitor functions whose
doNothing : LintImplementation a context
doNothing ctx _ =
( [], ctx )
{-| Count the number of errors of a given Severity in the list of errors.
-}
countErrors : Severity -> List ( File, List ( Severity, LintError ) ) -> Int
countErrors severity errors =
errors
|> List.map
(Tuple.second
>> List.filter
(Tuple.first
>> (==) severity
)
>> List.length
)
|> List.sum

View File

@ -30,7 +30,8 @@ module Lint.Rules.NoDebug exposing (rule)
-}
import Ast.Expression exposing (..)
import Elm.Syntax.Expression exposing (Expression(..))
import Elm.Syntax.Node exposing (Node, value)
import Lint exposing (doNothing, lint)
import Lint.Types exposing (Direction(..), LintError, LintRule, LintRuleImplementation)
@ -53,9 +54,7 @@ rule input =
implementation : LintRuleImplementation Context
implementation =
{ statementFn = doNothing
, typeFn = doNothing
, expressionFn = expressionFn
{ expressionFn = expressionFn
, moduleEndFn = \ctx -> ( [], ctx )
, initialContext = Context
}
@ -66,15 +65,20 @@ error =
LintError "NoDebug" "Forbidden use of Debug"
expressionFn : Context -> Direction Expression -> ( List LintError, Context )
expressionFn ctx node =
case node of
Enter (Variable vars) ->
if List.member "Debug" vars then
( [ error ], ctx )
expressionFn : Context -> Direction (Node Expression) -> ( List LintError, Context )
expressionFn ctx node_ =
case node_ of
Enter node ->
case value node of
FunctionOrValue moduleName fnName ->
if List.member "Debug" moduleName then
( [ error ], ctx )
else
( [], ctx )
else
( [], ctx )
_ ->
( [], ctx )
_ ->
( [], ctx )

View File

@ -1,8 +1,8 @@
module Lint.Types exposing
( LintError, Direction(..)
, LintRule, Severity(..), Reporter
, LintRule, Severity(..)
, LintRuleImplementation, LintImplementation
, Visitor, LintResult, File
, Visitor, LintResult
)
{-| This module contains types that are used for writing rules.
@ -15,7 +15,7 @@ module Lint.Types exposing
# Configuration
@docs LintRule, Severity, Reporter
@docs LintRule, Severity
# Writing rules
@ -25,12 +25,13 @@ module Lint.Types exposing
# Internal types
@docs Visitor, LintResult, File
@docs Visitor, LintResult
-}
import Ast.Expression exposing (..)
import Ast.Statement exposing (..)
import Elm.Syntax.Expression exposing (Expression)
import Elm.Syntax.File exposing (File)
import Elm.Syntax.Node exposing (Node)
{-| Value that describes an error found by a given rule, that contains the name of the rule that raised the error, and
@ -71,7 +72,7 @@ enforce.
-}
type alias LintImplementation nodeType context =
context -> Direction nodeType -> ( List LintError, context )
context -> Direction (Node nodeType) -> ( List LintError, context )
{-| When visiting the AST, nodes are visited twice:
@ -104,10 +105,6 @@ type Direction node
- initialContext: An initial context
- statementFn: A LintImplementation for Statement nodes
- typeFn: A LintImplementation for Type nodes
- expressionFn: A LintImplementation for Expression nodes
- moduleEndFn: A function that takes a context and returns a list of error. Similar to a LintImplementation, but will
@ -119,18 +116,14 @@ type Direction node
implementation : LintRuleImplementation Context
implementation =
{ statementFn = doNothing
, typeFn = doNothing
, expressionFn = expressionFn
{ expressionFn = expressionFn
, moduleEndFn = (\\ctx -> ( [], ctx ))
, initialContext = Context
}
-}
type alias LintRuleImplementation context =
{ statementFn : LintImplementation Statement context
, typeFn : LintImplementation Type context
, expressionFn : LintImplementation Expression context
{ expressionFn : LintImplementation Expression context
, moduleEndFn : context -> ( List LintError, context )
, initialContext : context
}
@ -145,7 +138,7 @@ type alias LintResult =
{-| Shortcut to a lint rule
-}
type alias LintRule =
List Statement -> List LintError
File -> List LintError
{-| Shorthand for a function that takes a rule's implementation, a context and returns ( List LintError, context ).
@ -169,17 +162,3 @@ type Severity
= Disabled
| Warning
| Critical
{-| Description of an Elm file.
-}
type alias File =
{ filename : String
, source : String
}
{-| Function that summarizes the result of the linting process.
-}
type alias Reporter a =
List ( File, List ( Severity, LintError ) ) -> a

View File

@ -1,7 +1,9 @@
module Lint.Visitor exposing (expressionToVisitors, transformStatementsIntoVisitors)
module Lint.Visitor exposing (expressionToVisitors, transformDeclarationsIntoVisitors)
import Ast.Expression exposing (..)
import Ast.Statement exposing (..)
import Elm.Syntax.Declaration exposing (Declaration(..))
import Elm.Syntax.Expression exposing (Expression(..), Function, FunctionImplementation, LetDeclaration(..))
import Elm.Syntax.Infix exposing (InfixDirection(..))
import Elm.Syntax.Node exposing (Node, value)
import Lint.Types exposing (Direction(..), LintRule, Visitor)
@ -19,74 +21,89 @@ moduleVisitor rule context =
rule.moduleEndFn context
expressionVisitor : Direction Expression -> Visitor context
expressionVisitor : Direction (Node Expression) -> Visitor context
expressionVisitor node rule context =
rule.expressionFn context node
statementVisitor : Direction Statement -> Visitor context
statementVisitor node rule context =
rule.statementFn context node
functionToExpression : Function -> Node Expression
functionToExpression { documentation, signature, declaration } =
let
{ name, arguments, expression } =
value declaration
in
expression
expressionToVisitors : Expression -> List (Visitor context)
expressionToVisitors : Node Expression -> List (Visitor context)
expressionToVisitors node =
let
children : List (Node Expression)
children =
case node of
Application expression1 expression2 ->
[ expression1, expression2 ]
case value node of
Application expressions ->
expressions
Access expression names ->
[ expression ]
Variable _ ->
[]
String _ ->
[]
Character _ ->
Literal _ ->
[]
Integer _ ->
[]
Float _ ->
Floatable _ ->
[]
List elements ->
UnitExpr ->
[]
ListExpr elements ->
elements
Record pairs ->
List.map Tuple.second pairs
FunctionOrValue _ _ ->
[]
RecordUpdate name updates ->
List.map Tuple.second updates
RecordUpdateExpression name setters ->
List.map (value >> (\( field, expr ) -> expr)) setters
BinOp operator left right ->
[ operator, left, right ]
OperatorApplication operator direction left right ->
case direction of
Left ->
[ left, right ]
If cond then_ else_ ->
Right ->
[ right, left ]
Non ->
[ left, right ]
IfBlock cond then_ else_ ->
[ cond, then_, else_ ]
Let declarations body ->
List.append
(List.map Tuple.second declarations)
[ body ]
LetExpression { expression, declarations } ->
List.map
(\declaration ->
case value declaration of
LetFunction function ->
functionToExpression function
Case target cases ->
List.append
[ target ]
(List.concatMap (\( a, b ) -> [ a, b ]) cases)
LetDestructuring pattern expr ->
expr
)
declarations
++ [ expression ]
Lambda names expression ->
CaseExpression { expression, cases } ->
[ expression ]
++ List.map (\( pattern, caseExpression ) -> caseExpression) cases
LambdaExpression { args, expression } ->
[ expression ]
Tuple expressions ->
TupledExpression expressions ->
expressions
AccessFunction name ->
-- TODO Implement the rest
_ ->
[]
childrenVisitors =
@ -95,30 +112,24 @@ expressionToVisitors node =
createExitAndEnterWithChildren expressionVisitor node childrenVisitors
typeToVisitors : Type -> List (Visitor context)
typeToVisitors node =
[]
statementToVisitors : Statement -> List (Visitor context)
statementToVisitors node =
declarationToVisitors : Declaration -> List (Visitor context)
declarationToVisitors declaration =
let
childrenVisitors =
case node of
FunctionTypeDeclaration name application ->
typeToVisitors application
FunctionDeclaration name params body ->
expressionToVisitors body
case declaration of
FunctionDeclaration function ->
functionToExpression function |> expressionToVisitors
-- TODO Implement the rest
_ ->
[]
in
createExitAndEnterWithChildren statementVisitor node childrenVisitors
-- createExitAndEnterWithChildren statementVisitor declaration childrenVisitors
childrenVisitors
transformStatementsIntoVisitors : List Statement -> List (Visitor context)
transformStatementsIntoVisitors statements =
statements
|> List.concatMap statementToVisitors
transformDeclarationsIntoVisitors : List (Node Declaration) -> List (Visitor context)
transformDeclarationsIntoVisitors declarations =
declarations
|> List.concatMap (value >> declarationToVisitors)
|> (\allVisitors -> List.append allVisitors [ moduleVisitor ])