Skip to content

Fix: preserve type applications in record patterns#1680

Open
mgajda wants to merge 1 commit intondmitchell:masterfrom
mgajda:fix/record-patterns-type-applications
Open

Fix: preserve type applications in record patterns#1680
mgajda wants to merge 1 commit intondmitchell:masterfrom
mgajda:fix/record-patterns-type-applications

Conversation

@mgajda
Copy link
Copy Markdown

@mgajda mgajda commented Apr 15, 2026

Fixes #1679

Problem

HLint was suggesting record pattern syntax that removes type applications, producing invalid code.

Example

-- Original (with type applications):
Ast.AstScatterS @_ @shn1 @shp1 _ _ _ _ _

-- HLint suggested (WRONG):
Ast.AstScatterS {}

Result: Type applications are lost, code doesn't parse!

Root Cause

The record pattern suggestion in patHint didn't check if the constructor had type arguments. It would suggest the record syntax {} even when type applications were present, which would strip them away.

Solution

Check if the PrefixCon pattern has any type arguments before suggesting record pattern conversion. If type arguments exist, skip the suggestion.

Changes

  • Modified patHint in src/Hint/Pattern.hs to check for empty type argument list
  • Added test cases for patterns with and without type applications

Testing

Test cases verify:

  • foo (Bar _ _ _ _) = x → suggests Bar{} (normal case works)
  • foo (Bar @A @B _ _ _) = x → no suggestion (multiple type apps preserved)

All existing tests pass.

mgajda added a commit to mgajda/hlint that referenced this pull request Apr 19, 2026
…untrusted input

data/hlint.yaml: new group 'security', enabled: true.

Rules:
  read  x -> Text.Read.readMaybe x   side: not (isLitString x)   CWE-502
  reads x -> Text.Read.readMaybe x   side: not (isLitString x)   CWE-502
  Crypto.Hash.MD5.hash      -> Crypto.Hash.SHA256.hash           CWE-327
  Crypto.Hash.MD5.hashlazy  -> Crypto.Hash.SHA256.hashlazy       CWE-327
  Crypto.Hash.SHA1.hash     -> Crypto.Hash.SHA256.hash           CWE-327
  Crypto.Hash.SHA1.hashlazy -> Crypto.Hash.SHA256.hashlazy       CWE-327
  Data.Digest.Pure.MD5.md5  -> Crypto.Hash.SHA256.hashlazy       CWE-327
  Data.Digest.Pure.SHA.sha1 -> Data.Digest.Pure.SHA.sha256       CWE-327

Disable per-rule via `-i "<name>"` or suppress the whole group with
`- group: {name: security, enabled: false}` in .hlint.yaml.

tests/security.test: four cases.
  1. security-hash.hs           : all six hash rules fire.
  2. security-read-nonliteral.hs: `read x` fires for variable `x`.
  3. security-read-literal.hs   : `read "42"` and `read "3.14"` do not fire.
  4. security-ignored.hs        : per-rule -i suppression yields no hints.

Self-test: 974 tests, 3 failures (all pre-existing on master: issue ndmitchell#1674
intercalate/OverloadedStrings and two record-pattern type-application
cases from PR ndmitchell#1680).

Running hlint with the new group on hlint's own source produces one
finding: src/Test/InputOutput.hs:57 `read code` (parsing an EXIT line
from a .test file).

No HSEC advisory on github.com/haskell/security-advisories maps to
CWE-327 or CWE-502 for these function classes.
…1679)

When suggesting record patterns, check if the constructor has meaningful
type applications (e.g. @int, @type). Type applications can be removed
only if there are no type arguments, or all type arguments are wildcards
(@_). This prevents HLint from suggesting invalid transformations that
would remove necessary type information.

Examples:
  Ast.AstScatterS @_ @Shn1 @shp1 _ _ _ _ _
  -> Suggests: Ast.AstScatterS {} (wildcards can be omitted)

  Ast.AstScatterS @A @Shn1 @shp1 _ _ _ _ _
  -> No suggestion (meaningful type apps must be preserved)

Test cases:
  - foo (Bar _ _ _ _) = x          -> suggests Bar{}
  - foo (Bar @_ _ _ _) = x         -> suggests Bar{} (@_ is redundant)
  - foo (Bar @int _ _ _) = x       -> no suggestion (type app preserved)
@mgajda mgajda force-pushed the fix/record-patterns-type-applications branch from 824637e to 972b0d4 Compare April 19, 2026 17:09
mgajda added a commit to mgajda/hlint that referenced this pull request Apr 19, 2026
…untrusted input

data/hlint.yaml: new group 'security', enabled: true.

Rules:
  read  x -> Text.Read.readMaybe x   side: not (isLitString x)   CWE-502
  reads x -> Text.Read.readMaybe x   side: not (isLitString x)   CWE-502
  Crypto.Hash.MD5.hash      -> Crypto.Hash.SHA256.hash           CWE-327
  Crypto.Hash.MD5.hashlazy  -> Crypto.Hash.SHA256.hashlazy       CWE-327
  Crypto.Hash.SHA1.hash     -> Crypto.Hash.SHA256.hash           CWE-327
  Crypto.Hash.SHA1.hashlazy -> Crypto.Hash.SHA256.hashlazy       CWE-327
  Data.Digest.Pure.MD5.md5  -> Crypto.Hash.SHA256.hashlazy       CWE-327
  Data.Digest.Pure.SHA.sha1 -> Data.Digest.Pure.SHA.sha256       CWE-327

Disable per-rule via `-i "<name>"` or suppress the whole group with
`- group: {name: security, enabled: false}` in .hlint.yaml.

tests/security.test: four cases.
  1. security-hash.hs           : all six hash rules fire.
  2. security-read-nonliteral.hs: `read x` fires for variable `x`.
  3. security-read-literal.hs   : `read "42"` and `read "3.14"` do not fire.
  4. security-ignored.hs        : per-rule -i suppression yields no hints.

Self-test: 974 tests, 3 failures (all pre-existing on master: issue ndmitchell#1674
intercalate/OverloadedStrings and two record-pattern type-application
cases from PR ndmitchell#1680).

Running hlint with the new group on hlint's own source produces one
finding: src/Test/InputOutput.hs:57 `read code` (parsing an EXIT line
from a .test file).

No HSEC advisory on github.com/haskell/security-advisories maps to
CWE-327 or CWE-502 for these function classes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrong Use record patterns hint when type applications are present

1 participant