Skip to content

Commit 63932c7

Browse files
authored
Add fuzz testing for parser, fix two bugs (#414)
1 parent 1fe20a6 commit 63932c7

File tree

5 files changed

+75
-5
lines changed

5 files changed

+75
-5
lines changed

internal/parser/jsdoc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ loop:
509509
whitespace := p.scanner.TokenText()
510510
// if the whitespace crosses the margin, take only the whitespace that passes the margin
511511
if margin > -1 && indent+len(whitespace) > margin {
512-
comments = append(comments, whitespace[margin-indent:])
512+
comments = append(comments, whitespace[max(margin-indent, 0):])
513513
state = jsdocStateSavingComments
514514
}
515515
indent += len(whitespace)

internal/parser/parser_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,59 @@ func allParsableFiles(tb testing.TB, root string) iter.Seq[parsableFile] {
126126
assert.NilError(tb, err)
127127
}
128128
}
129+
130+
func FuzzParser(f *testing.F) {
131+
repo.SkipIfNoTypeScriptSubmodule(f)
132+
133+
tests := []string{
134+
"src",
135+
"scripts",
136+
"Herebyfile.mjs",
137+
// "tests/cases",
138+
}
139+
140+
var extensions core.Set[string]
141+
for _, es := range tspath.AllSupportedExtensionsWithJson {
142+
for _, e := range es {
143+
extensions.Add(e)
144+
}
145+
}
146+
147+
for _, test := range tests {
148+
root := filepath.Join(repo.TypeScriptSubmodulePath, test)
149+
150+
for file := range allParsableFiles(f, root) {
151+
sourceText, err := os.ReadFile(file.path)
152+
assert.NilError(f, err)
153+
extension := tspath.TryGetExtensionFromPath(file.path)
154+
f.Add(extension, string(sourceText), int(core.ScriptTargetESNext), int(scanner.JSDocParsingModeParseAll))
155+
}
156+
}
157+
158+
f.Fuzz(func(t *testing.T, extension string, sourceText string, scriptTarget_ int, jsdocParsingMode_ int) {
159+
scriptTarget := core.ScriptTarget(scriptTarget_)
160+
jsdocParsingMode := scanner.JSDocParsingMode(jsdocParsingMode_)
161+
162+
if !extensions.Has(extension) {
163+
t.Skip()
164+
}
165+
166+
if scriptTarget < core.ScriptTargetNone || scriptTarget > core.ScriptTargetLatest {
167+
t.Skip()
168+
}
169+
170+
if jsdocParsingMode < scanner.JSDocParsingModeParseAll || jsdocParsingMode > scanner.JSDocParsingModeParseNone {
171+
t.Skip()
172+
}
173+
174+
fileName := "/index" + extension
175+
path := tspath.Path(fileName)
176+
177+
if extension == ".json" {
178+
ParseJSONText(fileName, path, sourceText)
179+
return
180+
}
181+
182+
ParseSourceFile(fileName, path, sourceText, scriptTarget, jsdocParsingMode)
183+
})
184+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
go test fuzz v1
2+
string(".ts")
3+
string("/**@0\n * */0")
4+
int(99)
5+
int(0)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
go test fuzz v1
2+
string(".ts")
3+
string("/")
4+
int(99)
5+
int(1)

internal/scanner/scanner.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,7 +2300,7 @@ func iterateCommentRanges(f *ast.NodeFactory, text string, pos int, trailing boo
23002300
ch, size := utf8.DecodeRuneInString(text[pos:])
23012301
switch ch {
23022302
case '\r':
2303-
if text[pos+1] == '\n' {
2303+
if pos+1 < len(text) && text[pos+1] == '\n' {
23042304
pos++
23052305
}
23062306
fallthrough
@@ -2320,7 +2320,10 @@ func iterateCommentRanges(f *ast.NodeFactory, text string, pos int, trailing boo
23202320
pos++
23212321
continue
23222322
case '/':
2323-
nextChar := text[pos+1]
2323+
var nextChar byte
2324+
if pos+1 < len(text) {
2325+
nextChar = text[pos+1]
2326+
}
23242327
hasTrailingNewLine := false
23252328
if nextChar == '/' || nextChar == '*' {
23262329
var kind ast.Kind
@@ -2343,11 +2346,12 @@ func iterateCommentRanges(f *ast.NodeFactory, text string, pos int, trailing boo
23432346
}
23442347
} else {
23452348
for pos < len(text) {
2346-
if text[pos] == '*' && text[pos+1] == '/' {
2349+
c, s := utf8.DecodeRuneInString(text[pos:])
2350+
if c == '*' && pos+1 < len(text) && text[pos+1] == '/' {
23472351
pos += 2
23482352
break
23492353
}
2350-
pos++
2354+
pos += s
23512355
}
23522356
}
23532357

0 commit comments

Comments
 (0)