Source file src/cmd/vendor/golang.org/x/tools/internal/astutil/comment.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package astutil
     6  
     7  import (
     8  	"go/ast"
     9  	"go/token"
    10  	"iter"
    11  	"sort"
    12  	"strings"
    13  )
    14  
    15  // Deprecation returns the paragraph of the doc comment that starts with the
    16  // conventional "Deprecation: " marker, as defined by
    17  // https://go.dev/wiki/Deprecated, or "" if the documented symbol is not
    18  // deprecated.
    19  func Deprecation(doc *ast.CommentGroup) string {
    20  	for p := range strings.SplitSeq(doc.Text(), "\n\n") {
    21  		// There is still some ambiguity for deprecation message. This function
    22  		// only returns the paragraph introduced by "Deprecated: ". More
    23  		// information related to the deprecation may follow in additional
    24  		// paragraphs, but the deprecation message should be able to stand on
    25  		// its own. See golang/go#38743.
    26  		if strings.HasPrefix(p, "Deprecated: ") {
    27  			return p
    28  		}
    29  	}
    30  	return ""
    31  }
    32  
    33  // -- plundered from the future (CL 605517, issue #68021) --
    34  
    35  // TODO(adonovan): replace with ast.Directive in go1.26 (#68021).
    36  // Beware of our local mods to handle analysistest
    37  // "want" comments on the same line.
    38  
    39  // A directive is a comment line with special meaning to the Go
    40  // toolchain or another tool. It has the form:
    41  //
    42  //	//tool:name args
    43  //
    44  // The "tool:" portion is missing for the three directives named
    45  // line, extern, and export.
    46  //
    47  // See https://go.dev/doc/comment#Syntax for details of Go comment
    48  // syntax and https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives
    49  // for details of directives used by the Go compiler.
    50  type Directive struct {
    51  	Pos  token.Pos // of preceding "//"
    52  	Tool string
    53  	Name string
    54  	Args string // may contain internal spaces
    55  }
    56  
    57  // isDirective reports whether c is a comment directive.
    58  // This code is also in go/printer.
    59  func isDirective(c string) bool {
    60  	// "//line " is a line directive.
    61  	// "//extern " is for gccgo.
    62  	// "//export " is for cgo.
    63  	// (The // has been removed.)
    64  	if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") {
    65  		return true
    66  	}
    67  
    68  	// "//[a-z0-9]+:[a-z0-9]"
    69  	// (The // has been removed.)
    70  	colon := strings.Index(c, ":")
    71  	if colon <= 0 || colon+1 >= len(c) {
    72  		return false
    73  	}
    74  	for i := 0; i <= colon+1; i++ {
    75  		if i == colon {
    76  			continue
    77  		}
    78  		b := c[i]
    79  		if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') {
    80  			return false
    81  		}
    82  	}
    83  	return true
    84  }
    85  
    86  // Directives returns the directives within the comment.
    87  func Directives(g *ast.CommentGroup) (res []*Directive) {
    88  	if g != nil {
    89  		// Avoid (*ast.CommentGroup).Text() as it swallows directives.
    90  		for _, c := range g.List {
    91  			if len(c.Text) > 2 &&
    92  				c.Text[1] == '/' &&
    93  				c.Text[2] != ' ' &&
    94  				isDirective(c.Text[2:]) {
    95  
    96  				tool, nameargs, ok := strings.Cut(c.Text[2:], ":")
    97  				if !ok {
    98  					// Must be one of {line,extern,export}.
    99  					tool, nameargs = "", tool
   100  				}
   101  				name, args, _ := strings.Cut(nameargs, " ") // tab??
   102  				// Permit an additional line comment after the args, chiefly to support
   103  				// [golang.org/x/tools/go/analysis/analysistest].
   104  				args, _, _ = strings.Cut(args, "//")
   105  				res = append(res, &Directive{
   106  					Pos:  c.Slash,
   107  					Tool: tool,
   108  					Name: name,
   109  					Args: strings.TrimSpace(args),
   110  				})
   111  			}
   112  		}
   113  	}
   114  	return
   115  }
   116  
   117  // Comments returns an iterator over the comments overlapping the specified interval.
   118  // Comments are sorted by position in the file, so we can use binary search.
   119  func Comments(file *ast.File, start, end token.Pos) iter.Seq[*ast.Comment] {
   120  	return func(yield func(*ast.Comment) bool) {
   121  		// Find the first comment group that overlaps the range.
   122  		i := sort.Search(len(file.Comments), func(i int) bool {
   123  			return file.Comments[i].End() >= start
   124  		})
   125  		for _, cg := range file.Comments[i:] {
   126  			if cg.Pos() > end {
   127  				return
   128  			}
   129  			// Find the first comment in the group that overlaps the range.
   130  			j := sort.Search(len(cg.List), func(j int) bool {
   131  				return cg.List[j].End() >= start
   132  			})
   133  			for _, co := range cg.List[j:] {
   134  				if co.Pos() > end {
   135  					return
   136  				}
   137  				if !yield(co) {
   138  					return
   139  				}
   140  			}
   141  		}
   142  	}
   143  }
   144  

View as plain text