@@ -7,9 +7,11 @@ import Data.Either (Either(..))
77import Data.Foldable (for_)
88import Data.Maybe (Maybe(..))
99import Data.Newtype (unwrap)
10+import Data.Traversable (traverse)
1011import Effect (Effect)
1112import Effect.Class.Console (log)
1213import Purelint.Fix (applyAllFixes)
14+import Purelint.Tidy (formatExpr)
1315import Purelint.Rules.FmapId (fmapIdRule)
1416import Purelint.Rules.MapFusion (mapFusionRule)
1517import Purelint.Rules.UseTraverse (useTraverseRule)
@@ -20,8 +22,66 @@ import Purelint.Rules.EtaReduce (etaReduceRule)
2022import Purelint.Rules.EtaReduceDecl (etaReduceDeclRule)
2123import Purelint.Rules.RedundantBind (redundantBindRule)
2224import Purelint.Rules.LetToWhere (letToWhereRule)
25+import Purelint.Rules.RedundantIf (redundantIfRule)
26+import Purelint.Rules.FunctorLaw (functorLawRule)
27+import Purelint.Rules.UseVoid (useVoidRule)
28+import Purelint.Rules.UseJoin (useJoinRule)
29+import Purelint.Rules.UseAny (useAnyRule)
30+import Purelint.Rules.UseAll (useAllRule)
31+import Purelint.Rules.BooleanSimplify (booleanSimplifyRule)
32+import Purelint.Rules.CollapseLambdas (collapseLambdasRule)
33+import Purelint.Rules.UseConst (useConstRule)
34+import Purelint.Rules.UseNull (useNullRule)
35+import Purelint.Rules.UseFromMaybe (useFromMaybeRule)
36+import Purelint.Rules.UseIsJust (useIsJustRule)
37+import Purelint.Rules.UseIsNothing (useIsNothingRule)
38+import Purelint.Rules.UseWhen (useWhenRule)
39+import Purelint.Rules.UseUnless (useUnlessRule)
40+import Purelint.Rules.RedundantFlip (redundantFlipRule)
41+import Purelint.Rules.UseNotElem (useNotElemRule)
42+import Purelint.Rules.UseMinMax (useMinMaxRule)
43+import Purelint.Rules.MonoidIdentity (monoidIdentityRule)
44+import Purelint.Rules.UseFold (useFoldRule)
45+import Purelint.Rules.UseSequence (useSequenceRule)
46+import Purelint.Rules.UseTraverseSequence (useTraverseSequenceRule)
47+import Purelint.Rules.RedundantNot (redundantNotRule)
48+import Purelint.Rules.UseFstSnd (useFstSndRule)
49+import Purelint.Rules.UseMapMaybe (useMapMaybeRule)
50+import Purelint.Rules.UseGuardMaybe (useGuardMaybeRule)
51+import Purelint.Rules.UseMaybeMap (useMaybeMapRule)
52+import Purelint.Rules.UseApplicative (useApplicativeRule)
53+import Purelint.Rules.UseBindFlip (useBindFlipRule)
54+import Purelint.Rules.UseFor (useForRule)
55+import Purelint.Rules.RedundantGuard (redundantGuardRule)
56+import Purelint.Rules.UseComparing (useComparingRule)
57+import Purelint.Rules.UseOn (useOnRule)
58+import Purelint.Rules.UseFindMap (useFindMapRule)
59+import Purelint.Rules.UseLastReverse (useLastReverseRule)
60+import Purelint.Rules.RedundantReverse (redundantReverseRule)
61+import Purelint.Rules.UseBreak (useBreakRule)
62+import Purelint.Rules.UseSpan (useSpanRule)
63+import Purelint.Rules.UseMinimumSort (useMinimumSortRule)
64+import Purelint.Rules.UseBimap (useBimapRule)
65+import Purelint.Rules.UseEitherMap (useEitherMapRule)
66+import Purelint.Rules.EvaluateFst (evaluateFstRule)
67+import Purelint.Rules.EvaluateConst (evaluateConstRule)
68+import Purelint.Rules.UseOr (useOrRule)
69+import Purelint.Rules.UseAnd (useAndRule)
70+import Purelint.Rules.UseFoldMapId (useFoldMapIdRule)
71+import Purelint.Rules.WhenNot (whenNotRule)
72+import Purelint.Rules.UnlessNot (unlessNotRule)
73+import Purelint.Rules.UseZip (useZipRule)
74+import Purelint.Rules.UseReplicate (useReplicateRule)
75+import Purelint.Rules.UseUncurry (useUncurryRule)
76+import Purelint.Rules.RedundantNegate (redundantNegateRule)
77+import Purelint.Rules.UseFromJust (useFromJustRule)
78+import Purelint.Rules.UseHead (useHeadRule)
79+import Purelint.Rules.RedundantId (redundantIdRule)
80+import Purelint.Rules.NothingBind (nothingBindRule)
81+import Purelint.Rules.UseElemIndex (useElemIndexRule)
2382import Purelint.Rule (Rule)
24โimport Purelint.Runner (runRules)
83+import Purelint.Runner (runRules, getImportsFromSource, getModuleNames)
84+import Purelint.Imports (hasValue, isPreludeModule)
2585import Purelint.Types (LintResult(..), LintWarning(..), RuleId(..), SourceCode(..), Suggestion(..))
2686
2787-- | Simple test framework
@@ -98,6 +158,54 @@ main = do
98158 <> etaReduceDeclTests
99159 <> redundantBindTests
100160 <> letToWhereTests
161+ <> redundantIfTests
162+ <> functorLawTests
163+ <> useVoidTests
164+ <> useJoinTests
165+ <> useAnyTests
166+ <> useAllTests
167+ <> booleanSimplifyTests
168+ <> collapseLambdasTests
169+ <> useConstTests
170+ <> useNullTests
171+ <> useFromMaybeTests
172+ <> useIsJustTests
173+ <> useIsNothingTests
174+ <> useWhenTests
175+ <> useUnlessTests
176+ <> redundantFlipTests
177+ <> useNotElemTests
178+ <> useMinMaxTests
179+ <> monoidIdentityTests
180+ <> useFoldTests
181+ <> useSequenceTests
182+ <> useTraverseSequenceTests
183+ <> redundantNotTests
184+ <> useFstSndTests
185+ <> useMapMaybeTests
186+ <> useApplicativeTests
187+ <> useBindFlipTests
188+ <> useForTests
189+ <> redundantGuardTests
190+ <> useComparingTests
191+ <> useOnTests
192+ <> useFindMapTests
193+ <> useLastReverseTests
194+ <> redundantReverseTests
195+ <> useMinimumSortTests
196+ <> useBimapTests
197+ <> evaluateFstTests
198+ <> evaluateConstTests
199+ <> useOrTests
200+ <> useAndTests
201+ <> useFoldMapIdTests
202+ <> whenNotTests
203+ <> unlessNotTests
204+ <> useZipTests
205+ <> redundantNegateTests
206+ <> redundantIdTests
207+ <> nothingBindTests
208+ <> goldenTests
101209
102210 let passed = Array.length $ Array.filter _.passed results
103211 let total = Array.length results
@@ -115,9 +223,23 @@ main = do
115223 when (passed < total) do
116224 log "\nSome tests failed!"
117225
226+ -- Run purs-tidy formatting tests
227+ log "\nRunning purs-tidy formatting tests..."
228+ tidyResults <- runTidyTests
229+ let tidyPassed = Array.length $ Array.filter _.passed tidyResults
230+ let tidyTotal = Array.length tidyResults
231+
232+ let tidyFailures = Array.filter (not <<< _.passed) tidyResults
233+ when (Array.length tidyFailures > 0) do
234+ log "TIDY FAILURES:"
235+ for_ tidyFailures (\r -> log (" โ " <> r.name <> ": " <> r.message))
236+ log ""
237+
238+ log $ "Tidy tests: " <> show tidyPassed <> "/" <> show tidyTotal <> " passed"
239+
118240-- | Helper: wrap code in a module with Prelude import
119241withPrelude :: String -> String
120โwithPrelude code = "module Test where\nimport Prelude\n" <> code
242+withPrelude code = "module Test where\nimport Prelude\nimport Data.Maybe\nimport Data.Foldable\nimport Data.Tuple\nimport Data.Monoid\nimport Data.Traversable\nimport Data.Array\nimport Control.Alternative\nimport Data.Ord\nimport Data.Either\nimport Data.Bifunctor\nimport Data.List\n" <> code
121243
122244-- | Helper: wrap code in module without imports
123245withoutImports :: String -> String
@@ -317,6 +439,12 @@ redundantBindTests =
317439 (withPrelude "x = [1] >>= pure") 1
318440 , assertRuleId "RedundantBind: correct rule id" [redundantBindRule]
319441 (withPrelude "x = [1] >>= pure") "RedundantBind"
442+ , assertSuggestion "RedundantBind: x >>= pure suggestion" [redundantBindRule]
443+ (withPrelude "x = [1] >>= pure") "[1]"
444+ , assertWarningCount "RedundantBind: pure x >>= f" [redundantBindRule]
445+ (withPrelude "x = pure 1 >>= show") 1
446+ , assertSuggestion "RedundantBind: pure x >>= f suggestion" [redundantBindRule]
447+ (withPrelude "x = pure 1 >>= show") "show 1"
320448
321449 -- Negative cases
322450 , assertWarningCount "RedundantBind: no fire on x >>= f" [redundantBindRule]
@@ -349,3 +477,1109 @@ letToWhereTests =
349477 , assertSuggestion "LetToWhere: preserves spaces with multiple bindings" [letToWhereRule]
350478 (withPrelude "f x = let a = x + 1\n b = x * 2 in a + b") "a + b\n where\n a = x + 1\n b = x * 2"
351479 ]
480+
481+-- ============================================================================
482+-- RedundantIf Tests
483+-- ============================================================================
484+
485+redundantIfTests :: Array TestResult
486+redundantIfTests =
487+ -- Positive cases
488+ [ assertWarningCount "RedundantIf: if a then true else false" [redundantIfRule]
489+ (withPrelude "x = if foo then true else false") 1
490+ , assertRuleId "RedundantIf: correct rule id" [redundantIfRule]
491+ (withPrelude "x = if foo then true else false") "RedundantIf"
492+ , assertSuggestion "RedundantIf: suggests condition" [redundantIfRule]
493+ (withPrelude "x = if foo then true else false") "foo"
494+ , assertWarningCount "RedundantIf: if a then false else true" [redundantIfRule]
495+ (withPrelude "x = if foo then false else true") 1
496+ , assertSuggestion "RedundantIf: suggests not condition" [redundantIfRule]
497+ (withPrelude "x = if foo then false else true") "not foo"
498+
499+ -- Negative cases
500+ , assertWarningCount "RedundantIf: no fire on normal if" [redundantIfRule]
501+ (withPrelude "x = if foo then 1 else 2") 0
502+ , assertWarningCount "RedundantIf: no fire on if a then true else x" [redundantIfRule]
503+ (withPrelude "x = if foo then true else bar") 0
504+ ]
505+
506+-- ============================================================================
507+-- FunctorLaw Tests
508+-- ============================================================================
509+
510+functorLawTests :: Array TestResult
511+functorLawTests =
512+ -- Positive cases
513+ [ assertWarningCount "FunctorLaw: map f (map g x)" [functorLawRule]
514+ (withPrelude "x = map f (map g [1])") 1
515+ , assertRuleId "FunctorLaw: correct rule id" [functorLawRule]
516+ (withPrelude "x = map f (map g [1])") "FunctorLaw"
517+ , assertWarningCount "FunctorLaw: f <$> (g <$> x)" [functorLawRule]
518+ (withPrelude "x = f <$> (g <$> [1])") 1
519+
520+ -- Negative cases
521+ , assertWarningCount "FunctorLaw: no fire on single map" [functorLawRule]
522+ (withPrelude "x = map f [1]") 0
523+ , assertWarningCount "FunctorLaw: no fire without import" [functorLawRule]
524+ (withoutImports "x = map f (map g [1])") 0
525+ ]
526+
527+-- ============================================================================
528+-- UseVoid Tests
529+-- ============================================================================
530+
531+useVoidTests :: Array TestResult
532+useVoidTests =
533+ -- Positive cases
534+ [ assertWarningCount "UseVoid: a *> pure unit" [useVoidRule]
535+ (withPrelude "x = foo *> pure unit") 1
536+ , assertRuleId "UseVoid: correct rule id" [useVoidRule]
537+ (withPrelude "x = foo *> pure unit") "UseVoid"
538+ , assertSuggestion "UseVoid: suggests void" [useVoidRule]
539+ (withPrelude "x = foo *> pure unit") "void foo"
540+
541+ -- Negative cases
542+ , assertWarningCount "UseVoid: no fire on a *> pure x" [useVoidRule]
543+ (withPrelude "x = foo *> pure 1") 0
544+ , assertWarningCount "UseVoid: no fire without import" [useVoidRule]
545+ (withoutImports "x = foo *> pure unit") 0
546+ ]
547+
548+-- ============================================================================
549+-- UseJoin Tests
550+-- ============================================================================
551+
552+useJoinTests :: Array TestResult
553+useJoinTests =
554+ -- Positive cases
555+ [ assertWarningCount "UseJoin: x >>= identity" [useJoinRule]
556+ (withPrelude "x = foo >>= identity") 1
557+ , assertRuleId "UseJoin: correct rule id" [useJoinRule]
558+ (withPrelude "x = foo >>= identity") "UseJoin"
559+ , assertSuggestion "UseJoin: suggests join" [useJoinRule]
560+ (withPrelude "x = foo >>= identity") "join foo"
561+
562+ -- Negative cases
563+ , assertWarningCount "UseJoin: no fire on x >>= f" [useJoinRule]
564+ (withPrelude "x = foo >>= show") 0
565+ , assertWarningCount "UseJoin: no fire without import" [useJoinRule]
566+ (withoutImports "x = foo >>= identity") 0
567+ ]
568+
569+-- ============================================================================
570+-- UseAny Tests
571+-- ============================================================================
572+
573+useAnyTests :: Array TestResult
574+useAnyTests =
575+ -- Positive cases
576+ [ assertWarningCount "UseAny: or (map f x)" [useAnyRule]
577+ (withPrelude "x = or (map f [1])") 1
578+ , assertRuleId "UseAny: correct rule id" [useAnyRule]
579+ (withPrelude "x = or (map f [1])") "UseAny"
580+ , assertSuggestion "UseAny: suggests any" [useAnyRule]
581+ (withPrelude "x = or (map f [1])") "any f [1]"
582+
583+ -- Negative cases
584+ , assertWarningCount "UseAny: no fire on or alone" [useAnyRule]
585+ (withPrelude "x = or [true]") 0
586+ , assertWarningCount "UseAny: no fire without import" [useAnyRule]
587+ (withoutImports "x = or (map f [1])") 0
588+ ]
589+
590+-- ============================================================================
591+-- UseAll Tests
592+-- ============================================================================
593+
594+useAllTests :: Array TestResult
595+useAllTests =
596+ -- Positive cases
597+ [ assertWarningCount "UseAll: and (map f x)" [useAllRule]
598+ (withPrelude "x = and (map f [1])") 1
599+ , assertRuleId "UseAll: correct rule id" [useAllRule]
600+ (withPrelude "x = and (map f [1])") "UseAll"
601+ , assertSuggestion "UseAll: suggests all" [useAllRule]
602+ (withPrelude "x = and (map f [1])") "all f [1]"
603+
604+ -- Negative cases
605+ , assertWarningCount "UseAll: no fire on and alone" [useAllRule]
606+ (withPrelude "x = and [true]") 0
607+ , assertWarningCount "UseAll: no fire without import" [useAllRule]
608+ (withoutImports "x = and (map f [1])") 0
609+ ]
610+
611+-- ============================================================================
612+-- BooleanSimplify Tests
613+-- ============================================================================
614+
615+booleanSimplifyTests :: Array TestResult
616+booleanSimplifyTests =
617+ -- Positive cases
618+ [ assertWarningCount "BooleanSimplify: x == true" [booleanSimplifyRule]
619+ (withPrelude "x = foo == true") 1
620+ , assertRuleId "BooleanSimplify: correct rule id" [booleanSimplifyRule]
621+ (withPrelude "x = foo == true") "BooleanSimplify"
622+ , assertSuggestion "BooleanSimplify: x == true -> x" [booleanSimplifyRule]
623+ (withPrelude "x = foo == true") "foo"
624+ , assertWarningCount "BooleanSimplify: x == false" [booleanSimplifyRule]
625+ (withPrelude "x = foo == false") 1
626+ , assertSuggestion "BooleanSimplify: x == false -> not x" [booleanSimplifyRule]
627+ (withPrelude "x = foo == false") "not foo"
628+ , assertWarningCount "BooleanSimplify: true == x" [booleanSimplifyRule]
629+ (withPrelude "x = true == foo") 1
630+ , assertWarningCount "BooleanSimplify: false == x" [booleanSimplifyRule]
631+ (withPrelude "x = false == foo") 1
632+
633+ -- Negative cases
634+ , assertWarningCount "BooleanSimplify: no fire on x == y" [booleanSimplifyRule]
635+ (withPrelude "x = foo == bar") 0
636+ , assertWarningCount "BooleanSimplify: no fire without import" [booleanSimplifyRule]
637+ (withoutImports "x = foo == true") 0
638+ ]
639+
640+-- ============================================================================
641+-- CollapseLambdas Tests
642+-- ============================================================================
643+
644+collapseLambdasTests :: Array TestResult
645+collapseLambdasTests =
646+ -- Positive cases
647+ [ assertWarningCount "CollapseLambdas: \\x -> \\y -> body" [collapseLambdasRule]
648+ (withPrelude "x = \\a -> \\b -> a + b") 1
649+ , assertRuleId "CollapseLambdas: correct rule id" [collapseLambdasRule]
650+ (withPrelude "x = \\a -> \\b -> a + b") "CollapseLambdas"
651+ , assertSuggestion "CollapseLambdas: suggests collapsed" [collapseLambdasRule]
652+ (withPrelude "x = \\a -> \\b -> a + b") "\\a b -> a + b"
653+
654+ -- Negative cases
655+ , assertWarningCount "CollapseLambdas: no fire on single lambda" [collapseLambdasRule]
656+ (withPrelude "x = \\a -> a + 1") 0
657+ ]
658+
659+-- ============================================================================
660+-- UseConst Tests
661+-- ============================================================================
662+
663+useConstTests :: Array TestResult
664+useConstTests =
665+ -- Positive cases
666+ [ assertWarningCount "UseConst: \\x -> y (unused)" [useConstRule]
667+ (withPrelude "x = \\a -> 42") 1
668+ , assertRuleId "UseConst: correct rule id" [useConstRule]
669+ (withPrelude "x = \\a -> 42") "UseConst"
670+ , assertSuggestion "UseConst: suggests const" [useConstRule]
671+ (withPrelude "x = \\a -> 42") "const 42"
672+
673+ -- Negative cases
674+ , assertWarningCount "UseConst: no fire when param used" [useConstRule]
675+ (withPrelude "x = \\a -> a + 1") 0
676+ , assertWarningCount "UseConst: no fire without import" [useConstRule]
677+ (withoutImports "x = \\a -> 42") 0
678+ ]
679+
680+-- ============================================================================
681+-- UseNull Tests
682+-- ============================================================================
683+
684+useNullTests :: Array TestResult
685+useNullTests =
686+ -- Positive cases
687+ [ assertWarningCount "UseNull: length x == 0" [useNullRule]
688+ (withPrelude "x = length [1] == 0") 1
689+ , assertRuleId "UseNull: correct rule id" [useNullRule]
690+ (withPrelude "x = length [1] == 0") "UseNull"
691+ , assertSuggestion "UseNull: suggests null" [useNullRule]
692+ (withPrelude "x = length [1] == 0") "null [1]"
693+ , assertWarningCount "UseNull: 0 == length x" [useNullRule]
694+ (withPrelude "x = 0 == length [1]") 1
695+
696+ -- Negative cases
697+ , assertWarningCount "UseNull: no fire on length x == 1" [useNullRule]
698+ (withPrelude "x = length [1] == 1") 0
699+ , assertWarningCount "UseNull: no fire without import" [useNullRule]
700+ (withoutImports "x = length [1] == 0") 0
701+ ]
702+
703+-- ============================================================================
704+-- UseFromMaybe Tests
705+-- ============================================================================
706+
707+useFromMaybeTests :: Array TestResult
708+useFromMaybeTests =
709+ [ assertWarningCount "UseFromMaybe: maybe x identity" [useFromMaybeRule]
710+ (withPrelude "x = maybe 0 identity") 1
711+ , assertRuleId "UseFromMaybe: correct rule id" [useFromMaybeRule]
712+ (withPrelude "x = maybe 0 identity") "UseFromMaybe"
713+ , assertSuggestion "UseFromMaybe: suggests fromMaybe" [useFromMaybeRule]
714+ (withPrelude "x = maybe 0 identity") "fromMaybe 0"
715+
716+ -- Negative cases
717+ , assertWarningCount "UseFromMaybe: no fire on maybe x f" [useFromMaybeRule]
718+ (withPrelude "x = maybe 0 show") 0
719+ ]
720+
721+-- ============================================================================
722+-- UseIsJust Tests
723+-- ============================================================================
724+
725+useIsJustTests :: Array TestResult
726+useIsJustTests =
727+ [ assertWarningCount "UseIsJust: maybe false (const true)" [useIsJustRule]
728+ (withPrelude "x = maybe false (const true)") 1
729+ , assertRuleId "UseIsJust: correct rule id" [useIsJustRule]
730+ (withPrelude "x = maybe false (const true)") "UseIsJust"
731+ , assertSuggestion "UseIsJust: suggests isJust" [useIsJustRule]
732+ (withPrelude "x = maybe false (const true)") "isJust"
733+
734+ -- Negative cases
735+ , assertWarningCount "UseIsJust: no fire on maybe true (const false)" [useIsJustRule]
736+ (withPrelude "x = maybe true (const false)") 0
737+ ]
738+
739+-- ============================================================================
740+-- UseIsNothing Tests
741+-- ============================================================================
742+
743+useIsNothingTests :: Array TestResult
744+useIsNothingTests =
745+ [ assertWarningCount "UseIsNothing: maybe true (const false)" [useIsNothingRule]
746+ (withPrelude "x = maybe true (const false)") 1
747+ , assertRuleId "UseIsNothing: correct rule id" [useIsNothingRule]
748+ (withPrelude "x = maybe true (const false)") "UseIsNothing"
749+ , assertSuggestion "UseIsNothing: suggests isNothing" [useIsNothingRule]
750+ (withPrelude "x = maybe true (const false)") "isNothing"
751+
752+ -- Negative cases
753+ , assertWarningCount "UseIsNothing: no fire on maybe false (const true)" [useIsNothingRule]
754+ (withPrelude "x = maybe false (const true)") 0
755+ ]
756+
757+-- ============================================================================
758+-- UseWhen Tests
759+-- ============================================================================
760+
761+useWhenTests :: Array TestResult
762+useWhenTests =
763+ [ assertWarningCount "UseWhen: if x then y else pure unit" [useWhenRule]
764+ (withPrelude "x = if true then log \"hi\" else pure unit") 1
765+ , assertRuleId "UseWhen: correct rule id" [useWhenRule]
766+ (withPrelude "x = if true then log \"hi\" else pure unit") "UseWhen"
767+
768+ -- Negative cases
769+ , assertWarningCount "UseWhen: no fire on if x then pure unit else y" [useWhenRule]
770+ (withPrelude "x = if true then pure unit else log \"hi\"") 0
771+ ]
772+
773+-- ============================================================================
774+-- UseUnless Tests
775+-- ============================================================================
776+
777+useUnlessTests :: Array TestResult
778+useUnlessTests =
779+ [ assertWarningCount "UseUnless: if x then pure unit else y" [useUnlessRule]
780+ (withPrelude "x = if true then pure unit else log \"hi\"") 1
781+ , assertRuleId "UseUnless: correct rule id" [useUnlessRule]
782+ (withPrelude "x = if true then pure unit else log \"hi\"") "UseUnless"
783+
784+ -- Negative cases
785+ , assertWarningCount "UseUnless: no fire on if x then y else pure unit" [useUnlessRule]
786+ (withPrelude "x = if true then log \"hi\" else pure unit") 0
787+ ]
788+
789+-- ============================================================================
790+-- RedundantFlip Tests
791+-- ============================================================================
792+
793+redundantFlipTests :: Array TestResult
794+redundantFlipTests =
795+ [ assertWarningCount "RedundantFlip: flip (flip f)" [redundantFlipRule]
796+ (withPrelude "x = flip (flip add)") 1
797+ , assertRuleId "RedundantFlip: correct rule id" [redundantFlipRule]
798+ (withPrelude "x = flip (flip add)") "RedundantFlip"
799+ , assertSuggestion "RedundantFlip: suggests f" [redundantFlipRule]
800+ (withPrelude "x = flip (flip add)") "add"
801+
802+ -- Negative cases
803+ , assertWarningCount "RedundantFlip: no fire on flip f" [redundantFlipRule]
804+ (withPrelude "x = flip add") 0
805+ ]
806+
807+-- ============================================================================
808+-- UseNotElem Tests
809+-- ============================================================================
810+
811+useNotElemTests :: Array TestResult
812+useNotElemTests =
813+ [ assertWarningCount "UseNotElem: not (elem x y)" [useNotElemRule]
814+ (withPrelude "x = not (elem 1 [1,2,3])") 1
815+ , assertRuleId "UseNotElem: correct rule id" [useNotElemRule]
816+ (withPrelude "x = not (elem 1 [1,2,3])") "UseNotElem"
817+ , assertSuggestion "UseNotElem: suggests notElem" [useNotElemRule]
818+ (withPrelude "x = not (elem 1 [1,2,3])") "notElem 1 [1,2,3]"
819+
820+ -- Negative cases
821+ , assertWarningCount "UseNotElem: no fire on not (foo x)" [useNotElemRule]
822+ (withPrelude "x = not (foo 1)") 0
823+ ]
824+
825+-- ============================================================================
826+-- UseMinMax Tests
827+-- ============================================================================
828+
829+useMinMaxTests :: Array TestResult
830+useMinMaxTests =
831+ [ assertWarningCount "UseMinMax: if a > b then a else b" [useMinMaxRule]
832+ (withPrelude "x = if a > b then a else b") 1
833+ , assertRuleId "UseMinMax: correct rule id" [useMinMaxRule]
834+ (withPrelude "x = if a > b then a else b") "UseMinMax"
835+ , assertSuggestion "UseMinMax: suggests max" [useMinMaxRule]
836+ (withPrelude "x = if a > b then a else b") "max a b"
837+ , assertWarningCount "UseMinMax: if a < b then a else b -> min" [useMinMaxRule]
838+ (withPrelude "x = if a < b then a else b") 1
839+ , assertSuggestion "UseMinMax: suggests min" [useMinMaxRule]
840+ (withPrelude "x = if a < b then a else b") "min a b"
841+
842+ -- Negative cases
843+ , assertWarningCount "UseMinMax: no fire on unrelated if" [useMinMaxRule]
844+ (withPrelude "x = if a > b then c else d") 0
845+ ]
846+
847+-- ============================================================================
848+-- MonoidIdentity Tests
849+-- ============================================================================
850+
851+monoidIdentityTests :: Array TestResult
852+monoidIdentityTests =
853+ [ assertWarningCount "MonoidIdentity: mempty <> x" [monoidIdentityRule]
854+ (withPrelude "x = mempty <> [1,2,3]") 1
855+ , assertRuleId "MonoidIdentity: correct rule id" [monoidIdentityRule]
856+ (withPrelude "x = mempty <> [1,2,3]") "MonoidIdentity"
857+ , assertSuggestion "MonoidIdentity: suggests x" [monoidIdentityRule]
858+ (withPrelude "x = mempty <> [1,2,3]") "[1,2,3]"
859+ , assertWarningCount "MonoidIdentity: x <> mempty" [monoidIdentityRule]
860+ (withPrelude "x = [1,2,3] <> mempty") 1
861+
862+ -- Negative cases
863+ , assertWarningCount "MonoidIdentity: no fire on x <> y" [monoidIdentityRule]
864+ (withPrelude "x = [1] <> [2]") 0
865+ ]
866+
867+-- ============================================================================
868+-- UseFold Tests
869+-- ============================================================================
870+
871+useFoldTests :: Array TestResult
872+useFoldTests =
873+ [ assertWarningCount "UseFold: foldMap identity" [useFoldRule]
874+ (withPrelude "x = foldMap identity") 1
875+ , assertRuleId "UseFold: correct rule id" [useFoldRule]
876+ (withPrelude "x = foldMap identity") "UseFold"
877+ , assertSuggestion "UseFold: suggests fold" [useFoldRule]
878+ (withPrelude "x = foldMap identity") "fold"
879+
880+ -- Negative cases
881+ , assertWarningCount "UseFold: no fire on foldMap f" [useFoldRule]
882+ (withPrelude "x = foldMap show") 0
883+ ]
884+
885+-- ============================================================================
886+-- UseSequence Tests
887+-- ============================================================================
888+
889+useSequenceTests :: Array TestResult
890+useSequenceTests =
891+ [ assertWarningCount "UseSequence: traverse identity" [useSequenceRule]
892+ (withPrelude "x = traverse identity") 1
893+ , assertRuleId "UseSequence: correct rule id" [useSequenceRule]
894+ (withPrelude "x = traverse identity") "UseSequence"
895+ , assertSuggestion "UseSequence: suggests sequence" [useSequenceRule]
896+ (withPrelude "x = traverse identity") "sequence"
897+
898+ -- Negative cases
899+ , assertWarningCount "UseSequence: no fire on traverse f" [useSequenceRule]
900+ (withPrelude "x = traverse show") 0
901+ ]
902+
903+-- ============================================================================
904+-- UseTraverseSequence Tests
905+-- ============================================================================
906+
907+useTraverseSequenceTests :: Array TestResult
908+useTraverseSequenceTests =
909+ [ assertWarningCount "UseTraverseSequence: sequence (map f x)" [useTraverseSequenceRule]
910+ (withPrelude "x = sequence (map show [1,2,3])") 1
911+ , assertRuleId "UseTraverseSequence: correct rule id" [useTraverseSequenceRule]
912+ (withPrelude "x = sequence (map show [1,2,3])") "UseTraverseSequence"
913+ , assertSuggestion "UseTraverseSequence: suggests traverse" [useTraverseSequenceRule]
914+ (withPrelude "x = sequence (map show [1,2,3])") "traverse show [1,2,3]"
915+
916+ -- Negative cases
917+ , assertWarningCount "UseTraverseSequence: no fire on sequence xs" [useTraverseSequenceRule]
918+ (withPrelude "x = sequence [Just 1]") 0
919+ ]
920+
921+-- ============================================================================
922+-- RedundantNot Tests
923+-- ============================================================================
924+
925+redundantNotTests :: Array TestResult
926+redundantNotTests =
927+ [ assertWarningCount "RedundantNot: not (not x)" [redundantNotRule]
928+ (withPrelude "x = not (not true)") 1
929+ , assertRuleId "RedundantNot: correct rule id" [redundantNotRule]
930+ (withPrelude "x = not (not true)") "RedundantNot"
931+ , assertSuggestion "RedundantNot: suggests x" [redundantNotRule]
932+ (withPrelude "x = not (not foo)") "foo"
933+
934+ -- Negative cases
935+ , assertWarningCount "RedundantNot: no fire on not x" [redundantNotRule]
936+ (withPrelude "x = not true") 0
937+ ]
938+
939+-- ============================================================================
940+-- UseFstSnd Tests
941+-- ============================================================================
942+
943+useFstSndTests :: Array TestResult
944+useFstSndTests =
945+ [ assertWarningCount "UseFstSnd: \\(Tuple x y) -> x" [useFstSndRule]
946+ (withPrelude "x = \\(Tuple a b) -> a") 1
947+ , assertRuleId "UseFstSnd: correct rule id" [useFstSndRule]
948+ (withPrelude "x = \\(Tuple a b) -> a") "UseFstSnd"
949+ , assertSuggestion "UseFstSnd: suggests fst" [useFstSndRule]
950+ (withPrelude "x = \\(Tuple a b) -> a") "fst"
951+ , assertWarningCount "UseFstSnd: \\(Tuple x y) -> y" [useFstSndRule]
952+ (withPrelude "x = \\(Tuple a b) -> b") 1
953+ , assertSuggestion "UseFstSnd: suggests snd" [useFstSndRule]
954+ (withPrelude "x = \\(Tuple a b) -> b") "snd"
955+
956+ -- Negative cases
957+ , assertWarningCount "UseFstSnd: no fire when using both" [useFstSndRule]
958+ (withPrelude "x = \\(Tuple a b) -> a + b") 0
959+ ]
960+
961+-- ============================================================================
962+-- UseMapMaybe Tests
963+-- ============================================================================
964+
965+useMapMaybeTests :: Array TestResult
966+useMapMaybeTests =
967+ [ assertWarningCount "UseMapMaybe: catMaybes (map f x)" [useMapMaybeRule]
968+ (withPrelude "x = catMaybes (map f [1,2,3])") 1
969+ , assertRuleId "UseMapMaybe: correct rule id" [useMapMaybeRule]
970+ (withPrelude "x = catMaybes (map f [1,2,3])") "UseMapMaybe"
971+ , assertSuggestion "UseMapMaybe: suggests mapMaybe" [useMapMaybeRule]
972+ (withPrelude "x = catMaybes (map f [1,2,3])") "mapMaybe f [1,2,3]"
973+ ]
974+
975+-- ============================================================================
976+-- UseApplicative Tests
977+-- ============================================================================
978+
979+useApplicativeTests :: Array TestResult
980+useApplicativeTests =
981+ [ assertWarningCount "UseApplicative: pure f <*> x" [useApplicativeRule]
982+ (withPrelude "x = pure f <*> y") 1
983+ , assertRuleId "UseApplicative: correct rule id" [useApplicativeRule]
984+ (withPrelude "x = pure f <*> y") "UseApplicative"
985+ , assertSuggestion "UseApplicative: suggests f <$> x" [useApplicativeRule]
986+ (withPrelude "x = pure f <*> y") "f <$> y"
987+ ]
988+
989+-- ============================================================================
990+-- UseBindFlip Tests
991+-- ============================================================================
992+
993+useBindFlipTests :: Array TestResult
994+useBindFlipTests =
995+ [ assertWarningCount "UseBindFlip: join (map f x)" [useBindFlipRule]
996+ (withPrelude "x = join (map f y)") 1
997+ , assertRuleId "UseBindFlip: correct rule id" [useBindFlipRule]
998+ (withPrelude "x = join (map f y)") "UseBindFlip"
999+ , assertSuggestion "UseBindFlip: suggests x >>= f" [useBindFlipRule]
1000+ (withPrelude "x = join (map f y)") "y >>= f"
1001+ ]
1002+
1003+-- ============================================================================
1004+-- UseFor Tests
1005+-- ============================================================================
1006+
1007+useForTests :: Array TestResult
1008+useForTests =
1009+ [ assertWarningCount "UseFor: flip traverse" [useForRule]
1010+ (withPrelude "x = flip traverse") 1
1011+ , assertRuleId "UseFor: correct rule id" [useForRule]
1012+ (withPrelude "x = flip traverse") "UseFor"
1013+ , assertSuggestion "UseFor: suggests for" [useForRule]
1014+ (withPrelude "x = flip traverse") "for"
1015+ , assertWarningCount "UseFor: flip traverse_" [useForRule]
1016+ (withPrelude "x = flip traverse_") 1
1017+ , assertSuggestion "UseFor: suggests for_" [useForRule]
1018+ (withPrelude "x = flip traverse_") "for_"
1019+ ]
1020+
1021+-- ============================================================================
1022+-- RedundantGuard Tests
1023+-- ============================================================================
1024+
1025+redundantGuardTests :: Array TestResult
1026+redundantGuardTests =
1027+ [ assertWarningCount "RedundantGuard: guard true" [redundantGuardRule]
1028+ (withPrelude "x = guard true") 1
1029+ , assertRuleId "RedundantGuard: correct rule id" [redundantGuardRule]
1030+ (withPrelude "x = guard true") "RedundantGuard"
1031+ , assertSuggestion "RedundantGuard: suggests pure unit" [redundantGuardRule]
1032+ (withPrelude "x = guard true") "pure unit"
1033+ , assertWarningCount "RedundantGuard: guard false" [redundantGuardRule]
1034+ (withPrelude "x = guard false") 1
1035+ ]
1036+
1037+-- ============================================================================
1038+-- UseComparing Tests
1039+-- ============================================================================
1040+
1041+useComparingTests :: Array TestResult
1042+useComparingTests =
1043+ [ assertWarningCount "UseComparing: \\x y -> compare (f x) (f y)" [useComparingRule]
1044+ (withPrelude "x = \\a b -> compare (f a) (f b)") 1
1045+ , assertRuleId "UseComparing: correct rule id" [useComparingRule]
1046+ (withPrelude "x = \\a b -> compare (f a) (f b)") "UseComparing"
1047+ , assertSuggestion "UseComparing: suggests comparing" [useComparingRule]
1048+ (withPrelude "x = \\a b -> compare (f a) (f b)") "comparing f"
1049+ ]
1050+
1051+-- ============================================================================
1052+-- UseOn Tests
1053+-- ============================================================================
1054+
1055+useOnTests :: Array TestResult
1056+useOnTests =
1057+ [ assertWarningCount "UseOn: \\x y -> f (g x) (g y)" [useOnRule]
1058+ (withPrelude "x = \\a b -> f (g a) (g b)") 1
1059+ , assertRuleId "UseOn: correct rule id" [useOnRule]
1060+ (withPrelude "x = \\a b -> f (g a) (g b)") "UseOn"
1061+ , assertSuggestion "UseOn: suggests on f g" [useOnRule]
1062+ (withPrelude "x = \\a b -> f (g a) (g b)") "on f g"
1063+ ]
1064+
1065+-- ============================================================================
1066+-- UseFindMap Tests
1067+-- ============================================================================
1068+
1069+useFindMapTests :: Array TestResult
1070+useFindMapTests =
1071+ [ assertWarningCount "UseFindMap: head (mapMaybe f x)" [useFindMapRule]
1072+ (withPrelude "x = head (mapMaybe f [1,2,3])") 1
1073+ , assertRuleId "UseFindMap: correct rule id" [useFindMapRule]
1074+ (withPrelude "x = head (mapMaybe f [1,2,3])") "UseFindMap"
1075+ , assertSuggestion "UseFindMap: suggests findMap" [useFindMapRule]
1076+ (withPrelude "x = head (mapMaybe f [1,2,3])") "findMap f [1,2,3]"
1077+ ]
1078+
1079+-- ============================================================================
1080+-- UseLastReverse Tests
1081+-- ============================================================================
1082+
1083+useLastReverseTests :: Array TestResult
1084+useLastReverseTests =
1085+ [ assertWarningCount "UseLastReverse: head (reverse x)" [useLastReverseRule]
1086+ (withPrelude "x = head (reverse [1,2,3])") 1
1087+ , assertRuleId "UseLastReverse: correct rule id" [useLastReverseRule]
1088+ (withPrelude "x = head (reverse [1,2,3])") "UseLastReverse"
1089+ , assertSuggestion "UseLastReverse: suggests last" [useLastReverseRule]
1090+ (withPrelude "x = head (reverse [1,2,3])") "last [1,2,3]"
1091+ ]
1092+
1093+-- ============================================================================
1094+-- RedundantReverse Tests
1095+-- ============================================================================
1096+
1097+redundantReverseTests :: Array TestResult
1098+redundantReverseTests =
1099+ [ assertWarningCount "RedundantReverse: reverse (reverse x)" [redundantReverseRule]
1100+ (withPrelude "x = reverse (reverse [1,2,3])") 1
1101+ , assertRuleId "RedundantReverse: correct rule id" [redundantReverseRule]
1102+ (withPrelude "x = reverse (reverse [1,2,3])") "RedundantReverse"
1103+ , assertSuggestion "RedundantReverse: suggests x" [redundantReverseRule]
1104+ (withPrelude "x = reverse (reverse foo)") "foo"
1105+ ]
1106+
1107+-- ============================================================================
1108+-- UseMinimumSort Tests
1109+-- ============================================================================
1110+
1111+useMinimumSortTests :: Array TestResult
1112+useMinimumSortTests =
1113+ [ assertWarningCount "UseMinimumSort: head (sort x)" [useMinimumSortRule]
1114+ (withPrelude "x = head (sort [1,2,3])") 1
1115+ , assertRuleId "UseMinimumSort: correct rule id" [useMinimumSortRule]
1116+ (withPrelude "x = head (sort [1,2,3])") "UseMinimumSort"
1117+ , assertSuggestion "UseMinimumSort: suggests minimum" [useMinimumSortRule]
1118+ (withPrelude "x = head (sort [1,2,3])") "minimum [1,2,3]"
1119+ ]
1120+
1121+-- ============================================================================
1122+-- UseBimap Tests
1123+-- ============================================================================
1124+
1125+useBimapTests :: Array TestResult
1126+useBimapTests =
1127+ [ assertWarningCount "UseBimap: bimap id g" [useBimapRule]
1128+ (withPrelude "x = bimap identity show") 1
1129+ , assertRuleId "UseBimap: correct rule id" [useBimapRule]
1130+ (withPrelude "x = bimap identity show") "UseBimap"
1131+ , assertSuggestion "UseBimap: suggests second" [useBimapRule]
1132+ (withPrelude "x = bimap identity show") "second show"
1133+ ]
1134+
1135+-- ============================================================================
1136+-- EvaluateFst Tests
1137+-- ============================================================================
1138+
1139+evaluateFstTests :: Array TestResult
1140+evaluateFstTests =
1141+ [ assertWarningCount "EvaluateFst: fst (Tuple x y)" [evaluateFstRule]
1142+ (withPrelude "x = fst (Tuple 1 2)") 1
1143+ , assertRuleId "EvaluateFst: correct rule id" [evaluateFstRule]
1144+ (withPrelude "x = fst (Tuple 1 2)") "EvaluateFst"
1145+ , assertSuggestion "EvaluateFst: suggests x" [evaluateFstRule]
1146+ (withPrelude "x = fst (Tuple 1 2)") "1"
1147+ , assertWarningCount "EvaluateFst: snd (Tuple x y)" [evaluateFstRule]
1148+ (withPrelude "x = snd (Tuple 1 2)") 1
1149+ , assertSuggestion "EvaluateFst: suggests y" [evaluateFstRule]
1150+ (withPrelude "x = snd (Tuple 1 2)") "2"
1151+ ]
1152+
1153+-- ============================================================================
1154+-- EvaluateConst Tests
1155+-- ============================================================================
1156+
1157+evaluateConstTests :: Array TestResult
1158+evaluateConstTests =
1159+ [ assertWarningCount "EvaluateConst: const x y" [evaluateConstRule]
1160+ (withPrelude "x = const 42 foo") 1
1161+ , assertRuleId "EvaluateConst: correct rule id" [evaluateConstRule]
1162+ (withPrelude "x = const 42 foo") "EvaluateConst"
1163+ , assertSuggestion "EvaluateConst: suggests x" [evaluateConstRule]
1164+ (withPrelude "x = const 42 foo") "42"
1165+ ]
1166+
1167+-- ============================================================================
1168+-- UseOr Tests
1169+-- ============================================================================
1170+
1171+useOrTests :: Array TestResult
1172+useOrTests =
1173+ [ assertWarningCount "UseOr: any identity" [useOrRule]
1174+ (withPrelude "x = any identity [true, false]") 1
1175+ , assertRuleId "UseOr: correct rule id" [useOrRule]
1176+ (withPrelude "x = any identity [true, false]") "UseOr"
1177+ , assertSuggestion "UseOr: suggests or" [useOrRule]
1178+ (withPrelude "x = any identity [true, false]") "or [true, false]"
1179+ ]
1180+
1181+-- ============================================================================
1182+-- UseAnd Tests
1183+-- ============================================================================
1184+
1185+useAndTests :: Array TestResult
1186+useAndTests =
1187+ [ assertWarningCount "UseAnd: all identity" [useAndRule]
1188+ (withPrelude "x = all identity [true, false]") 1
1189+ , assertRuleId "UseAnd: correct rule id" [useAndRule]
1190+ (withPrelude "x = all identity [true, false]") "UseAnd"
1191+ , assertSuggestion "UseAnd: suggests and" [useAndRule]
1192+ (withPrelude "x = all identity [true, false]") "and [true, false]"
1193+ ]
1194+
1195+-- ============================================================================
1196+-- UseFoldMapId Tests
1197+-- ============================================================================
1198+
1199+useFoldMapIdTests :: Array TestResult
1200+useFoldMapIdTests =
1201+ [ assertWarningCount "UseFoldMapId: foldMap identity" [useFoldMapIdRule]
1202+ (withPrelude "x = foldMap identity") 1
1203+ , assertRuleId "UseFoldMapId: correct rule id" [useFoldMapIdRule]
1204+ (withPrelude "x = foldMap identity") "UseFoldMapId"
1205+ , assertSuggestion "UseFoldMapId: suggests fold" [useFoldMapIdRule]
1206+ (withPrelude "x = foldMap identity") "fold"
1207+ ]
1208+
1209+-- ============================================================================
1210+-- WhenNot Tests
1211+-- ============================================================================
1212+
1213+whenNotTests :: Array TestResult
1214+whenNotTests =
1215+ [ assertWarningCount "WhenNot: when (not x)" [whenNotRule]
1216+ (withPrelude "x = when (not cond) action") 1
1217+ , assertRuleId "WhenNot: correct rule id" [whenNotRule]
1218+ (withPrelude "x = when (not cond) action") "WhenNot"
1219+ , assertSuggestion "WhenNot: suggests unless" [whenNotRule]
1220+ (withPrelude "x = when (not cond) action") "unless cond action"
1221+ ]
1222+
1223+-- ============================================================================
1224+-- UnlessNot Tests
1225+-- ============================================================================
1226+
1227+unlessNotTests :: Array TestResult
1228+unlessNotTests =
1229+ [ assertWarningCount "UnlessNot: unless (not x)" [unlessNotRule]
1230+ (withPrelude "x = unless (not cond) action") 1
1231+ , assertRuleId "UnlessNot: correct rule id" [unlessNotRule]
1232+ (withPrelude "x = unless (not cond) action") "UnlessNot"
1233+ , assertSuggestion "UnlessNot: suggests when" [unlessNotRule]
1234+ (withPrelude "x = unless (not cond) action") "when cond action"
1235+ ]
1236+
1237+-- ============================================================================
1238+-- UseZip Tests
1239+-- ============================================================================
1240+
1241+useZipTests :: Array TestResult
1242+useZipTests =
1243+ [ assertWarningCount "UseZip: zipWith Tuple" [useZipRule]
1244+ (withPrelude "x = zipWith Tuple [1] [2]") 1
1245+ , assertRuleId "UseZip: correct rule id" [useZipRule]
1246+ (withPrelude "x = zipWith Tuple [1] [2]") "UseZip"
1247+ , assertSuggestion "UseZip: suggests zip" [useZipRule]
1248+ (withPrelude "x = zipWith Tuple [1] [2]") "zip [1] [2]"
1249+ ]
1250+
1251+-- ============================================================================
1252+-- RedundantNegate Tests
1253+-- ============================================================================
1254+
1255+redundantNegateTests :: Array TestResult
1256+redundantNegateTests =
1257+ [ assertWarningCount "RedundantNegate: negate (negate x)" [redundantNegateRule]
1258+ (withPrelude "x = negate (negate 5)") 1
1259+ , assertRuleId "RedundantNegate: correct rule id" [redundantNegateRule]
1260+ (withPrelude "x = negate (negate 5)") "RedundantNegate"
1261+ , assertSuggestion "RedundantNegate: suggests x" [redundantNegateRule]
1262+ (withPrelude "x = negate (negate foo)") "foo"
1263+ ]
1264+
1265+-- ============================================================================
1266+-- RedundantId Tests
1267+-- ============================================================================
1268+
1269+redundantIdTests :: Array TestResult
1270+redundantIdTests =
1271+ [ assertWarningCount "RedundantId: identity x" [redundantIdRule]
1272+ (withPrelude "x = identity 42") 1
1273+ , assertRuleId "RedundantId: correct rule id" [redundantIdRule]
1274+ (withPrelude "x = identity 42") "RedundantId"
1275+ , assertSuggestion "RedundantId: suggests x" [redundantIdRule]
1276+ (withPrelude "x = identity foo") "foo"
1277+ ]
1278+
1279+-- ============================================================================
1280+-- NothingBind Tests
1281+-- ============================================================================
1282+
1283+nothingBindTests :: Array TestResult
1284+nothingBindTests =
1285+ [ assertWarningCount "NothingBind: Nothing >>= f" [nothingBindRule]
1286+ (withPrelude "x = Nothing >>= show") 1
1287+ , assertRuleId "NothingBind: correct rule id" [nothingBindRule]
1288+ (withPrelude "x = Nothing >>= show") "NothingBind"
1289+ , assertSuggestion "NothingBind: suggests Nothing" [nothingBindRule]
1290+ (withPrelude "x = Nothing >>= show") "Nothing"
1291+ ]
1292+
1293+-- ============================================================================
1294+-- Golden Tests: LintExamples.purs
1295+-- ============================================================================
1296+
1297+-- | All rules for golden testing
1298+allRulesForGolden :: Array Rule
1299+allRulesForGolden =
1300+ [ fmapIdRule
1301+ , mapFusionRule
1302+ , useTraverseRule
1303+ , concatMapRule
1304+ , notEqualRule
1305+ , useGuardRule
1306+ , etaReduceRule
1307+ , etaReduceDeclRule
1308+ , redundantBindRule
1309+ , letToWhereRule
1310+ , redundantIfRule
1311+ , functorLawRule
1312+ , useVoidRule
1313+ , useJoinRule
1314+ , useAnyRule
1315+ , useAllRule
1316+ , booleanSimplifyRule
1317+ , collapseLambdasRule
1318+ , useConstRule
1319+ , useNullRule
1320+ , useFromMaybeRule
1321+ , useIsJustRule
1322+ , useIsNothingRule
1323+ , useWhenRule
1324+ , useUnlessRule
1325+ , redundantFlipRule
1326+ , useNotElemRule
1327+ , useMinMaxRule
1328+ , monoidIdentityRule
1329+ , useFoldRule
1330+ , useSequenceRule
1331+ , useTraverseSequenceRule
1332+ , redundantNotRule
1333+ , useFstSndRule
1334+ , useMapMaybeRule
1335+ , useGuardMaybeRule
1336+ , useMaybeMapRule
1337+ , useApplicativeRule
1338+ , useBindFlipRule
1339+ , useForRule
1340+ , redundantGuardRule
1341+ , useComparingRule
1342+ , useOnRule
1343+ , useFindMapRule
1344+ ]
1345+
1346+-- | Assert that a specific rule fires at least once when running all rules
1347+assertRuleFires :: String -> String -> String -> TestResult
1348+assertRuleFires name source ruleId =
1349+ case runRules allRulesForGolden (SourceCode source) of
1350+ Left err -> runTest name false ("Parse error: " <> err)
1351+ Right (LintResult result) ->
1352+ let matches = Array.filter (\(LintWarning w) -> unwrap w.ruleId == ruleId) result.warnings
1353+ in if Array.length matches > 0
1354+ then runTest name true "OK"
1355+ else runTest name false ("Rule " <> ruleId <> " did not fire")
1356+
1357+-- | The embedded LintExamples source
1358+lintExamplesSource :: String
1359+lintExamplesSource = """module Test.LintExamples where
1360+
1361+import Prelude
1362+
1363+import Control.Alternative
1364+import Control.Monad
1365+import Data.Array
1366+import Data.Foldable
1367+import Data.Maybe
1368+import Data.Monoid
1369+import Data.Ord
1370+import Data.Traversable
1371+import Data.Tuple
1372+
1373+-- FmapId
1374+fmapId1 = map identity [ 1, 2, 3 ]
1375+
1376+-- MapFusion
1377+mapFusion1 = map show (map (_ + 1) [ 1, 2, 3 ])
1378+
1379+-- UseTraverse
1380+useTraverse1 f xs = sequence (map f xs)
1381+
1382+-- ConcatMap
1383+concatMap1 f xs = join (map f xs)
1384+
1385+-- NotEqual
1386+notEqual1 x y = not (x == y)
1387+
1388+-- UseGuard
1389+useGuard1 cond action = if cond then action else pure unit
1390+
1391+-- EtaReduce
1392+etaReduce1 = \x -> show x
1393+
1394+-- EtaReduceDecl
1395+etaReduceDecl1 x = show x
1396+
1397+-- RedundantBind
1398+redundantBind1 x f = pure x >>= f
1399+
1400+-- LetToWhere
1401+letToWhere1 y z = let x = y in z
1402+
1403+-- RedundantIf
1404+redundantIf1 a = if a then true else false
1405+
1406+-- UseVoid
1407+useVoid1 a = a *> pure unit
1408+
1409+-- UseJoin
1410+useJoin1 x = x >>= identity
1411+
1412+-- UseAny
1413+useAny1 f xs = or (map f xs)
1414+
1415+-- UseAll
1416+useAll1 f xs = and (map f xs)
1417+
1418+-- BooleanSimplify
1419+booleanSimplify1 x = x == true
1420+
1421+-- CollapseLambdas
1422+collapseLambdas1 = \x -> \y -> x + y
1423+
1424+-- UseConst
1425+useConst1 = \x -> 42
1426+
1427+-- UseNull
1428+useNull1 xs = length xs == 0
1429+
1430+-- UseFromMaybe
1431+useFromMaybe1 def = maybe def identity
1432+
1433+-- UseIsJust
1434+useIsJust1 = maybe false (const true)
1435+
1436+-- UseIsNothing
1437+useIsNothing1 = maybe true (const false)
1438+
1439+-- UseWhen
1440+useWhen1 cond action = if cond then action else pure unit
1441+
1442+-- UseUnless
1443+useUnless1 cond action = if cond then pure unit else action
1444+
1445+-- RedundantFlip
1446+redundantFlip1 f = flip (flip f)
1447+
1448+-- UseNotElem
1449+useNotElem1 x xs = not (elem x xs)
1450+
1451+-- UseMinMax
1452+useMax1 a b = if a > b then a else b
1453+
1454+-- MonoidIdentity
1455+monoidIdentity1 x = mempty <> x
1456+
1457+-- UseFold
1458+useFold1 = foldMap identity
1459+
1460+-- UseSequence
1461+useSequence1 = traverse identity
1462+
1463+-- UseTraverseSequence
1464+useTraverseSequence1 f xs = sequence (map f xs)
1465+
1466+-- RedundantNot
1467+redundantNot1 x = not (not x)
1468+
1469+-- UseFstSnd
1470+useFst1 = \(Tuple x y) -> x
1471+
1472+-- UseMapMaybe
1473+useMapMaybe1 = catMaybes (map f [1,2,3])
1474+
1475+-- UseApplicative
1476+useApplicative1 = pure f <*> x
1477+
1478+-- UseBindFlip
1479+useBindFlip1 = join (map f x)
1480+
1481+-- UseFor
1482+useFor1 = flip traverse
1483+
1484+-- RedundantGuard
1485+redundantGuard1 = guard true
1486+
1487+-- UseComparing
1488+useComparing1 = \a b -> compare (f a) (f b)
1489+
1490+-- UseOn
1491+useOn1 = \a b -> f (g a) (g b)
1492+
1493+-- UseFindMap
1494+useFindMap1 = head (mapMaybe f [1,2,3])
1495+"""
1496+
1497+goldenTests :: Array TestResult
1498+goldenTests =
1499+ [ assertRuleFires "Golden: FmapId fires" lintExamplesSource "FmapId"
1500+ , assertRuleFires "Golden: MapFusion fires" lintExamplesSource "MapFusion"
1501+ , assertRuleFires "Golden: UseTraverse fires" lintExamplesSource "UseTraverse"
1502+ , assertRuleFires "Golden: ConcatMap fires" lintExamplesSource "ConcatMap"
1503+ , assertRuleFires "Golden: NotEqual fires" lintExamplesSource "NotEqual"
1504+ , assertRuleFires "Golden: UseGuard fires" lintExamplesSource "UseGuard"
1505+ , assertRuleFires "Golden: EtaReduce fires" lintExamplesSource "EtaReduce"
1506+ , assertRuleFires "Golden: EtaReduceDecl fires" lintExamplesSource "EtaReduceDecl"
1507+ , assertRuleFires "Golden: RedundantBind fires" lintExamplesSource "RedundantBind"
1508+ , assertRuleFires "Golden: LetToWhere fires" lintExamplesSource "LetToWhere"
1509+ , assertRuleFires "Golden: RedundantIf fires" lintExamplesSource "RedundantIf"
1510+ , assertRuleFires "Golden: UseVoid fires" lintExamplesSource "UseVoid"
1511+ , assertRuleFires "Golden: UseJoin fires" lintExamplesSource "UseJoin"
1512+ , assertRuleFires "Golden: UseAny fires" lintExamplesSource "UseAny"
1513+ , assertRuleFires "Golden: UseAll fires" lintExamplesSource "UseAll"
1514+ , assertRuleFires "Golden: BooleanSimplify fires" lintExamplesSource "BooleanSimplify"
1515+ , assertRuleFires "Golden: CollapseLambdas fires" lintExamplesSource "CollapseLambdas"
1516+ , assertRuleFires "Golden: UseConst fires" lintExamplesSource "UseConst"
1517+ , assertRuleFires "Golden: UseNull fires" lintExamplesSource "UseNull"
1518+ , assertRuleFires "Golden: UseFromMaybe fires" lintExamplesSource "UseFromMaybe"
1519+ , assertRuleFires "Golden: UseIsJust fires" lintExamplesSource "UseIsJust"
1520+ , assertRuleFires "Golden: UseIsNothing fires" lintExamplesSource "UseIsNothing"
1521+ , assertRuleFires "Golden: UseWhen fires" lintExamplesSource "UseWhen"
1522+ , assertRuleFires "Golden: UseUnless fires" lintExamplesSource "UseUnless"
1523+ , assertRuleFires "Golden: RedundantFlip fires" lintExamplesSource "RedundantFlip"
1524+ , assertRuleFires "Golden: UseNotElem fires" lintExamplesSource "UseNotElem"
1525+ , assertRuleFires "Golden: UseMinMax fires" lintExamplesSource "UseMinMax"
1526+ , assertRuleFires "Golden: MonoidIdentity fires" lintExamplesSource "MonoidIdentity"
1527+ , assertRuleFires "Golden: UseFold fires" lintExamplesSource "UseFold"
1528+ , assertRuleFires "Golden: UseSequence fires" lintExamplesSource "UseSequence"
1529+ , assertRuleFires "Golden: UseTraverseSequence fires" lintExamplesSource "UseTraverseSequence"
1530+ , assertRuleFires "Golden: RedundantNot fires" lintExamplesSource "RedundantNot"
1531+ , assertRuleFires "Golden: UseFstSnd fires" lintExamplesSource "UseFstSnd"
1532+ , assertRuleFires "Golden: UseMapMaybe fires" lintExamplesSource "UseMapMaybe"
1533+ , assertRuleFires "Golden: UseApplicative fires" lintExamplesSource "UseApplicative"
1534+ , assertRuleFires "Golden: UseBindFlip fires" lintExamplesSource "UseBindFlip"
1535+ , assertRuleFires "Golden: UseFor fires" lintExamplesSource "UseFor"
1536+ , assertRuleFires "Golden: RedundantGuard fires" lintExamplesSource "RedundantGuard"
1537+ , assertRuleFires "Golden: UseComparing fires" lintExamplesSource "UseComparing"
1538+ , assertRuleFires "Golden: UseOn fires" lintExamplesSource "UseOn"
1539+ , assertRuleFires "Golden: UseFindMap fires" lintExamplesSource "UseFindMap"
1540+ ]
1541+
1542+-- ============================================================================
1543+-- Purs-Tidy Formatting Tests
1544+-- ============================================================================
1545+
1546+-- | Test that a suggestion is already purs-tidy formatted
1547+assertTidyFormatted :: String -> String -> Effect TestResult
1548+assertTidyFormatted name suggestion = do
1549+ formatted <- formatExpr suggestion
1550+ pure $ if formatted == suggestion
1551+ then runTest name true "OK"
1552+ else runTest name false ("Not tidy-formatted. Got: '" <> formatted <> "' Expected: '" <> suggestion <> "'")
1553+
1554+-- | Suggestions that should be purs-tidy formatted
1555+tidySuggestions :: Array { name :: String, suggestion :: String }
1556+tidySuggestions =
1557+ [ { name: "void foo", suggestion: "void foo" }
1558+ , { name: "join foo", suggestion: "join foo" }
1559+ , { name: "any f xs", suggestion: "any f xs" }
1560+ , { name: "all f xs", suggestion: "all f xs" }
1561+ , { name: "const 42", suggestion: "const 42" }
1562+ , { name: "notElem x xs", suggestion: "notElem x xs" }
1563+ , { name: "max a b", suggestion: "max a b" }
1564+ , { name: "min a b", suggestion: "min a b" }
1565+ , { name: "null xs", suggestion: "null xs" }
1566+ , { name: "fromMaybe x", suggestion: "fromMaybe x" }
1567+ , { name: "isJust", suggestion: "isJust" }
1568+ , { name: "isNothing", suggestion: "isNothing" }
1569+ , { name: "when cond action", suggestion: "when cond action" }
1570+ , { name: "unless cond action", suggestion: "unless cond action" }
1571+ , { name: "fst", suggestion: "fst" }
1572+ , { name: "snd", suggestion: "snd" }
1573+ , { name: "fold", suggestion: "fold" }
1574+ , { name: "sequence", suggestion: "sequence" }
1575+ , { name: "traverse f xs", suggestion: "traverse f xs" }
1576+ , { name: "collapsed lambda", suggestion: "\\a b -> a + b" }
1577+ , { name: "map fusion", suggestion: "map (f <<< g) x" }
1578+ , { name: "fmap fusion", suggestion: "(f <<< g) <$> x" }
1579+ , { name: "flipped map fusion", suggestion: "x <#> (g >>> f)" }
1580+ , { name: "where clause", suggestion: "y + 1\n where\n y = 1" }
1581+ ]
1582+
1583+-- | Run all tidy formatting tests
1584+runTidyTests :: Effect (Array TestResult)
1585+runTidyTests = traverse (\t -> assertTidyFormatted ("Tidy: " <> t.name) t.suggestion) tidySuggestions