Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
bdf4d77
parser: regex recovery ignores comments and strings
tabladrum Oct 10, 2026
c319a60
test: comment/string-as-code regression suite (audit 2026-10-10)
tabladrum Oct 10, 2026
448b473
graph: mask comments and strings with astkit textmask
tabladrum Oct 10, 2026
60c219e
native: run every text pass on textmask output
tabladrum Oct 10, 2026
bea1df9
graph: parse masked signatures and bodies in edge builders
tabladrum Oct 10, 2026
50cc1b6
graph: Python abstract methods from decorators and the first statement
tabladrum Oct 10, 2026
a391756
graph: COBOL field references by statement, rename only code occurrences
tabladrum Oct 10, 2026
a6643a7
Merge stream N: native text passes on textmask
tabladrum Oct 10, 2026
a57d4fc
graph: Rust test targets are not named crates
tabladrum Oct 10, 2026
b1096f0
graph: tests for masked signatures, COBOL statements and rename
tabladrum Oct 10, 2026
e1fddfc
Merge stream E: edge builders and tools on masked text
tabladrum Oct 10, 2026
cd1212c
graph: implicit super bases from the masked class header
tabladrum Oct 10, 2026
07d0662
graph: local-type field maps and line recoveries read masked code only
tabladrum Oct 10, 2026
f5a500a
Merge branch 'fix/comment-string-masking' into fix/cs-l
tabladrum Oct 10, 2026
210b3a6
parser: sniff .h language on code, not comments or __cplusplus guards
tabladrum Oct 10, 2026
7703fbe
parser: JS export clauses scanned on textmask-masked source
tabladrum Oct 10, 2026
ac7629a
parser: line scanners, spans and docstrings read textmask-masked code
tabladrum Oct 10, 2026
c2fa708
Merge stream P: parser scans textmask-masked code
tabladrum Oct 10, 2026
ad00b73
graph: Kotlin/Swift/ObjC/Rust signatures and memberclassify regexes r…
tabladrum Oct 10, 2026
be288ef
Merge stream L: local-type inference reads masked code
tabladrum Oct 10, 2026
dbca833
graph: split declared parameter lists on masked text
tabladrum Oct 10, 2026
1956fa6
graph: Python base walks read the masked class header
tabladrum Oct 10, 2026
73f90c6
parser: import paths carry no comments
tabladrum Oct 10, 2026
d87c287
parser: C#/Java recovery does not read call statements as methods
tabladrum Oct 10, 2026
c554972
graph: Java lambdas see their enclosing method's locals
tabladrum Oct 10, 2026
cb5da98
graph: Java declaration heads skip annotations on masked text
tabladrum Oct 10, 2026
f3932a2
graph: Java locals declared without an initializer are typed
tabladrum Oct 10, 2026
8f9130f
graph: anonymous classes resolve only in their own file
tabladrum Oct 10, 2026
b7d3e9a
graph: Java parameters with spaced generic arguments are typed
tabladrum Oct 10, 2026
511639f
graph: a Java receiver chain to an external type dispatches like a local
tabladrum Oct 10, 2026
e475cf5
graph: C# uses-type reads attribute arguments on masked text
tabladrum Oct 10, 2026
d63d883
graph: C/C++ parameter types read from masked text
tabladrum Oct 10, 2026
bf609ea
parser: C-family line scanner and namespaces ignore comment lines
tabladrum Oct 10, 2026
040d807
parser: broken C-family files parse from code alone
tabladrum Oct 10, 2026
578ff54
Merge C/C++/ObjC: broken files parse from code alone
tabladrum Oct 10, 2026
aa6144d
parser: Python recovery body window counts code lines only
tabladrum Oct 10, 2026
d7d35ef
Merge branch 'fix/comment-string-masking' into fix/cs2-j
tabladrum Oct 10, 2026
b97878b
parser: recovered C#/Java methods take the later-opening enclosing class
tabladrum Oct 10, 2026
d7e1fbc
Merge Java/C#: anonymous classes by file, annotations on masked text,…
tabladrum Oct 10, 2026
aec7387
go.mod: astkit with textmask (comment-string-masking branch)
tabladrum Oct 10, 2026
d068f2c
test: run twin cases in parallel; skip native mode in -short on Windows
tabladrum Oct 10, 2026
1aa3516
test: keep native twin cases serial so compiler-timeout tests are not…
tabladrum Oct 10, 2026
42a36bd
go.mod: astkit v0.16.0
tabladrum Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eval/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/provasign/astkit v0.15.4 // indirect
github.com/provasign/astkit v0.16.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 // indirect
github.com/sourcegraph/beaut v0.0.0-20240611013027-627e4c25335a // indirect
Expand Down
4 changes: 2 additions & 2 deletions eval/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/provasign/astkit v0.15.4 h1:Zdo9Dmu6hOcXh8iAijK9nUUIrsbLSdcZjobaprqdX1c=
github.com/provasign/astkit v0.15.4/go.mod h1:uSQxIxw3dVpK/6KmHvqIE8+DK/nhxDuPklKv7g96Jr4=
github.com/provasign/astkit v0.16.0 h1:0CGyrZex64wlqK9z6wiPyOLEk26Mhfi+yjWN1xGUaJk=
github.com/provasign/astkit v0.16.0/go.mod h1:uSQxIxw3dVpK/6KmHvqIE8+DK/nhxDuPklKv7g96Jr4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/scip-code/scip/bindings/go/scip v0.8.1 h1:ZkyT9RDgkbd6ffvMbi9nYdwh7+gLoz8Z9ftFMxgUiVs=
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.26
require modernc.org/sqlite v1.51.0

