Conversation
Adding -Wall to the test suite in #66 surfaced these: both bound `scm <- getSchema ...` and never touched it, because they called typeToText on a hand-written AST literal instead. scm <- getSchema "./fixtures/test_model_atomic.xsd" let actual = typeToText expected1 -- the literal, not scm So each read a fixture and asserted nothing about it. They were fine as unit tests of typeToText, but the fixture in the name was decorative, and a parser regression could not have failed them. Feed typeToText the parsed type instead. The assertions are unchanged, and they still hold: typeToText reads only the restriction base, so NonEmptyString's minLength does not affect it, and nameForTest extends List82 with refname fixed to BibleContents, which is the branch that returns the refname. No duplicate parse assertion is added — TestModel already checks expected1 and expected4 against the same two fixtures a few cases earlier, which is also what confirms the literals match what parsing produces. Also drop a dead `key` binding in the mixed-HTML case, the third thing -Wall pointed at. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P8rZakwAiz1jpViUX34x1Z
#71 inherited the broken YAML through its base. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P8rZakwAiz1jpViUX34x1Z
総評差分そのものは正しいです。ソースを追った限りコンパイルは通り、2 つの assert も通ります。新しい式に出てくる識別子( 指摘推奨 1 — CI は一度「テストが 1 行もコンパイルされないまま」落ちていた(解消済み・確認のみ)
推奨 2 —
|
| 識別子 | 供給元 | import | 型 |
|---|---|---|---|
unwrap |
src/Util.hs |
import Util(無修飾・export list に記載) |
Maybe a -> a |
M.lookup |
Data.Map |
import qualified Data.Map as M |
QName -> Map QName Type -> Maybe Type |
makeTargetQName |
test/TestUtils.hs |
import TestUtils (makeTargetQName) |
Text -> QName |
schemaTypes |
src/Xsd.hs:28 |
import Xsd(Schema (..) で export) |
Schema -> Map QName Type |
typeToText |
src/Model.hs:14 |
import Model(無修飾) |
X.Type -> Text |
合成 typeToText . unwrap . M.lookup k . schemaTypes は Schema -> Map QName Type -> Maybe Type -> Type -> Text で繋がります。assertEqual "..." "string" actual は OverloadedStrings(1 行目)で Text に解決。
名前衝突の懸念を 1 件潰しました: src/Xsd/Parser.hs:655 にも同名の makeTargetQName :: Text -> P Xsd.QName がありますが、
module Xsd.Parser
( parse, parseFile, parseLazyByteString, ParseError (..), Config (..), defaultConfig )
whereと export list が明示されていて Xsd 経由で再 export されないため、import Xsd と import TestUtils (makeTargetQName) は曖昧になりません(既存の 166 行等が現に通っていることとも整合)。
QName キーの一致
src/Xsd/Parser.hs:165-176:
parseTopSimpleType c = do
name <- theAttribute "name" c >>= makeTargetQName
...
parseTopComplexType c = do
name <- theAttribute "name" c >>= makeTargetQNamesrc/Xsd/Parser.hs:655-662:
makeTargetQName name = do
tns <- asks envTargetNamespace
return Xsd.QName { Xsd.qnNamespace = tns, Xsd.qnName = name }envTargetNamespace は parseSchema(同 30-33 行)で targetNamespace 属性から設定され、src/Xsd.hs:70-74 の xsdToSchema が ChildType n t -> (n, t) : ts でそのまま Map のキーにします。両フィクスチャの targetNamespace="http://www.editeur.org/onix/2.1/reference" は test/TestUtils.hs の makeTargetQName が作る namespace と一致するので lookup は Just を返します(unwrap が Unreachable を投げることはありません)。
(a) NonEmptyString — 取る分岐
fixtures/test_model_atomic.xsd は xs:string を minLength で restriction。パーサの parseConstrains(src/Xsd/Parser.hs:219-225)は
parseConstrains c = do
enumerationAxis <- makeElemAxis "enumeration"
forM (c $/ enumerationAxis) $ \e -> do ...と enumeration 子要素しか拾わず、Constraint 型も = Enumeration Text [Annotation] の 1 コンストラクタのみ(src/Xsd/Types.hs:236-238)。つまり minLength はパース時に捨てられ simpleRestrictionConstraints = [] になります(ファイル 15-23 行の expected1 がまさにそれを示しています)。
取る分岐は src/Model.hs:156-159:
typeToText (X.TypeSimple (X.AtomicType X.SimpleRestriction {X.simpleRestrictionBase} _annotations)) =
case simpleRestrictionBase of
X.Ref n -> X.qnName nNamedFieldPuns で simpleRestrictionBase だけを見て constraints を一切参照しないので、facet が残っていようがいまいが結果は "string"。assert は通ります。
(b) nameForTest — 取る分岐
TypeComplex → ContentSimple (SimpleContentExtension ...) で src/Model.hs:162-170:
typeToText (X.TypeComplex X.ComplexType {X.complexContent}) = case complexContent of
X.ContentSimple (X.SimpleContentExtension X.SimpleExtension {X.simpleExtensionBase, X.simpleExtensionAttributes}) ->
if T.isPrefixOf "List" qnName
then unwrap $ findFixedOf "refname" simpleExtensionAttributes
else ...
where
qnName = X.qnName simpleExtensionBasesimpleExtensionBase は base="List82" 由来なので qnName = "List82" → T.isPrefixOf "List" が True → then 節。findFixedOf "refname"(src/Model.hs:114-135)は属性リストから attributeInlineName.qnName == "refname" の InlineAttribute を find し、attributeInlineFixed = Just name なら Just name を返します。フィクスチャの第 1 属性が <xs:attribute name="refname" type="xs:NMTOKEN" fixed="BibleContents" /> なので "BibleContents"。assert は通ります。
「重複 assert は不要」という主張
正しいです。test/TestModel.hs:93-98 が test_model_atomic.xsd を expected1 と、139-144 が test_model_atom_ref_bylist.xsd を expected4 と突き合わせています。どちらのフィクスチャも型定義が 1 つだけなので、そこで使われている head . map snd . M.toList . schemaTypes は今回 lookup する型と同一物です。
リテラルの未使用化
expected1 は 97 行、expected2 は 131 行、expected3 は 137 行、expected4 は 143 行で今も使われているため、-Wall の -Wunused-top-binds は鳴りません。key を消した箇所も当該 let 内で参照ゼロだったので削除は安全で、残る 7 箇所の let key(166/184/209/225/234/259/279 行)はすべて直後で使われています。
検証できなかったこと
- 実ビルド・実行はしていません。 このレビュー環境に GHC / stack がないため、上記はすべてソース読みによる推論です。
- 現 head(
e51b871)の "Test haskell codes" の最終結果を確認できていません(執筆時点でin_progress)。-Wall由来の警告が実際に何本出るかも、この目では数えていません。 - editeur.org への egress が塞がれているため、フィクスチャが参照しうる外部スキーマの解決挙動は確認していません(今回の 2 フィクスチャは自己完結しており
include/importを持たないので、この制約は結論に影響しません)。
Generated by Claude Code
|
レビューありがとうございます。任意 3 の指摘が正しく、PR 本文の主張が誇張でした。 訂正します。 任意 3: 「パーサが壊れてもこれらは落ちません」は言い過ぎでしたご指摘のとおりです。93-98 行と 139-144 行に同じフィクスチャをパースして 実際の価値は衛生面(フィクスチャを読んで捨てていた状態の解消と、 推奨 1: CI が赤だった件そのとおりで、私が base の #66 で 903f72f で修正し、この PR にも e51b871 でマージ済みです。マージ前に緑を目視確認します。 推奨 2: 同ファイルの
|
#66 でテストスイートに
-Wallを足したことで見つかった問題です。base は #66。2 つのテストが、読み込んだフィクスチャを使っていませんでした
expected1/expected4はtest/TestModel.hsの先頭で手書きされた AST リテラルです。つまりこの 2 つはフィクスチャを読んで捨てており、typeToTextの単体テストとしてしか機能していませんでした。-Wallが「scmが束縛されて使われていない」と指摘したことで発覚しました。警告を入れた目的がそのまま回収された形です。修正
typeToTextにパース結果を渡します。表明の内容は変えていません。 そのうえで成立します。
typeToTextは制約を見ずに restriction の base 名だけを返すので、NonEmptyStringのminLengthは結果に影響しませんnameForTestはList82を simpleContent extension しており、refnameがBibleContentsに固定されています。typeToTextはこの分岐で refname を返しますあわせて、mixed-HTML のケースにあった死んだ
key束縛も削除しました。-Wallが指摘した 3 つ目です。この PR で増えないもの: 回帰検出能力
当初の本文では「パーサが壊れてもこれらは落ちません」と書いていましたが、これは言い過ぎでした。訂正します。
TestModel.hsの数ケース手前(93-98 行目、139-144 行目)に、同じ 2 つのフィクスチャをexpected1/expected4と突き合わせるテストが既にあります。各フィクスチャは型を 1 つしか宣言していないので、M.lookupが返すものがexpected1/expected4と一致することは既に担保されており、修正の前後は論理的に等価です。パーサが壊れれば、どちらにせよ先行するケースが落ちます。したがって実際の価値は衛生面です。
-Wallの警告 3 件(scm2 件 +key1 件)の解消確認
typeToTextの実装(src/Model.hs:155-175)を読み、両ケースで同じ結果になることを確認unwrap/M.lookup/makeTargetQName/schemaTypes/typeToText)がすべてTestModel.hsの import に含まれていることを確認なお
test/にはまだ-Wallの警告が残っています(未使用 import と型シグネチャ欠落)。これは「空振りテストの修正」という単位が崩れるため、別の警告整理 PR で片付けます。🤖 Generated with Claude Code
https://claude.ai/code/session_01P8rZakwAiz1jpViUX34x1Z
Generated by Claude Code