From a977ee7967ead69e1d33c46f16559b7778684d42 Mon Sep 17 00:00:00 2001 From: Kazuhiro NISHIYAMA Date: Tue, 21 Jul 2026 20:55:50 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E8=A1=8C=E3=81=A7=E5=90=8D=E5=89=8D=E5=88=A5?= =?UTF-8?q?=E3=81=AE=20since/until=20=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bitclust#132 P4。RD/MD 両パーサーが共有する Context#method_attributes を拡張し、予約語だった {: since="X"} / {: until="X"} を受理するように した。X は数字とドットのみ(typo 検出用。1.6 のようなラダー外の値も フロア救済のため意図的に許可)。 - kv 属性はシグネチャ単位で束縛され(kramdown IAL のまま)、nomethod/undef と違って全シグネチャ一致を要求しない。別名ごとに異なる追加/削除時期を 持てるのが本来の用途。同じシグネチャ上でキーが重複したらエラー - 対象の名前はシグネチャ行(rd の `--- ...` / md の `### def ...` 等)を MethodSignature.parse で読み直して求める。md 側は mdcompiler/mdparser と同じ接頭辞正規化を再利用する - method_attributes の返り値を Array[String] から Context::MethodAttributes(flags, since_until)に拡張。define_method は chunk.names を確定させた後に fill_since/fill_until を呼ぶ(未設定時のみ 書き込むため、methodsince サブコマンド(P2)の算出値をパース時の明示値が 常に上書きする側になる) - MARKUP_SPEC.md §4 の「予約」記述を実際の構文・意味・優先順位の説明に 差し替え 案a(#@since 分岐のパース時取得)は実装しない: バージョンラダー(P2)が 既に全版横断で存在を算出しており、#@since で追加されたメソッドは対象 バージョンの DB に「存在する」という形でそのまま反映されるため、算出は 同じ since に到達する。パース時に必要なのは算出が届かない場合の明示的な 上書き手段(このコミット)だけで足りる。 rbs validate / steep check ともエラー0。既存の巡回で見つかる lib/bitclust/whole_file_gate.rb の背景 ERROR ログはこの変更前から出ている 無関係な既知ノイズ(最終結果は変更前後とも "No type error detected")。 refs #132 Co-Authored-By: Claude Fable 5 --- doc/markdown-samples/MARKUP_SPEC.md | 26 +++++- lib/bitclust/rrdparser.rb | 95 +++++++++++++++++--- sig/bitclust/rrdparser.rbs | 26 +++++- test/test_mdparser.rb | 45 ++++++++++ test/test_method_since_calculator.rb | 21 +++++ test/test_rrdparser.rb | 125 ++++++++++++++++++++++++++- 6 files changed, 319 insertions(+), 19 deletions(-) diff --git a/doc/markdown-samples/MARKUP_SPEC.md b/doc/markdown-samples/MARKUP_SPEC.md index 2e01010..ceb210b 100644 --- a/doc/markdown-samples/MARKUP_SPEC.md +++ b/doc/markdown-samples/MARKUP_SPEC.md @@ -458,6 +458,11 @@ nth 番目の要素を返します。 - `undef` — そのクラスで意図的に未定義化されているメソッド (例: `Complex` における `Comparable` の比較メソッド。旧 `@undef` 相当)。 `kind = :undefined` になり、ページ生成・検索の対象外になる +- `since="X"` / `until="X"` — このシグネチャの名前が Ruby X から存在する + (`since`)/ Ruby X で削除される(`until`。半開区間: X 未満のバージョンでは + 存在し、X 以降には存在しない)ことを明示する。X は `"3.2"` のように数字と + ドットのみをダブルクォートで囲んで書く。シグネチャ見出しの横に + since/until バッジとして表示される 規則: @@ -465,13 +470,30 @@ nth 番目の要素を返します。 同じ解釈)。別名(複数シグネチャ)のエントリで使う場合はすべてのシグネチャ見出しの 直後にそれぞれ付ける。一部にだけ付いているとビルドエラー (kind はエントリ単位でしか持てないため) +- ただし `since=`/`until=` は `nomethod`/`undef` と違い**シグネチャ単位**の + 値であり、別名ごとに異なるバージョンを持ってよい(全シグネチャで揃える必要は + ない)。追加/削除時期が別名ごとに違う場合が本来の用途: + + ```markdown + ### def -@ -> object + {: since="2.0.0"} + ### def dedup -> object + {: since="3.0"} + ``` + + (本体 `-@` は Ruby 2.0.0 から、別名 `dedup` はそれより後の Ruby 3.0 から、 + という例。それぞれのシグネチャ見出しの横に別々の since バッジが付く) - シグネチャ見出し行と属性行が連続している範囲がひとつのエントリ(別名グループ)に なる。属性行の直後に別エントリを続ける場合は空行で区切る - パーサーの属性行探索はシグネチャブロックの直後で打ち切る (コードブロック中の `{: ...}` 風の行を誤検出しない。空行を挟んだ `{: ...}` は ただの本文) -- 未知の属性はビルドエラー(typo 検出)。`since` / `until` は将来のバージョン情報表示 - (バージョン別 DB へのメタデータ付与)のために予約されており、対応するまではエラー +- 未知の属性・不正な形式(`since=X` のような非引用値、`since=""` のような + 空値、数字とドット以外を含む値、未知のキーなど)はビルドエラー(typo 検出) +- 明示された `since`/`until` は、全バージョンの DB を横断して自動算出する + `bitclust methodsince` サブコマンドの結果より常に優先される(算出側は既に + 値がある名前を上書きしない)。主な用途は自動算出が届かない 1.8.7 以前からの + 存在(フロア救済)や、算出結果の補正 --- diff --git a/lib/bitclust/rrdparser.rb b/lib/bitclust/rrdparser.rb index eacff76..deca564 100644 --- a/lib/bitclust/rrdparser.rb +++ b/lib/bitclust/rrdparser.rb @@ -12,6 +12,7 @@ require 'bitclust/preprocessor' require 'bitclust/methodid' require 'bitclust/methoddatabase' +require 'bitclust/methodsignature' require 'bitclust/lineinput' require 'bitclust/parseutils' require 'bitclust/nameutils' @@ -482,9 +483,9 @@ def define_method(chunk) attrs = method_attributes(chunk) @db.open_method(id) {|m| m.names = chunk.names.sort - m.kind = if attrs.include?('undef') + m.kind = if attrs.flags.include?('undef') :undefined - elsif attrs.include?('nomethod') + elsif attrs.flags.include?('nomethod') :nomethod else @kind @@ -494,6 +495,10 @@ def define_method(chunk) # steep:ignore:end m.source = chunk.source m.source_location = chunk.source.location + attrs.since_until.each do |name, kv| + m.fill_since(name, kv['since'] || raise) if kv['since'] + m.fill_until(name, kv['until'] || raise) if kv['until'] + end case @kind when :added, :redefined @library.add_method m @@ -502,42 +507,108 @@ def define_method(chunk) end # 現在サポートするメソッド属性({: ...} 属性行のトークン)。 - # since/until は将来のバージョン情報表示(#132)用に予約 + # - nomethod/undef: 裸語。kind はエントリ単位でしか持てないので、 + # 別名(複数シグネチャ)のエントリでは全シグネチャに同じ属性が + # 付いていることを要求する + # - since="X"/until="X"(bitclust#132 P4): シグネチャ単位で束縛され、 + # そのシグネチャの名前だけに適用される。nomethod/undef と違い、 + # 別名ごとに異なる値を持てる(全シグネチャ一致は要求しない)のが + # 本来の用途。X は "3.2" のように数字とドットのみ METHOD_ATTRIBUTES = %w[nomethod undef] + KV_METHOD_ATTRIBUTES = %w[since until] + KV_METHOD_ATTRIBUTE_VALUE_RE = /\A\d+(?:\.\d+)*\z/ + + # md の `### def name ...`/`### module_function def name ...`/ + # `### const name`/`### gvar $name` シグネチャ行を rd 形式 + # (`--- name ...`)へ正規化するための接頭辞(MDParser::SIG_RE と + # 同じパターン)。属性の紐付け先となるメソッド名を + # MethodSignature.parse で取り出すために使う + MD_METHOD_SIG_PREFIX_RE = /\A### (?:module_function def |def |const |gvar )/ + + # method_attributes の返り値。 + # - flags: 裸語トークン(nomethod/undef)の集合。エントリ単位の属性 + # なので全シグネチャで同一であることを method_attributes が保証済み + # - since_until: シグネチャ単位で束縛された since="X"/until="X" を + # { メソッド名 => { "since" => "X", "until" => "X" } } の形で保持する + MethodAttributes = Struct.new(:flags, :since_until) # kramdown Block IAL 風の {: ...} 属性行からメタデータを集める。 # 属性行は「直前のシグネチャ行のみ」に束縛される(kramdown と同じ解釈)。 - # kind はエントリ単位でしか持てないので、別名(複数シグネチャ)の - # エントリでは全シグネチャに同じ属性が付いていることを要求する。 + # 裸語トークン(nomethod/undef)は kind がエントリ単位でしか持てないため + # 全シグネチャで同じであることを要求するが、since=/until= はシグネチャ + # 単位でそのままの名前に適用するため、この一致要求からは除外する。 # 本文に入ったら探索を打ち切る(コード例中の {: ...} を誤検出しないため) def method_attributes(chunk) per_sig = [] #: Array[Array[String]] + since_until = {} #: Hash[String, Hash[String, String]] + current_name = nil #: String? + current_keys = nil #: Hash[String, bool]? chunk.source.each_line do |line_| line = line_.chomp case line when /\A---\s/, /\A\#\#\#\s/ per_sig.push [] + current_name = method_attribute_target_name(line) + current_keys = {} when /\A\{:(.*)\}[ \t]*\z/ break if per_sig.empty? ($1 || raise).strip.split(/\s+/).each do |token| - unless METHOD_ATTRIBUTES.include?(token) - raise ParseError, - "#{chunk.source.location}: unknown method attribute #{token.inspect} (supported: #{METHOD_ATTRIBUTES.join(', ')})" + if METHOD_ATTRIBUTES.include?(token) + per_sig.last&.push token + else + key, value = parse_kv_method_attribute(token, chunk) + keys = current_keys or raise + if keys.key?(key) + raise ParseError, + "#{chunk.source.location}: duplicate method attribute #{key.inspect} on the same signature" + end + keys[key] = true + name = current_name or raise "must not happen: kv attribute without a preceding signature name" + (since_until[name] ||= {})[key] = value end - per_sig.last&.push token end else break end end - return [] if per_sig.empty? + return MethodAttributes.new([], {}) if per_sig.empty? sets = per_sig.map {|a| a.uniq.sort } unless sets.uniq.size == 1 raise ParseError, "#{chunk.source.location}: method attributes must be the same on every signature of an entry: #{sets.inspect}" end - sets.first || raise - end + MethodAttributes.new(sets.first || raise, since_until) + end + + # since="X"/until="X" 形式のトークンを解析して [key, value] を返す。 + # 受理する形式以外(キーが不明・値が非引用・値が数字とドット以外を + # 含む等)はすべて ParseError にする + def parse_kv_method_attribute(token, chunk) + m = /\A(\w+)=(.*)\z/.match(token) + if m + key = m[1] || raise + raw_value = m[2] || raise + quoted = /\A"(.*)"\z/.match(raw_value) + if quoted && KV_METHOD_ATTRIBUTES.include?(key) && KV_METHOD_ATTRIBUTE_VALUE_RE.match?(quoted[1] || raise) + return [key, (quoted[1] || raise)] + end + end + raise ParseError, + "#{chunk.source.location}: invalid method attribute #{token.inspect} " \ + "(supported: #{METHOD_ATTRIBUTES.join(', ')}, " \ + "#{KV_METHOD_ATTRIBUTES.map {|k| %Q(#{k}="X") }.join('/')} where X is digits and dots, e.g. since=\"3.2\")" + end + private :parse_kv_method_attribute + + # シグネチャ行(rd の `--- ...` / md の `### def ...` 等)から、 + # 属性の紐付け先となるメソッド名を取り出す + def method_attribute_target_name(line) + normalized = line.sub(MD_METHOD_SIG_PREFIX_RE, '--- ') + MethodSignature.parse(normalized).name + rescue ParseError + nil + end + private :method_attribute_target_name def method_id(chunk) id = MethodID.new diff --git a/sig/bitclust/rrdparser.rbs b/sig/bitclust/rrdparser.rbs index 0089f06..229e236 100644 --- a/sig/bitclust/rrdparser.rbs +++ b/sig/bitclust/rrdparser.rbs @@ -127,9 +127,33 @@ module BitClust METHOD_ATTRIBUTES: Array[String] - def method_attributes: (Chunk chunk) -> Array[String] + KV_METHOD_ATTRIBUTES: Array[String] + + KV_METHOD_ATTRIBUTE_VALUE_RE: ::Regexp + + MD_METHOD_SIG_PREFIX_RE: ::Regexp + + def method_attributes: (Chunk chunk) -> MethodAttributes + + private + + def parse_kv_method_attribute: (String token, Chunk chunk) -> [String, String] + + def method_attribute_target_name: (String line) -> String? + + public def method_id: (Chunk chunk) -> MethodID + + # method_attributes の返り値 + class MethodAttributes < Struct[untyped] + attr_accessor flags: Array[String] + + attr_accessor since_until: Hash[String, Hash[String, String]] + + def self.new: (Array[String] flags, Hash[String, Hash[String, String]] since_until) -> instance + | ... + end end class Chunk diff --git a/test/test_mdparser.rb b/test/test_mdparser.rb index 14932c7..dd212ca 100644 --- a/test/test_mdparser.rb +++ b/test/test_mdparser.rb @@ -490,4 +490,49 @@ def test_method_attribute_on_every_alias_signature assert_equal(:nomethod, fuga.kind) assert_equal(['fuga', 'fuga2'], fuga.names) end + + def test_method_attribute_since_is_recorded + md = <<~MD + --- + library: _builtin + --- + # class Hoge + + クラスの説明。 + + ## Instance Methods + + ### def fuga -> nil + {: since="3.1"} + + 説明のためのエントリです。 + MD + db, = parse_md(md) + entry = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga')) + assert_equal('3.1', entry.since_of('fuga')) + end + + def test_method_attribute_since_differs_per_alias_signature + md = <<~MD + --- + library: _builtin + --- + # class Hoge + + クラスの説明。 + + ## Instance Methods + + ### def fuga -> nil + {: since="2.0.0"} + ### def fuga2 -> nil + {: since="3.0"} + + 説明のためのエントリです。 + MD + db, = parse_md(md) + entry = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga')) + assert_equal('2.0.0', entry.since_of('fuga')) + assert_equal('3.0', entry.since_of('fuga2')) + end end diff --git a/test/test_method_since_calculator.rb b/test/test_method_since_calculator.rb index 43740be..05c7946 100644 --- a/test/test_method_since_calculator.rb +++ b/test/test_method_since_calculator.rb @@ -26,6 +26,8 @@ # [x] apply は冪等: 2回目は floor_skipped 以外すべて0 # [x] 既に値がある名前は上書きしない(著者値が算出値より優先) # [x] 対象DBのバージョンがラダーに無ければ apply は UserError +# [x] 統合: `{: since="X"}` 属性行(#132 P4)でパース時に記録された値は +# 算出結果で上書きされない(RD ソース上の明示注記が算出値より優先) class TestMethodSinceCalculator < Test::Unit::TestCase RD = <<~'RD' description @@ -42,6 +44,13 @@ class TestMethodSinceCalculator < Test::Unit::TestCase 説明 #@end + #@since 3.0 + --- overridden + {: since="2.5"} + + 説明 + #@end + #@since 2.0.0 --- -@ #@since 3.0 @@ -203,6 +212,18 @@ def test_author_value_takes_precedence_over_computed_value assert_equal '1.9.9', find_entry('3.0', 'i', 'newmeth').since_of('newmeth') end + def test_author_since_attribute_parsed_from_source_survives_apply + # overridden は #@since 3.0 でゲートされているのでラダー上は 3.0 と算出 + # されるが、RD ソースの {: since="2.5"} がパース時点(update_by_stdlibtree) + # で既に記録されているため、apply(fill_since は未設定時のみ書く)では + # 上書きされずそのまま残る + run_calculator('2.0.0') + run_calculator('3.0') + + assert_nil find_entry('2.0.0', 'i', 'overridden') + assert_equal '2.5', find_entry('3.0', 'i', 'overridden').since_of('overridden') + end + def test_apply_rejects_target_whose_version_is_not_in_ladder build_db('4.0') calc = BitClust::MethodSinceCalculator.new(ladder_dbs) diff --git a/test/test_rrdparser.rb b/test/test_rrdparser.rb index f46d61e..59bb72a 100644 --- a/test/test_rrdparser.rb +++ b/test/test_rrdparser.rb @@ -68,16 +68,61 @@ def test_unknown_method_attribute end end - def test_reserved_method_attribute_is_rejected - # since/until は #132 で対応予定。未対応のうちはエラーにして気づけるようにする + def test_since_attribute_sets_since_of_name + _library, db = BitClust::RRDParser.parse(< Date: Tue, 21 Jul 2026 21:04:09 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E7=89=B9=E6=AE=8A=E5=A4=89=E6=95=B0?= =?UTF-8?q?=E3=81=AE=20$=20=E4=BB=98=E3=81=8D=E3=82=B7=E3=82=B0=E3=83=8D?= =?UTF-8?q?=E3=83=81=E3=83=A3=E5=90=8D=E3=82=92=E3=82=A8=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=83=AA=E5=90=8D=E3=81=AB=E6=AD=A3=E8=A6=8F=E5=8C=96=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bitclust#132 P4 の査読で発見した名前規約の食い違いを補正する。特殊変数の シグネチャ行から MethodSignature.parse で取り出した名前は "$SAFE" のように $ 付きだが、エントリの names(= since_by_name/until_by_name のキー)は 先頭の $ を除いた形("SAFE"。"$$" なら "$")で格納されている (rrdparser の method_signature が Signature.new(nil, '$', name[1..-1]) と する規約)。 - 属性の紐付け先名(method_attribute_target_name)とバッジ描画の マップ参照(VersionBadges)の両方で先頭の $ を1つ剥がす。正規化が 無いと、特殊変数への {: since} は参照されないキーで格納され、算出 バッジも mixed 経路とサーバーのメソッド表で黙って消えていた - 安全網として、since/until の紐付け先名が chunk.names に無い場合は 黙って捨てずに ParseError にする(名前導出の規約ずれを CI で検出)。 紐付け先シグネチャ名が導出できない場合の raise も ParseError に変更 - 判別テスト2本(パーサ側=$0/$PROGRAM_NAME 別名の since 格納先、 描画側=mixed 経路で該当 dt にバッジが付くこと)。どちらも正規化を 外すと失敗することをミューテーションで確認済み refs #132 Co-Authored-By: Claude Fable 5 --- lib/bitclust/rrdparser.rb | 20 +++++++++++++++++--- lib/bitclust/version_badges.rb | 10 ++++++++++ sig/bitclust/version_badges.rbs | 2 ++ test/test_rdcompiler.rb | 23 +++++++++++++++++++++++ test/test_rrdparser.rb | 20 ++++++++++++++++++++ 5 files changed, 72 insertions(+), 3 deletions(-) diff --git a/lib/bitclust/rrdparser.rb b/lib/bitclust/rrdparser.rb index deca564..6407ee6 100644 --- a/lib/bitclust/rrdparser.rb +++ b/lib/bitclust/rrdparser.rb @@ -563,7 +563,9 @@ def method_attributes(chunk) "#{chunk.source.location}: duplicate method attribute #{key.inspect} on the same signature" end keys[key] = true - name = current_name or raise "must not happen: kv attribute without a preceding signature name" + name = current_name or + raise ParseError, + "#{chunk.source.location}: cannot determine the signature name to bind method attribute #{token.inspect} to" (since_until[name] ||= {})[key] = value end end @@ -577,6 +579,14 @@ def method_attributes(chunk) raise ParseError, "#{chunk.source.location}: method attributes must be the same on every signature of an entry: #{sets.inspect}" end + # 紐付け先の名前がエントリの names と食い違ったら黙って捨てずに + # エラーにする(名前導出の規約ずれを CI で検出するための安全網) + since_until.each_key do |name| + unless chunk.names.include?(name) + raise ParseError, + "#{chunk.source.location}: since/until attribute bound to unknown signature name #{name.inspect} (entry names: #{chunk.names.join(', ')})" + end + end MethodAttributes.new(sets.first || raise, since_until) end @@ -601,10 +611,14 @@ def parse_kv_method_attribute(token, chunk) private :parse_kv_method_attribute # シグネチャ行(rd の `--- ...` / md の `### def ...` 等)から、 - # 属性の紐付け先となるメソッド名を取り出す + # 属性の紐付け先となるメソッド名を取り出す。 + # 特殊変数のシグネチャ名は "$SAFE" のように $ 付きだが、エントリの + # names は先頭の $ を除いた形("SAFE"。"$$" なら "$")で格納される + # (method_signature の Signature.new(nil, '$', name[1..-1]) と同じ規約) + # ため、since_by_name のキーも names に合わせて $ を1つ剥がす def method_attribute_target_name(line) normalized = line.sub(MD_METHOD_SIG_PREFIX_RE, '--- ') - MethodSignature.parse(normalized).name + MethodSignature.parse(normalized).name.sub(/\A\$/, '') rescue ParseError nil end diff --git a/lib/bitclust/version_badges.rb b/lib/bitclust/version_badges.rb index bd154cc..2c1f0fc 100644 --- a/lib/bitclust/version_badges.rb +++ b/lib/bitclust/version_badges.rb @@ -32,6 +32,7 @@ module VersionBadges # signature's
(+first+ true), instead of being repeated on every # alias's
; otherwise (mixed/partial) it is looked up per +name+. def heading_version_badges(entry, name, first) + name = badge_lookup_name(name) join_badge_spans( heading_badge_span(entry, name, first, entry.since_map, SINCE_CSS_CLASS, SINCE_CATALOG_KEY), @@ -46,6 +47,7 @@ def heading_version_badges(entry, name, first) # its own line, so this always looks the version up by +name+ directly # -- no uniform-across-all-names aggregation, unlike heading_version_badges. def row_version_badges(entry, name) + name = badge_lookup_name(name) join_badge_spans( badge_span(entry.since_map[name], SINCE_CSS_CLASS, SINCE_CATALOG_KEY), badge_span(entry.until_map[name], UNTIL_CSS_CLASS, UNTIL_CATALOG_KEY) @@ -54,6 +56,14 @@ def row_version_badges(entry, name) private + # シグネチャ行から取り出した名前を since_map/until_map のキーに合わせる。 + # 特殊変数のシグネチャ名は "$SAFE" のように $ 付きだが、エントリの names + # (= マップのキー)は先頭の $ を除いた形("SAFE"。"$$" なら "$")で + # 格納されている(rrdparser の method_signature と同じ規約) + def badge_lookup_name(name) + name.sub(/\A\$/, '') + end + def heading_badge_span(entry, name, first, map, css_class, catalog_key) return nil if map.empty? version = uniform_map?(entry, map) ? (first ? map.values.first : nil) : map[name] diff --git a/sig/bitclust/version_badges.rbs b/sig/bitclust/version_badges.rbs index 33b5e53..a36d474 100644 --- a/sig/bitclust/version_badges.rbs +++ b/sig/bitclust/version_badges.rbs @@ -14,6 +14,8 @@ module BitClust private + def badge_lookup_name: (String name) -> String + def heading_badge_span: (MethodEntry entry, String name, bool first, Hash[String, String] map, String css_class, String catalog_key) -> String? def uniform_map?: (MethodEntry entry, Hash[String, String] map) -> bool diff --git a/test/test_rdcompiler.rb b/test/test_rdcompiler.rb index acd2281..6baf369 100644 --- a/test/test_rdcompiler.rb +++ b/test/test_rdcompiler.rb @@ -598,6 +598,29 @@ def test_method_signature_since_and_until_badges_on_same_dt names: ['hoge'], since_map: {'hoge' => '2.0.0'}, until_map: {'hoge' => '4.0'}) end + # bitclust#132: 特殊変数のシグネチャ名($ 付き)はエントリの names + # ($ なし)へ正規化して since_map を引く。正規化が無いと mixed 経路で + # 該当 dt にバッジが出ない + def test_method_signature_since_badge_special_variable_alias + @c = ja_catalog_compiler + src = <<'HERE' +--- $0 +--- $PROGRAM_NAME +bar +HERE + expected = <<'HERE' +
$0[permalink][rdoc]
+
$PROGRAM_NAMERuby 1.9.1 から
+
+

+bar +

+
+HERE + assert_compiled_method_source(expected, src, + names: ['0', 'PROGRAM_NAME'], since_map: {'PROGRAM_NAME' => '1.9.1'}) + end + def test_ulist_simple src = <<'HERE' * hoge1 diff --git a/test/test_rrdparser.rb b/test/test_rrdparser.rb index 59bb72a..0c03cd9 100644 --- a/test/test_rrdparser.rb +++ b/test/test_rrdparser.rb @@ -98,6 +98,26 @@ def test_until_attribute_sets_until_of_name assert_nil(entry.since_of('test_until')) end + # bitclust#132: 特殊変数のシグネチャ名は "$PROGRAM_NAME" のように $ 付き + # だが、エントリの names は $ を剥がした形で格納される。since_by_name の + # キーは names に合わせる(正規化しないと黙って参照されない値になる) + def test_since_attribute_on_special_variable_uses_entry_name + _library, db = BitClust::RRDParser.parse(<