Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10979] ファイルの概要

このコミットは、Go言語の標準ライブラリの一部であるgo/docパッケージ内の内部型名typeDoctypeInfoにリネームするものです。この変更は、外部のドキュメンテーションツールや概念であるTypeDocとの混同を避けることを目的とした、セマンティックな意味合いを持たない純粋なリファクタリングです。コードの動作には一切影響を与えません。

コミット

go/doc: s/typeDoc/typeInfo/

To avoid confusion between typeDoc and TypeDoc.
No semantic change.

R=r
CC=golang-dev
https://golang.org/cl/5502071

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/c0589a21c9ec9f075d27037a62c809a7b0db200d

元コミット内容

commit c0589a21c9ec9f075d27037a62c809a7b0db200d
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Dec 22 14:00:52 2011 -0800

    go/doc: s/typeDoc/typeInfo/
    
    To avoid confusion between typeDoc and TypeDoc.
    No semantic change.
    
    R=r
    CC=golang-dev
    https://golang.org/cl/5502071
---
 src/pkg/go/doc/doc.go | 58 +++++++++++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 29 deletions(-)

diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index 8d7a78ca1b..888fbe1bfb 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -17,11 +17,11 @@ import (
 // embeddedType describes the type of an anonymous field.
 //
 type embeddedType struct {
-	typ *typeDoc // the corresponding base type
-	ptr bool     // if set, the anonymous field type is a pointer
+	typ *typeInfo // the corresponding base type
+	ptr bool      // if set, the anonymous field type is a pointer
 }
 
-type typeDoc struct {
+type typeInfo struct {
 	// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
 	// if the type declaration hasn't been seen yet, decl is nil
 	decl     *ast.GenDecl
@@ -45,16 +45,16 @@ type docReader struct {
 	doc      *ast.CommentGroup // package documentation, if any
 	pkgName  string
 	values   []*ast.GenDecl // consts and vars
-	types    map[string]*typeDoc
-	embedded map[string]*typeDoc // embedded types, possibly not exported
+	types    map[string]*typeInfo
+	embedded map[string]*typeInfo // embedded types, possibly not exported
 	funcs    map[string]*ast.FuncDecl
 	bugs     []*ast.CommentGroup
 }
 
 func (doc *docReader) init(pkgName string) {
 	doc.pkgName = pkgName
-	doc.types = make(map[string]*typeDoc)
-	doc.embedded = make(map[string]*typeDoc)
+	doc.types = make(map[string]*typeInfo)
+	doc.embedded = make(map[string]*typeInfo)
 	doc.funcs = make(map[string]*ast.FuncDecl)
 }
 
@@ -72,20 +72,20 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
 	doc.doc.List = append(list, comments.List...)
 }
 
-func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
+func (doc *docReader) lookupTypeInfo(name string) *typeInfo {
 	if name == "" || name == "_" {
 		return nil // no type docs for anonymous types
 	}
-	if tdoc, found := doc.types[name]; found {
-		return tdoc
+	if info, found := doc.types[name]; found {
+		return info
 	}
 	// type wasn't found - add one without declaration
-	tdoc := &typeDoc{
+	info := &typeInfo{
 		factories: make(map[string]*ast.FuncDecl),
 		methods:   make(map[string]*ast.FuncDecl),
 	}
-	doc.types[name] = tdoc
-	return tdoc
+	doc.types[name] = info
+	return info
 }
 
 func baseTypeName(typ ast.Expr, allTypes bool) string {
@@ -144,7 +144,7 @@ func (doc *docReader) addValue(decl *ast.GenDecl) {\n 	values := &doc.values\n 	if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {\n 		// typed entries are sufficiently frequent\n-		typ := doc.lookupTypeDoc(domName)\n+		typ := doc.lookupTypeInfo(domName)\n 		if typ != nil {\n 			values = &typ.values // associate with that type
 		}\n@@ -174,7 +174,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {\n 	// determine if it should be associated with a type\n 	if fun.Recv != nil {\n 		// method\n-		typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type, false))\n+		typ := doc.lookupTypeInfo(baseTypeName(fun.Recv.List[0].Type, false))\n 		if typ != nil {\n 			// exported receiver type\n 			setFunc(typ.methods, fun)\n@@ -196,7 +196,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {\n 			// with the first type in result signature (there may\n 			// be more than one result)\n 			tname := baseTypeName(res.Type, false)\n-			typ := doc.lookupTypeDoc(tname)\n+			typ := doc.lookupTypeInfo(tname)\n 			if typ != nil {\n 				// named and exported result type\n 				setFunc(typ.factories, fun)\n@@ -222,8 +222,8 @@ func (doc *docReader) addDecl(decl ast.Decl) {\n 				for _, spec := range d.Specs {\n 					tspec := spec.(*ast.TypeSpec)\n 					// add the type to the documentation\n-					tdoc := doc.lookupTypeDoc(tspec.Name.Name)\n-					if tdoc == nil {\n+					info := doc.lookupTypeInfo(tspec.Name.Name)\n+					if info == nil {\n 						continue // no name - ignore the type\n 					}\n 					// Make a (fake) GenDecl node for this TypeSpec\n@@ -240,9 +240,9 @@ func (doc *docReader) addDecl(decl ast.Decl) {\n 					// has documentation as well.\n 					fake := &ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos,\n 						[]ast.Spec{tspec}, token.NoPos}\n-					// A type should be added at most once, so tdoc.decl\n+					// A type should be added at most once, so info.decl\n 					// should be nil - if it isn't, simply overwrite it.\n-					tdoc.decl = fake\n+					info.decl = fake\n 					// Look for anonymous fields that might contribute methods.\n 					var fields *ast.FieldList\n 					switch typ := spec.(*ast.TypeSpec).Type.(type) {\n@@ -255,12 +255,12 @@ func (doc *docReader) addDecl(decl ast.Decl) {\n 						for _, field := range fields.List {\n 							if len(field.Names) == 0 {\n 								// anonymous field - add corresponding type\n-								// to the tdoc and collect it in doc\n+								// to the info and collect it in doc\n 								name := baseTypeName(field.Type, true)\n-								edoc := doc.lookupTypeDoc(name)\n+								edoc := doc.lookupTypeInfo(name)\n 								if edoc != nil {\n 									_, ptr := field.Type.(*ast.StarExpr)\n-									tdoc.embedded = append(tdoc.embedded, embeddedType{edoc, ptr})\n+									info.embedded = append(info.embedded, embeddedType{edoc, ptr})\n 								}\n 							}\n 						}\n@@ -478,7 +478,7 @@ func (p sortTypeDoc) Less(i, j int) bool {\n // NOTE(rsc): This would appear not to be correct for type ( )\n // blocks, but the doc extractor above has split them into\n // individual declarations.\n-func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n+func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n 	// TODO(gri) Consider computing the embedded method information\n 	//           before calling makeTypeDocs. Then this function can\n 	//           be single-phased again. Also, it might simplify some\n@@ -488,7 +488,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n 	list := make([]*TypeDoc, len(m))\n 	i := 0\n 	for _, old := range m {\n-\t\t// all typeDocs should have a declaration associated with\n+\t\t// all typeInfos should have a declaration associated with\n \t\t// them after processing an entire package - be conservative\n \t\t// and check\n \t\tif decl := old.decl; decl != nil {\n@@ -540,7 +540,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n 	}\n 	list = list[0:i] // some types may have been ignored\n \n-\t// phase 2: collect embedded methods for each processed typeDoc\n+\t// phase 2: collect embedded methods for each processed typeInfo\n \tfor _, old := range m {\n \t\tif t := old.forward; t != nil {\n \t\t\t// old has been processed into t; collect embedded\n@@ -585,13 +585,13 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n }\n \n // collectEmbeddedMethods collects the embedded methods from all\n-// processed embedded types found in tdoc in mset. It considers\n+// processed embedded types found in info in mset. It considers\n // embedded types at the most shallow level first so that more\n // deeply nested embedded methods with conflicting names are\n // excluded.\n //\n-func collectEmbeddedMethods(mset methodSet, tdoc *typeDoc, recvTypeName string) {\n-\tfor _, e := range tdoc.embedded {\n+func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string) {\n+\tfor _, e := range info.embedded {\n \t\tif e.typ.forward != nil { // == e was processed\n \t\t\tfor _, m := range e.typ.forward.methods {\n \t\t\t\tmset.add(customizeRecv(m, e.ptr, recvTypeName))\n```

## 変更の背景

この変更の主な背景は、コミットメッセージに明記されている通り、「`typeDoc`と`TypeDoc`の混同を避けるため」です。

Go言語では、エクスポートされる(パッケージ外部から参照可能な)識別子は大文字で始まり、パッケージ内部でのみ使用される識別子(非エクスポート)は小文字で始まるという命名規則があります。`go/doc`パッケージ内で定義されていた`typeDoc`という構造体は、小文字で始まるためパッケージ内部でのみ使用されることを意図していました。

しかし、世の中には`TypeDoc`という名前の別のツールや概念が存在する可能性があります。例えば、TypeScriptのドキュメンテーションジェネレーターである`TypeDoc`などが挙げられます。このような外部の有名なツールや概念と、Goの内部的な型名が似ていると、以下のような混乱が生じる可能性があります。

1.  **誤解**: コードを読んだ開発者が、内部の`typeDoc`が外部の`TypeDoc`と何らかの関係があると誤解する。
2.  **検索の困難さ**: ドキュメントやコードベースを検索する際に、意図しない検索結果がヒットし、目的の情報を見つけにくくなる。
3.  **コミュニケーションの曖昧さ**: 開発者間の議論で「TypeDoc」という言葉が出た際に、それがGoの内部型を指すのか、外部ツールを指すのかが不明瞭になる。

このような混乱を未然に防ぎ、コードの可読性と保守性を向上させるために、内部的な型名をより明確で誤解の余地のない`typeInfo`に変更することが決定されました。この変更は機能的な影響を一切持たず、純粋にコードの明確性を高めるためのリファクタリングです。

## 前提知識の解説

### Go言語の`go/doc`パッケージ

`go/doc`パッケージは、Go言語のソースコードからドキュメンテーションを生成するための標準ライブラリです。Goのソースコードを解析し、パッケージ、型、関数、変数などの情報を抽出し、それらのドキュメンテーションコメントを読み取って、プログラム的にアクセス可能な構造体として提供します。これにより、`go doc`コマンドやGoの公式ドキュメンテーションサイト(pkg.go.devなど)が生成されています。このパッケージは、Goのコードベースの自己文書化を可能にする重要な役割を担っています。

### Go言語の命名規則

Go言語には、識別子の可視性(エクスポートされるか否か)を制御するための明確な命名規則があります。

*   **エクスポートされる識別子**: パッケージの外部からアクセス可能にするには、識別子(変数、関数、型、メソッドなど)の名前を**大文字**で始める必要があります。例: `MyFunction`, `TypeDefinition`, `VariableName`。
*   **非エクスポート(内部)識別子**: パッケージ内部でのみ使用される識別子は、名前を**小文字**で始めます。例: `myFunction`, `typeDefinition`, `variableName`。

この規則は、Goのコードベースのモジュール性とカプセル化を促進し、どの要素が公開APIの一部であるかを一目でわかるようにします。今回のコミットでは、内部的な`typeDoc`が外部の`TypeDoc`と混同される可能性があったため、この命名規則の文脈で問題が認識されました。

### Go言語の型定義(`struct`)

Go言語では、`struct`キーワードを使用して複合データ型を定義します。`struct`は、異なる型のフィールド(プロパティ)をまとめることができます。

```go
type MyStruct struct {
    Field1 string
    Field2 int
}

go/docパッケージでは、Goのソースコードから抽出された様々な情報を表現するために、多くの構造体が定義されています。typeDoc(変更後はtypeInfo)もその一つで、Goの型に関する詳細な情報を保持していました。

リファクタリング

リファクタリングとは、ソフトウェアの外部的な振る舞いを変更せずに、内部構造を改善するプロセスです。コードの可読性、保守性、拡張性を向上させることを目的とします。今回のコミットは、機能的な変更を伴わない純粋なリファクタリングの典型例です。名前の変更は、コードの意図をより明確にし、将来的な誤解やバグのリスクを減らすのに役立ちます。

技術的詳細

このコミットで行われた技術的な変更は、src/pkg/go/doc/doc.goファイル内のtypeDocという名前の構造体とその全ての参照をtypeInfoに置き換えることです。これは、Goのコードベースにおける一般的なリファクタリング手法の一つである「シンボル名の変更」に該当します。

具体的には、以下の要素が変更されました。

  1. 構造体定義の変更: type typeDoc struct { ... }type typeInfo struct { ... } に変更されました。これにより、go/docパッケージ内で型情報を表現するための主要な構造体の名前が更新されました。

  2. フィールドの型変更: embeddedType構造体内のtypフィールドの型が*typeDocから*typeInfoに変更されました。embeddedTypeは、Goの構造体における匿名フィールド(埋め込みフィールド)の型を記述するために使用されます。

    // embeddedType describes the type of an anonymous field.
    type embeddedType struct {
    	typ *typeInfo // the corresponding base type (formerly *typeDoc)
    	ptr bool      // if set, the anonymous field type is a pointer
    }
    
  3. マップのキー/値の型変更: docReader構造体内のtypesembeddedというマップの型が、map[string]*typeDocからmap[string]*typeInfoに変更されました。docReaderは、Goのパッケージのドキュメンテーションを読み取るための主要な構造体であり、これらのマップはパッケージ内の型情報を管理するために使用されます。

    type docReader struct {
        // ...
    	types    map[string]*typeInfo    // formerly map[string]*typeDoc
    	embedded map[string]*typeInfo    // formerly map[string]*typeDoc // embedded types, possibly not exported
        // ...
    }
    
  4. 関数名の変更: lookupTypeDocという関数がlookupTypeInfoにリネームされました。この関数は、指定された名前の型情報を検索または新しく作成するために使用されます。関数名が変更されたことに伴い、その内部でtypeDoc構造体をインスタンス化していた箇所もtypeInfoに変更されています。

    func (doc *docReader) lookupTypeInfo(name string) *typeInfo { // formerly lookupTypeDoc
        // ...
    	info := &typeInfo{ // formerly tdoc := &typeDoc{
            // ...
    	}
    	doc.types[name] = info // formerly doc.types[name] = tdoc
    	return info            // formerly return tdoc
    }
    
  5. 変数名の変更とコメントの更新: コード全体でtypeDoc型の変数が使用されている箇所(例: tdoc)がinfoなどのより適切な名前に変更され、関連するコメントも更新されています。これにより、コードの意図がより明確になります。

これらの変更は、Goのコンパイラやリンカによって自動的に検出され、コンパイル時にエラーが発生しないように、全ての参照箇所で一貫して行われています。このような大規模な名前変更は、IDEの強力なリファクタリング機能や、go fixのようなGoツールチェーンの機能によって容易に行うことができます。

コアとなるコードの変更箇所

変更はsrc/pkg/go/doc/doc.goファイルに集中しています。

  1. embeddedType構造体のフィールド型変更:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -17,11 +17,11 @@ import (
     // embeddedType describes the type of an anonymous field.
     //
     type embeddedType struct {
    -	typ *typeDoc // the corresponding base type
    -	ptr bool     // if set, the anonymous field type is a pointer
    +	typ *typeInfo // the corresponding base type
    +	ptr bool      // if set, the anonymous field type is a pointer
     }
    
  2. typeDoc構造体からtypeInfo構造体へのリネーム:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -17,11 +17,11 @@ import (
     // embeddedType describes the type of an anonymous field.
     //
     type embeddedType struct {
    -	typ *typeDoc // the corresponding base type
    -	ptr bool     // if set, the anonymous field type is a pointer
    +	typ *typeInfo // the corresponding base type
    +	ptr bool      // if set, the anonymous field type is a pointer
     }
     
    -type typeDoc struct {
    +type typeInfo struct {
     	// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
     	// if the type declaration hasn't been seen yet, decl is nil
     	decl     *ast.GenDecl
    
  3. docReader構造体内のマップ型変更:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -45,16 +45,16 @@ type docReader struct {
      	doc      *ast.CommentGroup // package documentation, if any
      	pkgName  string
      	values   []*ast.GenDecl // consts and vars
    -	types    map[string]*typeDoc
    -	embedded map[string]*typeDoc // embedded types, possibly not exported
    +	types    map[string]*typeInfo
    +	embedded map[string]*typeInfo // embedded types, possibly not exported
      	funcs    map[string]*ast.FuncDecl
      	bugs     []*ast.CommentGroup
      }
      
      func (doc *docReader) init(pkgName string) {
      	doc.pkgName = pkgName
    -	doc.types = make(map[string]*typeDoc)
    -	doc.embedded = make(map[string]*typeDoc)
    +	doc.types = make(map[string]*typeInfo)
    +	doc.embedded = make(map[string]*typeInfo)
      	doc.funcs = make(map[string]*ast.FuncDecl)
      }
    
  4. lookupTypeDoc関数からlookupTypeInfo関数へのリネームと内部変更:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -72,20 +72,20 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
      	doc.doc.List = append(list, comments.List...)
      }
      
    -func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
    +func (doc *docReader) lookupTypeInfo(name string) *typeInfo {
      	if name == "" || name == "_" {
      		return nil // no type docs for anonymous types
      	}
    -	if tdoc, found := doc.types[name]; found {
    -		return tdoc
    +	if info, found := doc.types[name]; found {
    +		return info
      	}
      	// type wasn't found - add one without declaration
    -	tdoc := &typeDoc{
    +	info := &typeInfo{
      		factories: make(map[string]*ast.FuncDecl),
      		methods:   make(map[string]*ast.FuncDecl),
      	}
    -	doc.types[name] = tdoc
    -	return tdoc
    +	doc.types[name] = info
    +	return info
      }
    

これら以外にも、addValue, addFunc, addDecl, makeTypeDocs, collectEmbeddedMethodsといった関数内でtypeDocが参照されている箇所が全てtypeInfoに更新されています。また、関連するコメント内のtypeDocという記述もtypeInfoに修正されています。

コアとなるコードの解説

このコミットのコアとなる変更は、go/docパッケージがGoの型情報をどのように内部的に表現し、操作しているかに関わっています。

  • typeInfo構造体(旧typeDoc: この構造体は、Goのソースコードから抽出された単一の型(例: struct, interface, intなどの基本型)に関する包括的な情報を保持するために設計されています。具体的には、以下の情報を含みます。

    • decl: 型の宣言(type MyType struct { ... }のようなast.GenDeclノード)。
    • values: その型に関連付けられた定数や変数(例: type MyType int; const ( A MyType = 1 ))。
    • factories: その型を返すファクトリ関数(例: func NewMyType() MyType { ... })。
    • methods: その型に紐付けられたメソッド(例: func (m MyType) DoSomething() { ... })。
    • embedded: 構造体に埋め込まれた匿名フィールドの型情報。
    • forward: ドキュメンテーション生成の複数フェーズで型情報をリンクするためのポインタ。

    この構造体の名前がtypeDocからtypeInfoに変更されたことで、その役割が「型に関するドキュメンテーション情報」だけでなく、「型に関する一般的な情報」を保持するものであることがより明確になりました。

  • embeddedType構造体: この構造体は、Goの構造体における匿名フィールド(埋め込みフィールド)の型を記述します。例えば、struct { io.Reader }のような場合、io.Readerが匿名フィールドです。embeddedTypetypフィールドは、埋め込まれた型のtypeInfo(旧typeDoc)へのポインタを保持していました。この変更により、一貫性が保たれています。

  • docReader構造体: docReaderは、Goのパッケージ全体を走査し、そのドキュメンテーション情報を収集する役割を担います。この構造体は、パッケージ内の全ての型情報をtypesというマップ(map[string]*typeInfo)で管理していました。また、埋め込み型に関する情報もembeddedマップで管理していました。これらのマップの型が*typeDocから*typeInfoに変更されたことで、docReaderが扱う型情報の参照先が正しく更新されました。

  • lookupTypeInfo関数(旧lookupTypeDoc: この関数は、docReaderのメソッドであり、指定された型名に対応するtypeInfoオブジェクトを取得するために使用されます。もしその型名がまだdocReadertypesマップに存在しない場合、新しいtypeInfoオブジェクトを作成してマップに追加し、それを返します。この関数のリネームは、その目的が「型ドキュメントを検索する」から「型情報を検索する」へと、より広範な意味合いを持つようになったことを反映しています。

  • その他の関連関数の変更: addValue, addFunc, addDeclなどの関数は、GoのソースコードのAST(抽象構文木)を解析し、抽出した情報を適切なtypeInfoオブジェクトに関連付ける役割を担っています。これらの関数内でlookupTypeDocが呼び出されていた箇所や、typeDoc型の変数が使用されていた箇所が全てlookupTypeInfotypeInfoに更新されています。これにより、コードベース全体で新しい命名規則が適用され、整合性が保たれています。

このコミットは、Goの内部コードの品質と明確性を向上させるための、細部へのこだわりを示す良い例です。

関連リンク

  • Go言語の公式ドキュメンテーション: https://go.dev/
  • Go言語のgo/docパッケージドキュメンテーション: https://pkg.go.dev/go/doc
  • Gerrit Change-ID: https://golang.org/cl/5502071
    • Goプロジェクトでは、コードレビューにGerritを使用しています。このリンクは、このコミットがGerrit上でレビューされた際の変更セット(Change-ID)を指します。Gerritは、Gitリポジトリに対する変更を管理し、レビュープロセスを円滑に進めるためのウェブベースのコードレビューシステムです。

参考にした情報源リンク

  • Go言語の公式ドキュメンテーション
  • Go言語のソースコード(特にsrc/pkg/go/doc/doc.go
  • Go言語の命名規則に関する一般的な情報源
  • Gerrit Code Reviewの仕組みに関する情報
  • TypeScriptのTypeDocプロジェクト (混同の可能性のある外部ツールとして)

この解説は、提供されたコミット情報と一般的なGo言語の知識、およびWeb検索で得られた情報を基に作成されました。```

[インデックス 10979] ファイルの概要

このコミットは、Go言語の標準ライブラリの一部であるgo/docパッケージ内の内部型名typeDoctypeInfoにリネームするものです。この変更は、外部のドキュメンテーションツールや概念であるTypeDocとの混同を避けることを目的とした、セマンティックな意味合いを持たない純粋なリファクタリングです。コードの動作には一切影響を与えません。

コミット

go/doc: s/typeDoc/typeInfo/

To avoid confusion between typeDoc and TypeDoc.
No semantic change.

R=r
CC=golang-dev
https://golang.org/cl/5502071

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/c0589a21c9ec9f075d27037a62c809a7b0db200d

元コミット内容

commit c0589a21c9ec9f075d27037a62c809a7b0db200d
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Dec 22 14:00:52 2011 -0800

    go/doc: s/typeDoc/typeInfo/
    
    To avoid confusion between typeDoc and TypeDoc.
    No semantic change.
    
    R=r
    CC=golang-dev
    https://golang.org/cl/5502071
---
 src/pkg/go/doc/doc.go | 58 +++++++++++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 29 deletions(-).

diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index 8d7a78ca1b..888fbe1bfb 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -17,11 +17,11 @@ import (
 // embeddedType describes the type of an anonymous field.
 //
 type embeddedType struct {
-	typ *typeDoc // the corresponding base type
-	ptr bool     // if set, the anonymous field type is a pointer
+	typ *typeInfo // the corresponding base type
+	ptr bool      // if set, the anonymous field type is a pointer
 }
 
-type typeDoc struct {
+type typeInfo struct {
 	// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
 	// if the type declaration hasn't been seen yet, decl is nil
 	decl     *ast.GenDecl
@@ -45,16 +45,16 @@ type docReader struct {
 	doc      *ast.CommentGroup // package documentation, if any
 	pkgName  string
 	values   []*ast.GenDecl // consts and vars
-	types    map[string]*typeDoc
-	embedded map[string]*typeDoc // embedded types, possibly not exported
+	types    map[string]*typeInfo
+	embedded map[string]*typeInfo // embedded types, possibly not exported
 	funcs    map[string]*ast.FuncDecl
 	bugs     []*ast.CommentGroup
 }
 
 func (doc *docReader) init(pkgName string) {
 	doc.pkgName = pkgName
-	doc.types = make(map[string]*typeDoc)
-	doc.embedded = make(map[string]*typeDoc)
+	doc.types = make(map[string]*typeInfo)
+	doc.embedded = make(map[string]*typeInfo)
 	doc.funcs = make(map[string]*ast.FuncDecl)
 }
 
@@ -72,20 +72,20 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
 	doc.doc.List = append(list, comments.List...)
 }
 
-func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
+func (doc *docReader) lookupTypeInfo(name string) *typeInfo {
 	if name == "" || name == "_" {
 		return nil // no type docs for anonymous types
 	}
-	if tdoc, found := doc.types[name]; found {
-		return tdoc
+	if info, found := doc.types[name]; found {
+		return info
 	}
 	// type wasn't found - add one without declaration
-	tdoc := &typeDoc{
+	info := &typeInfo{
 		factories: make(map[string]*ast.FuncDecl),
 		methods:   make(map[string]*ast.FuncDecl),
 	}
-	doc.types[name] = tdoc
-	return tdoc
+	doc.types[name] = info
+	return info
 }
 
 func baseTypeName(typ ast.Expr, allTypes bool) string {
@@ -144,7 +144,7 @@ func (doc *docReader) addValue(decl *ast.GenDecl) {\n 	values := &doc.values\n 	if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {\n 		// typed entries are sufficiently frequent\n-		typ := doc.lookupTypeDoc(domName)\n+		typ := doc.lookupTypeInfo(domName)\n 		if typ != nil {\n 			values = &typ.values // associate with that type
 		}\n@@ -174,7 +174,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {\n 	// determine if it should be associated with a type\n 	if fun.Recv != nil {\n 		// method\n-		typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type, false))\n+		typ := doc.lookupTypeInfo(baseTypeName(fun.Recv.List[0].Type, false))\n 		if typ != nil {\n 			// exported receiver type\n 			setFunc(typ.methods, fun)\n@@ -196,7 +196,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {\n 			// with the first type in result signature (there may\n 			// be more than one result)\n 			tname := baseTypeName(res.Type, false)\n-			typ := doc.lookupTypeDoc(tname)\n+			typ := doc.lookupTypeInfo(tname)\n 			if typ != nil {\n 				// named and exported result type\n 				setFunc(typ.factories, fun)\n@@ -222,8 +222,8 @@ func (doc *docReader) addDecl(decl ast.Decl) {\n 				for _, spec := range d.Specs {\n 					tspec := spec.(*ast.TypeSpec)\n 					// add the type to the documentation\n-					tdoc := doc.lookupTypeDoc(tspec.Name.Name)\n-					if tdoc == nil {\n+					info := doc.lookupTypeInfo(tspec.Name.Name)\n+					if info == nil {\n 						continue // no name - ignore the type\n 					}\n 					// Make a (fake) GenDecl node for this TypeSpec\n@@ -240,9 +240,9 @@ func (doc *docReader) addDecl(decl ast.Decl) {\n 					// has documentation as well.\n 					fake := &ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos,\n 						[]ast.Spec{tspec}, token.NoPos}\n-					// A type should be added at most once, so tdoc.decl\n+					// A type should be added at most once, so info.decl\n 					// should be nil - if it isn't, simply overwrite it.\n-					tdoc.decl = fake\n+					info.decl = fake\n 					// Look for anonymous fields that might contribute methods.\n 					var fields *ast.FieldList\n 					switch typ := spec.(*ast.TypeSpec).Type.(type) {\n@@ -255,12 +255,12 @@ func (doc *docReader) addDecl(decl ast.Decl) {\n 						for _, field := range fields.List {\n 							if len(field.Names) == 0 {\n 								// anonymous field - add corresponding type\n-								// to the tdoc and collect it in doc\n+								// to the info and collect it in doc\n 								name := baseTypeName(field.Type, true)\n-								edoc := doc.lookupTypeDoc(name)\n+								edoc := doc.lookupTypeInfo(name)\n 								if edoc != nil {\n 									_, ptr := field.Type.(*ast.StarExpr)\n-									tdoc.embedded = append(tdoc.embedded, embeddedType{edoc, ptr})\n+									info.embedded = append(info.embedded, embeddedType{edoc, ptr})\n 								}\n 							}\n 						}\n@@ -478,7 +478,7 @@ func (p sortTypeDoc) Less(i, j int) bool {\n // NOTE(rsc): This would appear not to be correct for type ( )\n // blocks, but the doc extractor above has split them into\n // individual declarations.\n-func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n+func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {\n 	// TODO(gri) Consider computing the embedded method information\n 	//           before calling makeTypeDocs. Then this function can\n 	//           be single-phased again. Also, it might simplify some\n@@ -488,7 +488,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n 	list := make([]*TypeDoc, len(m))\n 	i := 0\n 	for _, old := range m {\n-\t\t// all typeDocs should have a declaration associated with\n+\t\t// all typeInfos should have a declaration associated with\n \t\t// them after processing an entire package - be conservative\n \t\t// and check\n \t\tif decl := old.decl; decl != nil {\n@@ -540,7 +540,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n 	}\n 	list = list[0:i] // some types may have been ignored\n \n-\t// phase 2: collect embedded methods for each processed typeDoc\n+\t// phase 2: collect embedded methods for each processed typeInfo\n \tfor _, old := range m {\n \t\tif t := old.forward; t != nil {\n \t\t\t// old has been processed into t; collect embedded\n@@ -585,13 +585,13 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {\n }\n \n // collectEmbeddedMethods collects the embedded methods from all\n-// processed embedded types found in tdoc in mset. It considers\n+// processed embedded types found in info in mset. It considers\n // embedded types at the most shallow level first so that more\n // deeply nested embedded methods with conflicting names are\n // excluded.\n //\n-func collectEmbeddedMethods(mset methodSet, tdoc *typeDoc, recvTypeName string) {\n-\tfor _, e := range tdoc.embedded {\n+func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string) {\n+\tfor _, e := range info.embedded {\n \t\tif e.typ.forward != nil { // == e was processed\n \t\t\tfor _, m := range e.typ.forward.methods {\n \t\t\t\tmset.add(customizeRecv(m, e.ptr, recvTypeName))\n```

## 変更の背景

この変更の主な背景は、コミットメッセージに明記されている通り、「`typeDoc`と`TypeDoc`の混同を避けるため」です。

Go言語では、エクスポートされる(パッケージ外部から参照可能な)識別子は大文字で始まり、パッケージ内部でのみ使用される識別子(非エクスポート)は小文字で始まるという命名規則があります。`go/doc`パッケージ内で定義されていた`typeDoc`という構造体は、小文字で始まるためパッケージ内部でのみ使用されることを意図していました。

しかし、世の中には`TypeDoc`という名前の別のツールや概念が存在する可能性があります。例えば、TypeScriptのドキュメンテーションジェネレーターである`TypeDoc`などが挙げられます。このような外部の有名なツールや概念と、Goの内部的な型名が似ていると、以下のような混乱が生じる可能性があります。

1.  **誤解**: コードを読んだ開発者が、内部の`typeDoc`が外部の`TypeDoc`と何らかの関係があると誤解する。
2.  **検索の困難さ**: ドキュメントやコードベースを検索する際に、意図しない検索結果がヒットし、目的の情報を見つけにくくなる。
3.  **コミュニケーションの曖昧さ**: 開発者間の議論で「TypeDoc」という言葉が出た際に、それがGoの内部型を指すのか、外部ツールを指すのかが不明瞭になる。

このような混乱を未然に防ぎ、コードの可読性と保守性を向上させるために、内部的な型名をより明確で誤解の余地のない`typeInfo`に変更することが決定されました。この変更は機能的な影響を一切持たず、純粋にコードの明確性を高めるためのリファクタリングです。

## 前提知識の解説

### Go言語の`go/doc`パッケージ

`go/doc`パッケージは、Go言語のソースコードからドキュメンテーションを生成するための標準ライブラリです。Goのソースコードを解析し、パッケージ、型、関数、変数などの情報を抽出し、それらのドキュメンテーションコメントを読み取って、プログラム的にアクセス可能な構造体として提供します。これにより、`go doc`コマンドやGoの公式ドキュメンテーションサイト(pkg.go.devなど)が生成されています。このパッケージは、Goのコードベースの自己文書化を可能にする重要な役割を担っています。

### Go言語の命名規則

Go言語には、識別子の可視性(エクスポートされるか否か)を制御するための明確な命名規則があります。

*   **エクスポートされる識別子**: パッケージの外部からアクセス可能にするには、識別子(変数、関数、型、メソッドなど)の名前を**大文字**で始める必要があります。例: `MyFunction`, `TypeDefinition`, `VariableName`。
*   **非エクスポート(内部)識別子**: パッケージ内部でのみ使用される識別子は、名前を**小文字**で始めます。例: `myFunction`, `typeDefinition`, `variableName`。

この規則は、Goのコードベースのモジュール性とカプセル化を促進し、どの要素が公開APIの一部であるかを一目でわかるようにします。今回のコミットでは、内部的な`typeDoc`が外部の`TypeDoc`と混同される可能性があったため、この命名規則の文脈で問題が認識されました。

### Go言語の型定義(`struct`)

Go言語では、`struct`キーワードを使用して複合データ型を定義します。`struct`は、異なる型のフィールド(プロパティ)をまとめることができます。

```go
type MyStruct struct {
    Field1 string
    Field2 int
}

go/docパッケージでは、Goのソースコードから抽出された様々な情報を表現するために、多くの構造体が定義されています。typeDoc(変更後はtypeInfo)もその一つで、Goの型に関する詳細な情報を保持していました。

リファクタリング

リファクタリングとは、ソフトウェアの外部的な振る舞いを変更せずに、内部構造を改善するプロセスです。コードの可読性、保守性、拡張性を向上させることを目的とします。今回のコミットは、機能的な変更を伴わない純粋なリファクタリングの典型例です。名前の変更は、コードの意図をより明確にし、将来的な誤解やバグのリスクを減らすのに役立ちます。

技術的詳細

このコミットで行われた技術的な変更は、src/pkg/go/doc/doc.goファイル内のtypeDocという名前の構造体とその全ての参照をtypeInfoに置き換えることです。これは、Goのコードベースにおける一般的なリファクタリング手法の一つである「シンボル名の変更」に該当します。

具体的には、以下の要素が変更されました。

  1. 構造体定義の変更: type typeDoc struct { ... }type typeInfo struct { ... } に変更されました。これにより、go/docパッケージ内で型情報を表現するための主要な構造体の名前が更新されました。

  2. フィールドの型変更: embeddedType構造体内のtypフィールドの型が*typeDocから*typeInfoに変更されました。embeddedTypeは、Goの構造体における匿名フィールド(埋め込みフィールド)の型を記述するために使用されます。

    // embeddedType describes the type of an anonymous field.
    type embeddedType struct {
    	typ *typeInfo // the corresponding base type (formerly *typeDoc)
    	ptr bool      // if set, the anonymous field type is a pointer
    }
    
  3. マップのキー/値の型変更: docReader構造体内のtypesembeddedというマップの型が、map[string]*typeDocからmap[string]*typeInfoに変更されました。docReaderは、Goのパッケージのドキュメンテーションを読み取るための主要な構造体であり、これらのマップはパッケージ内の型情報を管理するために使用されます。

    type docReader struct {
        // ...
    	types    map[string]*typeInfo    // formerly map[string]*typeDoc
    	embedded map[string]*typeInfo    // formerly map[string]*typeDoc // embedded types, possibly not exported
        // ...
    }
    
  4. 関数名の変更: lookupTypeDocという関数がlookupTypeInfoにリネームされました。この関数は、指定された名前の型情報を検索または新しく作成するために使用されます。関数名が変更されたことに伴い、その内部でtypeDoc構造体をインスタンス化していた箇所もtypeInfoに変更されています。

    func (doc *docReader) lookupTypeInfo(name string) *typeInfo { // formerly lookupTypeDoc
        // ...
    	info := &typeInfo{ // formerly tdoc := &typeDoc{
            // ...
    	}
    	doc.types[name] = info // formerly doc.types[name] = tdoc
    	return info            // formerly return tdoc
    }
    
  5. 変数名の変更とコメントの更新: コード全体でtypeDoc型の変数が使用されている箇所(例: tdoc)がinfoなどのより適切な名前に変更され、関連するコメントも更新されています。これにより、コードの意図がより明確になります。

これらの変更は、Goのコンパイラやリンカによって自動的に検出され、コンパイル時にエラーが発生しないように、全ての参照箇所で一貫して行われています。このような大規模な名前変更は、IDEの強力なリファクタリング機能や、go fixのようなGoツールチェーンの機能によって容易に行うことができます。

コアとなるコードの変更箇所

変更はsrc/pkg/go/doc/doc.goファイルに集中しています。

  1. embeddedType構造体のフィールド型変更:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -17,11 +17,11 @@ import (
     // embeddedType describes the type of an anonymous field.
     //
     type embeddedType struct {
    -	typ *typeDoc // the corresponding base type
    -	ptr bool     // if set, the anonymous field type is a pointer
    +	typ *typeInfo // the corresponding base type
    +	ptr bool      // if set, the anonymous field type is a pointer
     }
    
  2. typeDoc構造体からtypeInfo構造体へのリネーム:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -17,11 +17,11 @@ import (
     // embeddedType describes the type of an anonymous field.
     //
     type embeddedType struct {
    -	typ *typeDoc // the corresponding base type
    -	ptr bool     // if set, the anonymous field type is a pointer
    +	typ *typeInfo // the corresponding base type
    +	ptr bool      // if set, the anonymous field type is a pointer
     }
     
    -type typeDoc struct {
    +type typeInfo struct {
     	// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
     	// if the type declaration hasn't been seen yet, decl is nil
     	decl     *ast.GenDecl
    
  3. docReader構造体内のマップ型変更:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -45,16 +45,16 @@ type docReader struct {
      	doc      *ast.CommentGroup // package documentation, if any
      	pkgName  string
      	values   []*ast.GenDecl // consts and vars
    -	types    map[string]*typeDoc
    -	embedded map[string]*typeDoc // embedded types, possibly not exported
    +	types    map[string]*typeInfo
    +	embedded map[string]*typeInfo // embedded types, possibly not exported
      	funcs    map[string]*ast.FuncDecl
      	bugs     []*ast.CommentGroup
      }
      
      func (doc *docReader) init(pkgName string) {
      	doc.pkgName = pkgName
    -	doc.types = make(map[string]*typeDoc)
    -	doc.embedded = make(map[string]*typeDoc)
    +	doc.types = make(map[string]*typeInfo)
    +	doc.embedded = make(map[string]*typeInfo)
      	doc.funcs = make(map[string]*ast.FuncDecl)
      }
    
  4. lookupTypeDoc関数からlookupTypeInfo関数へのリネームと内部変更:

    --- a/src/pkg/go/doc/doc.go
    +++ b/src/pkg/go/doc/doc.go
    @@ -72,20 +72,20 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
      	doc.doc.List = append(list, comments.List...)
      }
      
    -func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
    +func (doc *docReader) lookupTypeInfo(name string) *typeInfo {
      	if name == "" || name == "_" {
      		return nil // no type docs for anonymous types
      	}
    -	if tdoc, found := doc.types[name]; found {
    -		return tdoc
    +	if info, found := doc.types[name]; found {
    +		return info
      	}
      	// type wasn't found - add one without declaration
    -	tdoc := &typeDoc{
    +	info := &typeInfo{
      		factories: make(map[string]*ast.FuncDecl),
      		methods:   make(map[string]*ast.FuncDecl),
      	}
    -	doc.types[name] = tdoc
    -	return tdoc
    +	doc.types[name] = info
    +	return info
      }
    

これら以外にも、addValue, addFunc, addDecl, makeTypeDocs, collectEmbeddedMethodsといった関数内でtypeDocが参照されている箇所が全てtypeInfoに更新されています。また、関連するコメント内のtypeDocという記述もtypeInfoに修正されています。

コアとなるコードの解説

このコミットのコアとなる変更は、go/docパッケージがGoの型情報をどのように内部的に表現し、操作しているかに関わっています。

  • typeInfo構造体(旧typeDoc: この構造体は、Goのソースコードから抽出された単一の型(例: struct, interface, intなどの基本型)に関する包括的な情報を保持するために設計されています。具体的には、以下の情報を含みます。

    • decl: 型の宣言(type MyType struct { ... }のようなast.GenDeclノード)。
    • values: その型に関連付けられた定数や変数(例: type MyType int; const ( A MyType = 1 ))。
    • factories: その型を返すファクトリ関数(例: func NewMyType() MyType { ... })。
    • methods: その型に紐付けられたメソッド(例: func (m MyType) DoSomething() { ... })。
    • embedded: 構造体に埋め込まれた匿名フィールドの型情報。
    • forward: ドキュメンテーション生成の複数フェーズで型情報をリンクするためのポインタ。

    この構造体の名前がtypeDocからtypeInfoに変更されたことで、その役割が「型に関するドキュメンテーション情報」だけでなく、「型に関する一般的な情報」を保持するものであることがより明確になりました。

  • embeddedType構造体: この構造体は、Goの構造体における匿名フィールド(埋め込みフィールド)の型を記述します。例えば、struct { io.Reader }のような場合、io.Readerが匿名フィールドです。embeddedTypetypフィールドは、埋め込まれた型のtypeInfo(旧typeDoc)へのポインタを保持していました。この変更により、一貫性が保たれています。

  • docReader構造体: docReaderは、Goのパッケージ全体を走査し、そのドキュメンテーション情報を収集する役割を担います。この構造体は、パッケージ内の全ての型情報をtypesというマップ(map[string]*typeInfo)で管理していました。また、埋め込み型に関する情報もembeddedマップで管理していました。これらのマップの型が*typeDocから*typeInfoに変更されたことで、docReaderが扱う型情報の参照先が正しく更新されました。

  • lookupTypeInfo関数(旧lookupTypeDoc: この関数は、docReaderのメソッドであり、指定された型名に対応するtypeInfoオブジェクトを取得するために使用されます。もしその型名がまだdocReadertypesマップに存在しない場合、新しいtypeInfoオブジェクトを作成してマップに追加し、それを返します。この関数のリネームは、その目的が「型ドキュメントを検索する」から「型情報を検索する」へと、より広範な意味合いを持つようになったことを反映しています。

  • その他の関連関数の変更: addValue, addFunc, addDeclなどの関数は、GoのソースコードのAST(抽象構文木)を解析し、抽出した情報を適切なtypeInfoオブジェクトに関連付ける役割を担っています。これらの関数内でlookupTypeDocが呼び出されていた箇所や、typeDoc型の変数が使用されていた箇所が全てlookupTypeInfotypeInfoに更新されています。これにより、コードベース全体で新しい命名規則が適用され、整合性が保たれています。

このコミットは、Goの内部コードの品質と明確性を向上させるための、細部へのこだわりを示す良い例です。

関連リンク

  • Go言語の公式ドキュメンテーション: https://go.dev/
  • Go言語のgo/docパッケージドキュメンテーション: https://pkg.go.dev/go/doc
  • Gerrit Change-ID: https://golang.org/cl/5502071
    • Goプロジェクトでは、コードレビューにGerritを使用しています。このリンクは、このコミットがGerrit上でレビューされた際の変更セット(Change-ID)を指します。Gerritは、Gitリポジトリに対する変更を管理し、レビュープロセスを円滑に進めるためのウェブベースのコードレビューシステムです。

参考にした情報源リンク

  • Go言語の公式ドキュメンテーション
  • Go言語のソースコード(特にsrc/pkg/go/doc/doc.go
  • Go言語の命名規則に関する一般的な情報源
  • Gerrit Code Reviewの仕組みに関する情報
  • TypeScriptのTypeDocプロジェクト (混同の可能性のある外部ツールとして)

この解説は、提供されたコミット情報と一般的なGo言語の知識、およびWeb検索で得られた情報を基に作成されました。