src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

comment.go (4107B)


      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 }