From c2366b27abe7e18b1b5642e5a9a009f5c41723ae Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Wed, 4 Apr 2018 14:59:27 +0530 Subject: [PATCH] go/ast: hide unexported fields in composite literals In ast/ast.go, added an Incomplete field inside CompositeLit to denote that fields are missing. In ast/filter.go, added a new function to go through the expression list checking for KeyValue expressions inside composite literals. Filter out entries with an unexported key. In printer/nodes.go, checking if the Incomplete field is set, and accordingly print the filtered message with proper indentation. Copying over similar functionality in doc/exports.go so as to maintain parity with ast/filter.go and such that godoc can show the output correctly. Fixes #22803 Change-Id: I57a3b999521933e32411a18e02d0b94d2ea2e6f6 Reviewed-on: https://go-review.googlesource.com/106395 Run-TryBot: Robert Griesemer TryBot-Result: Gobot Gobot Reviewed-by: Robert Griesemer --- src/go/ast/ast.go | 9 ++-- src/go/ast/filter.go | 29 ++++++++++++ src/go/doc/exports.go | 29 ++++++++++++ src/go/printer/nodes.go | 50 +++++++++++++++------ src/go/printer/printer_test.go | 1 + src/go/printer/testdata/complit.input | 65 +++++++++++++++++++++++++++ src/go/printer/testdata/complit.x | 62 +++++++++++++++++++++++++ 7 files changed, 228 insertions(+), 17 deletions(-) create mode 100644 src/go/printer/testdata/complit.input create mode 100644 src/go/printer/testdata/complit.x diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index c07dd5a91e..d73c323178 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -264,10 +264,11 @@ type ( // A CompositeLit node represents a composite literal. CompositeLit struct { - Type Expr // literal type; or nil - Lbrace token.Pos // position of "{" - Elts []Expr // list of composite elements; or nil - Rbrace token.Pos // position of "}" + Type Expr // literal type; or nil + Lbrace token.Pos // position of "{" + Elts []Expr // list of composite elements; or nil + Rbrace token.Pos // position of "}" + Incomplete bool // true if (source) expressions are missing in the Elts list } // A ParenExpr node represents a parenthesized expression. diff --git a/src/go/ast/filter.go b/src/go/ast/filter.go index bb571166f4..32352cb92c 100644 --- a/src/go/ast/filter.go +++ b/src/go/ast/filter.go @@ -109,6 +109,34 @@ func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFiel return } +func filterCompositeLit(lit *CompositeLit, filter Filter, export bool) { + n := len(lit.Elts) + lit.Elts = filterExprList(lit.Elts, filter, export) + if len(lit.Elts) < n { + lit.Incomplete = true + } +} + +func filterExprList(list []Expr, filter Filter, export bool) []Expr { + j := 0 + for _, exp := range list { + switch x := exp.(type) { + case *CompositeLit: + filterCompositeLit(x, filter, export) + case *KeyValueExpr: + if x, ok := x.Key.(*Ident); ok && !filter(x.Name) { + continue + } + if x, ok := x.Value.(*CompositeLit); ok { + filterCompositeLit(x, filter, export) + } + } + list[j] = exp + j++ + } + return list[0:j] +} + func filterParamList(fields *FieldList, filter Filter, export bool) bool { if fields == nil { return false @@ -158,6 +186,7 @@ func filterSpec(spec Spec, f Filter, export bool) bool { switch s := spec.(type) { case *ValueSpec: s.Names = filterIdentList(s.Names, f) + s.Values = filterExprList(s.Values, f, export) if len(s.Names) > 0 { if export { filterType(s.Type, f, export) diff --git a/src/go/doc/exports.go b/src/go/doc/exports.go index ccdefcb967..5f99bf7772 100644 --- a/src/go/doc/exports.go +++ b/src/go/doc/exports.go @@ -27,6 +27,34 @@ func filterIdentList(list []*ast.Ident) []*ast.Ident { var underscore = ast.NewIdent("_") +func filterCompositeLit(lit *ast.CompositeLit, filter Filter, export bool) { + n := len(lit.Elts) + lit.Elts = filterExprList(lit.Elts, filter, export) + if len(lit.Elts) < n { + lit.Incomplete = true + } +} + +func filterExprList(list []ast.Expr, filter Filter, export bool) []ast.Expr { + j := 0 + for _, exp := range list { + switch x := exp.(type) { + case *ast.CompositeLit: + filterCompositeLit(x, filter, export) + case *ast.KeyValueExpr: + if x, ok := x.Key.(*ast.Ident); ok && !filter(x.Name) { + continue + } + if x, ok := x.Value.(*ast.CompositeLit); ok { + filterCompositeLit(x, filter, export) + } + } + list[j] = exp + j++ + } + return list[0:j] +} + // updateIdentList replaces all unexported identifiers with underscore // and reports whether at least one exported name exists. func updateIdentList(list []*ast.Ident) (hasExported bool) { @@ -171,6 +199,7 @@ func (r *reader) filterSpec(spec ast.Spec) bool { // always keep imports so we can collect them return true case *ast.ValueSpec: + s.Values = filterExprList(s.Values, ast.IsExported, true) if len(s.Values) > 0 || s.Type == nil && len(s.Values) == 0 { // If there are values declared on RHS, just replace the unexported // identifiers on the LHS with underscore, so that it matches diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go index 52284dafe2..18f2371d24 100644 --- a/src/go/printer/nodes.go +++ b/src/go/printer/nodes.go @@ -112,9 +112,11 @@ func (p *printer) identList(list []*ast.Ident, indent bool) { if !indent { mode = noIndent } - p.exprList(token.NoPos, xlist, 1, mode, token.NoPos) + p.exprList(token.NoPos, xlist, 1, mode, token.NoPos, false) } +const filteredMsg = "contains filtered or unexported fields" + // Print a list of expressions. If the list spans multiple // source lines, the original line breaks are respected between // expressions. @@ -122,8 +124,18 @@ func (p *printer) identList(list []*ast.Ident, indent bool) { // TODO(gri) Consider rewriting this to be independent of []ast.Expr // so that we can use the algorithm for any kind of list // (e.g., pass list via a channel over which to range). -func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos) { +func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) { if len(list) == 0 { + if isIncomplete { + prev := p.posFor(prev0) + next := p.posFor(next0) + if prev.IsValid() && prev.Line == next.Line { + p.print("/* " + filteredMsg + " */") + } else { + p.print(newline) + p.print(indent, "// "+filteredMsg, unindent, newline) + } + } return } @@ -142,6 +154,9 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp } p.expr0(x, depth) } + if isIncomplete { + p.print(token.COMMA, blank, "/* "+filteredMsg+" */") + } return } @@ -272,6 +287,10 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line { // Print a terminating comma if the next token is on a new line. p.print(token.COMMA) + if isIncomplete { + p.print(newline) + p.print("// " + filteredMsg) + } if ws == ignore && mode&noIndent == 0 { // unindent if we indented p.print(unindent) @@ -280,6 +299,11 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp return } + if isIncomplete { + p.print(token.COMMA, newline) + p.print("// "+filteredMsg, newline) + } + if ws == ignore && mode&noIndent == 0 { // unindent if we indented p.print(unindent) @@ -499,7 +523,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) p.print(formfeed) } p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment - p.setLineComment("// contains filtered or unexported fields") + p.setLineComment("// " + filteredMsg) } } else { // interface @@ -851,13 +875,13 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { } p.print(x.Lparen, token.LPAREN) if x.Ellipsis.IsValid() { - p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis) + p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false) p.print(x.Ellipsis, token.ELLIPSIS) if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) { p.print(token.COMMA, formfeed) } } else { - p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen) + p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false) } p.print(x.Rparen, token.RPAREN) if wasIndented { @@ -871,7 +895,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { } p.level++ p.print(x.Lbrace, token.LBRACE) - p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace) + p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace, x.Incomplete) // do not insert extra line break following a /*-style comment // before the closing '}' as it might break the code if there // is no trailing ',' @@ -1181,9 +1205,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { if len(s.Lhs) > 1 && len(s.Rhs) > 1 { depth++ } - p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos) + p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos, false) p.print(blank, s.TokPos, s.Tok, blank) - p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos) + p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos, false) case *ast.GoStmt: p.print(token.GO, blank) @@ -1204,10 +1228,10 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { // lead to more nicely formatted code in general. if p.indentList(s.Results) { p.print(indent) - p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos) + p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos, false) p.print(unindent) } else { - p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos) + p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos, false) } } @@ -1243,7 +1267,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { case *ast.CaseClause: if s.List != nil { p.print(token.CASE, blank) - p.exprList(s.Pos(), s.List, 1, 0, s.Colon) + p.exprList(s.Pos(), s.List, 1, 0, s.Colon, false) } else { p.print(token.DEFAULT) } @@ -1395,7 +1419,7 @@ func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) { } if s.Values != nil { p.print(vtab, token.ASSIGN, blank) - p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos) + p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false) extraTabs-- } if s.Comment != nil { @@ -1477,7 +1501,7 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) { } if s.Values != nil { p.print(blank, token.ASSIGN, blank) - p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos) + p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false) } p.setComment(s.Comment) diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go index 43bdc239b6..27d46df6b1 100644 --- a/src/go/printer/printer_test.go +++ b/src/go/printer/printer_test.go @@ -195,6 +195,7 @@ var data = []entry{ {"declarations.input", "declarations.golden", 0}, {"statements.input", "statements.golden", 0}, {"slow.input", "slow.golden", idempotent}, + {"complit.input", "complit.x", export}, } func TestFiles(t *testing.T) { diff --git a/src/go/printer/testdata/complit.input b/src/go/printer/testdata/complit.input new file mode 100644 index 0000000000..82806a4233 --- /dev/null +++ b/src/go/printer/testdata/complit.input @@ -0,0 +1,65 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package complit + +var ( + // Multi-line declarations + V1 = T{ + F1: "hello", + f2: 1, + } + V2 = T{ + f2: 1, + F1: "hello", + } + V3 = T{ + F1: "hello", + F2: T2{ + A: "world", + b: "hidden", + }, + f3: T2{ + A: "world", + }, + } + V4 = T{ + f2: 1, + } + + // Single-line declarations + V5 = T{F1: "hello", f2: 1} + V6 = T{f2: 1, F1: "hello"} + V7 = T{f2: 1} + + // Mixed-mode declarations + V8 = T{ + F1: "hello", f2: 1, + F3: "world", + f4: 2} + V9 = T{ + f2: 1, F1: "hello",} + V10 = T{ + F1: "hello", f2: 1, + f3: 2, + F4: "world", f5: 3, + } + + // Other miscellaneous declarations + V11 = T{ + t{ + A: "world", + b: "hidden", + }, + f2: t{ + A: "world", + b: "hidden", + }, + } + V12 = T{ + F1: make(chan int), + f2: []int{}, + F3: make(map[int]string), f4: 1, + } +) \ No newline at end of file diff --git a/src/go/printer/testdata/complit.x b/src/go/printer/testdata/complit.x new file mode 100644 index 0000000000..458ac6117a --- /dev/null +++ b/src/go/printer/testdata/complit.x @@ -0,0 +1,62 @@ +package complit + +var ( + // Multi-line declarations + V1 = T{ + F1: "hello", + // contains filtered or unexported fields + } + V2 = T{ + + F1: "hello", + // contains filtered or unexported fields + } + V3 = T{ + F1: "hello", + F2: T2{ + A: "world", + // contains filtered or unexported fields + }, + // contains filtered or unexported fields + } + V4 = T{ + // contains filtered or unexported fields + } + + // Single-line declarations + V5 = T{F1: "hello", /* contains filtered or unexported fields */} + V6 = T{F1: "hello", /* contains filtered or unexported fields */} + V7 = T{/* contains filtered or unexported fields */} + + // Mixed-mode declarations + V8 = T{ + F1: "hello", + F3: "world", + // contains filtered or unexported fields + } + V9 = T{ + F1: "hello", + // contains filtered or unexported fields + } + V10 = T{ + F1: "hello", + + F4: "world", + // contains filtered or unexported fields + } + + // Other miscellaneous declarations + V11 = T{ + t{ + A: "world", + // contains filtered or unexported fields + }, + // contains filtered or unexported fields + } + V12 = T{ + F1: make(chan int), + + F3: make(map[int]string), + // contains filtered or unexported fields + } +) -- 2.50.0