mirror of
https://github.com/jfmengels/elm-review.git
synced 2024-08-16 19:40:22 +03:00
Compare commits
4 Commits
267d2038ba
...
88e7f8b375
Author | SHA1 | Date | |
---|---|---|---|
|
88e7f8b375 | ||
|
1db1a05c69 | ||
|
fceaaf33e2 | ||
|
efd3fc151a |
@ -13,6 +13,7 @@
|
||||
"elm/project-metadata-utils": "1.0.2",
|
||||
"elm/regex": "1.0.0",
|
||||
"jfmengels/elm-review": "2.11.0",
|
||||
"pzp1997/assoc-list": "1.0.0",
|
||||
"stil4m/elm-syntax": "7.2.9"
|
||||
},
|
||||
"indirect": {
|
||||
|
@ -34,6 +34,7 @@ import NoUnused.Patterns
|
||||
import NoUnused.Variables
|
||||
import Review.Rule as Rule exposing (Rule)
|
||||
import NoUnused.CustomTypeConstructorArgs
|
||||
import Simplify
|
||||
|
||||
config : List Rule
|
||||
config =
|
||||
@ -69,6 +70,11 @@ config =
|
||||
, NoUnused.Variables.rule
|
||||
, NoSimpleLetBody.rule
|
||||
, NoPrematureLetComputation.rule
|
||||
, Simplify.rule Simplify.defaults
|
||||
|> Rule.ignoreErrorsForFiles
|
||||
[ "src/Review/Test/Dependencies/Unsafe.elm"
|
||||
, "src/Review/Logger.elm"
|
||||
]
|
||||
, NoForbiddenWords.rule [ "REPLACEME" ]
|
||||
]
|
||||
|> List.map (Rule.ignoreErrorsForDirectories [ "src/Vendor/", "tests/Vendor/" ])
|
||||
|
@ -83,9 +83,11 @@ rememberBadAlias { lookupAlias, canMissAliases } (Node moduleNameRange moduleNam
|
||||
|
||||
moduleCallVisitor : Node ( ModuleName, String ) -> Context.Module -> ( List (Error {}), Context.Module )
|
||||
moduleCallVisitor node context =
|
||||
case Node.value node of
|
||||
( moduleName, function ) ->
|
||||
( [], Context.addModuleCall moduleName function (Node.range node) context )
|
||||
let
|
||||
( moduleName, function ) =
|
||||
Node.value node
|
||||
in
|
||||
( [], Context.addModuleCall moduleName function (Node.range node) context )
|
||||
|
||||
|
||||
finalEvaluation : Options.AliasLookup -> Context.Module -> List (Error {})
|
||||
|
@ -124,28 +124,32 @@ rememberExposedNames { moduleName, moduleAlias, exposingList } context =
|
||||
|
||||
valueVisitor : Node ( ModuleName, String ) -> Context.Module -> ( List (Error {}), Context.Module )
|
||||
valueVisitor node context =
|
||||
case Node.value node of
|
||||
( moduleName, name ) ->
|
||||
if Context.isFunctionExposed context moduleName name then
|
||||
( [ moduleOnExposedValueError name (Node.range node) ]
|
||||
, context
|
||||
)
|
||||
let
|
||||
( moduleName, name ) =
|
||||
Node.value node
|
||||
in
|
||||
if Context.isFunctionExposed context moduleName name then
|
||||
( [ moduleOnExposedValueError name (Node.range node) ]
|
||||
, context
|
||||
)
|
||||
|
||||
else
|
||||
( [], context )
|
||||
else
|
||||
( [], context )
|
||||
|
||||
|
||||
typeVisitor : Node ( ModuleName, String ) -> Context.Module -> ( List (Error {}), Context.Module )
|
||||
typeVisitor node context =
|
||||
case Node.value node of
|
||||
( moduleName, name ) ->
|
||||
if Context.isTypeExposed context moduleName name then
|
||||
( [ moduleOnExposedTypeError name (Node.range node) ]
|
||||
, context
|
||||
)
|
||||
let
|
||||
( moduleName, name ) =
|
||||
Node.value node
|
||||
in
|
||||
if Context.isTypeExposed context moduleName name then
|
||||
( [ moduleOnExposedTypeError name (Node.range node) ]
|
||||
, context
|
||||
)
|
||||
|
||||
else
|
||||
( [], context )
|
||||
else
|
||||
( [], context )
|
||||
|
||||
|
||||
moduleOnExposedValueError : String -> Range -> Error {}
|
||||
|
@ -22,6 +22,7 @@ import List.Extra
|
||||
import Review.ModuleNameLookupTable as ModuleNameLookupTable exposing (ModuleNameLookupTable)
|
||||
import Review.Rule as Rule exposing (Error, Rule)
|
||||
import Set exposing (Set)
|
||||
import String.Extra
|
||||
|
||||
|
||||
{-| Reports arguments of custom type constructors that are never used.
|
||||
@ -194,13 +195,13 @@ fromModuleToProject =
|
||||
replaceLocalModuleNameForSet : ModuleName -> Set ( ModuleName, comparable ) -> Set ( ModuleName, comparable )
|
||||
replaceLocalModuleNameForSet moduleName set =
|
||||
Set.map
|
||||
(\( moduleNameForType, name ) ->
|
||||
(\(( moduleNameForType, name ) as untouched) ->
|
||||
case moduleNameForType of
|
||||
[] ->
|
||||
( moduleName, name )
|
||||
|
||||
_ ->
|
||||
( moduleNameForType, name )
|
||||
untouched
|
||||
)
|
||||
set
|
||||
|
||||
@ -208,13 +209,18 @@ replaceLocalModuleNameForSet moduleName set =
|
||||
replaceLocalModuleNameForDict : ModuleName -> Dict ( ModuleName, comparable ) b -> Dict ( ModuleName, comparable ) b
|
||||
replaceLocalModuleNameForDict moduleName dict =
|
||||
Dict.foldl
|
||||
(\( moduleNameForType, name ) value acc ->
|
||||
case moduleNameForType of
|
||||
[] ->
|
||||
Dict.insert ( moduleName, name ) value acc
|
||||
(\(( moduleNameForType, name ) as key) value acc ->
|
||||
let
|
||||
newKey : ( ModuleName, comparable )
|
||||
newKey =
|
||||
case moduleNameForType of
|
||||
[] ->
|
||||
( moduleName, name )
|
||||
|
||||
_ ->
|
||||
Dict.insert ( moduleNameForType, name ) value acc
|
||||
_ ->
|
||||
key
|
||||
in
|
||||
Dict.insert newKey value acc
|
||||
)
|
||||
Dict.empty
|
||||
dict
|
||||
@ -231,22 +237,22 @@ getNonExposedCustomTypes moduleContext =
|
||||
let
|
||||
exposedCustomTypes : Set String
|
||||
exposedCustomTypes =
|
||||
list
|
||||
|> List.filterMap
|
||||
(\exposed ->
|
||||
case Node.value exposed of
|
||||
Exposing.TypeExpose { name, open } ->
|
||||
case open of
|
||||
Just _ ->
|
||||
Just name
|
||||
List.foldl
|
||||
(\exposed acc ->
|
||||
case Node.value exposed of
|
||||
Exposing.TypeExpose { name, open } ->
|
||||
case open of
|
||||
Just _ ->
|
||||
Set.insert name acc
|
||||
|
||||
Nothing ->
|
||||
Nothing
|
||||
Nothing ->
|
||||
acc
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
|> Set.fromList
|
||||
_ ->
|
||||
acc
|
||||
)
|
||||
Set.empty
|
||||
list
|
||||
in
|
||||
List.foldl
|
||||
(\( typeName, args ) acc ->
|
||||
@ -451,7 +457,7 @@ findCustomTypesHelp lookupTable nodes acc =
|
||||
node :: restOfNodes ->
|
||||
case Node.value node of
|
||||
Expression.FunctionOrValue rawModuleName functionName ->
|
||||
if isCustomTypeConstructor functionName then
|
||||
if String.Extra.isCapitalized functionName then
|
||||
case ModuleNameLookupTable.moduleNameFor lookupTable node of
|
||||
Just moduleName ->
|
||||
findCustomTypesHelp lookupTable restOfNodes (( moduleName, functionName ) :: acc)
|
||||
@ -469,7 +475,7 @@ findCustomTypesHelp lookupTable nodes acc =
|
||||
findCustomTypesHelp lookupTable (expression :: restOfNodes) acc
|
||||
|
||||
Expression.Application (((Node _ (Expression.FunctionOrValue _ functionName)) as first) :: expressions) ->
|
||||
if isCustomTypeConstructor functionName then
|
||||
if String.Extra.isCapitalized functionName then
|
||||
findCustomTypesHelp lookupTable (first :: (expressions ++ restOfNodes)) acc
|
||||
|
||||
else
|
||||
@ -488,12 +494,6 @@ findCustomTypesHelp lookupTable nodes acc =
|
||||
findCustomTypesHelp lookupTable restOfNodes acc
|
||||
|
||||
|
||||
isCustomTypeConstructor : String -> Bool
|
||||
isCustomTypeConstructor functionName =
|
||||
String.slice 0 1 functionName
|
||||
|> String.all Char.isUpper
|
||||
|
||||
|
||||
registerUsedPatterns : List ( ( ModuleName, String ), Set Int ) -> Dict ( ModuleName, String ) (Set Int) -> Dict ( ModuleName, String ) (Set Int)
|
||||
registerUsedPatterns newUsedArguments previouslyUsedArguments =
|
||||
List.foldl
|
||||
|
@ -431,6 +431,16 @@ module MyModule exposing (a, b)
|
||||
type Foo = Unused Int | B
|
||||
a = Unused 0 == b
|
||||
b = B
|
||||
"""
|
||||
|> Review.Test.runWithProjectData packageProject rule
|
||||
|> Review.Test.expectNoErrors
|
||||
, test "should not report args for type constructors starting with a non-ASCII letter used in an equality expression (==)" <|
|
||||
\() ->
|
||||
"""
|
||||
module MyModule exposing (a, b)
|
||||
type Foo = Ö_Unused Int | Ö_B
|
||||
a = Ö_Unused 0 == b
|
||||
b = Ö_B
|
||||
"""
|
||||
|> Review.Test.runWithProjectData packageProject rule
|
||||
|> Review.Test.expectNoErrors
|
||||
@ -451,6 +461,22 @@ module MyModule exposing (a, b)
|
||||
type Foo = Unused Int | B
|
||||
a = Unused <| b
|
||||
b = B
|
||||
"""
|
||||
|> Review.Test.runWithProjectData packageProject rule
|
||||
|> Review.Test.expectErrors
|
||||
[ Review.Test.error
|
||||
{ message = message
|
||||
, details = details
|
||||
, under = "Int"
|
||||
}
|
||||
]
|
||||
, test "should report args for type constructors starting with a non-ASCII letter used in non-equality operator expressions" <|
|
||||
\() ->
|
||||
"""
|
||||
module MyModule exposing (a, b)
|
||||
type Foo = Ö_Unused Int | Ö_B
|
||||
a = Ö_Unused <| b
|
||||
b = Ö_B
|
||||
"""
|
||||
|> Review.Test.runWithProjectData packageProject rule
|
||||
|> Review.Test.expectErrors
|
||||
|
@ -24,6 +24,7 @@ import Review.Fix as Fix exposing (Fix)
|
||||
import Review.ModuleNameLookupTable as ModuleNameLookupTable exposing (ModuleNameLookupTable)
|
||||
import Review.Rule as Rule exposing (Error, Rule)
|
||||
import Set exposing (Set)
|
||||
import String.Extra
|
||||
|
||||
|
||||
{-| Forbid having unused custom type constructors.
|
||||
@ -326,15 +327,17 @@ fromModuleToProject =
|
||||
)
|
||||
moduleContext.wasUsedInComparisons
|
||||
, wasUsedInOtherModules =
|
||||
List.foldl
|
||||
(\( moduleName_, constructors ) acc ->
|
||||
Set.union
|
||||
(Set.map (Tuple.pair moduleName_) constructors)
|
||||
acc
|
||||
)
|
||||
moduleContext.wasUsedInOtherModules
|
||||
-- TODO add test to make sure we don't fix something that is pattern matched in other modules
|
||||
(Dict.toList <| Dict.remove "" moduleContext.usedFunctionsOrValues)
|
||||
-- TODO add test to make sure we don't fix something that is pattern matched in other modules
|
||||
moduleContext.usedFunctionsOrValues
|
||||
|> Dict.remove ""
|
||||
|> Dict.foldl
|
||||
(\moduleName_ constructors acc ->
|
||||
Set.foldl
|
||||
(\constructor subAcc -> Set.insert ( moduleName_, constructor ) subAcc)
|
||||
acc
|
||||
constructors
|
||||
)
|
||||
moduleContext.wasUsedInOtherModules
|
||||
, fixesForRemovingConstructor =
|
||||
mapDictKeys
|
||||
(\constructorName ->
|
||||
@ -771,7 +774,7 @@ caseBranchEnterVisitor caseExpression ( casePattern, body ) moduleContext =
|
||||
|
||||
fixes : Dict ConstructorName (List Fix)
|
||||
fixes =
|
||||
List.foldl
|
||||
Set.foldl
|
||||
(\constructorName acc ->
|
||||
let
|
||||
fix : Fix
|
||||
@ -784,7 +787,7 @@ caseBranchEnterVisitor caseExpression ( casePattern, body ) moduleContext =
|
||||
updateToAdd constructorName fix acc
|
||||
)
|
||||
moduleContext.fixesForRemovingConstructor
|
||||
(Set.toList constructors.fromThisModule)
|
||||
constructors.fromThisModule
|
||||
|
||||
constructorsToIgnore : Set ( ModuleName, ConstructorName )
|
||||
constructorsToIgnore =
|
||||
@ -830,7 +833,7 @@ staticRanges nodes acc =
|
||||
staticRanges restOfNodes (Node.range node :: acc)
|
||||
|
||||
Expression.Application ((Node _ (Expression.FunctionOrValue _ name)) :: restOfArgs) ->
|
||||
if isCapitalized name then
|
||||
if String.Extra.isCapitalized name then
|
||||
staticRanges (restOfArgs ++ restOfNodes) (Node.range node :: acc)
|
||||
|
||||
else
|
||||
@ -900,7 +903,7 @@ findConstructorsHelp lookupTable nodes acc =
|
||||
node :: restOfNodes ->
|
||||
case Node.value node of
|
||||
Expression.FunctionOrValue _ name ->
|
||||
if isCapitalized name then
|
||||
if String.Extra.isCapitalized name then
|
||||
findConstructorsHelp
|
||||
lookupTable
|
||||
restOfNodes
|
||||
@ -910,7 +913,7 @@ findConstructorsHelp lookupTable nodes acc =
|
||||
findConstructorsHelp lookupTable restOfNodes acc
|
||||
|
||||
Expression.Application ((Node _ (Expression.FunctionOrValue _ name)) :: restOfArgs) ->
|
||||
if isCapitalized name then
|
||||
if String.Extra.isCapitalized name then
|
||||
findConstructorsHelp
|
||||
lookupTable
|
||||
(restOfArgs ++ restOfNodes)
|
||||
@ -1043,7 +1046,7 @@ constructorsInPattern lookupTable nodes acc =
|
||||
|
||||
registerUsedFunctionOrValue : Range -> ModuleName -> ConstructorName -> ModuleContext -> ModuleContext
|
||||
registerUsedFunctionOrValue range moduleName name moduleContext =
|
||||
if not (isCapitalized name) then
|
||||
if not (String.Extra.isCapitalized name) then
|
||||
moduleContext
|
||||
|
||||
else if List.member range moduleContext.ignoredComparisonRanges then
|
||||
@ -1061,16 +1064,6 @@ registerUsedFunctionOrValue range moduleName name moduleContext =
|
||||
{ moduleContext | usedFunctionsOrValues = updateToInsert (String.join "." moduleName) name moduleContext.usedFunctionsOrValues }
|
||||
|
||||
|
||||
isCapitalized : String -> Bool
|
||||
isCapitalized name =
|
||||
case String.uncons name of
|
||||
Just ( char, _ ) ->
|
||||
Char.isUpper char
|
||||
|
||||
Nothing ->
|
||||
False
|
||||
|
||||
|
||||
|
||||
-- FINAL PROJECT EVALUATION
|
||||
|
||||
|
@ -152,6 +152,25 @@ type Foo = Baz"""
|
||||
module MyModule exposing (b)
|
||||
type Foo = Bar"""
|
||||
]
|
||||
, test "should report unused and not report used type constructors starting with a non-ASCII letter" <|
|
||||
\() ->
|
||||
"""
|
||||
module MyModule exposing (b)
|
||||
type Foo = Ö_Used | Ö_NotUsed
|
||||
b = Ö_Used"""
|
||||
|> Review.Test.runWithProjectData project (rule [])
|
||||
|> Review.Test.expectErrors
|
||||
[ Review.Test.error
|
||||
{ message = "Type constructor `Ö_NotUsed` is not used."
|
||||
, details = [ defaultDetails ]
|
||||
, under = "Ö_NotUsed"
|
||||
}
|
||||
|> Review.Test.whenFixed
|
||||
"""
|
||||
module MyModule exposing (b)
|
||||
type Foo = Ö_Used
|
||||
b = Ö_Used"""
|
||||
]
|
||||
, test "should report unused type constructors, even if the type is exposed" <|
|
||||
\() ->
|
||||
"""
|
||||
@ -222,12 +241,12 @@ a = case () of
|
||||
}
|
||||
|> Review.Test.atExactly { start = { row = 3, column = 19 }, end = { row = 3, column = 25 } }
|
||||
|> Review.Test.whenFixed
|
||||
("""
|
||||
"""
|
||||
module MyModule exposing (a)
|
||||
type Foo = Used
|
||||
a = case () of
|
||||
Used -> Used
|
||||
""" |> String.replace "$" " ")
|
||||
"""
|
||||
]
|
||||
, test "should report type constructors that are only used inside deep pattern matches that require themselves" <|
|
||||
\() ->
|
||||
|
@ -292,23 +292,27 @@ finalEvaluationForProject projectContext =
|
||||
projectContext.used
|
||||
projectContext.used
|
||||
|
||||
( usedModules, unusedModules ) =
|
||||
projectContext.modules
|
||||
|> removeExposedPackages projectContext
|
||||
|> Dict.partition (\moduleName _ -> Set.member moduleName projectContext.usedModules)
|
||||
filterExposedPackage_ : ModuleName -> Bool
|
||||
filterExposedPackage_ =
|
||||
filterExposedPackage projectContext
|
||||
in
|
||||
List.concat
|
||||
[ usedModules
|
||||
|> Dict.toList
|
||||
|> List.concatMap (errorsForModule projectContext used)
|
||||
, unusedModules
|
||||
|> Dict.toList
|
||||
|> List.map unusedModuleError
|
||||
]
|
||||
Dict.foldl
|
||||
(\moduleName module_ acc ->
|
||||
if not (filterExposedPackage_ moduleName) then
|
||||
acc
|
||||
|
||||
else if Set.member moduleName projectContext.usedModules then
|
||||
errorsForModule projectContext used moduleName module_ acc
|
||||
|
||||
else
|
||||
unusedModuleError moduleName module_ :: acc
|
||||
)
|
||||
[]
|
||||
projectContext.modules
|
||||
|
||||
|
||||
unusedModuleError : ( ModuleName, { a | moduleKey : Rule.ModuleKey, moduleNameLocation : Range } ) -> Error scope
|
||||
unusedModuleError ( moduleName, { moduleKey, moduleNameLocation } ) =
|
||||
unusedModuleError : ModuleName -> { a | moduleKey : Rule.ModuleKey, moduleNameLocation : Range } -> Error scope
|
||||
unusedModuleError moduleName { moduleKey, moduleNameLocation } =
|
||||
Rule.errorForModule moduleKey
|
||||
{ message = "Module `" ++ String.join "." moduleName ++ "` is never used."
|
||||
, details = [ "This module is never used. You may want to remove it to keep your project clean, and maybe detect some unused code in your project." ]
|
||||
@ -316,15 +320,14 @@ unusedModuleError ( moduleName, { moduleKey, moduleNameLocation } ) =
|
||||
moduleNameLocation
|
||||
|
||||
|
||||
errorsForModule : ProjectContext -> Set ( ModuleName, String ) -> ( ModuleName, { a | moduleKey : Rule.ModuleKey, exposed : Dict String ExposedElement } ) -> List (Error scope)
|
||||
errorsForModule projectContext used ( moduleName, { moduleKey, exposed } ) =
|
||||
exposed
|
||||
|> removeApplicationExceptions projectContext
|
||||
|> removeReviewConfig moduleName
|
||||
|> Dict.filter (\name _ -> not <| Set.member ( moduleName, name ) used)
|
||||
|> Dict.toList
|
||||
|> List.concatMap
|
||||
(\( name, element ) ->
|
||||
errorsForModule : ProjectContext -> Set ( ModuleName, String ) -> ModuleName -> { a | moduleKey : Rule.ModuleKey, exposed : Dict String ExposedElement } -> List (Error scope) -> List (Error scope)
|
||||
errorsForModule projectContext used moduleName { moduleKey, exposed } acc =
|
||||
Dict.foldl
|
||||
(\name element subAcc ->
|
||||
if isUsedOrException projectContext used moduleName name then
|
||||
subAcc
|
||||
|
||||
else
|
||||
let
|
||||
what : String
|
||||
what =
|
||||
@ -338,48 +341,46 @@ errorsForModule projectContext used ( moduleName, { moduleKey, exposed } ) =
|
||||
ExposedType _ ->
|
||||
"Exposed type"
|
||||
in
|
||||
[ Rule.errorForModuleWithFix moduleKey
|
||||
Rule.errorForModuleWithFix moduleKey
|
||||
{ message = what ++ " `" ++ name ++ "` is never used outside this module."
|
||||
, details = [ "This exposed element is never used. You may want to remove it to keep your project clean, and maybe detect some unused code in your project." ]
|
||||
}
|
||||
element.range
|
||||
(List.map Fix.removeRange element.rangesToRemove)
|
||||
]
|
||||
)
|
||||
:: subAcc
|
||||
)
|
||||
acc
|
||||
exposed
|
||||
|
||||
|
||||
removeExposedPackages : ProjectContext -> Dict ModuleName a -> Dict ModuleName a
|
||||
removeExposedPackages projectContext dict =
|
||||
filterExposedPackage : ProjectContext -> ModuleName -> Bool
|
||||
filterExposedPackage projectContext =
|
||||
case projectContext.projectType of
|
||||
IsApplication _ ->
|
||||
dict
|
||||
always True
|
||||
|
||||
IsPackage exposedModuleNames ->
|
||||
Dict.filter (\name _ -> not <| Set.member name exposedModuleNames) dict
|
||||
\moduleName -> not <| Set.member moduleName exposedModuleNames
|
||||
|
||||
|
||||
removeApplicationExceptions : ProjectContext -> Dict String a -> Dict String a
|
||||
removeApplicationExceptions projectContext dict =
|
||||
isUsedOrException : ProjectContext -> Set ( ModuleName, String ) -> List String -> String -> Bool
|
||||
isUsedOrException projectContext used moduleName name =
|
||||
Set.member ( moduleName, name ) used
|
||||
|| isApplicationException projectContext name
|
||||
|| (moduleName == [ "ReviewConfig" ])
|
||||
|
||||
|
||||
isApplicationException : ProjectContext -> String -> Bool
|
||||
isApplicationException projectContext name =
|
||||
case projectContext.projectType of
|
||||
IsPackage _ ->
|
||||
dict
|
||||
False
|
||||
|
||||
IsApplication ElmApplication ->
|
||||
Dict.remove "main" dict
|
||||
name == "main"
|
||||
|
||||
IsApplication LamderaApplication ->
|
||||
dict
|
||||
|> Dict.remove "main"
|
||||
|> Dict.remove "app"
|
||||
|
||||
|
||||
removeReviewConfig : ModuleName -> Dict String a -> Dict String a
|
||||
removeReviewConfig moduleName dict =
|
||||
if moduleName == [ "ReviewConfig" ] then
|
||||
Dict.remove "config" dict
|
||||
|
||||
else
|
||||
dict
|
||||
name == "main" || name == "app"
|
||||
|
||||
|
||||
getRangesToRemove : List ( Int, String ) -> Bool -> String -> Int -> Maybe Range -> Range -> Range -> List Range
|
||||
|
@ -225,10 +225,10 @@ report context =
|
||||
)
|
||||
in
|
||||
( errors
|
||||
, List.foldl
|
||||
, Set.foldl
|
||||
useValue
|
||||
restOfScopes
|
||||
(Set.toList nonUsedVars)
|
||||
nonUsedVars
|
||||
)
|
||||
|
||||
_ ->
|
||||
|
4556
tests/Simplify.elm
4556
tests/Simplify.elm
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@ module Simplify.Normalize exposing (Comparison(..), areAllTheSame, compare, comp
|
||||
|
||||
import Dict
|
||||
import Elm.Syntax.Expression as Expression exposing (Expression)
|
||||
import Elm.Syntax.Infix as Infix
|
||||
import Elm.Syntax.Node as Node exposing (Node(..))
|
||||
import Elm.Syntax.Pattern as Pattern exposing (Pattern)
|
||||
import Elm.Syntax.Range as Range
|
||||
@ -68,6 +69,9 @@ normalize resources node =
|
||||
(normalize resources function)
|
||||
(normalize resources extraArgument)
|
||||
|
||||
Expression.OperatorApplication "<<" _ left right ->
|
||||
toNode (Expression.OperatorApplication ">>" Infix.Right (normalize resources right) (normalize resources left))
|
||||
|
||||
Expression.OperatorApplication "::" infixDirection element list ->
|
||||
let
|
||||
normalizedElement : Node Expression
|
||||
|
@ -8,7 +8,7 @@ import Elm.Processing
|
||||
import Elm.Syntax.Declaration as Declaration exposing (Declaration)
|
||||
import Elm.Syntax.Expression exposing (Expression(..), LetDeclaration(..))
|
||||
import Elm.Syntax.File exposing (File)
|
||||
import Elm.Syntax.Infix as Infix
|
||||
import Elm.Syntax.Infix as Infix exposing (InfixDirection(..))
|
||||
import Elm.Syntax.ModuleName exposing (ModuleName)
|
||||
import Elm.Syntax.Node as Node exposing (Node(..))
|
||||
import Elm.Syntax.Pattern exposing (Pattern(..))
|
||||
@ -488,6 +488,31 @@ simpleNormalizationTests =
|
||||
, expression = n (FunctionOrValue [] "x")
|
||||
}
|
||||
)
|
||||
, test "should not change anything about >>" <|
|
||||
\() ->
|
||||
"a >> b >> c >> d"
|
||||
|> normalizeAndExpect
|
||||
(OperatorApplication ">>"
|
||||
Right
|
||||
(n (FunctionOrValue [] "a"))
|
||||
(n
|
||||
(OperatorApplication ">>"
|
||||
Right
|
||||
(n (FunctionOrValue [] "b"))
|
||||
(n
|
||||
(OperatorApplication ">>"
|
||||
Right
|
||||
(n (FunctionOrValue [] "c"))
|
||||
(n (FunctionOrValue [] "d"))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
, test "should switch << to use >>" <|
|
||||
\() ->
|
||||
normalizeSourceCode [] Infer.empty "d << c << b << a"
|
||||
|> Expect.equal (normalizeSourceCode [] Infer.empty "a >> b >> c >> d")
|
||||
]
|
||||
|
||||
|
||||
@ -621,6 +646,12 @@ normalizeWithInferredAndExpect moduleNames inferredList expected source =
|
||||
|
||||
normalizeBase : List ( Range, ModuleName ) -> Infer.Inferred -> Expression -> String -> Expect.Expectation
|
||||
normalizeBase moduleNames inferred expected source =
|
||||
normalizeSourceCode moduleNames inferred source
|
||||
|> Expect.equal expected
|
||||
|
||||
|
||||
normalizeSourceCode : List ( Range, ModuleName ) -> Infer.Inferred -> String -> Expression
|
||||
normalizeSourceCode moduleNames inferred source =
|
||||
("module A exposing (..)\nvalue = " ++ source)
|
||||
|> parse
|
||||
|> getValue
|
||||
@ -629,7 +660,6 @@ normalizeBase moduleNames inferred expected source =
|
||||
, inferredConstants = ( inferred, [] )
|
||||
}
|
||||
|> Node.value
|
||||
|> Expect.equal expected
|
||||
|
||||
|
||||
{-| Parse source code into a AST.
|
||||
|
@ -1,31 +1,75 @@
|
||||
module Simplify.RangeDict exposing (RangeDict, empty, get, insert, member)
|
||||
module Simplify.RangeDict exposing (RangeDict, any, empty, get, insert, mapFromList, member, remove, singleton, union)
|
||||
|
||||
import Dict exposing (Dict)
|
||||
import Elm.Syntax.Range exposing (Range)
|
||||
|
||||
|
||||
type alias RangeDict v =
|
||||
Dict String v
|
||||
type RangeDict v
|
||||
= RangeDict (Dict String v)
|
||||
|
||||
|
||||
empty : RangeDict v
|
||||
empty =
|
||||
Dict.empty
|
||||
RangeDict Dict.empty
|
||||
|
||||
|
||||
singleton : Range -> v -> RangeDict v
|
||||
singleton range value =
|
||||
RangeDict (Dict.singleton (rangeAsString range) value)
|
||||
|
||||
|
||||
{-| Indirect conversion from a list to key-value pairs to avoid successive List.map calls.
|
||||
-}
|
||||
mapFromList : (a -> ( Range, v )) -> List a -> RangeDict v
|
||||
mapFromList toAssociation list =
|
||||
List.foldl
|
||||
(\element acc ->
|
||||
let
|
||||
( range, v ) =
|
||||
toAssociation element
|
||||
in
|
||||
Dict.insert (rangeAsString range) v acc
|
||||
)
|
||||
Dict.empty
|
||||
list
|
||||
|> RangeDict
|
||||
|
||||
|
||||
insert : Range -> v -> RangeDict v -> RangeDict v
|
||||
insert range =
|
||||
Dict.insert (rangeAsString range)
|
||||
insert range value (RangeDict rangeDict) =
|
||||
RangeDict (Dict.insert (rangeAsString range) value rangeDict)
|
||||
|
||||
|
||||
remove : Range -> RangeDict v -> RangeDict v
|
||||
remove range (RangeDict rangeDict) =
|
||||
RangeDict (Dict.remove (rangeAsString range) rangeDict)
|
||||
|
||||
|
||||
get : Range -> RangeDict v -> Maybe v
|
||||
get range =
|
||||
Dict.get (rangeAsString range)
|
||||
get range (RangeDict rangeDict) =
|
||||
Dict.get (rangeAsString range) rangeDict
|
||||
|
||||
|
||||
member : Range -> RangeDict v -> Bool
|
||||
member range =
|
||||
Dict.member (rangeAsString range)
|
||||
member range (RangeDict rangeDict) =
|
||||
Dict.member (rangeAsString range) rangeDict
|
||||
|
||||
|
||||
foldl : (v -> folded -> folded) -> folded -> RangeDict v -> folded
|
||||
foldl reduce initialFolded (RangeDict rangeDict) =
|
||||
Dict.foldl (\_ -> reduce) initialFolded rangeDict
|
||||
|
||||
|
||||
any : (v -> Bool) -> RangeDict v -> Bool
|
||||
any isFound rangeDict =
|
||||
foldl (\value soFar -> soFar || isFound value)
|
||||
False
|
||||
rangeDict
|
||||
|
||||
|
||||
union : RangeDict v -> RangeDict v -> RangeDict v
|
||||
union (RangeDict aRangeDict) (RangeDict bRangeDict) =
|
||||
RangeDict (Dict.union aRangeDict bRangeDict)
|
||||
|
||||
|
||||
rangeAsString : Range -> String
|
||||
|
File diff suppressed because it is too large
Load Diff
23
tests/String/Extra.elm
Normal file
23
tests/String/Extra.elm
Normal file
@ -0,0 +1,23 @@
|
||||
module String.Extra exposing (isCapitalized)
|
||||
|
||||
{-| Some utilities.
|
||||
-}
|
||||
|
||||
|
||||
{-| Check if the first character of a string is upper case, unicode aware.
|
||||
|
||||
Note that `Char.isUpper` returns the correct result only for ASCII characters, even though it can be called with any Unicode character.
|
||||
See <https://github.com/elm/core/pull/1138>
|
||||
|
||||
-}
|
||||
isCapitalized : String -> Bool
|
||||
isCapitalized string =
|
||||
case String.uncons string of
|
||||
Just ( char, _ ) ->
|
||||
-- 1. The character is its own upper variant.
|
||||
(char == Char.toUpper char)
|
||||
-- 2. The character is not its own lower variant.
|
||||
&& (char /= Char.toLower char)
|
||||
|
||||
Nothing ->
|
||||
False
|
Loading…
Reference in New Issue
Block a user