From e51f6d7b90866393d832f4c52f152d623fb4300a Mon Sep 17 00:00:00 2001 From: Nebula Lavelle Date: Sun, 5 Apr 2026 18:03:12 -0400 Subject: [PATCH 1/3] Only considers imports with real source spans GHC (and possibly other plugins) can inject synthetic imports for e.g. Backpack signatures that a module transitively depends on. Before this change, it was not possible to write a qualification rule for a signature in some circumstances, e.g. when a module that imports a signature directly is imported in a module that doesn't direclty import the signature. --- henforcer.cabal | 2 +- src/CompatGHC.hs | 2 ++ src/Henforcer/CodeStructure/Import/Import.hs | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/henforcer.cabal b/henforcer.cabal index d93bc97..b248072 100644 --- a/henforcer.cabal +++ b/henforcer.cabal @@ -1,7 +1,7 @@ cabal-version: 3.4 name: henforcer -version: 1.0.0.1 +version: 1.0.0.2 synopsis: GHC plugin to enforce user specified rules on code. description: Please see the README on GitHub at category: Development, Compiler Plugin, static-analysis diff --git a/src/CompatGHC.hs b/src/CompatGHC.hs index 34b8975..b9a5457 100644 --- a/src/CompatGHC.hs +++ b/src/CompatGHC.hs @@ -29,6 +29,7 @@ module CompatGHC , SrcSpan , generatedSrcSpan , getLoc + , isGoodSrcSpan , ideclAs , ideclName , ideclPkgQual @@ -110,6 +111,7 @@ import GHC , ideclPkgQual , ideclQualified , ideclSafe + , isGoodSrcSpan , locA , mkModuleName , moduleName diff --git a/src/Henforcer/CodeStructure/Import/Import.hs b/src/Henforcer/CodeStructure/Import/Import.hs index a5907b7..24a422d 100644 --- a/src/Henforcer/CodeStructure/Import/Import.hs +++ b/src/Henforcer/CodeStructure/Import/Import.hs @@ -13,8 +13,10 @@ module Henforcer.CodeStructure.Import.Import , importIsOpenWithNoHidingOrAlias ) where -import qualified CompatGHC +import qualified Data.Maybe as Maybe + import Henforcer.CodeStructure.Import.Scheme (Alias (WithoutAlias), Scheme (Scheme), buildScheme) +import qualified CompatGHC {- | `Import` is a subset of a CompatGHC.HsModule to be a slightly more ergonomic interface. @@ -47,8 +49,17 @@ getImports :: CompatGHC.TcGblEnv -> [Import] getImports tcGblEnv = let name = CompatGHC.moduleName $ CompatGHC.tcg_mod tcGblEnv + sourcedImport = + CompatGHC.isGoodSrcSpan . CompatGHC.locA . CompatGHC.getLoc in - fmap (Import name) $ CompatGHC.tcg_rn_imports tcGblEnv + Maybe.mapMaybe + (\imp -> + -- Remove synthetic imports, e.g. transitively imported Backpack signatures + if sourcedImport imp + then Just (Import name imp) + else Nothing + ) + (CompatGHC.tcg_rn_imports tcGblEnv) {- | Determine if the import is open, with no qualification, no alias, and no hiding From ee1c8eb6073d7bad27a66bef9c4ab853651f2b7f Mon Sep 17 00:00:00 2001 From: Nebula Lavelle Date: Mon, 6 Apr 2026 17:50:25 -0400 Subject: [PATCH 2/3] Inlines let binding predicate GHC wants a type signature for it otherwise because we use -Wmonomorphism-restriction. Simpler to inline. --- src/Henforcer/CodeStructure/Import/Import.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Henforcer/CodeStructure/Import/Import.hs b/src/Henforcer/CodeStructure/Import/Import.hs index 24a422d..1d54e81 100644 --- a/src/Henforcer/CodeStructure/Import/Import.hs +++ b/src/Henforcer/CodeStructure/Import/Import.hs @@ -49,13 +49,11 @@ getImports :: CompatGHC.TcGblEnv -> [Import] getImports tcGblEnv = let name = CompatGHC.moduleName $ CompatGHC.tcg_mod tcGblEnv - sourcedImport = - CompatGHC.isGoodSrcSpan . CompatGHC.locA . CompatGHC.getLoc in Maybe.mapMaybe (\imp -> -- Remove synthetic imports, e.g. transitively imported Backpack signatures - if sourcedImport imp + if CompatGHC.isGoodSrcSpan . CompatGHC.locA $ CompatGHC.getLoc imp then Just (Import name imp) else Nothing ) From 1485d0c3c237e64e0d0f69bf9700eee1b19efab3 Mon Sep 17 00:00:00 2001 From: Nebula Lavelle Date: Mon, 6 Apr 2026 18:49:49 -0400 Subject: [PATCH 3/3] Fixes formatting --- src/Henforcer/CodeStructure/Import/Import.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Henforcer/CodeStructure/Import/Import.hs b/src/Henforcer/CodeStructure/Import/Import.hs index 1d54e81..26fdbee 100644 --- a/src/Henforcer/CodeStructure/Import/Import.hs +++ b/src/Henforcer/CodeStructure/Import/Import.hs @@ -15,8 +15,8 @@ module Henforcer.CodeStructure.Import.Import import qualified Data.Maybe as Maybe -import Henforcer.CodeStructure.Import.Scheme (Alias (WithoutAlias), Scheme (Scheme), buildScheme) import qualified CompatGHC +import Henforcer.CodeStructure.Import.Scheme (Alias (WithoutAlias), Scheme (Scheme), buildScheme) {- | `Import` is a subset of a CompatGHC.HsModule to be a slightly more ergonomic interface. @@ -51,11 +51,11 @@ getImports tcGblEnv = name = CompatGHC.moduleName $ CompatGHC.tcg_mod tcGblEnv in Maybe.mapMaybe - (\imp -> - -- Remove synthetic imports, e.g. transitively imported Backpack signatures - if CompatGHC.isGoodSrcSpan . CompatGHC.locA $ CompatGHC.getLoc imp - then Just (Import name imp) - else Nothing + ( \imp -> + -- Remove synthetic imports, e.g. transitively imported Backpack signatures + if CompatGHC.isGoodSrcSpan . CompatGHC.locA $ CompatGHC.getLoc imp + then Just (Import name imp) + else Nothing ) (CompatGHC.tcg_rn_imports tcGblEnv)