forked from zzet/gortex
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpp_signature.go
More file actions
151 lines (141 loc) · 4.84 KB
/
Copy pathcpp_signature.go
File metadata and controls
151 lines (141 loc) · 4.84 KB
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package languages
import (
"strings"
"github.com/zzet/gortex/internal/graph"
sitter "github.com/zzet/gortex/internal/parser/tsitter"
)
// C++ signature metadata extraction. The overload resolver needs each
// function/method's parameter types, count, required count (minus defaults),
// variadic flag, and per-param shape (value / pointer / lvalue-ref / rvalue-ref
// + const) to rank candidates. These are stamped as compact Meta strings
// (gob/sqlite-roundtrip-safe) on the node:
//
// cpp_param_types = comma-joined normalized base types ("int,double")
// cpp_param_shapes = parallel shape codes ("v,cp,l") (see paramShapeCode)
// cpp_req_params = number of non-default parameters
// cpp_variadic = "1" when the signature ends in "..."
//
// param count is len(cpp_param_types). The base type is normalized so int vs
// long stays distinct (unlike GitNexus which collapses them) while cv/ref/ptr
// and namespace qualifiers are stripped for a stable comparison key.
// cppSignature is the extracted per-call-target signature.
type cppSignature struct {
ParamTypes []string
ParamShapes []string
ReqParams int
Variadic bool
}
// extractCppSignature walks a function_definition (or declaration) node's
// parameter list. ok is false only when no function_declarator is reachable.
func extractCppSignature(funcNode *sitter.Node, src []byte) (sig cppSignature, ok bool) {
if funcNode == nil {
return sig, false
}
decl := findCppFunctionDeclarator(funcNode)
if decl == nil {
return sig, false
}
params := decl.ChildByFieldName("parameters")
if params == nil {
return sig, true // no parameter list visible — treat as zero params
}
// `...` may be an anonymous token rather than a named
// variadic_parameter_declaration node depending on grammar version.
if strings.Contains(params.Content(src), "...") {
sig.Variadic = true
}
n := int(params.NamedChildCount())
for i := 0; i < n; i++ {
p := params.NamedChild(i)
if p == nil {
continue
}
switch p.Type() {
case "variadic_parameter_declaration":
sig.Variadic = true
case "parameter_declaration", "optional_parameter_declaration":
typeText := cppParamTypeText(p, src)
// `(void)` is C++ for an explicit empty parameter list.
if n == 1 && strings.TrimSpace(typeText) == "void" && p.ChildByFieldName("declarator") == nil {
return sig, true
}
sig.ParamTypes = append(sig.ParamTypes, graph.NormalizeCppType(typeText))
sig.ParamShapes = append(sig.ParamShapes, paramShapeCode(p, typeText, src))
if p.Type() == "parameter_declaration" {
sig.ReqParams++
}
}
}
return sig, true
}
// stampCppSignature writes the extracted signature onto a node's Meta. The
// cpp_sig marker lets the resolver tell "0 params, signature known" apart from
// "no signature extracted".
func stampCppSignature(meta map[string]any, funcNode *sitter.Node, src []byte) {
sig, ok := extractCppSignature(funcNode, src)
if !ok {
return
}
meta["cpp_sig"] = "1"
if len(sig.ParamTypes) > 0 {
meta["cpp_param_types"] = strings.Join(sig.ParamTypes, ",")
meta["cpp_param_shapes"] = strings.Join(sig.ParamShapes, ",")
}
if sig.ReqParams > 0 {
meta["cpp_req_params"] = sig.ReqParams
}
if sig.Variadic {
meta["cpp_variadic"] = "1"
}
}
// findCppFunctionDeclarator descends through return-type pointer/reference
// wrappers to the function_declarator carrying the parameter list.
func findCppFunctionDeclarator(node *sitter.Node) *sitter.Node {
cur := node.ChildByFieldName("declarator")
for cur != nil {
switch cur.Type() {
case "function_declarator":
return cur
case "pointer_declarator", "reference_declarator", "parenthesized_declarator":
cur = cur.ChildByFieldName("declarator")
default:
return nil
}
}
return nil
}
func cppParamTypeText(p *sitter.Node, src []byte) string {
if t := p.ChildByFieldName("type"); t != nil {
return t.Content(src)
}
return ""
}
// paramShapeCode encodes a parameter's indirection + const-ness into one token:
// optional 'c' (const) followed by v(alue) / p(ointer) / l(value-ref) /
// r(value-ref). The const flag is read from the type field text.
func paramShapeCode(p *sitter.Node, typeText string, src []byte) string {
prefix := ""
// `const` is a sibling type_qualifier, not part of the type field, so check
// the whole parameter spelling.
if _ = typeText; strings.Contains(p.Content(src), "const") {
prefix = "c"
}
d := p.ChildByFieldName("declarator")
for d != nil {
switch d.Type() {
case "pointer_declarator", "abstract_pointer_declarator":
return prefix + "p"
case "reference_declarator", "abstract_reference_declarator":
if strings.Contains(d.Content(src), "&&") {
return prefix + "r"
}
return prefix + "l"
case "parenthesized_declarator":
d = d.ChildByFieldName("declarator")
default:
return prefix + "v"
}
}
return prefix + "v"
}
// Type normalization lives in graph.NormalizeCppType so the resolver shares it.