Commit c2366b27 authored by Agniva De Sarker's avatar Agniva De Sarker Committed by Robert Griesemer

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 <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 548e1f89
......@@ -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.
......
......@@ -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)
......
......@@ -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
......
......@@ -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)
......
......@@ -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) {
......
// 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
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
}
)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment