YARD で拡張ライブラリのドキュメントを書くときに気をつけること

Ruby の拡張ライブラリを書いていていて、YARD でドキュメントを生成しようと思ったらうまく生成されなくてしばらくはまっていた。

YARD も RDoc も、pure Ruby なライブラリだけでなく C で書く拡張ライブラリのドキュメンテーションにも対応している。 そのはずなんだけど、RDoc ではちゃんとメソッドのドキュメントが生成されているのに、YARD だと生成されない、という現象に悩まされた。 いろいろ調べた結果、YARD は簡単に言うと

  1. Ruby のメソッドの本体として使われていそうな関数定義を見つけたら、関数名からそのコメントや定義へのマッピングを追加 (symbol_handler.rb)
  2. 関数内で rb_define_method しているのを見つけたら、マッピングからコメントや定義を取得して、メソッド名と関連付ける (method_handler.rb)

みたいなアルゴリズムになっていて、つまり rb_define_method している行よりソースコード的に上でコメント付きのメソッド本体の関数の定義 (!= 宣言) する必要がある。

なので、

#include <ruby.h>

VALUE rb_cFoo;
static VALUE foo(VALUE self, VALUE x);

void Init_foo(void)
{
  rb_cFoo = rb_define_class("Foo", rb_cObject);
  rb_define_method(rb_cFoo, "foo", RUBY_METHOD_FUNC(foo), 1);
}

/* call-seq: foo(x)
 *
 * Foo method
 *
 * @param [Fixnum] x the argument
 * @return [Fixnum] the return value
 */
VALUE foo(VALUE self, VALUE x)
{
  return x;
}

みたいに書くと YARD のドキュメンテーションに失敗する。

#include <ruby.h>

VALUE rb_cFoo;

/* call-seq: foo(x)
 *
 * Foo method
 *
 * @param [Fixnum] x the argument
 * @return [Fixnum] the return value
 */
static VALUE foo(VALUE self, VALUE x)
{
  return x;
}

void Init_foo(void)
{
  rb_cFoo = rb_define_class("Foo", rb_cObject);
  rb_define_method(rb_cFoo, "foo", RUBY_METHOD_FUNC(foo), 1);
}

こう書くと成功する。

一方 RDoc では、どっちで書いてもちゃんとメソッドに対するドキュメントが生成される。