@@ -269,13 +269,30 @@ def read_chunks(f)
269269 result
270270 end
271271
272+ # シグネチャ行に続くメソッド属性行({: ...}。直前のシグネチャ行に束縛)
273+ METHOD_ATTRIBUTE_LINE_RE = /\A \{ :.*\} [ \t ]*$/
274+
272275 def read_chunk ( f )
273- header = f . span ( /\A ---/ )
276+ # シグネチャ行の直後には {: ...} 属性行を置ける。属性行を挟んでも
277+ # ひとつのチャンク(別名グループ)として読む
278+ header = [ ] #: Array[String]
279+ sig_lines = [ ] #: Array[String]
280+ while f . next?
281+ if /\A ---/ =~ f . peek
282+ line = f . gets or raise
283+ header . push line
284+ sig_lines . push line
285+ elsif !sig_lines . empty? && METHOD_ATTRIBUTE_LINE_RE =~ f . peek
286+ header . push ( f . gets || raise )
287+ else
288+ break
289+ end
290+ end
274291 body = f . break ( /\A (?:---|={1,2}[^=])/ )
275292 src = ( header + body ) . join ( '' )
276- src . location = header [ 0 ] . location
277- sigs = header . map { |line | method_signature ( line ) }
278- mainsig = check_chunk_signatures ( sigs , header [ 0 ] )
293+ src . location = sig_lines [ 0 ] . location
294+ sigs = sig_lines . map { |line | method_signature ( line ) }
295+ mainsig = check_chunk_signatures ( sigs , sig_lines [ 0 ] )
279296 names = sigs . map { |s | s . name } . compact . uniq . sort
280297 Chunk . new ( mainsig , names , src )
281298 end
@@ -462,9 +479,16 @@ def signature
462479
463480 def define_method ( chunk )
464481 id = method_id ( chunk )
482+ attrs = method_attributes ( chunk )
465483 @db . open_method ( id ) { |m |
466484 m . names = chunk . names . sort
467- m . kind = chunk . source . match? ( /^@undef$/ ) ? :undefined : @kind
485+ m . kind = if attrs . include? ( 'undef' )
486+ :undefined
487+ elsif attrs . include? ( 'nomethod' )
488+ :nomethod
489+ else
490+ @kind
491+ end
468492 # steep:ignore:start
469493 m . visibility = @visibility || :public
470494 # steep:ignore:end
@@ -477,6 +501,44 @@ def define_method(chunk)
477501 }
478502 end
479503
504+ # 現在サポートするメソッド属性({: ...} 属性行のトークン)。
505+ # since/until は将来のバージョン情報表示(#132)用に予約
506+ METHOD_ATTRIBUTES = %w[ nomethod undef ]
507+
508+ # kramdown Block IAL 風の {: ...} 属性行からメタデータを集める。
509+ # 属性行は「直前のシグネチャ行のみ」に束縛される(kramdown と同じ解釈)。
510+ # kind はエントリ単位でしか持てないので、別名(複数シグネチャ)の
511+ # エントリでは全シグネチャに同じ属性が付いていることを要求する。
512+ # 本文に入ったら探索を打ち切る(コード例中の {: ...} を誤検出しないため)
513+ def method_attributes ( chunk )
514+ per_sig = [ ] #: Array[Array[String]]
515+ chunk . source . each_line do |line_ |
516+ line = line_ . chomp
517+ case line
518+ when /\A ---\s / , /\A \# \# \# \s /
519+ per_sig . push [ ]
520+ when /\A \{ :(.*)\} [ \t ]*\z /
521+ break if per_sig . empty?
522+ ( $1 || raise ) . strip . split ( /\s +/ ) . each do |token |
523+ unless METHOD_ATTRIBUTES . include? ( token )
524+ raise ParseError ,
525+ "#{ chunk . source . location } : unknown method attribute #{ token . inspect } (supported: #{ METHOD_ATTRIBUTES . join ( ', ' ) } )"
526+ end
527+ per_sig . last &.push token
528+ end
529+ else
530+ break
531+ end
532+ end
533+ return [ ] if per_sig . empty?
534+ sets = per_sig . map { |a | a . uniq . sort }
535+ unless sets . uniq . size == 1
536+ raise ParseError ,
537+ "#{ chunk . source . location } : method attributes must be the same on every signature of an entry: #{ sets . inspect } "
538+ end
539+ sets . first || raise
540+ end
541+
480542 def method_id ( chunk )
481543 id = MethodID . new
482544 id . library = @library
0 commit comments