blob: c3a256c987cbc354a14579dff1b2abac66cabb83 [file] [log] [blame]
// Copyright 2025 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.
package astutil
import (
"go/ast"
"go/token"
"strings"
)
// Deprecation returns the paragraph of the doc comment that starts with the
// conventional "Deprecation: " marker, as defined by
// https://go.dev/wiki/Deprecated, or "" if the documented symbol is not
// deprecated.
func Deprecation(doc *ast.CommentGroup) string {
for p := range strings.SplitSeq(doc.Text(), "\n\n") {
// There is still some ambiguity for deprecation message. This function
// only returns the paragraph introduced by "Deprecated: ". More
// information related to the deprecation may follow in additional
// paragraphs, but the deprecation message should be able to stand on
// its own. See golang/go#38743.
if strings.HasPrefix(p, "Deprecated: ") {
return p
}
}
return ""
}
// -- plundered from the future (CL 605517, issue #68021) --
// TODO(adonovan): replace with ast.Directive after go1.25 (#68021).
// Beware of our local mods to handle analysistest
// "want" comments on the same line.
// A directive is a comment line with special meaning to the Go
// toolchain or another tool. It has the form:
//
// //tool:name args
//
// The "tool:" portion is missing for the three directives named
// line, extern, and export.
//
// See https://go.dev/doc/comment#Syntax for details of Go comment
// syntax and https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives
// for details of directives used by the Go compiler.
type Directive struct {
Pos token.Pos // of preceding "//"
Tool string
Name string
Args string // may contain internal spaces
}
// isDirective reports whether c is a comment directive.
// This code is also in go/printer.
func isDirective(c string) bool {
// "//line " is a line directive.
// "//extern " is for gccgo.
// "//export " is for cgo.
// (The // has been removed.)
if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") {
return true
}
// "//[a-z0-9]+:[a-z0-9]"
// (The // has been removed.)
colon := strings.Index(c, ":")
if colon <= 0 || colon+1 >= len(c) {
return false
}
for i := 0; i <= colon+1; i++ {
if i == colon {
continue
}
b := c[i]
if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') {
return false
}
}
return true
}
// Directives returns the directives within the comment.
func Directives(g *ast.CommentGroup) (res []*Directive) {
if g != nil {
// Avoid (*ast.CommentGroup).Text() as it swallows directives.
for _, c := range g.List {
if len(c.Text) > 2 &&
c.Text[1] == '/' &&
c.Text[2] != ' ' &&
isDirective(c.Text[2:]) {
tool, nameargs, ok := strings.Cut(c.Text[2:], ":")
if !ok {
// Must be one of {line,extern,export}.
tool, nameargs = "", tool
}
name, args, _ := strings.Cut(nameargs, " ") // tab??
// Permit an additional line comment after the args, chiefly to support
// [golang.org/x/tools/go/analysis/analysistest].
args, _, _ = strings.Cut(args, "//")
res = append(res, &Directive{
Pos: c.Slash,
Tool: tool,
Name: name,
Args: strings.TrimSpace(args),
})
}
}
}
return
}