]> Cypherpunks repositories - gostls13.git/commitdiff
go/ast: hide unexported fields in composite literals
authorAgniva De Sarker <agnivade@yahoo.co.in>
Wed, 4 Apr 2018 09:29:27 +0000 (14:59 +0530)
committerRobert Griesemer <gri@golang.org>
Tue, 1 May 2018 19:53:32 +0000 (19:53 +0000)
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: Robert Griesemer <gri@golang.org>
src/go/ast/ast.go
src/go/ast/filter.go
src/go/doc/exports.go
src/go/printer/nodes.go
src/go/printer/printer_test.go
src/go/printer/testdata/complit.input [new file with mode: 0644]
src/go/printer/testdata/complit.x [new file with mode: 0644]

index c07dd5a91ee4e5abc7ef9b8f9431f503b6c12fa7..d73c323178dbd7b64d4009d3d44da75ea8848890 100644 (file)
@@ -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.
index bb571166f47209bd738514ac231db83150336766..32352cb92c56d2a66863bb9df4004da0ef14be81 100644 (file)
@@ -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)
index ccdefcb967b814df50bc7dd887dd17c459190814..5f99bf7772c175e8376833337605e6578ed4b709 100644 (file)
@@ -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
index 52284dafe2ae47823588030ac1936a2c23df09d3..18f2371d24b545b0f582ac2548fb5d9fc3aa5481 100644 (file)
@@ -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)
 
index 43bdc239b63fa27ee7dfd17d0beb1c23aebd1a30..27d46df6b186bcc6e313ea980573b20fc35d5f5a 100644 (file)
@@ -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 (file)
index 0000000..82806a4
--- /dev/null
@@ -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 (file)
index 0000000..458ac61
--- /dev/null
@@ -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
+       }
+)