Skip to content

Reparse non-identifier jsdoc names where possible, error otherwise#4058

Open
weswigham wants to merge 2 commits into
microsoft:mainfrom
weswigham:reparser-name-filtering
Open

Reparse non-identifier jsdoc names where possible, error otherwise#4058
weswigham wants to merge 2 commits into
microsoft:mainfrom
weswigham:reparser-name-filtering

Conversation

@weswigham
Copy link
Copy Markdown
Member

Fixes #4011

A lot of the changed baselines are a new Identifier expected error on the now unsupported nameless typedef pattern (which used to pull a name from the next expression, but currently parses an empty string for the name).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the JSDoc reparse logic so that non-identifier names from JSDoc tags either get rewritten into something emittable (string-literal property names for @property, sanitized identifier names for @param) or produce an Identifier expected diagnostic (e.g. on nameless @typedef, callback type-parameter names, function/method declaration names, type-parameter constraint heads). This fixes #4011, where property names with hyphens were emitted unquoted in .d.ts output.

Changes:

  • Add checkNonIdentifierName to emit Identifier expected on invalid identifier names used in reparsed typedef/function/method/type-parameter positions.
  • Rewrite invalid JSDoc @param names into sanitized identifiers (replacing non-id chars with _, falling back to _<index>), and JSDoc @property names into string literals so emit produces quoted property names.
  • Add a new compiler test jsdocNonIdentifierPropertiesAndParams and update affected submodule baselines (mostly adding the new TS1003 Identifier expected diagnostic and one converged : error: any types output).

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/parser/reparser.go Core change: new checkNonIdentifierName/addTransformedReparse helpers and rewriting of param/property names; threads checks through typedef, callback, signature, and type-parameter reparse paths.
testdata/tests/cases/compiler/jsdocNonIdentifierPropertiesAndParams.ts New compiler test exercising hyphenated @property names and a hyphenated @param name in a @callback.
testdata/baselines/reference/compiler/jsdocNonIdentifierPropertiesAndParams.{js,types,symbols} New baselines verifying quoted property names in .d.ts and props_like sanitized parameter name.
testdata/baselines/reference/submodule/compiler/misspelledJsDocTypedefTags.* New diagnostic for nameless typedef trailing junk; type for Request access now resolves to any (closer to TS).
testdata/baselines/reference/submodule/compiler/jsdocTypedefNoCrash{,2}.errors.txt(.diff) New Identifier expected diagnostic on nameless @typedef {{ }}.
testdata/baselines/reference/submodule/compiler/jsEnumCrossFileExport.errors.txt(.diff) Adds extra Identifier expected diagnostic from nameless @typedef {string}.
testdata/baselines/reference/submodule/conformance/checkJsdocTypedefOnlySourceFile.errors.txt(.diff) Same: extra Identifier expected on nameless typedef.
testdata/baselines/reference/submodule/conformance/noAssertForUnparseableTypedefs.errors.txt(.diff) Same diagnostic added at the unparseable typedef site.
testdata/baselines/reference/submodule/conformance/typedefInnerNamepaths.errors.txt(.diff) New Identifier expected at the C~B typedef name.
testdata/baselines/reference/submodule/conformance/typedefTagWrapping.errors.txt(.diff) New Identifier expected for nameless function-typed typedefs across mod1/mod3/mod4.

Comment thread internal/parser/reparser.go Outdated
return clone
}

func (p *Parser) addTransformedReparse(new *ast.Node, old *ast.Node) *ast.Node {
Comment on lines +175 to +200
name := jsparam.Name()
if ast.IsIdentifier(name) && !scanner.IsValidIdentifier(name.AsIdentifier().Text) {
// drop invalid chars for _, if empty, write _0, etc., so we have a valid param name to emit later
result := strings.Builder{}
for i, ch := range name.AsIdentifier().Text {
if i == 0 {
if !scanner.IsIdentifierStart(ch) {
result.WriteRune('_')
} else {
result.WriteRune(ch)
}
continue
} else if !scanner.IsIdentifierPart(ch) {
result.WriteRune('_')
} else {
result.WriteRune(ch)
}
}
if result.Len() == 0 {
result.WriteRune('_')
result.WriteString(strconv.Itoa(pi))
}
name = p.addTransformedReparse(p.factory.NewIdentifier(result.String()), name)
} else {
name = p.addDeepCloneReparse(name)
}
@DanielRosenwasser
Copy link
Copy Markdown
Member

Can we give a more-specialized error message for reserved names?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Property names from JSDoc types not escaped in declaration files

3 participants