Hello community, here is the log from the commit of package hlint for openSUSE:Factory checked in at 2020-10-23 15:15:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hlint (Old) and /work/SRC/openSUSE:Factory/.hlint.new.3463 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "hlint" Fri Oct 23 15:15:44 2020 rev:4 rq:842779 version:3.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/hlint/hlint.changes 2020-09-27 11:49:11.944006121 +0200 +++ /work/SRC/openSUSE:Factory/.hlint.new.3463/hlint.changes 2020-10-23 15:15:46.338175488 +0200 @@ -1,0 +2,13 @@ +Fri Oct 16 02:00:52 UTC 2020 - psim...@suse.com + +- Update hlint to version 3.2.1. + 3.2.1, released 2020-10-15 + #1150, remove the Duplicate hint (was slow) + #1149, allow within to use module wildcards, e.g. **.Foo + #1141, make redundant return highlight just the return + #1142, suggest newtype instead of data for data family instances + #1138, show allowed fields in YAML error message + #1131, fix potential variable capture in zipWith/repeat hint + #1129, add hints to use const and \_ x -> x where appropriate + +------------------------------------------------------------------- Old: ---- hlint-3.2.tar.gz New: ---- hlint-3.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hlint.spec ++++++ --- /var/tmp/diff_new_pack.YOKhi1/_old 2020-10-23 15:15:47.394175997 +0200 +++ /var/tmp/diff_new_pack.YOKhi1/_new 2020-10-23 15:15:47.398175999 +0200 @@ -18,7 +18,7 @@ %global pkg_name hlint Name: %{pkg_name} -Version: 3.2 +Version: 3.2.1 Release: 0 Summary: Source code suggestions License: BSD-3-Clause ++++++ hlint-3.2.tar.gz -> hlint-3.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/CHANGES.txt new/hlint-3.2.1/CHANGES.txt --- old/hlint-3.2/CHANGES.txt 2020-09-14 17:49:14.000000000 +0200 +++ new/hlint-3.2.1/CHANGES.txt 2020-10-15 12:12:40.000000000 +0200 @@ -1,5 +1,13 @@ Changelog for HLint (* = breaking change) +3.2.1, released 2020-10-15 + #1150, remove the Duplicate hint (was slow) + #1149, allow within to use module wildcards, e.g. **.Foo + #1141, make redundant return highlight just the return + #1142, suggest newtype instead of data for data family instances + #1138, show allowed fields in YAML error message + #1131, fix potential variable capture in zipWith/repeat hint + #1129, add hints to use const and \_ x -> x where appropriate 3.2, released 2020-09-14 #75, make Windows 10 use color terminals Make sure the extension removed matches what you called it diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/README.md new/hlint-3.2.1/README.md --- old/hlint-3.2/README.md 2020-09-13 12:43:06.000000000 +0200 +++ new/hlint-3.2.1/README.md 2020-09-19 21:09:47.000000000 +0200 @@ -2,16 +2,16 @@ HLint is a tool for suggesting possible improvements to Haskell code. These suggestions include ideas such as using alternative functions, simplifying code and spotting redundancies. This document is structured as follows: -* [Installing and running HLint](#installing-and-running-hlint) -* [FAQ](#faq) -* [Customizing the hints](#customizing-the-hints) -* [Hacking HLint](#hacking-hlint) +* [Installing and running HLint](./README.md#installing-and-running-hlint) +* [FAQ](./README.md#faq) +* [Customizing the hints](./README.md#customizing-the-hints) +* [Hacking HLint](./README.md#hacking-hlint) ### Bugs and limitations Bugs can be reported [on the bug tracker](https://github.com/ndmitchell/hlint/issues). There are some issues that I do not intend to fix: -* HLint operates on each module at a time in isolation, as a result HLint does not know about types or which names are in scope. This decision is deliberate, allowing HLint to parallelise and be used incrementally on code that may not type-check. If fixities are required to parse the code properly, they [can be supplied](#why-doesnt-hlint-know-the-fixity-for-my-custom--operator). +* HLint operates on each module at a time in isolation, as a result HLint does not know about types or which names are in scope. This decision is deliberate, allowing HLint to parallelise and be used incrementally on code that may not type-check. If fixities are required to parse the code properly, they [can be supplied](./README.md#why-doesnt-hlint-know-the-fixity-for-my-custom--operator). * The presence of `seq` may cause some hints (i.e. eta-reduction) to change the semantics of a program. * Some transformed programs may require additional type signatures, particularly if the transformations trigger the monomorphism restriction or involve rank-2 types. * Sometimes HLint will change the code in a way that causes values to default to different types, which may change the behaviour. @@ -55,7 +55,7 @@ The first hint is marked as an warning, because using `concatMap` in preference to the two separate functions is always desirable. In contrast, the removal of brackets is probably a good idea, but not always. Reasons that a hint might be a suggestion include requiring an additional import, something not everyone agrees on, and functions only available in more recent versions of the base library. -Any configuration can be done via [.hlint.yaml](#customizing-the-hints) file. +Any configuration can be done via [.hlint.yaml](./README.md#customizing-the-hints) file. **Bug reports:** The suggested replacement should be equivalent - please report all incorrect suggestions not mentioned as known limitations. @@ -65,7 +65,7 @@ 1. Initially, run `hlint . --report` to generate `report.html` containing a list of all issues HLint has found. Fix those you think are worth fixing and keep repeating. 1. Once you are happy, run `hlint . --default > .hlint.yaml`, which will generate a settings file ignoring all the hints currently outstanding. Over time you may wish to edit the list. -1. For larger projects, add [custom hints or rules](#customizing-the-hints). +1. For larger projects, add [custom hints or rules](./README.md#customizing-the-hints). Most hints are intended to be a good idea in most circumstances, but not universally - judgement is required. When contributing to someone else's project, HLint can identify pieces of code to look at, but only make changes you consider improvements - not merely to adhere to HLint rules. @@ -143,58 +143,70 @@ ## FAQ -### Why are hints not applied recursively? +### Usage -Consider: +#### Why doesn't the compiler automatically apply the optimisations? -```haskell -foo xs = concat (map op xs) -``` +HLint doesn't suggest optimisations, it suggests code improvements - the intention is to make the code simpler, rather than making the code perform faster. The [GHC compiler](http://haskell.org/ghc/) automatically applies many of the rules suggested by HLint, so HLint suggestions will rarely improve performance. -This will suggest eta reduction to `concat . map op`, and then after making that change and running HLint again, will suggest use of `concatMap`. Many people wonder why HLint doesn't directly suggest `concatMap op`. There are a number of reasons: +#### Why do I sometimes get a "Note" with my hint? -* HLint aims to both improve code, and to teach the author better style. Doing modifications individually helps this process. -* Sometimes the steps are reasonably complex, by automatically composing them the user may become confused. -* Sometimes HLint gets transformations wrong. If suggestions are applied recursively, one error will cascade. -* Some people only make use of some of the suggestions. In the above example using concatMap is a good idea, but sometimes eta reduction isn't. By suggesting them separately, people can pick and choose. -* Sometimes a transformed expression will be large, and a further hint will apply to some small part of the result, which appears confusing. -* Consider `f $ (a b)`. There are two valid hints, either remove the $ or remove the brackets, but only one can be applied. +Most hints are perfect substitutions, and these are displayed without any notes. However, some hints change the semantics of your program - typically in irrelevant ways - but HLint shows a warning note. HLint does not warn when assuming typeclass laws (such as `==` being symmetric). Some notes you may see include: -### Why doesn't the compiler automatically apply the optimisations? +* __Increases laziness__ - for example `foldl (&&) True` suggests `and` including this note. The new code will work on infinite lists, while the old code would not. Increasing laziness is usually a good idea. +* __Decreases laziness__ - for example `(fst a, snd a)` suggests `a` including this note. On evaluation the new code will raise an error if a is an error, while the old code would produce a pair containing two error values. Only a small number of hints decrease laziness, and anyone relying on the laziness of the original code would be advised to include a comment. +* __Removes error__ - for example `foldr1 (&&)` suggests `and` including the note `Removes error on []`. The new code will produce `True` on the empty list, while the old code would raise an error. Unless you are relying on the exception thrown by the empty list, this hint is safe - and if you do rely on the exception, you would be advised to add a comment. -HLint doesn't suggest optimisations, it suggests code improvements - the intention is to make the code simpler, rather than making the code perform faster. The [GHC compiler](http://haskell.org/ghc/) automatically applies many of the rules suggested by HLint, so HLint suggestions will rarely improve performance. +#### What is the difference between error/warning/suggestion? + +Every hint has a severity level: + +* __Error__ - by default only used for parse errors. +* __Warning__ - for example `concat (map f x)` suggests `concatMap f x` as a "warning" severity hint. From a style point of view, you should always replace a combination of `concat` and `map` with `concatMap`. +* __Suggestion__ - for example `x !! 0` suggests `head x` as a "suggestion" severity hint. Typically `head` is a simpler way of expressing the first element of a list, especially if you are treating the list inductively. However, in the expression `f (x !! 4) (x !! 0) (x !! 7)`, replacing the middle argument with `head` makes it harder to follow the pattern, and is probably a bad idea. Suggestion hints are often worthwhile, but should not be applied blindly. + +The difference between warning and suggestion is one of personal taste, typically my personal taste. If you already have a well developed sense of Haskell style, you should ignore the difference. If you are a beginner Haskell programmer you may wish to focus on warning hints before suggestion hints. + +#### Why do I get a parse error? + +HLint enables/disables a set of extensions designed to allow as many files to parse as possible, but sometimes you'll need to enable an additional extension (e.g. Arrows, QuasiQuotes, ...), or disable some (e.g. MagicHash) to enable your code to parse. + +You can enable extensions by specifying additional command line arguments in [.hlint.yaml](./README.md#customizing-the-hints), e.g.: `- arguments: [-XQuasiQuotes]`. + +### Configuration -### Why doesn't HLint know the fixity for my custom !@%$ operator? +#### Why doesn't HLint know the fixity for my custom !@%$ operator? HLint knows the fixities for all the operators in the base library, as well as operators whose fixities are declared in the module being linted, but no others. HLint works on a single file at a time, and does not resolve imports, so cannot see fixity declarations from imported modules. You can tell HLint about fixities by putting them in a hint file named `.hlint.yaml` with the syntax `- fixity: "infixr 5 !@%$"`. You can also use `--find` to automatically produce a list of fixity declarations in a file. -### Which hints are ignored? +#### Which hints are ignored? Some hints are off-by-default. Some are ignored by the configuration settings. To see all hints pass `--show`. This feature is often useful in conjunction with `--report` which shows the hints in an interactive web page, allowing them to be browsed broken down by hint. -### Which hints are used? +#### Which hints are used? HLint uses the `hlint.yaml` file it ships with by default (containing things like the `concatMap` hint above), along with the first `.hlint.yaml` file it finds in the current directory or any parent thereof. To include other hints, pass `--hint=filename.yaml`. -### Why do I sometimes get a "Note" with my hint? +### Design -Most hints are perfect substitutions, and these are displayed without any notes. However, some hints change the semantics of your program - typically in irrelevant ways - but HLint shows a warning note. HLint does not warn when assuming typeclass laws (such as `==` being symmetric). Some notes you may see include: +#### Why are hints not applied recursively? -* __Increases laziness__ - for example `foldl (&&) True` suggests `and` including this note. The new code will work on infinite lists, while the old code would not. Increasing laziness is usually a good idea. -* __Decreases laziness__ - for example `(fst a, snd a)` suggests `a` including this note. On evaluation the new code will raise an error if a is an error, while the old code would produce a pair containing two error values. Only a small number of hints decrease laziness, and anyone relying on the laziness of the original code would be advised to include a comment. -* __Removes error__ - for example `foldr1 (&&)` suggests `and` including the note `Removes error on []`. The new code will produce `True` on the empty list, while the old code would raise an error. Unless you are relying on the exception thrown by the empty list, this hint is safe - and if you do rely on the exception, you would be advised to add a comment. - -### What is the difference between error/warning/suggestion? +Consider: -Every hint has a severity level: +```haskell +foo xs = concat (map op xs) +``` -* __Error__ - by default only used for parse errors. -* __Warning__ - for example `concat (map f x)` suggests `concatMap f x` as a "warning" severity hint. From a style point of view, you should always replace a combination of `concat` and `map` with `concatMap`. -* __Suggestion__ - for example `x !! 0` suggests `head x` as a "suggestion" severity hint. Typically `head` is a simpler way of expressing the first element of a list, especially if you are treating the list inductively. However, in the expression `f (x !! 4) (x !! 0) (x !! 7)`, replacing the middle argument with `head` makes it harder to follow the pattern, and is probably a bad idea. Suggestion hints are often worthwhile, but should not be applied blindly. +This will suggest eta reduction to `concat . map op`, and then after making that change and running HLint again, will suggest use of `concatMap`. Many people wonder why HLint doesn't directly suggest `concatMap op`. There are a number of reasons: -The difference between warning and suggestion is one of personal taste, typically my personal taste. If you already have a well developed sense of Haskell style, you should ignore the difference. If you are a beginner Haskell programmer you may wish to focus on warning hints before suggestion hints. +* HLint aims to both improve code, and to teach the author better style. Doing modifications individually helps this process. +* Sometimes the steps are reasonably complex, by automatically composing them the user may become confused. +* Sometimes HLint gets transformations wrong. If suggestions are applied recursively, one error will cascade. +* Some people only make use of some of the suggestions. In the above example using concatMap is a good idea, but sometimes eta reduction isn't. By suggesting them separately, people can pick and choose. +* Sometimes a transformed expression will be large, and a further hint will apply to some small part of the result, which appears confusing. +* Consider `f $ (a b)`. There are two valid hints, either remove the $ or remove the brackets, but only one can be applied. -### Is it possible to use pragma annotations in code that is read by `ghci` (conflicts with `OverloadedStrings`)? +#### Is it possible to use pragma annotations in code that is read by `ghci` (conflicts with `OverloadedStrings`)? Short answer: yes, it is! @@ -213,12 +225,6 @@ See discussion in [issue #372](https://github.com/ndmitchell/hlint/issues/372). -### Why do I get a parse error? - -HLint enables/disables a set of extensions designed to allow as many files to parse as possible, but sometimes you'll need to enable an additional extension (e.g. Arrows, QuasiQuotes, ...), or disable some (e.g. MagicHash) to enable your code to parse. - -You can enable extensions by specifying additional command line arguments in [.hlint.yaml](#customizing-the-hints), e.g.: `- arguments: [-XQuasiQuotes]`. - ## Customizing the hints To customize the hints given by HLint, create a file `.hlint.yaml` in the root of your project. For a suitable default run: @@ -238,6 +244,24 @@ If you wish to use the [Dhall configuration language](https://github.com/dhall-lang/dhall-lang) to customize HLint, there [is an example](https://kowainik.github.io/posts/2018-09-09-dhall-to-hlint) and [type definition](https://github.com/kowainik/relude/blob/master/hlint/Rule.dhall). +### Finding the name of a hint + +Hints are named with the string they display in their help message + +For example, if hlints outputs a warning like + +``` +./backend/tests/api-tests/src/Main.hs:116:51: Warning: Redundant == +Found: + regIsEnabled rr == True +Perhaps: + regIsEnabled rr +``` + +the name of the lint is `Redundant ==`. + +You can use that name to refer to the lint in the configuration file and `ANN` pragmas, see the following sections. + ### Ignoring hints Some of the hints are subjective, and some users believe they should be ignored. Some hints are applicable usually, but occasionally don't always make sense. The ignoring mechanism provides features for suppressing certain hints. Ignore directives can either be written as pragmas in the file being analysed, or in the hint files. Examples of pragmas are: @@ -332,9 +356,15 @@ ## Hacking HLint -Contributions to HLint are most welcome, following [my standard contribution guidelines](https://github.com/ndmitchell/neil/blob/master/README.md#contributions). You can run the tests either from within a `ghci` session by typing `:test` or by running the standalone binary's tests via `cabal run -- hlint --test` or `stack run -- hlint --test`. After changing hints, you will need to regenerate the [hints.md](hints.md) file with `hlint --generate-summary`. +Contributions to HLint are most welcome, following [my standard contribution guidelines](https://github.com/ndmitchell/neil/blob/master/README.md#contributions). + +### How to run tests + +You can run the tests either from within a `ghci` session by typing `:test` or by running the standalone binary's tests via `cabal run -- hlint --test` or `stack run -- hlint --test`. After changing hints, you will need to regenerate the [hints.md](hints.md) file with `hlint --generate-summary`. -New tests for individual hints can be added directly to source and hint files by adding annotations bracketed in `<TEST></TEST>` code comment blocks. As some examples: +### How to add tests + +New tests for individual hints can be added directly to source and hint files by adding annotations bracketed in `<TEST></TEST>` code comment blocks. Here are some examples: ```haskell {- @@ -350,6 +380,12 @@ The general syntax is `lhs -- rhs` with `lhs` being the expression you expect to be rewritten as `rhs`. The absence of `rhs` means you expect no hints to fire. In addition `???` lets you assert a warning without a particular suggestion, while `@` tags require a specific severity -- both these features are used less commonly. +### Printing abstract syntax + +Getting started on problems in HLint often means wanting to inspect a GHC parse tree to get a sense of what it looks like (to see how to match on it for example). Given a source program `Foo.hs` (say), you can get GHC to print a textual representation of `Foo`'s AST via the `-ddump-parsed-ast` flag e.g. `ghc -fforce-recomp -ddump-parsed-ast -c Foo.hs`. + +When you have an [`HsSyn`](https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/compiler/hs-syn-type) term in your program, it's quite common to want to print it (e.g. via `Debug.Trace.trace`). Types in `HsSyn` aren't in [`Show`](https://hoogle.haskell.org/?hoogle=Show). Not all types in `HsSyn` are [`Outputable`](https://hoogle.haskell.org/?hoogle=Outputable) but when they are you can call `ppr` to get `SDoc`s. This idiom is common enough that there exists [`unsafePrettyPrint`](https://hackage.haskell.org/package/ghc-lib-parser-ex-8.10.0.16/docs/Language-Haskell-GhclibParserEx-GHC-Utils-Outputable.html#v:unsafePrettyPrint). The function [`showAstData`](https://hoogle.haskell.org/?hoogle=showAstData) can be called on any `HsSyn` term to get output like with the `dump-parsed-ast` flag. The `showAstData` approach is preferable to `ppr` when both choices exist in that two ASTs that differ only in fixity arrangments will render differently with the former. + ### Acknowledgements Many improvements to this program have been made by [Niklas Broberg](http://www.nbroberg.se) in response to feature requests. Additionally, many people have provided help and patches, including Lennart Augustsson, Malcolm Wallace, Henk-Jan van Tuyl, Gwern Branwen, Alex Ott, Andy Stewart, Roman Leshchinskiy, Johannes Lippmann, Iustin Pop, Steve Purcell, Mitchell Rosen and others. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/data/default.yaml new/hlint-3.2.1/data/default.yaml --- old/hlint-3.2/data/default.yaml 2017-04-17 16:54:16.000000000 +0200 +++ new/hlint-3.2.1/data/default.yaml 2020-09-19 22:22:35.000000000 +0200 @@ -34,6 +34,12 @@ # Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar" # - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x} +# The hints are named by the string they display in warning messages. +# For example, if you see a warning starting like +# +# Main.hs:116:51: Warning: Redundant == +# +# You can refer to that hint with `{name: Redundant ==}` (see below). # Turn on hints that are off by default # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/data/hlint.yaml new/hlint-3.2.1/data/hlint.yaml --- old/hlint-3.2/data/hlint.yaml 2020-09-12 15:23:46.000000000 +0200 +++ new/hlint-3.2.1/data/hlint.yaml 2020-09-16 22:43:29.000000000 +0200 @@ -227,7 +227,7 @@ - warn: {lhs: head (scanr f z x), rhs: foldr f z x} - warn: {lhs: iterate id, rhs: repeat} - warn: {lhs: zipWith f (repeat x), rhs: map (f x)} - - warn: {lhs: zipWith f y (repeat z), rhs: map (\x -> f x z) y} + - warn: {lhs: zipWith f y (repeat z), rhs: map (`f` z) y} - warn: {lhs: listToMaybe (filter p x), rhs: find p x} - warn: {lhs: zip (take n x) (take n y), rhs: take n (zip x y)} - warn: {lhs: zip (take n x) (take m y), rhs: take (min n m) (zip x y), side: notEq n m, note: [IncreasesLaziness, DecreasesLaziness], name: Redundant take} @@ -296,6 +296,9 @@ - warn: {lhs: \x -> x, rhs: id} - warn: {lhs: \x y -> x, rhs: const} + - warn: {lhs: curry fst, rhs: const} + - warn: {lhs: curry snd, rhs: \_ x -> x, note: "Alternatively, use const id"} + - warn: {lhs: flip const, rhs: \_ x -> x, note: "Alternatively, use const id"} - warn: {lhs: "\\(x,y) -> y", rhs: snd} - warn: {lhs: "\\(x,y) -> x", rhs: fst} - hint: {lhs: "\\x y -> f (x,y)", rhs: curry f} @@ -1173,7 +1176,8 @@ # f condition tChar tBool = if condition then _monoField tChar else _monoField tBool # foo = maybe Bar{..} id -- Data.Maybe.fromMaybe Bar{..} # foo = (\a -> Foo {..}) 1 -# foo = zipWith SymInfo [0 ..] (repeat ty) -- map (\ x -> SymInfo x ty) [0 ..] +# foo = zipWith SymInfo [0 ..] (repeat ty) -- map (`SymInfo` ty) [0 ..] @NoRefactor +# foo = zipWith (SymInfo q) [0 ..] (repeat ty) -- map (( \ x_ -> SymInfo q x_ ty)) [0 .. ] @NoRefactor # f rec = rec # mean x = fst $ foldl (\(m, n) x' -> (m+(x'-m)/(n+1),n+1)) (0,0) x # {-# LANGUAGE TypeApplications #-} \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/hlint.cabal new/hlint-3.2.1/hlint.cabal --- old/hlint-3.2/hlint.cabal 2020-09-14 17:49:27.000000000 +0200 +++ new/hlint-3.2.1/hlint.cabal 2020-10-15 12:12:47.000000000 +0200 @@ -1,7 +1,7 @@ cabal-version: >= 1.18 build-type: Simple name: hlint -version: 3.2 +version: 3.2.1 license: BSD3 license-file: LICENSE category: Development diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/src/Apply.hs new/hlint-3.2.1/src/Apply.hs --- old/hlint-3.2/src/Apply.hs 2020-06-14 20:45:05.000000000 +0200 +++ new/hlint-3.2.1/src/Apply.hs 2020-10-15 11:40:07.000000000 +0200 @@ -20,6 +20,7 @@ import Language.Haskell.GhclibParserEx.GHC.Hs import qualified Data.HashSet as Set import Prelude +import System.FilePattern (FilePattern, (?==)) -- | Apply hints to a single file, you may have the contents of the file. @@ -113,5 +114,21 @@ f :: Idea -> Severity -> Classify -> Severity f i r c | classifyHint c ~~= ideaHint i && classifyModule c ~= ideaModule i && classifyDecl c ~= ideaDecl i = classifySeverity c | otherwise = r - x ~= y = x == "" || x `elem` y + x ~= y = x == "" || any (wildcardMatch x) y x ~~= y = x == "" || x == y || ((x ++ ":") `isPrefixOf` y) + +-- | Returns true if the pattern matches the string. For example: +-- +-- >>> let isSpec = wildcardMatch "**.*Spec" +-- >>> isSpec "Example" +-- False +-- >>> isSpec "ExampleSpec" +-- True +-- >>> isSpec "Namespaced.ExampleSpec" +-- True +-- >>> isSpec "Deeply.Nested.ExampleSpec" +-- True +-- +-- See this issue for details: <https://github.com/ndmitchell/hlint/issues/402>. +wildcardMatch :: FilePattern -> String -> Bool +wildcardMatch p m = let f = replace "." "/" in f p ?== f m diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/src/Config/Yaml.hs new/hlint-3.2.1/src/Config/Yaml.hs --- old/hlint-3.2/src/Config/Yaml.hs 2020-06-14 20:45:05.000000000 +0200 +++ new/hlint-3.2.1/src/Config/Yaml.hs 2020-10-15 11:40:07.000000000 +0200 @@ -186,7 +186,9 @@ mp <- parseObject v let bad = map T.unpack (Map.keys mp) \\ allow when (bad /= []) $ - parseFail v $ "Not allowed keys: " ++ unwords bad + parseFail v + $ "Not allowed keys: " ++ unwords bad + ++ ", Allowed keys: " ++ unwords allow parseGHC :: (ParseFlags -> String -> ParseResult v) -> Val -> Parser v parseGHC parser v = do @@ -302,14 +304,18 @@ parseWithin :: Val -> Parser [(String, String)] -- (module, decl) parseWithin v = do - x <- parseGHC parseExpGhcWithMode v - case x of - L _ (HsVar _ (L _ (Unqual x))) -> pure $ f "" (occNameString x) - L _ (HsVar _ (L _ (Qual mod x))) -> pure $ f (moduleNameString mod) (occNameString x) - _ -> parseFail v "Bad classification rule" - where - f mod name@(c:_) | isUpper c = [(mod,name),(mod ++ ['.' | mod /= ""] ++ name, "")] - f mod name = [(mod, name)] + s <- parseString v + if '*' `elem` s + then pure [(s, "")] + else do + x <- parseGHC parseExpGhcWithMode v + case x of + L _ (HsVar _ (L _ (Unqual x))) -> pure $ f "" (occNameString x) + L _ (HsVar _ (L _ (Qual mod x))) -> pure $ f (moduleNameString mod) (occNameString x) + _ -> parseFail v "Bad classification rule" + where + f mod name@(c:_) | isUpper c = [(mod,name),(mod ++ ['.' | mod /= ""] ++ name, "")] + f mod name = [(mod, name)] parseSeverityKey :: Val -> Parser (Severity, Val) parseSeverityKey v = do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/src/Hint/All.hs new/hlint-3.2.1/src/Hint/All.hs --- old/hlint-3.2/src/Hint/All.hs 2020-05-21 17:48:31.000000000 +0200 +++ new/hlint-3.2.1/src/Hint/All.hs 2020-10-15 11:44:47.000000000 +0200 @@ -41,6 +41,9 @@ HintComment | HintNewType | HintSmell deriving (Show,Eq,Ord,Bounded,Enum) +-- See https://github.com/ndmitchell/hlint/issues/1150 - Duplicate is too slow +-- and doesn't provide much value anyway. +issue1150 = True builtin :: HintBuiltin -> Hint builtin x = case x of @@ -50,7 +53,7 @@ HintExport -> modu exportHint HintComment -> modu commentHint HintPragma -> modu pragmaHint - HintDuplicate -> mods duplicateHint + HintDuplicate -> if issue1150 then mempty else mods duplicateHint HintRestrict -> mempty{hintModule=restrictHint} HintList -> decl listHint HintNewType -> decl newtypeHint diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/src/Hint/Duplicate.hs new/hlint-3.2.1/src/Hint/Duplicate.hs --- old/hlint-3.2/src/Hint/Duplicate.hs 2020-07-19 12:48:09.000000000 +0200 +++ new/hlint-3.2.1/src/Hint/Duplicate.hs 2020-10-15 11:44:11.000000000 +0200 @@ -5,7 +5,7 @@ Find bindings within a let, and lists of statements If you have n the same, error out -<TEST> +<TEST_DISABLED_1150> foo = a where {a = 1; b = 2; c = 3} \ bar = a where {a = 1; b = 2; c = 3} -- ??? main = do a; a; a; a @@ -17,7 +17,7 @@ {-# ANN main "HLint: ignore Reduce duplication" #-}; main = do a; a; a; a; a; a -- @Ignore ??? {-# HLINT ignore main "Reduce duplication" #-}; main = do a; a; a; a; a; a -- @Ignore ??? {- HLINT ignore main "Reduce duplication" -}; main = do a; a; a; a; a; a -- @Ignore ??? -</TEST> +</TEST_DISABLED_1150> -} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/src/Hint/Monad.hs new/hlint-3.2.1/src/Hint/Monad.hs --- old/hlint-3.2/src/Hint/Monad.hs 2020-08-31 12:17:03.000000000 +0200 +++ new/hlint-3.2.1/src/Hint/Monad.hs 2020-09-27 14:15:26.000000000 +0200 @@ -43,7 +43,7 @@ {-# LANGUAGE BlockArguments #-}; main = print do 17 -- main = f $ do g a $ sleep 10 -- main = do f a $ sleep 10 -- @Ignore -main = do foo x; return 3; bar z -- do foo x; bar z +main = do foo x; return 3; bar z -- main = void $ forM_ f xs -- forM_ f xs main = void $ forM f xs -- void $ forM_ f xs main = do _ <- forM_ f xs; bar -- forM_ f xs @@ -60,9 +60,9 @@ module Hint.Monad(monadHint) where -import Hint.Type(DeclHint,Idea(..),Severity(..),ideaNote,warn,ideaRemove,toSS,suggest,Note(Note)) +import Hint.Type -import GHC.Hs +import GHC.Hs hiding (Warning) import SrcLoc import BasicTypes import TcEvidence @@ -181,8 +181,8 @@ -> [ExprLStmt GhcPs] -> [Idea] -- Rewrite 'do return x; $2' as 'do $2'. -monadStep wrap os@(o@(L _ (BodyStmt _ (fromRet -> Just (ret, _)) _ _ )) : xs@(_:_)) - = [warn ("Redundant " ++ ret) (wrap os) (wrap xs) [Delete Stmt (toSS o)]] +monadStep wrap (o@(L _ (BodyStmt _ (fromRet -> Just (ret, _)) _ _ )) : xs@(_:_)) + = [ideaRemove Warning ("Redundant " ++ ret) (getLoc o) (unsafePrettyPrint o) [Delete Stmt (toSS o)]] -- Rewrite 'do a <- $1; return a' as 'do $1'. monadStep wrap o@[ g@(L _ (BindStmt _ (LL _ (VarPat _ (L _ p))) x _ _ )) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/src/Hint/NewType.hs new/hlint-3.2.1/src/Hint/NewType.hs --- old/hlint-3.2/src/Hint/NewType.hs 2020-07-19 12:48:09.000000000 +0200 +++ new/hlint-3.2.1/src/Hint/NewType.hs 2020-09-27 14:10:52.000000000 +0200 @@ -27,6 +27,15 @@ newtype Foo = Foo Int deriving (Show, Eq) -- newtype Foo = Foo { getFoo :: Int } deriving (Show, Eq) -- newtype Foo = Foo Int deriving stock Show +data instance Foo Int = Bar Bool -- newtype instance Foo Int = Bar Bool +data instance Foo Int = Bar {field :: Bool} -- newtype instance Foo Int = Bar {field :: Bool} +data instance Foo Int = Bar {field :: Int#} +data instance Foo Int = Bar +data instance Foo Int = Bar {field1 :: Bool, field2 :: ()} +newtype instance Foo Int = Bar Bool deriving (Show, Eq) -- +newtype instance Foo Int = Bar {field :: Bool} deriving Show -- +newtype instance Foo Int = Bar {field :: Bool} deriving stock Show +{-# LANGUAGE RankNTypes #-}; data instance Foo Int = forall a. Show a => Foo a </TEST> -} module Hint.NewType (newtypeHint) where @@ -51,9 +60,15 @@ newTypeDerivingStrategiesHintDecl :: LHsDecl GhcPs -> [Idea] newTypeDerivingStrategiesHintDecl decl@(L _ (TyClD _ (DataDecl _ _ _ _ dataDef))) = - [ignoreNoSuggestion "Use DerivingStrategies" decl | not $ isData dataDef, not $ hasAllStrategies dataDef] + [ignoreNoSuggestion "Use DerivingStrategies" decl | shouldSuggestStrategies dataDef] +newTypeDerivingStrategiesHintDecl decl@(L _ (InstD _ (DataFamInstD _ (DataFamInstDecl (HsIB _ (FamEqn _ _ _ _ _ dataDef)))))) = + [ignoreNoSuggestion "Use DerivingStrategies" decl | shouldSuggestStrategies dataDef] newTypeDerivingStrategiesHintDecl _ = [] +-- | Determine if the given data definition should use deriving strategies. +shouldSuggestStrategies :: HsDataDefn GhcPs -> Bool +shouldSuggestStrategies dataDef = not (isData dataDef) && not (hasAllStrategies dataDef) + hasAllStrategies :: HsDataDefn GhcPs -> Bool hasAllStrategies (HsDataDefn _ NewType _ _ _ _ (L _ xs)) = all hasStrategyClause xs hasAllStrategies _ = False @@ -80,18 +95,36 @@ -- * Single record field constructors get newtyped - @data X = X {getX :: Int}@ -> @newtype X = X {getX :: Int}@ -- * All other declarations are ignored. singleSimpleField :: LHsDecl GhcPs -> Maybe WarnNewtype -singleSimpleField (L loc (TyClD ext decl@(DataDecl _ _ _ _ dataDef@(HsDataDefn _ DataType _ _ _ [L _ constructor] _)))) - | Just inType <- simpleCons constructor = +singleSimpleField (L loc (TyClD ext decl@(DataDecl _ _ _ _ dataDef))) + | Just inType <- simpleHsDataDefn dataDef = Just WarnNewtype { newDecl = L loc $ TyClD ext decl {tcdDataDefn = dataDef { dd_ND = NewType - , dd_cons = map (\(L consloc x) -> L consloc $ dropConsBang x) $ dd_cons dataDef + , dd_cons = dropBangs dataDef + }} + , insideType = inType + } +singleSimpleField (L loc (InstD ext inst@(DataFamInstD instExt (DataFamInstDecl (HsIB hsibExt famEqn@(FamEqn _ _ _ _ _ dataDef)))))) + | Just inType <- simpleHsDataDefn dataDef = + Just WarnNewtype + { newDecl = L loc $ InstD ext $ DataFamInstD instExt $ DataFamInstDecl $ HsIB hsibExt famEqn {feqn_rhs = dataDef + { dd_ND = NewType + , dd_cons = dropBangs dataDef }} , insideType = inType } singleSimpleField _ = Nothing --- | Checks whether its argument is a \"simple constructor\" (see criteria in 'singleSimpleFieldNew') +dropBangs :: HsDataDefn GhcPs -> [LConDecl GhcPs] +dropBangs = map (fmap dropConsBang) . dd_cons + +-- | Checks whether its argument is a \"simple\" data definition (see 'singleSimpleField') +-- returning the type inside its constructor if it is. +simpleHsDataDefn :: HsDataDefn GhcPs -> Maybe (HsType GhcPs) +simpleHsDataDefn dataDef@(HsDataDefn _ DataType _ _ _ [L _ constructor] _) = simpleCons constructor +simpleHsDataDefn _ = Nothing + +-- | Checks whether its argument is a \"simple\" constructor (see criteria in 'singleSimpleField') -- returning the type inside the constructor if it is. This is needed for strictness analysis. simpleCons :: ConDecl GhcPs -> Maybe (HsType GhcPs) simpleCons (ConDeclH98 _ _ _ [] context (PrefixCon [L _ inType]) _) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hlint-3.2/src/Summary.hs new/hlint-3.2.1/src/Summary.hs --- old/hlint-3.2/src/Summary.hs 2020-09-13 12:21:46.000000000 +0200 +++ new/hlint-3.2.1/src/Summary.hs 2020-09-15 23:23:36.000000000 +0200 @@ -30,7 +30,7 @@ dedupeBuiltin :: [(BuiltinKey, BuiltinValue)] -> [(BuiltinKey, BuiltinValue)] -dedupeBuiltin = Map.toAscList . Map.fromListWith (curry snd) +dedupeBuiltin = Map.toAscList . Map.fromListWith (\_ old -> old) -- | Generate a summary of hints, including built-in hints and YAML-configured hints