mirror of
https://github.com/jfmengels/elm-review.git
synced 2024-11-23 23:05:35 +03:00
Use stil4m/elm-syntax for parsing
This commit is contained in:
parent
a16c660048
commit
9a56823f20
3
elm.json
3
elm.json
@ -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": {}
|
||||
}
|
||||
|
68
src/Lint.elm
68
src/Lint.elm
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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 ])
|
||||
|
Loading…
Reference in New Issue
Block a user