in util/doc.go [411:602]
func (config *Config) parseHeader(path string) (*HeaderFile, error) {
headerPath := filepath.Join(config.BaseDirectory, path)
headerFile, err := os.Open(headerPath)
if err != nil {
return nil, err
}
defer headerFile.Close()
scanner := bufio.NewScanner(headerFile)
var lines, oldLines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return nil, err
}
lineNo := 1
found := false
for i, line := range lines {
if line == cppGuard {
lines = lines[i+1:]
lineNo += i + 1
found = true
break
}
}
if !found {
return nil, errors.New("no C++ guard found")
}
if len(lines) == 0 || lines[0] != "extern \"C\" {" {
return nil, errors.New("no extern \"C\" found after C++ guard")
}
lineNo += 2
lines = lines[2:]
header := &HeaderFile{
Name: filepath.Base(path),
AllDecls: make(map[string]string),
}
for i, line := range lines {
if len(line) > 0 {
lines = lines[i:]
lineNo += i
break
}
}
oldLines = lines
if len(lines) > 0 && isComment(lines[0]) {
comment, rest, restLineNo, err := extractComment(lines, lineNo)
if err != nil {
return nil, err
}
if len(rest) > 0 && len(rest[0]) == 0 {
if len(rest) < 2 || len(rest[1]) != 0 {
return nil, errors.New("preamble comment should be followed by two blank lines")
}
header.Preamble = comment
lineNo = restLineNo + 2
lines = rest[2:]
} else {
lines = oldLines
}
}
allAnchors := make(map[string]struct{})
for {
// Start of a section.
if len(lines) == 0 {
return nil, errors.New("unexpected end of file")
}
line := lines[0]
if line == cppGuard {
break
}
if len(line) == 0 {
return nil, fmt.Errorf("blank line at start of section on line %d", lineNo)
}
var section HeaderSection
if isComment(line) {
comment, rest, restLineNo, err := extractComment(lines, lineNo)
if err != nil {
return nil, err
}
if len(rest) > 0 && len(rest[0]) == 0 {
heading := firstSentence(comment)
anchor := sanitizeAnchor(heading)
if len(anchor) > 0 {
if _, ok := allAnchors[anchor]; ok {
return nil, fmt.Errorf("duplicate anchor: %s", anchor)
}
allAnchors[anchor] = struct{}{}
}
section.Preamble = comment
section.IsPrivate = isPrivateSection(heading)
section.Anchor = anchor
lines = rest[1:]
lineNo = restLineNo + 1
}
}
for len(lines) > 0 {
line := lines[0]
if len(line) == 0 {
lines = lines[1:]
lineNo++
break
}
if line == cppGuard {
return nil, fmt.Errorf("hit ending C++ guard while in section on line %d (possibly missing two empty lines ahead of guard?)", lineNo)
}
var comment []CommentBlock
var decl string
if isComment(line) {
comment, lines, lineNo, err = extractComment(lines, lineNo)
if err != nil {
return nil, err
}
}
if len(lines) == 0 {
return nil, fmt.Errorf("expected decl at EOF on line %d", lineNo)
}
declLineNo := lineNo
decl, lines, lineNo, err = extractDecl(lines, lineNo)
if err != nil {
return nil, err
}
name, ok := getNameFromDecl(decl)
if !ok {
name = ""
}
if last := len(section.Decls) - 1; len(name) == 0 && len(comment) == 0 && last >= 0 {
section.Decls[last].Decl += "\n" + decl
} else {
// As a matter of style, comments should start
// with the name of the thing that they are
// commenting on. We make an exception here for
// collective comments.
sentence := firstSentence(comment)
if len(comment) > 0 &&
len(name) > 0 &&
!isCollectiveComment(sentence) {
subject := commentSubject(sentence)
ok := subject == name
if l := len(subject); l > 0 && subject[l-1] == '*' {
// Groups of names, notably #defines, are often
// denoted with a wildcard.
ok = strings.HasPrefix(name, subject[:l-1])
}
if !ok {
return nil, fmt.Errorf("comment for %q doesn't seem to match line %s:%d\n", name, path, declLineNo)
}
}
anchor := sanitizeAnchor(name)
// TODO(davidben): Enforce uniqueness. This is
// skipped because #ifdefs currently result in
// duplicate table-of-contents entries.
allAnchors[anchor] = struct{}{}
header.AllDecls[name] = anchor
section.Decls = append(section.Decls, HeaderDecl{
Comment: comment,
Name: name,
Decl: decl,
Anchor: anchor,
})
}
if len(lines) > 0 && len(lines[0]) == 0 {
lines = lines[1:]
lineNo++
}
}
header.Sections = append(header.Sections, section)
}
return header, nil
}