Repository: VKCOM/noverify
Pull request #666 (2020-08-13 06:28:01)

linter: move FileIsAutoGenerated to workspace pkg
This is done mostly for the convenient code reuse.
I need that function outside of the linter package.

Exporting FileIsAutoGenerated from linter package looks
like a misuse and bad structure (we already have a lot
of unrelated things located there).

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>


Patch:
diff --git a/src/linter/root.go b/src/linter/root.go
index 7804c91..fe46a42 100644
--- a/src/linter/root.go
+++ b/src/linter/root.go
@@ -24,6 +24,7 @@ import (
 	"github.com/VKCOM/noverify/src/solver"
 	"github.com/VKCOM/noverify/src/state"
 	"github.com/VKCOM/noverify/src/vscode"
+	"github.com/VKCOM/noverify/src/workspace"
 )
 
 const (
@@ -122,43 +123,7 @@ func (d *RootWalker) InitFromParser(contents []byte, parser *php7.Parser) {
 	d.fileContents = contents
 	d.LinesPositions = linesPositions
 	d.Lines = lines
-	d.autoGenerated = d.fileIsAutoGenerated(lines)
-}
-
-func (d *RootWalker) fileIsAutoGenerated(lines [][]byte) bool {
-	// Since we don't have a separate comments list, it's easier
-	// to check few leading lines manually. We might want to have a
-	// more reliable way to handle comments in future.
-	// See #112.
-	maxLinesPeek := 10
-	if len(lines) < maxLinesPeek {
-		maxLinesPeek = len(lines)
-	}
-	doNotEdit := false
-	autoGenerated := false
-	for _, l := range lines[:maxLinesPeek] {
-		s := strings.ToLower(string(l))
-		looksLikeComment := strings.HasPrefix(s, "//") ||
-			strings.HasPrefix(s, "/*") ||
-			strings.HasPrefix(s, " *")
-		if !looksLikeComment {
-			continue
-		}
-
-		if strings.Contains(s, "do not edit") {
-			doNotEdit = true
-		}
-		if strings.Contains(s, "auto-generated") ||
-			strings.Contains(s, "autogenerated") ||
-			strings.Contains(s, "generated by") {
-			autoGenerated = true
-		}
-
-		if doNotEdit && autoGenerated {
-			return true
-		}
-	}
-	return false
+	d.autoGenerated = workspace.FileIsAutoGenerated(contents)
 }
 
 // InitCustom is needed to initialize walker state
diff --git a/src/workspace/files.go b/src/workspace/files.go
index 1644beb..e6ed19a 100644
--- a/src/workspace/files.go
+++ b/src/workspace/files.go
@@ -1,9 +1,11 @@
 package workspace
 
 import (
+	"bytes"
 	"log"
 	"os"
 	"path/filepath"
+	"strings"
 
 	"github.com/VKCOM/noverify/src/git"
 	"github.com/karrick/godirwalk"
@@ -17,6 +19,46 @@ type FileInfo struct {
 	LineRanges []git.LineRange
 }
 
+func FileIsAutoGenerated(contents []byte) bool {
+	src := contents
+	maxLinesPeek := 15
+
+	doNotEdit := false
+	autoGenerated := false
+
+	for line := 0; line < maxLinesPeek; line++ {
+		if doNotEdit && autoGenerated {
+			return true
+		}
+		newline := bytes.IndexByte(src, '\n')
+		if newline == -1 {
+			break
+		}
+
+		l := strings.ToLower(string(src[:newline]))
+		src = src[newline+len("\n"):]
+
+		looksLikeComment := strings.HasPrefix(l, "//") ||
+			strings.HasPrefix(l, "/*") ||
+			strings.HasPrefix(l, " *")
+		if !looksLikeComment {
+			continue
+		}
+
+		doNotEdit = doNotEdit ||
+			strings.Contains(l, "do not edit") ||
+			strings.Contains(l, "don't edit") ||
+			strings.Contains(l, "do not modify") ||
+			strings.Contains(l, "don't modify")
+		autoGenerated = autoGenerated ||
+			strings.Contains(l, "auto-generated") ||
+			strings.Contains(l, "autogenerated") ||
+			strings.Contains(l, "generated by")
+	}
+
+	return doNotEdit && autoGenerated
+}
+
 // ReadFilenames returns callback that reads filenames into channel
 func ReadFilenames(filenames []string, filter *FilenameFilter) ReadCallback {
 	return func(ch chan FileInfo) {


Test patch:
diff --git a/src/workspace/files_test.go b/src/workspace/files_test.go
new file mode 100644
index 0000000..edeb8d4
--- /dev/null
+++ b/src/workspace/files_test.go
@@ -0,0 +1,91 @@
+package workspace
+
+import (
+	"testing"
+)
+
+func TestFileIsAutoGenerated(t *testing.T) {
+	tests := []struct {
+		autogen  bool
+		contents string
+	}{
+		{
+			autogen: true,
+			contents: `<?php
+/** file is auto-generated; do not edit! */
+`,
+		},
+		{
+			autogen: true,
+			contents: `<?php
+
+// THIS FILE IS GENERATED BY codegen.php
+// DON'T EDIT IT MANUALLY
+`,
+		},
+		{
+			autogen: true,
+			contents: `<?php
+
+// Copyright 2020 The NoVerify Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// AUTOGENERATED, DO NOT EDIT!
+`,
+		},
+		{
+			autogen: true,
+			contents: `<?php
+/**
+ * Foo.php
+ * Generated by codegen.php, don't modify this file directly!
+ */
+`,
+		},
+
+		{
+			autogen:  false,
+			contents: ``,
+		},
+
+		// Only "autogen" part.
+		{
+			autogen: false,
+			contents: `<?php
+// This file is auto-generated, edit it as you need.
+
+class PageStub {
+  public function __construct() {}
+}
+`,
+		},
+		// Only "don't edit" part.
+		{
+			autogen: false,
+			contents: `<?php
+// Do not edit this file if you don't know what you're doing.
+
+function checkRights() { return true; }
+`,
+		},
+		// Both parts, but not inside a comment.
+		{
+			autogen: false,
+			contents: `<?php
+
+$header = "Generated by codegen.php, don't modify this file directly!";
+
+class Foo {}
+`,
+		},
+	}
+
+	for _, test := range tests {
+		autogen := FileIsAutoGenerated([]byte(test.contents))
+		if autogen != test.autogen {
+			t.Errorf("result mismatch: have %v, want %v\ninput: %s",
+				autogen, test.autogen, test.contents)
+		}
+	}
+}