Skip to content

Fix two classes of "unreachable code" warnings in Firefox. #1071

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 4, 2021
Merged
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
18 changes: 18 additions & 0 deletions compiler/astutil/astutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,21 @@ func FindLoopStmt(stack []ast.Node, branch *ast.BranchStmt, typeInfo *types.Info
// This should never happen in a source that passed type checking.
panic(fmt.Errorf("continue/break statement %v doesn't have a matching loop statement among ancestors", branch))
}

// EndsWithReturn returns true if the last effective statement is a "return".
func EndsWithReturn(stmts []ast.Stmt) bool {
if len(stmts) == 0 {
return false
}
last := stmts[len(stmts)-1]
switch l := last.(type) {
case *ast.ReturnStmt:
return true
case *ast.LabeledStmt:
return EndsWithReturn([]ast.Stmt{l.Stmt})
case *ast.BlockStmt:
return EndsWithReturn(l.List)
default:
return false
}
}
52 changes: 52 additions & 0 deletions compiler/astutil/astutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,58 @@ func TestPruneOriginal(t *testing.T) {
}
}

func TestEndsWithReturn(t *testing.T) {
tests := []struct {
desc string
src string
want bool
}{
{
desc: "empty function",
src: `func foo() {}`,
want: false,
}, {
desc: "implicit return",
src: `func foo() { a() }`,
want: false,
}, {
desc: "explicit return",
src: `func foo() { a(); return }`,
want: true,
}, {
desc: "labelled return",
src: `func foo() { Label: return }`,
want: true,
}, {
desc: "labelled call",
src: `func foo() { Label: a() }`,
want: false,
}, {
desc: "return in a block",
src: `func foo() { a(); { b(); return; } }`,
want: true,
}, {
desc: "a block without return",
src: `func foo() { a(); { b(); c(); } }`,
want: false,
}, {
desc: "conditional block",
src: `func foo() { a(); if x { b(); return; } }`,
want: false,
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
fdecl := parseFuncDecl(t, "package testpackage\n"+test.src)
got := EndsWithReturn(fdecl.Body.List)
if got != test.want {
t.Errorf("EndsWithReturn() returned %t, want %t", got, test.want)
}
})
}
}

func parse(t *testing.T, fset *token.FileSet, src string) *ast.File {
t.Helper()
f, err := parser.ParseFile(fset, "test.go", src, parser.ParseComments)
Expand Down
3 changes: 2 additions & 1 deletion compiler/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"

"github.com/gopherjs/gopherjs/compiler/analysis"
"github.com/gopherjs/gopherjs/compiler/astutil"
"github.com/neelance/astrewrite"
"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/go/types/typeutil"
Expand Down Expand Up @@ -768,7 +769,7 @@ func translateFunction(typ *ast.FuncType, recv *ast.Ident, body *ast.BlockStmt,
}

c.translateStmtList(body.List)
if len(c.Flattened) != 0 && !endsWithReturn(body.List) {
if len(c.Flattened) != 0 && !astutil.EndsWithReturn(body.List) {
c.translateStmt(&ast.ReturnStmt{}, nil)
}
}))
Expand Down
16 changes: 13 additions & 3 deletions compiler/statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ func (fc *funcContext) translateBranchingStmt(caseClauses []*ast.CaseClause, def
fc.PrintCond(!flatten, fmt.Sprintf("%sif (%s) {", prefix, condStrs[i]), fmt.Sprintf("case %d:", caseOffset+i))
fc.Indent(func() {
fc.translateStmtList(clause.Body)
if flatten && (i < len(caseClauses)-1 || defaultClause != nil) && !endsWithReturn(clause.Body) {
if flatten && (i < len(caseClauses)-1 || defaultClause != nil) && !astutil.EndsWithReturn(clause.Body) {
fc.Printf("$s = %d; continue;", endCase)
}
})
Expand Down Expand Up @@ -681,6 +681,7 @@ func (fc *funcContext) translateLoopingStmt(cond func() string, body *ast.BlockS
if !flatten && label != nil {
fc.Printf("%s:", label.Name())
}
isTerminated := false
fc.PrintCond(!flatten, "while (true) {", fmt.Sprintf("case %d:", data.beginCase))
fc.Indent(func() {
condStr := cond()
Expand All @@ -695,7 +696,6 @@ func (fc *funcContext) translateLoopingStmt(cond func() string, body *ast.BlockS
bodyPrefix()
}
fc.translateStmtList(body.List)
isTerminated := false
if len(body.List) != 0 {
switch body.List[len(body.List)-1].(type) {
case *ast.ReturnStmt, *ast.BranchStmt:
Expand All @@ -708,7 +708,17 @@ func (fc *funcContext) translateLoopingStmt(cond func() string, body *ast.BlockS

fc.pkgCtx.escapingVars = prevEV
})
fc.PrintCond(!flatten, "}", fmt.Sprintf("$s = %d; continue; case %d:", data.beginCase, data.endCase))
if flatten {
// If the last statement of the loop is a return or unconditional branching
// statement, there's no need for an instruction to go back to the beginning
// of the loop.
if !isTerminated {
fc.Printf("$s = %d; continue;", data.beginCase)
}
fc.Printf("case %d:", data.endCase)
} else {
fc.Printf("}")
}
}

func (fc *funcContext) translateAssign(lhs, rhs ast.Expr, define bool) string {
Expand Down
9 changes: 0 additions & 9 deletions compiler/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,15 +677,6 @@ func rangeCheck(pattern string, constantIndex, array bool) string {
return "(" + check + ` ? ($throwRuntimeError("index out of range"), undefined) : ` + pattern + ")"
}

func endsWithReturn(stmts []ast.Stmt) bool {
if len(stmts) > 0 {
if _, ok := stmts[len(stmts)-1].(*ast.ReturnStmt); ok {
return true
}
}
return false
}

func encodeIdent(name string) string {
return strings.Replace(url.QueryEscape(name), "%", "$", -1)
}
Expand Down