require (
github.com/provasign/astkit v0.15.4
github.com/provasign/astkit v0.16.0
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82
golang.org/x/sys v0.46.0
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/provasign/astkit v0.15.4 h1:Zdo9Dmu6hOcXh8iAijK9nUUIrsbLSdcZjobaprqdX1c=
github.com/provasign/astkit v0.15.4/go.mod h1:uSQxIxw3dVpK/6KmHvqIE8+DK/nhxDuPklKv7g96Jr4=
github.com/provasign/astkit v0.16.0 h1:0CGyrZex64wlqK9z6wiPyOLEk26Mhfi+yjWN1xGUaJk=
github.com/provasign/astkit v0.16.0/go.mod h1:uSQxIxw3dVpK/6KmHvqIE8+DK/nhxDuPklKv7g96Jr4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 h1:6C8qej6f1bStuePVkLSFxoU22XBS165D3klxlzRg8F4=
Expand Down
22 changes: 13 additions & 9 deletions internal/graph/cfamilylocaltypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,9 @@ func cFamilyLocalTypes(idx *edgeIndex, symbol *core.SymbolRecord) map[string]str
default:
continue
}
body := cls.RawText
if i := strings.IndexByte(body, '{'); i >= 0 {
body = body[i+1:]
}
for _, m := range cppLocalDeclRe.FindAllStringSubmatch(body, -1) {
// Top-level members only (classBody): an inline method's
// local or a commented-out declaration is not a field.
for _, m := range cppLocalDeclRe.FindAllStringSubmatch(classBody(idx, cls), -1) {
if t := cFamilyBareType(m[1]); t != "" {
if _, exists := out[m[2]]; !exists {
record(m[2], t)
Expand All @@ -69,14 +67,20 @@ func cFamilyLocalTypes(idx *edgeIndex, symbol *core.SymbolRecord) map[string]str
}
}

// Parameters.
for name, typ := range cFamilyParamTypes(symbol.Signature, symbol.RawText) {
// Parameters. Read on masked text: a symbol whose signature has no
// parameter list falls back to its body, and the first `(` there
// could be in a comment (`// ghost() { helper(1) }`), which hid the
// real parameters.
body := ""
if symbol.RawText != "" {
body = maskCode(symbol.Language, symbol.RawText)
}
for name, typ := range cFamilyParamTypes(maskCode(symbol.Language, symbol.Signature), body) {
record(name, typ)
}

// Body locals (highest precedence): new-expressions.
if symbol.RawText != "" {
body := stripCommentsAndStrings(symbol.RawText)
if body != "" {
for _, m := range cppLocalDeclRe.FindAllStringSubmatch(body, -1) {
if t := cFamilyBareType(m[1]); t != "" {
record(m[2], t)
Expand Down
47 changes: 11 additions & 36 deletions internal/graph/changeimpact.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,10 +739,7 @@ func declaredSuperNames(s *core.SymbolRecord) []string {
if s.Kind != core.KindClass && s.Kind != core.KindInterface {
return nil
}
text := s.Signature
if text == "" {
text = firstLine(s.RawText)
}
text := declHeader(s)
text = stripAngleBrackets(text)
names := matchNameList(extendsRe, text)
return append(names, matchNameList(implementsRe, text)...)
Expand All @@ -753,29 +750,20 @@ func declaredSuperNames(s *core.SymbolRecord) []string {
if s.Kind != core.KindClass && s.Kind != core.KindInterface && s.Kind != core.KindStruct {
return nil
}
text := s.Signature
if text == "" {
text = firstLine(s.RawText)
}
text := declHeader(s)
return csharpBaseNames(stripAngleBrackets(text))
case "swift":
if s.Kind != core.KindClass && s.Kind != core.KindStruct &&
s.Kind != core.KindEnum && s.Kind != core.KindInterface {
return nil
}
text := s.Signature
if text == "" {
text = firstLine(s.RawText)
}
text := declHeader(s)
return swiftBaseNames(text)
case "kotlin":
if s.Kind != core.KindClass && s.Kind != core.KindInterface {
return nil
}
text := s.Signature
if text == "" {
text = firstLine(s.RawText)
}
text := declHeader(s)
var out []string
for _, raw := range kotlinBaseNames(text) {
if name, _ := kotlinBaseNameAndCtor(raw); name != "" {
Expand All @@ -787,10 +775,7 @@ func declaredSuperNames(s *core.SymbolRecord) []string {
if s.Kind != core.KindClass && s.Kind != core.KindInterface {
return nil
}
text := s.Signature
if text == "" {
text = firstLine(s.RawText)
}
text := declHeader(s)
var out []string
if m := objcSuperclassRe.FindStringSubmatch(text); len(m) == 2 {
out = append(out, m[1])
Expand All @@ -800,17 +785,7 @@ func declaredSuperNames(s *core.SymbolRecord) []string {
if s.Kind != core.KindClass {
return nil
}
m := pythonClassBase.FindStringSubmatch(firstLine(s.RawText))
if len(m) < 2 {
return nil
}
var out []string
for _, base := range splitTrim(m[1], ',') {
if base = stripPythonBase(base); base != "" {
out = append(out, base)
}
}
return out
return pythonClassBases(s)
}
// Go and Rust have NO declared-supertype clause on the type: Go interface
// satisfaction is structural (derived by method-set inclusion, and only
Expand Down Expand Up @@ -900,7 +875,7 @@ func (g *CodeGraph) typeDeclaresMember(t *core.SymbolRecord, method string) bool
default:
return false
}
body := stripCommentsAndStrings(t.RawText)
body := maskCode(t.Language, t.RawText)
if i := strings.IndexByte(body, '{'); i >= 0 {
body = body[i+1:]
}
Expand Down Expand Up @@ -1357,7 +1332,7 @@ func paramTypesOf(s *core.SymbolRecord) []string {
if !strings.Contains(src, ")") {
src = s.RawText
}
inner := tsDeclParams(src)
inner := tsDeclParams(s.Language, src)
if s.Language == "go" {
gi, ok := goDeclParamsOK(src)
if !ok {
Expand All @@ -1383,7 +1358,7 @@ func paramTypesOf(s *core.SymbolRecord) []string {
return pyParamTypeTokens(inner)
}
var out []string
for _, gr := range splitTopLevel(inner, ',') {
for _, gr := range splitParams(s.Language, inner) {
token := bareTypeToken(gr)
switch s.Language {
case "typescript", "tsx", "javascript":
Expand All @@ -1405,7 +1380,7 @@ func paramTypesOf(s *core.SymbolRecord) []string {
// so one unannotated parameter makes the whole list neutral (nil).
func pyParamTypeTokens(inner string) []string {
var out []string
for i, gr := range splitTopLevel(inner, ',') {
for i, gr := range splitParams("python", inner) {
gr = strings.TrimSpace(gr)
name, ann, annotated := strings.Cut(gr, ":")
name = strings.TrimSpace(name)
Expand Down Expand Up @@ -1443,7 +1418,7 @@ func pyParamTypeTokens(inner string) []string {
// Returns nil (neutral evidence, like paramTypesOf) when a token cannot be
// recovered.
func goParamTokens(inner string) []string {
groups := splitTopLevel(inner, ',')
groups := splitParams("go", inner)
n := len(groups)
types := make([]string, n)
named := false
Expand Down
36 changes: 36 additions & 0 deletions internal/graph/csharp_attribute_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package graph

import (
"reflect"
"testing"

"github.com/provasign/grove/internal/core"
)

func TestCSharpAttributeTypeNames(t *testing.T) {
idx := newEdgeIndex([]core.SymbolRecord{
{ID: "a.cs::JsonPropertyAttribute", FilePath: "a.cs", Language: "csharp", Kind: core.KindClass,
Name: "JsonPropertyAttribute", QualifiedName: "JsonPropertyAttribute"},
})
for _, tc := range []struct {
ann string
want []string
}{
{`[JsonConverter(typeof(StringEnumConverter), typeof(CamelCaseNamingStrategy))]`,
[]string{"JsonConverter", "StringEnumConverter", "CamelCaseNamingStrategy"}},
{`[JsonProperty(NullValueHandling = NullValueHandling.Ignore, Order = 2)]`,
[]string{"JsonPropertyAttribute", "NullValueHandling"}},
{`[DataMember(Name = "first_name")]`, []string{"DataMember"}},
{`[Obsolete("Use TypeNameAssemblyFormatHandling instead.")]`, []string{"Obsolete"}},
{`[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", CanBeNull=false)]`, []string{"ColumnAttribute"}},
{`[CompilationMapping(SourceConstructFlags.UnionCase, 1)]`, []string{"CompilationMapping", "SourceConstructFlags"}},
{`[return: NotNull, Flags]`, []string{"NotNull", "Flags"}},
{`[JsonConverter(typeof(Outer.Converter), new object[] { Mode.Fast /* Fake.Thing */ })]`,
[]string{"JsonConverter", "Converter", "Mode"}},
} {
s := core.SymbolRecord{Language: "csharp", Annotations: []string{tc.ann}}
if got := csharpAttributeTypeNames(idx, &s); !reflect.DeepEqual(got, tc.want) {
t.Errorf("%s: got %v, want %v", tc.ann, got, tc.want)
}
}
}
40 changes: 13 additions & 27 deletions internal/graph/csharplocaltypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,9 @@ func csharpLocalTypes(idx *edgeIndex, symbol *core.SymbolRecord) map[string]stri
}
seen[className] = true
for _, cls := range csharpTypeFragments(idx, className, dirOf(symbol.FilePath)) {
if cls.RawText == "" {
continue
}
body := cls.RawText
if i := strings.IndexByte(body, '{'); i >= 0 {
body = body[i+1:]
}
for _, m := range csharpFieldRe.FindAllStringSubmatch(body, -1) {
// Top-level members only (classBody): a method's local
// `Foo helper = ...` or a commented-out field is not one.
for _, m := range csharpFieldRe.FindAllStringSubmatch(classBody(idx, cls), -1) {
if t := javaBareType(m[1]); t != "" && !csharpKeyword(t) {
if _, exists := out[m[2]]; !exists {
out[m[2]] = t
Expand All @@ -66,8 +61,8 @@ func csharpLocalTypes(idx *edgeIndex, symbol *core.SymbolRecord) map[string]stri
}

// Parameters: "Type name" pairs from the declaration's paren group.
if params := tsDeclParams(symbol.RawText); params != "" {
for _, g := range splitTopLevel(params, ',') {
if params := tsDeclParams(symbol.Language, symbol.RawText); params != "" {
for _, g := range splitParams(symbol.Language, params) {
fields := strings.Fields(strings.TrimSpace(g))
// Drop C# parameter modifiers and attributes.
for len(fields) > 2 || (len(fields) == 2 && csharpParamModifier(fields[0])) {
Expand All @@ -84,7 +79,7 @@ func csharpLocalTypes(idx *edgeIndex, symbol *core.SymbolRecord) map[string]stri

// Body declarations (highest precedence).
if symbol.RawText != "" {
body := stripCommentsAndStrings(symbol.RawText)
body := maskCode(symbol.Language, symbol.RawText)
if i := strings.IndexByte(body, '{'); i >= 0 {
body = body[i+1:]
}
Expand Down Expand Up @@ -262,11 +257,11 @@ func csDeclSource(s *core.SymbolRecord) string {
// csParamTypes parses a C# callable's parameter type tokens (normalized)
// and whether the last one is a `params` array.
func csParamTypes(s *core.SymbolRecord) (types []string, variadic bool) {
params := tsDeclParams(csDeclSource(s))
params := tsDeclParams("csharp", csDeclSource(s))
if params == "" {
return nil, false
}
for _, g := range splitTopLevel(params, ',') {
for _, g := range splitParams("csharp", params) {
g = strings.TrimSpace(g)
if eq := strings.Index(g, "="); eq >= 0 {
g = g[:eq] // default value
Expand Down Expand Up @@ -314,8 +309,8 @@ func csharpArgTypes(idx *edgeIndex, symbol *core.SymbolRecord) map[string]string
out[name] = typ
}
}
if params := tsDeclParams(csDeclSource(symbol)); params != "" {
for _, g := range splitTopLevel(params, ',') {
if params := tsDeclParams("csharp", csDeclSource(symbol)); params != "" {
for _, g := range splitParams("csharp", params) {
g = strings.TrimSpace(g)
if eq := strings.Index(g, "="); eq >= 0 {
g = g[:eq]
Expand All @@ -330,7 +325,7 @@ func csharpArgTypes(idx *edgeIndex, symbol *core.SymbolRecord) map[string]string
}
}
if symbol.RawText != "" {
body := stripCommentsAndStrings(symbol.RawText)
body := maskCode(symbol.Language, symbol.RawText)
if i := strings.IndexByte(body, '{'); i >= 0 {
body = body[i+1:]
}
Expand Down Expand Up @@ -936,7 +931,8 @@ func csRecoverArgs(idx *edgeIndex, symbol *core.SymbolRecord, cs core.CallSite,
if cs.Argc == 0 || cs.Line < symbol.Span.Start || symbol.RawText == "" {
return nil
}
lines := strings.Split(symbol.RawText, "\n")
// Masked: `Log("Foo(1, 2)"); Foo(x)` located the call inside the string.
lines := maskedSymbolLines(idx, symbol)
off := cs.Line - symbol.Span.Start
if off < 0 || off >= len(lines) {
return nil
Expand Down Expand Up @@ -992,16 +988,6 @@ func csRecoverArgs(idx *edgeIndex, symbol *core.SymbolRecord, cs core.CallSite,
if depth == 0 {
end = k
}
case '"', '\'':
// Skip a literal: overload-relevant literals were already
// classified by the extractor, so any call reaching here has
// none at top level worth parsing exactly.
q := line[k]
for k++; k < len(line) && line[k] != q; k++ {
if line[k] == '\\' {
k++
}
}
}
if end >= 0 {
break
Expand Down
Loading
Loading