doxygen-parser 0.1.1 → 0.1.2
raw patch · 4 files changed
+68/−2 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +10/−0
- doxygen-parser.cabal +1/−1
- src-internal/Doxygen/Parser/Internal.hs +42/−1
- test/Test/Doxygen/Parser/XMLFileResult.hs +15/−0
CHANGELOG.md view
@@ -10,6 +10,16 @@ ### Bug fixes +## 0.1.2 -- 2026-10-06++### Bug fixes++* Handle doxygen >= 1.18.0 anonymous struct/union compounds. Doxygen 1.18.0+ emits a separate XML file for each anonymous compound instead of flattening+ its fields into the enclosing struct. The parser now normalizes these compound+ names so the `DoxygenKey` map is stable across doxygen versions.+ ([hs-bindgen#2225](https://github.com/well-typed/hs-bindgen/issues/2225))+ ## 0.1.1 -- 2026-06-21 ### New features
doxygen-parser.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: doxygen-parser-version: 0.1.1+version: 0.1.2 license: BSD-3-Clause license-file: LICENSE copyright: 2024-2026 Well-Typed LLP and Anduril Industries Inc.
src-internal/Doxygen/Parser/Internal.hs view
@@ -59,6 +59,7 @@ , parseInlineChildren , normalizeWhitespace , trimEdges+ , stripAnonymousSuffix , XMLFileResult(..) , extractEntity , nodeElementName@@ -573,11 +574,15 @@ -- For struct/union compounds, all members are fields of that struct. -- For other compounds (groups, files), fields are identified by their -- <qualifiedname> (e.g. "config_t::id" → KeyField "config_t" "id").+ --+ -- 'stripAnonymousSuffix' normalizes compound names so that doxygen+ -- >= 1.18.0 anonymous-struct compounds (e.g. @outer::[struct].pos@)+ -- produce the same keys as the flattened layout used by older versions. toFieldMap :: Text -> Maybe Text -> [MemberInfo] -> [(DoxygenKey, Comment DoxyRef)] toFieldMap k mEntityName ms | k `elem` ["struct", "union"] , Just sname <- mEntityName- = [(KeyField sname mi.miName, mi.miComment) | mi <- ms]+ = [(KeyField (stripAnonymousSuffix sname) mi.miName, mi.miComment) | mi <- ms] | otherwise = [ (KeyField sname fname, mi.miComment) | mi <- ms@@ -1187,6 +1192,42 @@ isTitle c = case Cursor.node c of XML.NodeElement el -> XML.nameLocalName (XML.elementName el) == "title" _ -> False++-- | Strip the anonymous compound suffix added by doxygen >= 1.18.0.+--+-- Given the C declaration+--+-- @+-- typedef struct {+-- struct { int ua; int ub; } pos;+-- } outer_t;+-- @+--+-- doxygen < 1.18 flattened fields @ua@ and @ub@ into+-- @structouter__t.xml@ and keyed them as @KeyField \"outer_t\" \"ua\"@.+--+-- doxygen >= 1.18 emits a separate @structouter__t_1_1...pos.xml@ with+-- compound name @\"outer_t::[struct].pos\"@, so without normalization the+-- fields would be keyed as @KeyField \"outer_t::[struct].pos\" \"ua\"@.+--+-- This function strips the suffix so the keys match the pre-1.18 layout:+--+-- @+-- stripAnonymousSuffix \"outer_t::[struct].pos\" == \"outer_t\"+-- stripAnonymousSuffix \"outer_t::[struct].__unnamed0__\" == \"outer_t\"+-- stripAnonymousSuffix \"a::b::[union].data\" == \"a::b\"+-- stripAnonymousSuffix \"plain_t\" == \"plain_t\"+-- @+--+-- With doxygen < 1.18 these patterns never appear, so the function is+-- the identity.+stripAnonymousSuffix :: Text -> Text+stripAnonymousSuffix name =+ case Text.breakOn "::[struct]." name of+ (before, after) | not (Text.null after) -> before+ _ -> case Text.breakOn "::[union]." name of+ (before, after) | not (Text.null after) -> before+ _ -> name -- | Check if a @\<memberdef\>@ is a struct\/union field. --
test/Test/Doxygen/Parser/XMLFileResult.hs view
@@ -115,4 +115,19 @@ , "</doxygen>" ]) $ \result -> result.comments @?= Map.empty++ , testCase "anonymous struct fields keyed under parent (doxygen >= 1.18)" $+ withExtractedEntity+ (mkDoxygen $ mkCompound "struct"+ "structfoo_1_1anon" "foo_t::[struct].__unnamed0__" "" $+ mkSection "public-attrib" $ Text.concat+ [ mkMember "variable" "f_ua" "ua" "Unnamed inner a" ""+ , mkMember "variable" "f_ub" "ub" "Unnamed inner b" ""+ ]) $ \result -> do+ assertBool "field ua keyed under parent" $+ Map.member (KeyField "foo_t" "ua") result.comments+ assertBool "field ub keyed under parent" $+ Map.member (KeyField "foo_t" "ub") result.comments+ assertBool "NOT keyed under anonymous compound" $+ not (Map.member (KeyField "foo_t::[struct].__unnamed0__" "ua") result.comments) ]