1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! `Checker` implementation for Go.
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use super::*;
use crate::lang_helpers::doc_comment::{go_directive, node_bytes};
/// Whether `node` lies outside every function body: its nearest
/// `source_file`, `statement_list` or `block` ancestor is the
/// `source_file`.
fn go_outside_function_body<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> bool {
// The nearest of a source file and a statement list decides; folded
// down the chain the last one met is that nearest, and the fold is
// memoised per ancestor, so nested struct types cost one step each
// rather than a climb per field (#1597).
ancestors.fold_down(
node,
0,
FoldLane::DocScope,
0,
|outside, ancestor, _, _| match ancestor.kind_id().into() {
Go::SourceFile => 1,
Go::StatementList | Go::Block => 0,
_ => outside,
},
) == 1
}
impl Checker for GoCode {
fn is_comment(node: &Node) -> bool {
node.kind_id() == Go::Comment
}
fn is_doc_target<'a>(node: &Node<'a>, _code: &[u8], ancestors: Ancestors<'a, '_>) -> bool {
// go/doc documents the package clause, top-level declarations,
// the specs of a grouped `type ( … )` / `var ( … )` /
// `const ( … )` — a grouped alias (`B = int`) is a `type_alias`,
// not a `type_spec` — struct fields and interface elements
// (methods and embedded types); never an import. It never reads
// a declaration inside a function body, nor a field of a type
// declared there, so a comment above one stays a working comment.
match node.kind_id().into() {
Go::PackageClause | Go::FunctionDeclaration | Go::MethodDeclaration => true,
Go::TypeDeclaration
| Go::VarDeclaration
| Go::ConstDeclaration
| Go::TypeSpec
| Go::TypeAlias
| Go::VarSpec
| Go::ConstSpec
| Go::FieldDeclaration
| Go::MethodElem => go_outside_function_body(node, ancestors),
// An embedded element is documented only when it opens with a
// name (`io.Reader`, `int | string`, `G[int]`), the form
// go/parser reads as a method spec with a lead comment; a
// `~int` constraint and other non-name-led elements carry none.
// The grammar also parses each argument of an instantiation
// (`Map[string, int]`) as a `type_elem`, which no lead comment
// documents, so only an interface's own elements qualify.
Go::TypeElem => {
ancestors.parent_has_kind(node, Go::InterfaceType as u16)
&& node.child(0).is_some_and(|first| {
matches!(
first.kind_id().into(),
Go::TypeIdentifier | Go::QualifiedType | Go::GenericType
)
})
&& go_outside_function_body(node, ancestors)
}
_ => false,
}
}
fn is_doc_run_directive(node: &Node, code: &[u8]) -> bool {
go_directive(node_bytes(node, code))
}
fn is_func_space(node: &Node) -> bool {
matches!(
node.kind_id().into(),
Go::SourceFile | Go::FunctionDeclaration | Go::MethodDeclaration | Go::FuncLiteral
)
}
fn is_func<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> bool {
matches!(
node.kind_id().into(),
Go::FunctionDeclaration | Go::MethodDeclaration
)
}
fn is_closure<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> bool {
node.kind_id() == Go::FuncLiteral
}
fn is_call(node: &Node) -> bool {
node.kind_id() == Go::CallExpression
}
fn is_non_arg(node: &Node) -> bool {
matches!(node.kind_id().into(), Go::LPAREN | Go::COMMA | Go::RPAREN)
}
impl_simple_is_string!(Go, InterpretedStringLiteral, RawStringLiteral);
#[inline]
fn is_else_if<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> bool {
node.kind_id() == Go::IfStatement && ancestors.parent_has_kind(node, Go::IfStatement as u16)
}
}