Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6f7ff97
CreateExpressionFromEntityName
jakebailey Feb 20, 2026
2b0c69a
Fix yield precedence
jakebailey Feb 20, 2026
e5dcdba
big code
jakebailey Feb 20, 2026
0e042bb
No ES5
jakebailey Feb 20, 2026
c843586
Correct flag
jakebailey Feb 20, 2026
722d2bc
Fix arguments binding
jakebailey Feb 20, 2026
3aa19f3
Fix super, but at what cost
jakebailey Feb 20, 2026
323adeb
Don't even check if the name is not arguments
jakebailey Feb 20, 2026
9bdb2e2
Comment parity
jakebailey Feb 20, 2026
45a7771
Delete flags
jakebailey Feb 20, 2026
ae03692
Remove unused EFNoSubstitution
jakebailey Feb 20, 2026
be175d5
Rename, it's package scoped
jakebailey Feb 20, 2026
c3d1b6b
Fix missing parens
jakebailey Feb 20, 2026
d76ae7d
Fix source map diff
jakebailey Feb 20, 2026
912f1f6
Just do a simple name lookupm no resolver needed
jakebailey Feb 20, 2026
3ce161d
Fix bad appends
jakebailey Feb 20, 2026
159e36b
assignmentTargetContainsSuperProperty to avoid parent lookups
jakebailey Feb 20, 2026
3353611
prevent binding
jakebailey Feb 20, 2026
9a537d5
Use StartVariableEnvironment
jakebailey Feb 20, 2026
d51c6ac
Drop ES5 comments
jakebailey Feb 20, 2026
ba421b4
Implement makeFileLevelOptimisticUniqueName, and then delete slop
jakebailey Feb 20, 2026
e0ebc88
Clean up isEffectiveStrictModeSourceFile
jakebailey Feb 20, 2026
3e1546d
Steal FunctionFlags code from ID PR
jakebailey Feb 20, 2026
0c3404b
Track curent nodes and such to prevent parent accessses
jakebailey Feb 20, 2026
c5124d4
CR feedback again
jakebailey Feb 20, 2026
33739ac
Fix up differences, make order match better
jakebailey Feb 23, 2026
f73dddb
Fix arguments and super tracking into something I actually understand
jakebailey Feb 23, 2026
f96bdea
Fix facts
jakebailey Feb 23, 2026
663dc5a
Remove silly comment
jakebailey Feb 23, 2026
f9ae2d5
One more visitFunctionBody oops
jakebailey Feb 23, 2026
7e0a203
Fix flags
jakebailey Feb 23, 2026
0e0604e
Fix visitor recreation
jakebailey Feb 24, 2026
7aef41c
Restore comment
jakebailey Feb 25, 2026
b1a083a
extract descendInto
jakebailey Feb 25, 2026
aa65589
Use ordered set for capturedSuperProperties
jakebailey Feb 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2670,6 +2670,12 @@ func (node *Token) computeSubtreeFacts() SubtreeFacts {
return SubtreeContainsTypeScript
case KindAccessorKeyword:
return SubtreeContainsClassFields
case KindAsyncKeyword:
return SubtreeContainsAnyAwait
case KindSuperKeyword:
return SubtreeContainsLexicalSuper
case KindThisKeyword:
return SubtreeContainsLexicalThis
case KindAsteriskAsteriskToken, KindAsteriskAsteriskEqualsToken:
return SubtreeContainsExponentiationOperator
case KindQuestionQuestionToken:
Expand Down
37 changes: 37 additions & 0 deletions internal/ast/functionflags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ast

type FunctionFlags uint32

const (
FunctionFlagsNormal FunctionFlags = 0
FunctionFlagsGenerator FunctionFlags = 1 << 0
FunctionFlagsAsync FunctionFlags = 1 << 1
FunctionFlagsInvalid FunctionFlags = 1 << 2
FunctionFlagsAsyncGenerator FunctionFlags = FunctionFlagsAsync | FunctionFlagsGenerator
)

func GetFunctionFlags(node *Node) FunctionFlags {
if node == nil {
return FunctionFlagsInvalid
}
data := node.BodyData()
if data == nil {
return FunctionFlagsInvalid
}
flags := FunctionFlagsNormal
switch node.Kind {
case KindFunctionDeclaration, KindFunctionExpression, KindMethodDeclaration:
if data.AsteriskToken != nil {
flags |= FunctionFlagsGenerator
}
fallthrough
case KindArrowFunction:
if HasSyntacticModifier(node, ModifierFlagsAsync) {
flags |= FunctionFlagsAsync
}
}
if data.Body == nil {
flags |= FunctionFlagsInvalid
}
return flags
}
14 changes: 10 additions & 4 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -3850,7 +3850,7 @@ func TryGetPropertyNameOfBindingOrAssignmentElement(bindingElement *Node) *Node
// `1` in `let { 1: b } = ...`
if bindingElement.PropertyName() != nil {
propertyName := bindingElement.PropertyName()
// if ast.IsPrivateIdentifier(propertyName) {
// if IsPrivateIdentifier(propertyName) {
// return Debug.failBadSyntaxKind(propertyName) // !!!
// }
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.Expression()) {
Expand All @@ -3865,7 +3865,7 @@ func TryGetPropertyNameOfBindingOrAssignmentElement(bindingElement *Node) *Node
// `1` in `({ 1: b } = ...)`
if bindingElement.Name() != nil {
propertyName := bindingElement.Name()
// if ast.IsPrivateIdentifier(propertyName) {
// if IsPrivateIdentifier(propertyName) {
// return Debug.failBadSyntaxKind(propertyName) // !!!
// }
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.Expression()) {
Expand All @@ -3875,7 +3875,7 @@ func TryGetPropertyNameOfBindingOrAssignmentElement(bindingElement *Node) *Node
}
case KindSpreadAssignment:
// `a` in `({ ...a } = ...)`
// if ast.IsPrivateIdentifier(bindingElement.Name()) {
// if IsPrivateIdentifier(bindingElement.Name()) {
// return Debug.failBadSyntaxKind(bindingElement.Name()) // !!!
// }
return bindingElement.Name()
Expand Down Expand Up @@ -4188,7 +4188,7 @@ func GetAllAccessorDeclarationsForDeclaration(accessor *AccessorDeclaration, dec
} else {
panic(fmt.Sprintf("Unexpected node kind %q", accessor.Kind))
}
// otherAccessor := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(accessor), otherKind)
// otherAccessor := GetDeclarationOfKind(c.getSymbolOfDeclaration(accessor), otherKind)
var otherAccessor *AccessorDeclaration
for _, d := range declarationsOfSymbol {
if d.Kind == otherKind {
Expand Down Expand Up @@ -4357,3 +4357,9 @@ func findCloneInNode(node *Node, original *Node) *Node {
}
}
}

// IsSuperProperty checks if a node is super.x or super[x].
func IsSuperProperty(node *Node) bool {
return (IsPropertyAccessExpression(node) || IsElementAccessExpression(node)) &&
node.Expression().Kind == KindSuperKeyword
}
111 changes: 53 additions & 58 deletions internal/checker/checker.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/checker/grammarchecks.go
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,7 @@ func (c *Checker) checkGrammarForInOrForOfStatement(forInOrOfStatement *ast.ForI
diagnostic := createDiagnosticForNode(forInOrOfStatement.AwaitModifier, diagnostics.X_for_await_loops_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules)
containingFunc := ast.GetContainingFunction(forInOrOfStatement.AsNode())
if containingFunc != nil && containingFunc.Kind != ast.KindConstructor {
debug.Assert((getFunctionFlags(containingFunc)&FunctionFlagsAsync) == 0, "Enclosing function should never be an async function.")
debug.Assert((ast.GetFunctionFlags(containingFunc)&ast.FunctionFlagsAsync) == 0, "Enclosing function should never be an async function.")
if hasAsyncModifier(containingFunc) {
panic("Enclosing function should never be an async function.")
}
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/relater.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ func (c *Checker) elaborateArrowFunction(node *ast.Node, source *Type, target *T
if target.symbol != nil && len(target.symbol.Declarations) != 0 {
diagnostic.AddRelatedInfo(createDiagnosticForNode(target.symbol.Declarations[0], diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature))
}
if getFunctionFlags(node)&FunctionFlagsAsync == 0 && c.getTypeOfPropertyOfType(sourceReturn, "then") == nil && c.checkTypeRelatedTo(c.createPromiseType(sourceReturn), targetReturn, relation, nil /*errorNode*/) {
if ast.GetFunctionFlags(node)&ast.FunctionFlagsAsync == 0 && c.getTypeOfPropertyOfType(sourceReturn, "then") == nil && c.checkTypeRelatedTo(c.createPromiseType(sourceReturn), targetReturn, relation, nil /*errorNode*/) {
diagnostic.AddRelatedInfo(createDiagnosticForNode(node, diagnostics.Did_you_mean_to_mark_this_function_as_async))
}
c.reportDiagnostic(diagnostic, diagnosticOutput)
Expand Down
3 changes: 0 additions & 3 deletions internal/checker/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,6 @@ const (
NodeCheckFlagsSuperInstance NodeCheckFlags = 1 << 4 // Instance 'super' reference
NodeCheckFlagsSuperStatic NodeCheckFlags = 1 << 5 // Static 'super' reference
NodeCheckFlagsContextChecked NodeCheckFlags = 1 << 6 // Contextual types have been assigned
NodeCheckFlagsMethodWithSuperPropertyAccessInAsync NodeCheckFlags = 1 << 7 // A method that contains a SuperProperty access in an async context.
NodeCheckFlagsMethodWithSuperPropertyAssignmentInAsync NodeCheckFlags = 1 << 8 // A method that contains a SuperProperty assignment in an async context.
NodeCheckFlagsCaptureArguments NodeCheckFlags = 1 << 9 // Lexical 'arguments' used in body
NodeCheckFlagsEnumValuesComputed NodeCheckFlags = 1 << 10 // Values for enum members have been computed, and any errors have been reported for them.
NodeCheckFlagsLoopWithCapturedBlockScopedBinding NodeCheckFlags = 1 << 12 // Loop that contains block scoped variable captured in closure
NodeCheckFlagsContainsCapturedBlockScopeBinding NodeCheckFlags = 1 << 13 // Part of a loop that contains block scoped variable captured in closure
Expand Down
45 changes: 0 additions & 45 deletions internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1027,15 +1027,6 @@ func isSuperCall(n *ast.Node) bool {
return ast.IsCallExpression(n) && n.Expression().Kind == ast.KindSuperKeyword
}

/**
* Determines whether a node is a property or element access expression for `super`.
*
* @internal
*/
func isSuperProperty(node *ast.Node) bool {
return ast.IsAccessExpression(node) && node.Expression().Kind == ast.KindSuperKeyword
}

func getMembersOfDeclaration(node *ast.Node) []*ast.Node {
switch node.Kind {
case ast.KindInterfaceDeclaration, ast.KindClassDeclaration, ast.KindClassExpression, ast.KindTypeLiteral:
Expand All @@ -1046,42 +1037,6 @@ func getMembersOfDeclaration(node *ast.Node) []*ast.Node {
return nil
}

type FunctionFlags uint32

const (
FunctionFlagsNormal FunctionFlags = 0
FunctionFlagsGenerator FunctionFlags = 1 << 0
FunctionFlagsAsync FunctionFlags = 1 << 1
FunctionFlagsInvalid FunctionFlags = 1 << 2
FunctionFlagsAsyncGenerator FunctionFlags = FunctionFlagsAsync | FunctionFlagsGenerator
)

func getFunctionFlags(node *ast.Node) FunctionFlags {
if node == nil {
return FunctionFlagsInvalid
}
data := node.BodyData()
if data == nil {
return FunctionFlagsInvalid
}
flags := FunctionFlagsNormal
switch node.Kind {
case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindMethodDeclaration:
if data.AsteriskToken != nil {
flags |= FunctionFlagsGenerator
}
fallthrough
case ast.KindArrowFunction:
if ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) {
flags |= FunctionFlagsAsync
}
}
if data.Body == nil {
flags |= FunctionFlagsInvalid
}
return flags
}

func isInRightSideOfImportOrExportAssignment(node *ast.EntityName) bool {
for node.Parent.Kind == ast.KindQualifiedName {
node = node.Parent
Expand Down
1 change: 1 addition & 0 deletions internal/printer/emitflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
EFNeverApplyImportHelper // Do not apply an import helper to this node
EFStartOnNewLine // Start this node on a new line
EFIndirectCall // Emit CallExpression as an indirect call: `(0, f)()`
EFAsyncFunctionBody // The node was originally an async function body.
Comment thread
jakebailey marked this conversation as resolved.
)

const (
Expand Down
76 changes: 76 additions & 0 deletions internal/printer/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,24 @@ func (f *NodeFactory) InlineExpressions(expressions []*ast.Expression) *ast.Expr
// Utilities
//

func (f *NodeFactory) CreateExpressionFromEntityName(node *ast.Node) *ast.Expression {
if ast.IsQualifiedName(node) {
left := f.CreateExpressionFromEntityName(node.AsQualifiedName().Left)
right := node.AsQualifiedName().Right.Clone(f.AsNodeFactory())
right.Loc = node.AsQualifiedName().Right.Loc
// TODO(rbuckton): Does this need to be parented?
right.Parent = node.AsQualifiedName().Right.Parent
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This parent setting was TODO'd with a "does this need to be done" before it was moved. My question: does it? Most AST construction in the transformers doesn't need .Parent pointers setup, and setting them in that context is... odd.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm going to say no. The original Strada code was flipped the other way with the opposite TODO:

const left = createExpressionFromEntityName(factory, node.left);
        // TODO(rbuckton): Does this need to be parented?
        const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent);
        return setTextRange(factory.createPropertyAccessExpression(left, right), node);

I'll delete it here once and for all.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Er, sorry, not flipped, it also set parent.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I just tested, and while this first one works when unparented, the one below (plain clone) does not and causes:

--- old.unicodeEscapesInJsxtags.js
+++ new.unicodeEscapesInJsxtags.js
@@= skipped -38, +38 lines =@@
 React.createElement("a", null);
 React.createElement("a-b", null);
 React.createElement("a-c", null);
-React.createElement(Comp\u0061, { x: 12 });
+React.createElement(Compa, { x: 12 });
 React.createElement(x.\u0076ideo, null);
 React.createElement("a", null);
 React.createElement("a-b", null);
 React.createElement("a-c", null);
-React.createElement(Comp\u{0061}, { x: 12 });
+React.createElement(Compa, { x: 12 });
 React.createElement("video", { "data-video": true });
 React.createElement("video", { \u0073rc: "" });

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I restored the comments for now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, the parent pointers being set causes it to go back to the source file for text vs using the parsed text stored in the identifier. Kinda just highlights information we're not properly storing in the AST.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah. This to me means something is wrong, but there's not a great way to ban all parent accesses during transform...

propAccess := f.NewPropertyAccessExpression(left, nil, right, ast.NodeFlagsNone)
propAccess.Loc = node.Loc
return propAccess
}
res := node.Clone(f.AsNodeFactory())
res.Loc = node.Loc
// TODO(rbuckton): Does this need to be parented?
res.Parent = node.Parent
return res
}

func (f *NodeFactory) NewTypeCheck(value *ast.Node, tag string) *ast.Node {
if tag == "null" {
return f.NewStrictEqualityExpression(value, f.NewKeywordExpression(ast.KindNullKeyword))
Expand Down Expand Up @@ -650,6 +668,64 @@ func (f *NodeFactory) NewRestHelper(value *ast.Expression, elements []*ast.Node,

// !!! ES2017 Helpers

// Allocates a new Call expression to the `__awaiter` helper.
func (f *NodeFactory) NewAwaiterHelper(
hasLexicalThis bool,
argumentsExpression *ast.Expression,
parameters *ast.NodeList,
body *ast.BlockNode,
) *ast.Expression {
f.emitContext.RequestEmitHelper(awaiterHelper)

var params *ast.NodeList
if parameters != nil {
params = parameters
} else {
params = f.NewNodeList([]*ast.Node{})
}

generatorFunc := f.NewFunctionExpression(
nil, /*modifiers*/
f.NewToken(ast.KindAsteriskToken),
nil, /*name*/
nil, /*typeParameters*/
params,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)

// Mark this node as originally an async function body
f.emitContext.AddEmitFlags(generatorFunc, EFAsyncFunctionBody|EFReuseTempVariableScope)

var thisArg *ast.Expression
if hasLexicalThis {
thisArg = f.NewKeywordExpression(ast.KindThisKeyword)
} else {
thisArg = f.NewVoidZeroExpression()
}

var argsArg *ast.Expression
if argumentsExpression != nil {
argsArg = argumentsExpression
} else {
argsArg = f.NewVoidZeroExpression()
}

return f.NewCallExpression(
f.NewUnscopedHelperName("__awaiter"),
nil, /*questionDotToken*/
nil, /*typeArguments*/
f.NewNodeList([]*ast.Expression{
thisArg,
argsArg,
f.NewVoidZeroExpression(),
Comment thread
jakebailey marked this conversation as resolved.
generatorFunc,
}),
ast.NodeFlagsNone,
)
}

// ES2015 Helpers

func (f *NodeFactory) NewPropKeyHelper(expr *ast.Expression) *ast.Expression {
Expand Down
35 changes: 35 additions & 0 deletions internal/printer/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,41 @@ var restHelper = &EmitHelper{

// !!! ES2017 Helpers

var awaiterHelper = &EmitHelper{
Name: "typescript:awaiter",
ImportName: "__awaiter",
Scoped: false,
Priority: &Priority{5},
Text: `var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};`,
}

var AsyncSuperHelper = &EmitHelper{
Name: "typescript:async-super",
Scoped: true,
TextCallback: func(makeUniqueName func(string) string) string {
return "\nconst " + makeUniqueName("_superIndex") + " = name => super[name];"
},
}

var AdvancedAsyncSuperHelper = &EmitHelper{
Name: "typescript:advanced-async-super",
Scoped: true,
TextCallback: func(makeUniqueName func(string) string) string {
return "\nconst " + makeUniqueName("_superIndex") + " = (function (geti, seti) {\n" +
" const cache = Object.create(null);\n" +
" return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });\n" +
"})(name => super[name], (name, value) => super[name] = value);"
},
}
Comment thread
jakebailey marked this conversation as resolved.

// ES2015 Helpers

var propKeyHelper = &EmitHelper{
Expand Down
4 changes: 4 additions & 0 deletions internal/printer/namegenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ func (g *NameGenerator) makeUniqueName(baseName string, checkFn func(name string
}
}

func (g *NameGenerator) MakeFileLevelOptimisticUniqueName(name string) string {
return g.makeUniqueName(name, g.IsFileLevelUniqueNameInCurrentFile, true /*optimistic*/, false /*scoped*/, false /*privateName*/, "" /*prefix*/, "" /*suffix*/)
}

func (g *NameGenerator) checkUniqueName(name string, privateName bool, checkFn func(name string, privateName bool) bool) bool {
if checkFn != nil {
return checkFn(name, privateName)
Expand Down
13 changes: 12 additions & 1 deletion internal/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ func NewPrinter(options PrinterOptions, handlers PrintHandlers, emitContext *Emi
printer.nameGenerator.Context = printer.emitContext
printer.nameGenerator.GetTextOfNode = func(node *ast.Node) string { return printer.getTextOfNode(node, false) }
printer.nameGenerator.IsFileLevelUniqueNameInCurrentFile = printer.isFileLevelUniqueNameInCurrentFile
printer.makeFileLevelOptimisticUniqueName = func(name string) string {
return printer.nameGenerator.MakeFileLevelOptimisticUniqueName(name)
}
Comment on lines +179 to +181
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this not just

Suggested change
printer.makeFileLevelOptimisticUniqueName = func(name string) string {
return printer.nameGenerator.MakeFileLevelOptimisticUniqueName(name)
}
printer.makeFileLevelOptimisticUniqueName = printer.nameGenerator.MakeFileLevelOptimisticUniqueName

?

But moreover, why is it not just a method on printer?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's used like:

p.writeLines(helper.TextCallback(p.makeFileLevelOptimisticUniqueName))

So will be rebound to p each time. Perhaps that's not such a big deal.

printer.containerPos = -1
printer.containerEnd = -1
printer.declarationListContainerEnd = -1
Expand Down Expand Up @@ -1502,7 +1505,14 @@ func canEmitSimpleArrowHead(parentNode *ast.Node, parameters *ast.ParameterList)
parent := parentNode.AsArrowFunction()
parameter := parameters.Nodes[0].AsParameterDeclaration()

return parameter.Pos() == greatestEnd(parent.Pos(), parent.Modifiers()) && // may not have parsed tokens between modifiers/start of parent and parameter
// Only use modifiers for position check if they actually contain nodes.
// After transformation (e.g., async removal), modifiers may be an empty list with stale source positions.
modifiers := parent.Modifiers()
if modifiers != nil && len(modifiers.Nodes) == 0 {
modifiers = nil
}

return parameter.Pos() == greatestEnd(parent.Pos(), modifiers) && // may not have parsed tokens between modifiers/start of parent and parameter
parent.TypeParameters == nil && // parent may not have type parameters
parent.Type == nil && // parent may not have return type annotation
!parameters.HasTrailingComma() && // parameters may not have a trailing comma
Expand Down Expand Up @@ -2715,6 +2725,7 @@ func (p *Printer) getBinaryExpressionPrecedence(node *ast.BinaryExpression) (lef
case ast.OperatorPrecedenceAssignment:
// assignment is right-associative
leftPrec = ast.OperatorPrecedenceLeftHandSide
rightPrec = ast.OperatorPrecedenceYield
Comment thread
jakebailey marked this conversation as resolved.
case ast.OperatorPrecedenceLogicalOR:
rightPrec = ast.OperatorPrecedenceLogicalAND
case ast.OperatorPrecedenceLogicalAND:
Expand Down
Loading
Loading