Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 12 additions & 3 deletions tools/customlint/checkchildren.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
// conditionally, e.g.
//
// var exprType *Type
// if node.Expression() != nil {
// if !node.Expression().IsNil() {
// exprType = c.checkExpressionCached(node.Expression())
// }
// if c.grammarError(node) {
Expand Down Expand Up @@ -238,9 +238,18 @@ func guardedNil(ret inspector.Cursor, childExpr ast.Expr) bool {
}

// condImpliesNil reports whether `cond` evaluating to `condIsTrue` implies that
// `childExpr` is nil, i.e. `childExpr == nil` (when true) or `childExpr != nil`
// (when false).
// `childExpr` is nil, i.e. `childExpr == nil` or `childExpr.IsNil()` (when
// true) or `childExpr != nil` or `!childExpr.IsNil()` (when false).
func condImpliesNil(cond ast.Expr, childExpr ast.Expr, condIsTrue bool) bool {
if un, ok := cond.(*ast.UnaryExpr); ok && un.Op == token.NOT {
return condImpliesNil(un.X, childExpr, !condIsTrue)
}
if call, ok := cond.(*ast.CallExpr); ok && condIsTrue && len(call.Args) == 0 {
if sel, ok := call.Fun.(*ast.SelectorExpr); ok && sel.Sel.Name == "IsNil" {
return equalExpr(sel.X, childExpr)
}
return false
}
bin, ok := cond.(*ast.BinaryExpr)
if !ok {
return false
Expand Down
14 changes: 14 additions & 0 deletions tools/customlint/testdata/checkchildren/checkchildren.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ type Node struct{}

func (n *Node) Expression() *Node { return nil }

func (n *Node) IsNil() bool { return n == nil }

type Type struct{}

type CheckMode int
Expand Down Expand Up @@ -40,6 +42,8 @@ func (c *Checker) checkExpressionWorker(node *Node, checkMode CheckMode) *Type {
return c.checkCallLike(node)
case node != nil:
return c.checkNilGuardedReturn(node)
case node.IsNil():
return c.checkIsNilGuardedReturn(node)
default:
return c.checkReturnsInReturn(node)
}
Expand Down Expand Up @@ -112,6 +116,16 @@ func (c *Checker) checkNilGuardedReturn(node *Node) *Type {
return c.checkExpression(node.Expression(), 0)
}

// checkIsNilGuardedReturn is like checkNilGuardedReturn, but guards with an
// IsNil method call.
func (c *Checker) checkIsNilGuardedReturn(node *Node) *Type {
c.grammarError(node)
if node.Expression().IsNil() {
return c.errorType
}
return c.checkExpression(node.Expression(), 0)
}

// checkNeverDispatched is not reachable from a worker, so it is never analyzed.
func (c *Checker) checkNeverDispatched(node *Node) *Type {
if c.grammarError(node) {
Expand Down
14 changes: 14 additions & 0 deletions tools/customlint/testdata/checkchildren/checkchildren.go.golden
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

func (n *Node) Expression() *Node { return nil }

func (n *Node) IsNil() bool { return n == nil }

type Type struct{}

type CheckMode int
Expand Down Expand Up @@ -40,6 +42,8 @@
return c.checkCallLike(node)
case node != nil:
return c.checkNilGuardedReturn(node)
case node.IsNil():
return c.checkIsNilGuardedReturn(node)
default:
return c.checkReturnsInReturn(node)
}
Expand Down Expand Up @@ -116,6 +120,16 @@
return c.checkExpression(node.Expression(), 0)
}

// checkIsNilGuardedReturn is like checkNilGuardedReturn, but guards with an
// IsNil method call.
func (c *Checker) checkIsNilGuardedReturn(node *Node) *Type {
c.grammarError(node)
if node.Expression().IsNil() {
return c.errorType
}
return c.checkExpression(node.Expression(), 0)
}

// checkNeverDispatched is not reachable from a worker, so it is never analyzed.
func (c *Checker) checkNeverDispatched(node *Node) *Type {
if c.grammarError(node) {
Expand Down
2 changes: 1 addition & 1 deletion tools/scripts/tsc/ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@
"goOnly": true
},
"NextContainer": {
"type": "*Node",
"type": "Node",
"goOnly": true
}
},
Expand Down
57 changes: 27 additions & 30 deletions tools/scripts/tsc/generate-encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,14 @@ function goParamName(m: MemberInfo): string {
return m.goParamName();
}

/** Get the Go accessor for a member on a cast struct (e.g., "n.IsTypeOnly", "n.Name()"). */
/** Get the Go accessor for a member on a cast struct (e.g., "n.IsTypeOnly()", "n.Name()"). */
function goFieldAccess(m: MemberInfo): string {
if (m.private) return `n.${api.capitalize(m.name)}()`;
return `n.${m.name}`;
return `n.${api.capitalize(m.name)}()`;
}

/** Get the default Go value for a non-encoded factory parameter. */
function goDefaultValue(m: MemberInfo): string {
if (m.isChild()) return "nil";
if (m.isChild()) return childType(m) === "node" ? "ast.Node{}" : "nil";
const dt = m.declaredType;
if (dt.kind === "primitive") {
switch (dt.name) {
Expand Down Expand Up @@ -366,9 +365,9 @@ function generateGoEncoder(): string {
}

function generateGoGetNodeDataType(w: CodeWriter) {
w.write("func getNodeDataType(node *ast.Node) uint32 {");
w.write("func getNodeDataType(node ast.Node) uint32 {");
w.push();
w.write("switch node.Kind {");
w.write("switch node.Kind() {");

const stringKinds: string[] = [];
const extendedKinds: string[] = [];
Expand Down Expand Up @@ -409,9 +408,9 @@ function generateGoGetNodeDataType(w: CodeWriter) {
}

function generateGoGetChildrenPropertyMask(w: CodeWriter) {
w.write("func getChildrenPropertyMask(node *ast.Node) uint8 {");
w.write("func getChildrenPropertyMask(node ast.Node) uint8 {");
w.push();
w.write("switch node.Kind {");
w.write("switch node.Kind() {");

for (const node of api.nodes()) {
const info = analyzeNode(node);
Expand Down Expand Up @@ -440,7 +439,7 @@ function generateGoGetChildrenPropertyMask(w: CodeWriter) {
check = `len(${goFieldAccess(m)}) > 0`;
}
else {
check = `${goFieldAccess(m)} != nil`;
check = `!${goFieldAccess(m)}.IsNil()`;
}
parts.push(`(boolToByte(${check}) << ${i})`);
}
Expand All @@ -459,9 +458,9 @@ function generateGoGetChildrenPropertyMask(w: CodeWriter) {
}

function generateGoGetNodeCommonData(w: CodeWriter) {
w.write("func getNodeCommonData(node *ast.Node) uint32 {");
w.write("func getNodeCommonData(node ast.Node) uint32 {");
w.push();
w.write("switch node.Kind {");
w.write("switch node.Kind() {");

for (const node of api.nodes()) {
const info = analyzeNode(node);
Expand Down Expand Up @@ -525,29 +524,27 @@ function generateGoGetNodeCommonData(w: CodeWriter) {
}

function generateGoRecordNodeStrings(w: CodeWriter) {
w.write("func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 {");
w.write("func recordNodeStrings(node ast.Node, strs *stringTable) uint32 {");
w.push();
w.write("switch node.Kind {");
w.write("switch node.Kind() {");

for (const node of api.nodes()) {
const info = analyzeNode(node);
if (info.dataType !== "string" || !info.textMember) continue;

const kinds = getGoKinds(node);
const castName = goCastName(node);
const textAccess = info.textMember.private
? `node.${castName}().${api.capitalize(info.textMember.name)}()`
: `node.${castName}().${info.textMember.name}`;
const textAccess = `node.${castName}().${api.capitalize(info.textMember.name)}()`;

w.write(`case ${kinds.join(", ")}:`);
w.push();
w.write(`return strs.add(${textAccess}, node.Kind, node.Pos(), node.End())`);
w.write(`return strs.add(${textAccess}, node.Kind(), node.Pos(), node.End())`);
w.pop();
}

w.write("default:");
w.push();
w.write(`panic(fmt.Sprintf("Unexpected node kind %v", node.Kind))`);
w.write(`panic(fmt.Sprintf("Unexpected node kind %v", node.Kind()))`);
w.pop();
w.write("}");
w.pop();
Expand All @@ -556,10 +553,10 @@ function generateGoRecordNodeStrings(w: CodeWriter) {
}

function generateGoRecordExtendedData(w: CodeWriter) {
w.write("func recordExtendedData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {");
w.write("func recordExtendedData(node ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {");
w.push();
w.write("offset := uint32(len(*extendedData))");
w.write("switch node.Kind {");
w.write("switch node.Kind() {");

for (const node of api.nodes()) {
const info = analyzeNode(node);
Expand All @@ -575,7 +572,7 @@ function generateGoRecordExtendedData(w: CodeWriter) {

w.write("default:");
w.push();
w.write(`panic(fmt.Sprintf("unknown extended data node kind %v", node.Kind))`);
w.write(`panic(fmt.Sprintf("unknown extended data node kind %v", node.Kind()))`);
w.pop();
w.write("}");
w.write("return offset");
Expand Down Expand Up @@ -611,7 +608,7 @@ function generateGoDecoder(): string {
}

function generateGoCreateStringNode(w: CodeWriter) {
w.write("func (d *astDecoder) createStringNode(kind ast.Kind, data uint32, commonData uint8) (*ast.Node, error) {");
w.write("func (d *astDecoder) createStringNode(kind ast.Kind, data uint32, commonData uint8) (ast.Node, error) {");
w.push();
w.write("strIdx := data & NodeDataStringIndexMask");
w.write("text := d.getString(strIdx)");
Expand Down Expand Up @@ -646,7 +643,7 @@ function generateGoCreateStringNode(w: CodeWriter) {
}
}
else if (isEncodedChild(m)) {
args.push("nil");
args.push(goDefaultValue(m));
}
else {
const v = commonVars.get(m);
Expand All @@ -665,7 +662,7 @@ function generateGoCreateStringNode(w: CodeWriter) {

w.write("default:");
w.push();
w.write(`return nil, fmt.Errorf("unknown string node kind %v", kind)`);
w.write(`return ast.Node{}, fmt.Errorf("unknown string node kind %v", kind)`);
w.pop();
w.write("}");
w.pop();
Expand All @@ -674,7 +671,7 @@ function generateGoCreateStringNode(w: CodeWriter) {
}

function generateGoCreateExtendedNode(w: CodeWriter) {
w.write("func (d *astDecoder) createExtendedNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {");
w.write("func (d *astDecoder) createExtendedNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (ast.Node, error) {");
w.push();
w.write("switch kind {");

Expand All @@ -693,7 +690,7 @@ function generateGoCreateExtendedNode(w: CodeWriter) {

w.write("default:");
w.push();
w.write(`return nil, fmt.Errorf("unknown extended data node kind %v", kind)`);
w.write(`return ast.Node{}, fmt.Errorf("unknown extended data node kind %v", kind)`);
w.pop();
w.write("}");
w.pop();
Expand All @@ -702,7 +699,7 @@ function generateGoCreateExtendedNode(w: CodeWriter) {
}

function generateGoCreateChildrenNode(w: CodeWriter) {
w.write("func (d *astDecoder) createChildrenNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {");
w.write("func (d *astDecoder) createChildrenNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (ast.Node, error) {");
w.push();
w.write("mask := uint8(data & NodeDataChildMask)");
w.write("");
Expand Down Expand Up @@ -752,7 +749,7 @@ function generateGoCreateChildrenNode(w: CodeWriter) {
// Default: error for unhandled kinds
w.write("default:");
w.push();
w.write(`return nil, fmt.Errorf("unhandled node kind %v with %d children", kind, len(childIndices))`);
w.write(`return ast.Node{}, fmt.Errorf("unhandled node kind %v with %d children", kind, len(childIndices))`);
w.pop();

w.write("}");
Expand Down Expand Up @@ -960,10 +957,10 @@ function emitMultiChildDecoder(w: CodeWriter, node: NodeType, info: EncoderNodeI
else if (ct === "rawNodeList") {
const nlVar = `${varName}NL`;
w.write(`${nlVar} := d.nodeListAt(it.nextIf(mask, ${i}))`);
w.write(`var ${varName} []*ast.Node`);
w.write(`var ${varName} []ast.Node`);
w.write(`if ${nlVar} != nil {`);
w.push();
w.write(`${varName} = ${nlVar}.Nodes`);
w.write(`${varName} = ${nlVar}.Nodes()`);
w.pop();
w.write("}");
childVars.set(m, varName);
Expand Down
Loading
Loading