Source file src/simd/archsimd/_gen/specgen/doc.go

     1  // Copyright 2026 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 specgen
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  )
    11  
    12  // ParagraphKind classifies a doc comment paragraph.
    13  type ParagraphKind int
    14  
    15  const (
    16  	// SpecOwned paragraphs describe the behavioral semantics of an operation.
    17  	SpecOwned ParagraphKind = iota
    18  
    19  	// ImplementationNote paragraphs describe target-specific implementation
    20  	// details (such as assembly instructions, required CPU features, emulation
    21  	// notes, etc.).
    22  	//
    23  	// All implementation note paragraphs start with one of a fixed set of
    24  	// prefixes (see [HasNotePrefix]).
    25  	ImplementationNote
    26  
    27  	// DirectiveComment paragraphs represent compiler or tool directives (e.g.
    28  	// //go:noescape).
    29  	DirectiveComment
    30  )
    31  
    32  func (k ParagraphKind) String() string {
    33  	switch k {
    34  	case SpecOwned:
    35  		return "spec-owned paragraph"
    36  	case ImplementationNote:
    37  		return "implementation note"
    38  	case DirectiveComment:
    39  		return "directive comment"
    40  	default:
    41  		return fmt.Sprintf("ParagraphKind(%d)", k)
    42  	}
    43  }
    44  
    45  // Paragraph is a doc comment paragraph with its classification.
    46  type Paragraph struct {
    47  	Kind ParagraphKind
    48  	Text string // No leading "//" or trailing \n
    49  }
    50  
    51  // HasNotePrefix reports whether text starts with an implementation note prefix.
    52  // The text should NOT include any leading "//".
    53  func HasNotePrefix(text string) bool {
    54  	if strings.HasPrefix(text, "Asm:") ||
    55  		strings.HasPrefix(text, "CPU Feature:") ||
    56  		strings.HasPrefix(text, "Deprecated:") ||
    57  		strings.HasPrefix(text, "Performance:") ||
    58  		strings.HasPrefix(text, "Note:") {
    59  		return true
    60  	}
    61  	// "Emulated" notes are a little bit of a mess. They don't usually have any
    62  	// content, so there's no colon. (go.dev/issue/81568)
    63  	if strings.HasPrefix(text, "Emulated,") || text == "Emulated" || strings.HasPrefix(text, "Emulated\n") || strings.HasPrefix(text, "Emulated:") {
    64  		return true
    65  	}
    66  	return false
    67  }
    68  
    69  // parseSpecDoc splits expanded spec doc text (without comment markers) into
    70  // paragraphs.
    71  //
    72  // It reports errors to ctx if any paragraph in text is not "spec-owned". The
    73  // spec docs are merged with implementation notes and directive comments in
    74  // generated source. In order for this to be idempotent, we must only inject
    75  // spec text into source code that will still be recognized as spec text when we
    76  // need to strip it back out.
    77  func parseSpecDoc(ctx context, text string) []Paragraph {
    78  	text = strings.Trim(text, "\n")
    79  
    80  	var out []Paragraph
    81  	for para := range strings.SplitSeq(text, "\n\n") {
    82  		// Spec docs structurally cannot contain directive comments.
    83  		if HasNotePrefix(para) {
    84  			ctx.errorf("spec doc contains implementation note: %s", strings.TrimSpace(para))
    85  		} else {
    86  			out = append(out, Paragraph{SpecOwned, para})
    87  		}
    88  	}
    89  
    90  	return out
    91  }
    92  
    93  // FormatComment formats a slice of paragraphs into standard Go comment text.
    94  // The result has "//"-prefixed lines and ends with "\n" unless the whole result
    95  // is empty.
    96  //
    97  // FormatComment checks that [SpecOwned] paragraphs must appear strictly before
    98  // [ImplementationNote] paragraphs, which must appear strictly before
    99  // [DirectiveComment]s. If the ordering rule is violated, it returns an error.
   100  func FormatComment(paras []Paragraph) (string, error) {
   101  	if len(paras) == 0 {
   102  		return "", nil
   103  	}
   104  
   105  	lastKind := SpecOwned
   106  	for _, p := range paras {
   107  		if p.Kind < lastKind {
   108  			return "", fmt.Errorf("doc comment ordering violation: %s appeared after %s", p.Kind, lastKind)
   109  		}
   110  		lastKind = p.Kind
   111  	}
   112  
   113  	var buf strings.Builder
   114  	inDirectives := false
   115  	for i, p := range paras {
   116  		if i > 0 && !inDirectives {
   117  			buf.WriteString("//\n")
   118  		}
   119  		if p.Kind == DirectiveComment {
   120  			buf.WriteString("//")
   121  			buf.WriteString(p.Text)
   122  			buf.WriteByte('\n')
   123  			// Directives don't have paragraph separators
   124  			inDirectives = true
   125  		} else {
   126  			for line := range strings.SplitSeq(p.Text, "\n") {
   127  				if strings.HasPrefix(line, "\t") {
   128  					buf.WriteString("//")
   129  				} else {
   130  					buf.WriteString("// ")
   131  				}
   132  				buf.WriteString(line)
   133  				buf.WriteByte('\n')
   134  			}
   135  		}
   136  	}
   137  
   138  	return buf.String(), nil
   139  }
   140  

View as plain text