mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	Vendor in go1.5.1 dependency
* Changes to make vendored packages accept new home. * Fix go2idl to import vendored packages.
This commit is contained in:
		
							
								
								
									
										355
									
								
								third_party/golang/go/doc/example.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								third_party/golang/go/doc/example.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,355 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // Extract example functions from file ASTs. | ||||
|  | ||||
| package doc | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/kubernetes/third_party/golang/go/ast" | ||||
| 	"k8s.io/kubernetes/third_party/golang/go/token" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| // An Example represents an example function found in a source files. | ||||
| type Example struct { | ||||
| 	Name        string // name of the item being exemplified | ||||
| 	Doc         string // example function doc string | ||||
| 	Code        ast.Node | ||||
| 	Play        *ast.File // a whole program version of the example | ||||
| 	Comments    []*ast.CommentGroup | ||||
| 	Output      string // expected output | ||||
| 	EmptyOutput bool   // expect empty output | ||||
| 	Order       int    // original source code order | ||||
| } | ||||
|  | ||||
| // Examples returns the examples found in the files, sorted by Name field. | ||||
| // The Order fields record the order in which the examples were encountered. | ||||
| // | ||||
| // Playable Examples must be in a package whose name ends in "_test". | ||||
| // An Example is "playable" (the Play field is non-nil) in either of these | ||||
| // circumstances: | ||||
| //   - The example function is self-contained: the function references only | ||||
| //     identifiers from other packages (or predeclared identifiers, such as | ||||
| //     "int") and the test file does not include a dot import. | ||||
| //   - The entire test file is the example: the file contains exactly one | ||||
| //     example function, zero test or benchmark functions, and at least one | ||||
| //     top-level function, type, variable, or constant declaration other | ||||
| //     than the example function. | ||||
| func Examples(files ...*ast.File) []*Example { | ||||
| 	var list []*Example | ||||
| 	for _, file := range files { | ||||
| 		hasTests := false // file contains tests or benchmarks | ||||
| 		numDecl := 0      // number of non-import declarations in the file | ||||
| 		var flist []*Example | ||||
| 		for _, decl := range file.Decls { | ||||
| 			if g, ok := decl.(*ast.GenDecl); ok && g.Tok != token.IMPORT { | ||||
| 				numDecl++ | ||||
| 				continue | ||||
| 			} | ||||
| 			f, ok := decl.(*ast.FuncDecl) | ||||
| 			if !ok { | ||||
| 				continue | ||||
| 			} | ||||
| 			numDecl++ | ||||
| 			name := f.Name.Name | ||||
| 			if isTest(name, "Test") || isTest(name, "Benchmark") { | ||||
| 				hasTests = true | ||||
| 				continue | ||||
| 			} | ||||
| 			if !isTest(name, "Example") { | ||||
| 				continue | ||||
| 			} | ||||
| 			var doc string | ||||
| 			if f.Doc != nil { | ||||
| 				doc = f.Doc.Text() | ||||
| 			} | ||||
| 			output, hasOutput := exampleOutput(f.Body, file.Comments) | ||||
| 			flist = append(flist, &Example{ | ||||
| 				Name:        name[len("Example"):], | ||||
| 				Doc:         doc, | ||||
| 				Code:        f.Body, | ||||
| 				Play:        playExample(file, f.Body), | ||||
| 				Comments:    file.Comments, | ||||
| 				Output:      output, | ||||
| 				EmptyOutput: output == "" && hasOutput, | ||||
| 				Order:       len(flist), | ||||
| 			}) | ||||
| 		} | ||||
| 		if !hasTests && numDecl > 1 && len(flist) == 1 { | ||||
| 			// If this file only has one example function, some | ||||
| 			// other top-level declarations, and no tests or | ||||
| 			// benchmarks, use the whole file as the example. | ||||
| 			flist[0].Code = file | ||||
| 			flist[0].Play = playExampleFile(file) | ||||
| 		} | ||||
| 		list = append(list, flist...) | ||||
| 	} | ||||
| 	sort.Sort(exampleByName(list)) | ||||
| 	return list | ||||
| } | ||||
|  | ||||
| var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`) | ||||
|  | ||||
| // Extracts the expected output and whether there was a valid output comment | ||||
| func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output string, ok bool) { | ||||
| 	if _, last := lastComment(b, comments); last != nil { | ||||
| 		// test that it begins with the correct prefix | ||||
| 		text := last.Text() | ||||
| 		if loc := outputPrefix.FindStringIndex(text); loc != nil { | ||||
| 			text = text[loc[1]:] | ||||
| 			// Strip zero or more spaces followed by \n or a single space. | ||||
| 			text = strings.TrimLeft(text, " ") | ||||
| 			if len(text) > 0 && text[0] == '\n' { | ||||
| 				text = text[1:] | ||||
| 			} | ||||
| 			return text, true | ||||
| 		} | ||||
| 	} | ||||
| 	return "", false // no suitable comment found | ||||
| } | ||||
|  | ||||
| // isTest tells whether name looks like a test, example, or benchmark. | ||||
| // It is a Test (say) if there is a character after Test that is not a | ||||
| // lower-case letter. (We don't want Testiness.) | ||||
| func isTest(name, prefix string) bool { | ||||
| 	if !strings.HasPrefix(name, prefix) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(name) == len(prefix) { // "Test" is ok | ||||
| 		return true | ||||
| 	} | ||||
| 	rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) | ||||
| 	return !unicode.IsLower(rune) | ||||
| } | ||||
|  | ||||
| type exampleByName []*Example | ||||
|  | ||||
| func (s exampleByName) Len() int           { return len(s) } | ||||
| func (s exampleByName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] } | ||||
| func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name } | ||||
|  | ||||
| // playExample synthesizes a new *ast.File based on the provided | ||||
| // file with the provided function body as the body of main. | ||||
| func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { | ||||
| 	if !strings.HasSuffix(file.Name.Name, "_test") { | ||||
| 		// We don't support examples that are part of the | ||||
| 		// greater package (yet). | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Find top-level declarations in the file. | ||||
| 	topDecls := make(map[*ast.Object]bool) | ||||
| 	for _, decl := range file.Decls { | ||||
| 		switch d := decl.(type) { | ||||
| 		case *ast.FuncDecl: | ||||
| 			topDecls[d.Name.Obj] = true | ||||
| 		case *ast.GenDecl: | ||||
| 			for _, spec := range d.Specs { | ||||
| 				switch s := spec.(type) { | ||||
| 				case *ast.TypeSpec: | ||||
| 					topDecls[s.Name.Obj] = true | ||||
| 				case *ast.ValueSpec: | ||||
| 					for _, id := range s.Names { | ||||
| 						topDecls[id.Obj] = true | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Find unresolved identifiers and uses of top-level declarations. | ||||
| 	unresolved := make(map[string]bool) | ||||
| 	usesTopDecl := false | ||||
| 	var inspectFunc func(ast.Node) bool | ||||
| 	inspectFunc = func(n ast.Node) bool { | ||||
| 		// For selector expressions, only inspect the left hand side. | ||||
| 		// (For an expression like fmt.Println, only add "fmt" to the | ||||
| 		// set of unresolved names, not "Println".) | ||||
| 		if e, ok := n.(*ast.SelectorExpr); ok { | ||||
| 			ast.Inspect(e.X, inspectFunc) | ||||
| 			return false | ||||
| 		} | ||||
| 		// For key value expressions, only inspect the value | ||||
| 		// as the key should be resolved by the type of the | ||||
| 		// composite literal. | ||||
| 		if e, ok := n.(*ast.KeyValueExpr); ok { | ||||
| 			ast.Inspect(e.Value, inspectFunc) | ||||
| 			return false | ||||
| 		} | ||||
| 		if id, ok := n.(*ast.Ident); ok { | ||||
| 			if id.Obj == nil { | ||||
| 				unresolved[id.Name] = true | ||||
| 			} else if topDecls[id.Obj] { | ||||
| 				usesTopDecl = true | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
| 	ast.Inspect(body, inspectFunc) | ||||
| 	if usesTopDecl { | ||||
| 		// We don't support examples that are not self-contained (yet). | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Remove predeclared identifiers from unresolved list. | ||||
| 	for n := range unresolved { | ||||
| 		if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] { | ||||
| 			delete(unresolved, n) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Use unresolved identifiers to determine the imports used by this | ||||
| 	// example. The heuristic assumes package names match base import | ||||
| 	// paths for imports w/o renames (should be good enough most of the time). | ||||
| 	namedImports := make(map[string]string) // [name]path | ||||
| 	var blankImports []ast.Spec             // _ imports | ||||
| 	for _, s := range file.Imports { | ||||
| 		p, err := strconv.Unquote(s.Path.Value) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		n := path.Base(p) | ||||
| 		if s.Name != nil { | ||||
| 			n = s.Name.Name | ||||
| 			switch n { | ||||
| 			case "_": | ||||
| 				blankImports = append(blankImports, s) | ||||
| 				continue | ||||
| 			case ".": | ||||
| 				// We can't resolve dot imports (yet). | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		if unresolved[n] { | ||||
| 			namedImports[n] = p | ||||
| 			delete(unresolved, n) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// If there are other unresolved identifiers, give up because this | ||||
| 	// synthesized file is not going to build. | ||||
| 	if len(unresolved) > 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Include documentation belonging to blank imports. | ||||
| 	var comments []*ast.CommentGroup | ||||
| 	for _, s := range blankImports { | ||||
| 		if c := s.(*ast.ImportSpec).Doc; c != nil { | ||||
| 			comments = append(comments, c) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Include comments that are inside the function body. | ||||
| 	for _, c := range file.Comments { | ||||
| 		if body.Pos() <= c.Pos() && c.End() <= body.End() { | ||||
| 			comments = append(comments, c) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Strip "Output:" comment and adjust body end position. | ||||
| 	body, comments = stripOutputComment(body, comments) | ||||
|  | ||||
| 	// Synthesize import declaration. | ||||
| 	importDecl := &ast.GenDecl{ | ||||
| 		Tok:    token.IMPORT, | ||||
| 		Lparen: 1, // Need non-zero Lparen and Rparen so that printer | ||||
| 		Rparen: 1, // treats this as a factored import. | ||||
| 	} | ||||
| 	for n, p := range namedImports { | ||||
| 		s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}} | ||||
| 		if path.Base(p) != n { | ||||
| 			s.Name = ast.NewIdent(n) | ||||
| 		} | ||||
| 		importDecl.Specs = append(importDecl.Specs, s) | ||||
| 	} | ||||
| 	importDecl.Specs = append(importDecl.Specs, blankImports...) | ||||
|  | ||||
| 	// Synthesize main function. | ||||
| 	funcDecl := &ast.FuncDecl{ | ||||
| 		Name: ast.NewIdent("main"), | ||||
| 		Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil | ||||
| 		Body: body, | ||||
| 	} | ||||
|  | ||||
| 	// Synthesize file. | ||||
| 	return &ast.File{ | ||||
| 		Name:     ast.NewIdent("main"), | ||||
| 		Decls:    []ast.Decl{importDecl, funcDecl}, | ||||
| 		Comments: comments, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // playExampleFile takes a whole file example and synthesizes a new *ast.File | ||||
| // such that the example is function main in package main. | ||||
| func playExampleFile(file *ast.File) *ast.File { | ||||
| 	// Strip copyright comment if present. | ||||
| 	comments := file.Comments | ||||
| 	if len(comments) > 0 && strings.HasPrefix(comments[0].Text(), "Copyright") { | ||||
| 		comments = comments[1:] | ||||
| 	} | ||||
|  | ||||
| 	// Copy declaration slice, rewriting the ExampleX function to main. | ||||
| 	var decls []ast.Decl | ||||
| 	for _, d := range file.Decls { | ||||
| 		if f, ok := d.(*ast.FuncDecl); ok && isTest(f.Name.Name, "Example") { | ||||
| 			// Copy the FuncDecl, as it may be used elsewhere. | ||||
| 			newF := *f | ||||
| 			newF.Name = ast.NewIdent("main") | ||||
| 			newF.Body, comments = stripOutputComment(f.Body, comments) | ||||
| 			d = &newF | ||||
| 		} | ||||
| 		decls = append(decls, d) | ||||
| 	} | ||||
|  | ||||
| 	// Copy the File, as it may be used elsewhere. | ||||
| 	f := *file | ||||
| 	f.Name = ast.NewIdent("main") | ||||
| 	f.Decls = decls | ||||
| 	f.Comments = comments | ||||
| 	return &f | ||||
| } | ||||
|  | ||||
| // stripOutputComment finds and removes an "Output:" comment from body | ||||
| // and comments, and adjusts the body block's end position. | ||||
| func stripOutputComment(body *ast.BlockStmt, comments []*ast.CommentGroup) (*ast.BlockStmt, []*ast.CommentGroup) { | ||||
| 	// Do nothing if no "Output:" comment found. | ||||
| 	i, last := lastComment(body, comments) | ||||
| 	if last == nil || !outputPrefix.MatchString(last.Text()) { | ||||
| 		return body, comments | ||||
| 	} | ||||
|  | ||||
| 	// Copy body and comments, as the originals may be used elsewhere. | ||||
| 	newBody := &ast.BlockStmt{ | ||||
| 		Lbrace: body.Lbrace, | ||||
| 		List:   body.List, | ||||
| 		Rbrace: last.Pos(), | ||||
| 	} | ||||
| 	newComments := make([]*ast.CommentGroup, len(comments)-1) | ||||
| 	copy(newComments, comments[:i]) | ||||
| 	copy(newComments[i:], comments[i+1:]) | ||||
| 	return newBody, newComments | ||||
| } | ||||
|  | ||||
| // lastComment returns the last comment inside the provided block. | ||||
| func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.CommentGroup) { | ||||
| 	pos, end := b.Pos(), b.End() | ||||
| 	for j, cg := range c { | ||||
| 		if cg.Pos() < pos { | ||||
| 			continue | ||||
| 		} | ||||
| 		if cg.End() > end { | ||||
| 			break | ||||
| 		} | ||||
| 		i, last = j, cg | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
		Reference in New Issue
	
	Block a user