KDOC 22: whitespaceを読む

whitespaceは、無意味な空白を検知するGoのLinterである。

tasks

DONE どうやって空行を検知しているか

仕組み。

  • 関数定義一覧をとる
  • visitorを作成
  • walkの引数にvisitorを渡す
    • ast.Walkは探索アルゴリズムを切り替えるときに使う
    • visitorインターフェースがアルゴリズム
  • messagesをスライスに貯める
  • ループを抜けてmessagesを返却
if stmt, ok := node.(*ast.IfStmt); ok && v.settings.MultiIf {
	checkMultiLine(v, stmt.Body, stmt.Cond)
}

if stmt, ok := node.(*ast.FuncLit); ok && v.settings.MultiFunc {
	checkMultiLine(v, stmt.Body, stmt.Type)
}

if stmt, ok := node.(*ast.FuncDecl); ok && v.settings.MultiFunc {
	checkMultiLine(v, stmt.Body, stmt.Type)
}
if stmt, ok := node.(*ast.BlockStmt); ok {
	wantNewline := v.wantNewline[stmt]

	comments := v.comments
	if wantNewline {
		comments = nil // Comments also count as a newline if we want a newline
	}
	first, last := firstAndLast(comments, v.fset, stmt.Pos(), stmt.End(), stmt.List)

	startMsg := checkStart(v.fset, stmt.Lbrace, first)

	if wantNewline && startMsg == nil {
		v.messages = append(v.messages, Message{v.fset.Position(stmt.Pos()), MessageTypeAddAfter, `multi-line statement should be followed by a newline`})
	} else if !wantNewline && startMsg != nil {
		v.messages = append(v.messages, *startMsg)
	}

	if msg := checkEnd(v.fset, stmt.Rbrace, last); msg != nil {
		v.messages = append(v.messages, *msg)
	}
}
  • check系関数を使って、警告メッセージを格納する

DONE オプションはどうやって指定しているか

オプションを指定しているようだが、どうやっているか。

Settings構造体を初期化している箇所はない。呼び出し元の設定で、このコードからはわからない。

DONE checkMultilineは何をしているか

DONE 警告部分

func checkStart(fset *token.FileSet, start token.Pos, first ast.Node) *Message {
	if first == nil {
		return nil
	}

	if posLine(fset, start)+1 < posLine(fset, first.Pos()) {
		pos := fset.Position(start)
		return &Message{pos, MessageTypeLeading, `unnecessary leading newline`}
	}

	return nil
}
  • どうして、posLineの比較でわかるのか
    • 実際のコードの位置と、ASTから生成したコードの位置を比較しているぽい
    • 適切であればposは変わらない。余計なwhitespaceがあると、元の位置が大きくなる

memo

  • ast.FuncDeclは関数定義
  • linterじゃないが、キーワードの出現回数とかの統計情報を出せるわけか
  • 関数の一覧
    • 長さ
    • 行数
    • 並び替え