Repository: bombsimon/wsl (go)

Issue:
Correct Example syntax triggering "block should not end with a whitespace (or comment)"
[Example](https://golang.org/pkg/testing/#hdr-Examples) functions must end with comments, but wsl doesn't like that very much:

Here's a snippet that's failing for me in [go-which](https://github.com/hairyhenderson/go-which):

```go
func ExampleWhich() {
        path := Which("sh")
        fmt.Printf("Found sh at: %s", path)

        // Output: Found sh at: /bin/sh
}
```

```console
$ wsl which_test.go 
which_test.go:124: block should not end with a whitespace (or comment)
```

I'm thinking wsl should probably ignore this particular check in `Example` tests.

Pull request:
Allow comments at end of Example tests
Fixes #38 

I'm not sure if this is the cleanest way to fix it, but it was the most direct path I could hack together over a few minutes 😉

Signed-off-by: Dave Henderson <dhenderson@gmail.com>

Patch:
diff --git a/wsl.go b/wsl.go
index 9bc0fe2..fe62b62 100644
--- a/wsl.go
+++ b/wsl.go
@@ -7,6 +7,7 @@ import (
 	"go/token"
 	"io/ioutil"
 	"reflect"
+	"strings"
 )
 
 type Configuration struct {
@@ -172,7 +173,7 @@ func (p *Processor) process(filename string, data []byte) {
 	for _, d := range p.file.Decls {
 		switch v := d.(type) {
 		case *ast.FuncDecl:
-			p.parseBlockBody(v.Body)
+			p.parseBlockBody(v.Name, v.Body)
 		case *ast.GenDecl:
 			// `go fmt` will handle proper spacing for GenDecl such as imports,
 			// constants etc.
@@ -184,14 +185,14 @@ func (p *Processor) process(filename string, data []byte) {
 
 // parseBlockBody will parse any kind of block statements such as switch cases
 // and if statements. A list of Result is returned.
-func (p *Processor) parseBlockBody(block *ast.BlockStmt) {
+func (p *Processor) parseBlockBody(ident *ast.Ident, block *ast.BlockStmt) {
 	// Nothing to do if there's no value.
 	if reflect.ValueOf(block).IsNil() {
 		return
 	}
 
 	// Start by finding leading and trailing whitespaces.
-	p.findLeadingAndTrailingWhitespaces(block, nil)
+	p.findLeadingAndTrailingWhitespaces(ident, block, nil)
 
 	// Parse the block body contents.
 	p.parseBlockStatements(block.List)
@@ -207,7 +208,7 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
 		if as, isAssignStmt := stmt.(*ast.AssignStmt); isAssignStmt {
 			for _, rhs := range as.Rhs {
 				if fl, isFuncLit := rhs.(*ast.FuncLit); isFuncLit {
-					p.parseBlockBody(fl.Body)
+					p.parseBlockBody(nil, fl.Body)
 				}
 			}
 		}
@@ -556,7 +557,7 @@ func (p *Processor) firstBodyStatement(i int, allStmt []ast.Stmt) ast.Node {
 			}
 		}
 
-		p.parseBlockBody(statementBodyContent)
+		p.parseBlockBody(nil, statementBodyContent)
 	case []ast.Stmt:
 		// The Body field for an *ast.CaseClause or *ast.CommClause is of type
 		// []ast.Stmt. We must check leading and trailing whitespaces and then
@@ -569,7 +570,7 @@ func (p *Processor) firstBodyStatement(i int, allStmt []ast.Stmt) ast.Node {
 			nextStatement = allStmt[i+1]
 		}
 
-		p.findLeadingAndTrailingWhitespaces(stmt, nextStatement)
+		p.findLeadingAndTrailingWhitespaces(nil, stmt, nextStatement)
 		p.parseBlockStatements(statementBodyContent)
 	default:
 		p.addWarning(
@@ -773,7 +774,7 @@ func atLeastOneInListsMatch(listOne, listTwo []string) bool {
 // in a node. The method takes comments in consideration which will make the
 // parser more gentle.
 // nolint: gocognit
-func (p *Processor) findLeadingAndTrailingWhitespaces(stmt, nextStatement ast.Node) {
+func (p *Processor) findLeadingAndTrailingWhitespaces(ident *ast.Ident, stmt, nextStatement ast.Node) {
 	var (
 		allowedLinesBeforeFirstStatement = 1
 		commentMap                       = ast.NewCommentMap(p.fileSet, stmt, p.file.Comments)
@@ -876,7 +877,7 @@ func (p *Processor) findLeadingAndTrailingWhitespaces(stmt, nextStatement ast.No
 		blockEndLine = p.fileSet.Position(blockEndPos).Line
 	}
 
-	if p.nodeEnd(lastStatement) != blockEndLine-1 {
+	if p.nodeEnd(lastStatement) != blockEndLine-1 && !isExampleFunc(ident) {
 		p.addError(
 			blockEndPos,
 			"block should not end with a whitespace (or comment)",
@@ -884,6 +885,10 @@ func (p *Processor) findLeadingAndTrailingWhitespaces(stmt, nextStatement ast.No
 	}
 }
 
+func isExampleFunc(ident *ast.Ident) bool {
+	return ident != nil && strings.HasPrefix(ident.Name, "Example")
+}
+
 func (p *Processor) nodeStart(node ast.Node) int {
 	return p.fileSet.Position(node.Pos()).Line
 }


Test patch:
diff --git a/wsl_test.go b/wsl_test.go
index dcd2927..9dbbb2c 100644
--- a/wsl_test.go
+++ b/wsl_test.go
@@ -125,6 +125,21 @@ func TestShouldRemoveEmptyLines(t *testing.T) {
 				}
 			}`),
 		},
+		{
+			description: "Example tests parsed correct when ending with comments",
+			code: []byte(`package foo
+
+			func ExampleFoo() {
+				fmt.Println("hello world")
+
+				// Output: hello world
+			}
+
+			func ExampleBar() {
+				fmt.Println("hello world")
+				// Output: hello world
+			}`),
+		},
 		{
 			description: "whitespaces parsed correctly in case blocks",
 			code: []byte(`package main