summaryrefslogtreecommitdiff
path: root/pkgtools
diff options
context:
space:
mode:
authorrillig <rillig@pkgsrc.org>2019-09-08 22:47:47 +0000
committerrillig <rillig@pkgsrc.org>2019-09-08 22:47:47 +0000
commit16373ff0777a2b84caca8f1a6e9f6d87e922d00d (patch)
treebaa90ce7eba145730685f911ba6a030599bb6b1b /pkgtools
parent82ca01b68c17584dee09ef5a8f2a29eaaf3c2f1d (diff)
downloadpkgsrc-16373ff0777a2b84caca8f1a6e9f6d87e922d00d.tar.gz
pkgtools/pkglint: update to 5.7.23
Changes since 5.7.22: * Added a warning for lines that look empty but are actually follow-up lines from a previous line. * Added notes for unusual placement of the continuation backslash. It should always be preceded by a single space or tab, or be in column 73. * Improved check for needlessly complicated !empty(PKGPATH:Mcat/pkg) that is transformed into the simpler ${PKGPATH} == cat/pkg, even if the package name contains hyphens, dots, plus or slashes. * Added check for the _VARGROUPS section since that section contains many redundancies that can easily be checked. For example, in mk/compiler/gcc.mk the _VARGROUPS section is 67 lines long and contains far more than 100 variables. It's tedious to manually check this file for internal consistency. That's better left to pkglint. * The empty variable is no longer flagged as "used but not defined". It appears in expressions like ${:Ustring}. * When --source is combined with --explain, don't unnecessarily repeat the source code for a single line if there are several diagnostics. Instead, even omit the empty line between the diagnostics for the same line.
Diffstat (limited to 'pkgtools')
-rw-r--r--pkgtools/pkglint/Makefile4
-rw-r--r--pkgtools/pkglint/PLIST4
-rw-r--r--pkgtools/pkglint/files/autofix.go22
-rw-r--r--pkgtools/pkglint/files/autofix_test.go2
-rw-r--r--pkgtools/pkglint/files/category_test.go25
-rw-r--r--pkgtools/pkglint/files/lines.go6
-rw-r--r--pkgtools/pkglint/files/lines_test.go1
-rw-r--r--pkgtools/pkglint/files/logging.go46
-rw-r--r--pkgtools/pkglint/files/logging_test.go144
-rw-r--r--pkgtools/pkglint/files/mkline_test.go29
-rw-r--r--pkgtools/pkglint/files/mklinechecker.go100
-rw-r--r--pkgtools/pkglint/files/mklinechecker_test.go20
-rw-r--r--pkgtools/pkglint/files/mklines.go3
-rw-r--r--pkgtools/pkglint/files/mkparser.go6
-rw-r--r--pkgtools/pkglint/files/mkparser_test.go21
-rw-r--r--pkgtools/pkglint/files/package.go5
-rw-r--r--pkgtools/pkglint/files/package_test.go39
-rw-r--r--pkgtools/pkglint/files/pkglint.go1
-rw-r--r--pkgtools/pkglint/files/pkglint_test.go2
-rw-r--r--pkgtools/pkglint/files/pkgsrc.go9
-rw-r--r--pkgtools/pkglint/files/shell.go1
-rw-r--r--pkgtools/pkglint/files/tools.go2
-rw-r--r--pkgtools/pkglint/files/util.go38
-rw-r--r--pkgtools/pkglint/files/util_test.go9
-rw-r--r--pkgtools/pkglint/files/varalignblock.go600
-rw-r--r--pkgtools/pkglint/files/varalignblock_test.go1728
-rw-r--r--pkgtools/pkglint/files/vardefs.go14
-rw-r--r--pkgtools/pkglint/files/vargroups.go246
-rw-r--r--pkgtools/pkglint/files/vargroups_test.go142
29 files changed, 2205 insertions, 1064 deletions
diff --git a/pkgtools/pkglint/Makefile b/pkgtools/pkglint/Makefile
index 56b31ec2d89..90428517451 100644
--- a/pkgtools/pkglint/Makefile
+++ b/pkgtools/pkglint/Makefile
@@ -1,6 +1,6 @@
-# $NetBSD: Makefile,v 1.595 2019/08/25 21:47:11 rillig Exp $
+# $NetBSD: Makefile,v 1.596 2019/09/08 22:47:47 rillig Exp $
-PKGNAME= pkglint-5.7.22
+PKGNAME= pkglint-5.7.23
CATEGORIES= pkgtools
DISTNAME= tools
MASTER_SITES= ${MASTER_SITE_GITHUB:=golang/}
diff --git a/pkgtools/pkglint/PLIST b/pkgtools/pkglint/PLIST
index bf3bf592960..e5bc128d6f4 100644
--- a/pkgtools/pkglint/PLIST
+++ b/pkgtools/pkglint/PLIST
@@ -1,4 +1,4 @@
-@comment $NetBSD: PLIST,v 1.13 2019/07/14 21:25:47 rillig Exp $
+@comment $NetBSD: PLIST,v 1.14 2019/09/08 22:47:47 rillig Exp $
bin/pkglint
gopkg/pkg/${GO_PLATFORM}/netbsd.org/pkglint.a
gopkg/pkg/${GO_PLATFORM}/netbsd.org/pkglint/getopt.a
@@ -116,6 +116,8 @@ gopkg/src/netbsd.org/pkglint/varalignblock.go
gopkg/src/netbsd.org/pkglint/varalignblock_test.go
gopkg/src/netbsd.org/pkglint/vardefs.go
gopkg/src/netbsd.org/pkglint/vardefs_test.go
+gopkg/src/netbsd.org/pkglint/vargroups.go
+gopkg/src/netbsd.org/pkglint/vargroups_test.go
gopkg/src/netbsd.org/pkglint/vartype.go
gopkg/src/netbsd.org/pkglint/vartype_test.go
gopkg/src/netbsd.org/pkglint/vartypecheck.go
diff --git a/pkgtools/pkglint/files/autofix.go b/pkgtools/pkglint/files/autofix.go
index 936f09f3611..d31717849f3 100644
--- a/pkgtools/pkglint/files/autofix.go
+++ b/pkgtools/pkglint/files/autofix.go
@@ -127,7 +127,7 @@ func (fix *Autofix) ReplaceAfter(prefix, from string, to string) {
// ReplaceAt replaces the text "from" with "to", a single time.
// But only if the text at the given position is indeed "from".
-func (fix *Autofix) ReplaceAt(rawIndex int, column int, from string, to string) {
+func (fix *Autofix) ReplaceAt(rawIndex int, textIndex int, from string, to string) {
assert(from != to)
fix.assertRealLine()
@@ -136,11 +136,11 @@ func (fix *Autofix) ReplaceAt(rawIndex int, column int, from string, to string)
}
rawLine := fix.line.raw[rawIndex]
- if column >= len(rawLine.textnl) || !hasPrefix(rawLine.textnl[column:], from) {
+ if textIndex >= len(rawLine.textnl) || !hasPrefix(rawLine.textnl[textIndex:], from) {
return
}
- replaced := rawLine.textnl[:column] + to + rawLine.textnl[column+len(from):]
+ replaced := rawLine.textnl[:textIndex] + to + rawLine.textnl[textIndex+len(from):]
if G.Logger.IsAutofix() {
rawLine.textnl = replaced
@@ -328,7 +328,6 @@ func (fix *Autofix) Apply() {
fix.modified = true
}
- // Reduce number of calls to runtime.writeBarrier.
fix.autofixShortTerm = autofixShortTerm{}
}
@@ -364,20 +363,11 @@ func (fix *Autofix) Apply() {
}
G.Logger.Logf(AutofixLogLevel, line.Filename, lineno, AutofixFormat, action.description)
}
+ G.Logger.showSource(line)
}
- if logDiagnostic || logFix {
- if logFix {
- G.Logger.showSource(line)
- }
- if logDiagnostic && len(fix.explanation) > 0 {
- line.Explain(fix.explanation...)
- }
- if G.Logger.Opts.ShowSource {
- if !(G.Logger.Opts.Explain && logDiagnostic && len(fix.explanation) > 0) {
- G.Logger.out.Separate()
- }
- }
+ if logDiagnostic && len(fix.explanation) > 0 {
+ line.Explain(fix.explanation...)
}
reset()
diff --git a/pkgtools/pkglint/files/autofix_test.go b/pkgtools/pkglint/files/autofix_test.go
index 321f8bd6199..f336d9292c3 100644
--- a/pkgtools/pkglint/files/autofix_test.go
+++ b/pkgtools/pkglint/files/autofix_test.go
@@ -966,8 +966,6 @@ func (s *Suite) Test_Autofix_Apply__explanation_followed_by_note(c *check.C) {
t.CheckOutputLines(
">\ttext",
"WARN: README.txt:123: A warning with autofix.",
- "",
- ">\ttext",
"NOTE: README.txt:123: A note without fix.")
}
diff --git a/pkgtools/pkglint/files/category_test.go b/pkgtools/pkglint/files/category_test.go
index fa1e6a104f4..e279d41fee0 100644
--- a/pkgtools/pkglint/files/category_test.go
+++ b/pkgtools/pkglint/files/category_test.go
@@ -343,3 +343,28 @@ func (s *Suite) Test_CheckdirCategory__no_Makefile(c *check.C) {
t.CheckOutputLines(
"ERROR: ~/category/Makefile: Cannot be read.")
}
+
+func (s *Suite) Test_CheckdirCategory__case_mismatch(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpPkgsrc()
+ t.CreateFileLines("mk/misc/category.mk")
+ t.CreateFileLines("category/p5-Net-DNS/Makefile")
+ t.CreateFileLines("category/Makefile",
+ MkCvsID,
+ "",
+ "COMMENT=\tCategory comment",
+ "",
+ "SUBDIR+=\tp5-net-dns",
+ "",
+ ".include \"../mk/misc/category.mk\"")
+ t.FinishSetUp()
+
+ G.Check(t.File("category"))
+
+ t.CheckOutputLines(
+ "ERROR: ~/category/Makefile:5: \"p5-Net-DNS\" "+
+ "exists in the file system but not in the Makefile.",
+ "ERROR: ~/category/Makefile:5: \"p5-net-dns\" "+
+ "exists in the Makefile but not in the file system.")
+}
diff --git a/pkgtools/pkglint/files/lines.go b/pkgtools/pkglint/files/lines.go
index 77aef21e675..7d76fb318ea 100644
--- a/pkgtools/pkglint/files/lines.go
+++ b/pkgtools/pkglint/files/lines.go
@@ -42,7 +42,11 @@ func (ls *Lines) CheckCvsID(index int, prefixRe regex.Pattern, suggestedPrefix s
line := ls.Lines[index]
if m, expanded := line.IsCvsID(prefixRe); m {
- if G.Testing && G.Wip && expanded {
+ // This check is considered experimental because it produces a few
+ // thousand notes and doesn't really affect the functionality of
+ // the packages. The worst thing that might happen is that a file
+ // looks older than it really is.
+ if G.Experimental && G.Wip && expanded {
fix := line.Autofix()
fix.Notef("Expected exactly %q.", suggestedPrefix+"$"+"NetBSD$")
fix.Explain(
diff --git a/pkgtools/pkglint/files/lines_test.go b/pkgtools/pkglint/files/lines_test.go
index 3d105d74cd9..a065bd030b4 100644
--- a/pkgtools/pkglint/files/lines_test.go
+++ b/pkgtools/pkglint/files/lines_test.go
@@ -30,6 +30,7 @@ func (s *Suite) Test_Lines_CheckCvsID(c *check.C) {
func (s *Suite) Test_Lines_CheckCvsID__wip(c *check.C) {
t := s.Init(c)
+ G.Experimental = true
t.SetUpPkgsrc()
t.SetUpPackage("wip/package")
t.CreateFileLines("wip/package/file1.mk",
diff --git a/pkgtools/pkglint/files/logging.go b/pkgtools/pkglint/files/logging.go
index ea1cbdb5cd5..53bc5219a20 100644
--- a/pkgtools/pkglint/files/logging.go
+++ b/pkgtools/pkglint/files/logging.go
@@ -16,6 +16,7 @@ type Logger struct {
suppressDiag bool
suppressExpl bool
+ prevLine *Line
logged Once
explained Once
@@ -98,8 +99,16 @@ func (l *Logger) Explain(explanation ...string) {
return
}
+ // The explanation should fit nicely on a screen that is 80
+ // characters wide. The explanation is indented using a tab, and
+ // there should be a little margin at the right. The resulting
+ // number comes remarkably close to the line width recommended
+ // by typographers, which is 66.
+ const explanationWidth = 80 - 8 - 4
+
+ l.prevLine = nil
l.out.Separate()
- wrapped := wrap(68, explanation...)
+ wrapped := wrap(explanationWidth, explanation...)
for _, explanationLine := range wrapped {
if explanationLine != "" {
l.out.Write("\t")
@@ -114,6 +123,10 @@ func (l *Logger) ShowSummary() {
return
}
+ if l.Opts.ShowSource {
+ l.out.Separate()
+ }
+
if l.errors != 0 || l.warnings != 0 {
num := func(n int, singular, plural string) string {
if n == 0 {
@@ -187,12 +200,13 @@ func (l *Logger) Diag(line *Line, level *LogLevel, format string, args ...interf
}
if l.Opts.ShowSource {
+ if !l.IsAutofix() && line != l.prevLine && level != Fatal {
+ l.out.Separate()
+ }
l.showSource(line)
- l.Logf(level, filename, linenos, format, msg)
- l.out.Separate()
- } else {
- l.Logf(level, filename, linenos, format, msg)
}
+
+ l.Logf(level, filename, linenos, format, msg)
}
func (l *Logger) showSource(line *Line) {
@@ -200,6 +214,13 @@ func (l *Logger) showSource(line *Line) {
return
}
+ if !l.IsAutofix() {
+ if line == l.prevLine {
+ return
+ }
+ l.prevLine = line
+ }
+
out := l.out
writeLine := func(prefix, line string) {
out.Write(prefix)
@@ -229,6 +250,9 @@ func (l *Logger) showSource(line *Line) {
}
}
+ if !l.IsAutofix() {
+ l.out.Separate()
+ }
if line.autofix != nil {
for _, before := range line.autofix.linesBefore {
writeLine("+\t", before)
@@ -240,6 +264,9 @@ func (l *Logger) showSource(line *Line) {
} else {
printDiff(line.raw)
}
+ if l.IsAutofix() {
+ l.out.Separate()
+ }
}
func (l *Logger) Logf(level *LogLevel, filename, lineno, format, msg string) {
@@ -324,7 +351,7 @@ type SeparatorWriter struct {
}
func NewSeparatorWriter(out io.Writer) *SeparatorWriter {
- return &SeparatorWriter{out: out}
+ return &SeparatorWriter{out: out, state: 3}
}
func (wr *SeparatorWriter) WriteLine(text string) {
@@ -339,11 +366,10 @@ func (wr *SeparatorWriter) Write(text string) {
}
// Separate remembers to output an empty line before the next character.
-// If the writer is currently in the middle of a line, that line is terminated immediately.
+//
+// The writer must not be in the middle of a line.
func (wr *SeparatorWriter) Separate() {
- if wr.state == 1 {
- wr.write('\n')
- }
+ assert(wr.state != 1)
if wr.state < 2 {
wr.state = 2
}
diff --git a/pkgtools/pkglint/files/logging_test.go b/pkgtools/pkglint/files/logging_test.go
index 8b8f66e04b4..835c448749c 100644
--- a/pkgtools/pkglint/files/logging_test.go
+++ b/pkgtools/pkglint/files/logging_test.go
@@ -206,9 +206,107 @@ func (s *Suite) Test_Logger__show_source_separator(c *check.C) {
"",
">\tThe third line",
"WARN: ~/DESCR:3: Dummy warning.",
+ "WARN: ~/DESCR:3: Using \"third\" is deprecated.")
+}
+
+func (s *Suite) Test_Logger__show_source_with_explanation(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpCommandLine("--source", "--explain")
+ lines := t.SetUpFileLines("DESCR",
+ "The first line",
+ "The second line",
+ "The third line")
+
+ fix := lines.Lines[1].Autofix()
+ fix.Warnf("Using \"second\" is deprecated.")
+ fix.Explain("Explanation 1.")
+ fix.Replace("second", "silver medal")
+ fix.Apply()
+
+ lines.Lines[2].Warnf("Dummy warning.")
+
+ fix = lines.Lines[2].Autofix()
+ fix.Warnf("Using \"third\" is deprecated.")
+ fix.Explain("Explanation 2.")
+ fix.Replace("third", "bronze medal")
+ fix.Apply()
+
+ t.CheckOutputLines(
+ ">\tThe second line",
+ "WARN: ~/DESCR:2: Using \"second\" is deprecated.",
+ "",
+ "\tExplanation 1.",
"",
">\tThe third line",
- "WARN: ~/DESCR:3: Using \"third\" is deprecated.")
+ "WARN: ~/DESCR:3: Dummy warning.",
+ "WARN: ~/DESCR:3: Using \"third\" is deprecated.",
+ "",
+ "\tExplanation 2.",
+ "")
+}
+
+// In general, it is not necessary to repeat the source code for a line
+// if there are several diagnostics for the same line. In this case though,
+// there is an explanation between the diagnostics, and because it may get
+// quite long, it's better to repeat the source code once again.
+func (s *Suite) Test_Logger__show_source_with_explanation_in_same_line(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpCommandLine("--source", "--explain")
+ lines := t.SetUpFileLines("DESCR",
+ "The first line")
+
+ fix := lines.Lines[0].Autofix()
+ fix.Warnf("Using \"The\" is deprecated.")
+ fix.Explain("Explanation 1.")
+ fix.Replace("The", "A")
+ fix.Apply()
+
+ fix.Warnf("Using \"first\" is deprecated.")
+ fix.Explain("Explanation 2.")
+ fix.Replace("first", "1st")
+ fix.Apply()
+
+ t.CheckOutputLines(
+ ">\tThe first line",
+ "WARN: ~/DESCR:1: Using \"The\" is deprecated.",
+ "",
+ "\tExplanation 1.",
+ "",
+ ">\tThe first line",
+ "WARN: ~/DESCR:1: Using \"first\" is deprecated.",
+ "",
+ "\tExplanation 2.",
+ "")
+}
+
+// When there is no explanation after the first diagnostic, it is not
+// necessary to repeat the source code again for the second diagnostic.
+func (s *Suite) Test_Logger__show_source_without_explanation_in_same_line(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpCommandLine("--source", "--explain")
+ lines := t.SetUpFileLines("DESCR",
+ "The first line")
+
+ fix := lines.Lines[0].Autofix()
+ fix.Warnf("Using \"The\" is deprecated.")
+ fix.Replace("The", "A")
+ fix.Apply()
+
+ fix.Warnf("Using \"first\" is deprecated.")
+ fix.Explain("Explanation 2.")
+ fix.Replace("first", "1st")
+ fix.Apply()
+
+ t.CheckOutputLines(
+ ">\tThe first line",
+ "WARN: ~/DESCR:1: Using \"The\" is deprecated.",
+ "WARN: ~/DESCR:1: Using \"first\" is deprecated.",
+ "",
+ "\tExplanation 2.",
+ "")
}
// When the --show-autofix option is given, the warning is shown first,
@@ -250,6 +348,46 @@ func (s *Suite) Test__show_source_separator_show_autofix(c *check.C) {
"+\tThe bronze medal line")
}
+func (s *Suite) Test__show_source_separator_show_autofix_with_explanation(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpCommandLine("--source", "--show-autofix", "--explain")
+ lines := t.SetUpFileLines("DESCR",
+ "The first line",
+ "The second line",
+ "The third line")
+
+ fix := lines.Lines[1].Autofix()
+ fix.Warnf("Using \"second\" is deprecated.")
+ fix.Explain("Explanation 1.")
+ fix.Replace("second", "silver medal")
+ fix.Apply()
+
+ lines.Lines[2].Warnf("Dummy warning.")
+
+ fix = lines.Lines[2].Autofix()
+ fix.Warnf("Using \"third\" is deprecated.")
+ fix.Explain("Explanation 2.")
+ fix.Replace("third", "bronze medal")
+ fix.Apply()
+
+ t.CheckOutputLines(
+ "WARN: ~/DESCR:2: Using \"second\" is deprecated.",
+ "AUTOFIX: ~/DESCR:2: Replacing \"second\" with \"silver medal\".",
+ "-\tThe second line",
+ "+\tThe silver medal line",
+ "",
+ "\tExplanation 1.",
+ "",
+ "WARN: ~/DESCR:3: Using \"third\" is deprecated.",
+ "AUTOFIX: ~/DESCR:3: Replacing \"third\" with \"bronze medal\".",
+ "-\tThe third line",
+ "+\tThe bronze medal line",
+ "",
+ "\tExplanation 2.",
+ "")
+}
+
// See Test__show_source_separator_show_autofix for the ordering of the
// output lines.
//
@@ -892,6 +1030,10 @@ func (s *Suite) Test_SeparatorWriter_Flush(c *check.C) {
t.CheckEquals(sb.String(), "ab")
+ t.ExpectAssert(wr.Separate) // Must not be called in the middle of a line.
+
+ wr.WriteLine("")
+
wr.Separate()
// The current line is terminated immediately by the above Separate(),
diff --git a/pkgtools/pkglint/files/mkline_test.go b/pkgtools/pkglint/files/mkline_test.go
index 81d9b1d3e06..36e1eeba162 100644
--- a/pkgtools/pkglint/files/mkline_test.go
+++ b/pkgtools/pkglint/files/mkline_test.go
@@ -337,7 +337,11 @@ func (s *Suite) Test_MkLine__aligned(c *check.C) {
varalign.Finish()
output := t.Output()
- t.CheckEquals(output == "", expected)
+ if expected {
+ t.CheckEquals(output, "")
+ } else if output == "" {
+ t.Check(output, check.Not(check.Equals), "")
+ }
}
// The first line uses a space for indentation, which is typical of
@@ -380,12 +384,17 @@ func (s *Suite) Test_MkLine__aligned(c *check.C) {
// The second line is indented less than the first line. This looks
// confusing to the human reader because the actual values do not
// appear in a rectangular shape in the source code.
- //
- // There are several cases though where the follow-up lines are quite
- // long, therefore it is allowed to indent them with a single tab.
test(
- "CONFIGURE_ENV+=\tAWK=${AWK:Q} \\",
- "\tSED=${SED:Q}",
+ "VAR.param=\tvalue \\",
+ "\t10........20........30........40........50........60...4",
+ false)
+
+ // The second line is indented with a single tab because otherwise
+ // it would be longer than 72 characters. In this case it is ok to
+ // use the smaller indentation.
+ test(
+ "VAR.param=\tvalue \\",
+ "\t10........20........30........40........50........60....5",
true)
// Having the continuation line in column 0 looks even more confusing.
@@ -397,7 +406,7 @@ func (s *Suite) Test_MkLine__aligned(c *check.C) {
// Longer continuation lines may use internal indentation to represent
// AWK or shell code.
test(
- "GENERATE_PLIST+=\t/pattern/ {\\",
+ "GENERATE_PLIST+=\t/pattern/ { \\",
"\t\t\t action(); \\",
"\t\t\t}",
true)
@@ -405,7 +414,7 @@ func (s *Suite) Test_MkLine__aligned(c *check.C) {
// If any of the continuation lines is indented less than the first
// line, it looks confusing.
test(
- "GENERATE_PLIST+=\t/pattern/ {\\",
+ "GENERATE_PLIST+=\t/pattern/ { \\",
"\t action(); \\",
"\t}",
false)
@@ -415,7 +424,7 @@ func (s *Suite) Test_MkLine__aligned(c *check.C) {
// the right as the second line.
test(
"GENERATE_PLIST+= \\",
- "\t/pattern/ {\\",
+ "\t/pattern/ { \\",
"\t action(); \\",
"\t}",
true)
@@ -424,7 +433,7 @@ func (s *Suite) Test_MkLine__aligned(c *check.C) {
// line is not indented properly.
test(
"GENERATE_PLIST+= \\",
- "\t/pattern/ {\\",
+ "\t/pattern/ { \\",
"\t action(); \\",
"}",
false)
diff --git a/pkgtools/pkglint/files/mklinechecker.go b/pkgtools/pkglint/files/mklinechecker.go
index 9dfa903f033..cd6931ed3c6 100644
--- a/pkgtools/pkglint/files/mklinechecker.go
+++ b/pkgtools/pkglint/files/mklinechecker.go
@@ -2,6 +2,7 @@ package pkglint
import (
"netbsd.org/pkglint/regex"
+ "netbsd.org/pkglint/textproc"
"os"
"path"
"path/filepath"
@@ -20,6 +21,7 @@ func (ck MkLineChecker) Check() {
LineChecker{mkline.Line}.CheckTrailingWhitespace()
LineChecker{mkline.Line}.CheckValidCharacters()
+ ck.checkEmptyContinuation()
switch {
case mkline.IsVarassign():
@@ -36,6 +38,21 @@ func (ck MkLineChecker) Check() {
}
}
+func (ck MkLineChecker) checkEmptyContinuation() {
+ if !ck.MkLine.IsMultiline() {
+ return
+ }
+
+ line := ck.MkLine.Line
+ if line.raw[len(line.raw)-1].orignl == "\n" {
+ lastLine := NewLine(line.Filename, int(line.lastLine), "", line.raw[len(line.raw)-1])
+ lastLine.Warnf("This line looks empty but continues the previous line.")
+ lastLine.Explain(
+ "This line should be indented like other continuation lines,",
+ "and to make things clear, should be a comment line.")
+ }
+}
+
func (ck MkLineChecker) checkComment() {
mkline := ck.MkLine
@@ -529,37 +546,22 @@ func (ck MkLineChecker) checkVarUseBuildDefs(varname string) {
func (ck MkLineChecker) checkVaruseUndefined(vartype *Vartype, varname string) {
switch {
-
- case !G.Opts.WarnExtra:
- return
-
- case vartype != nil && !vartype.Guessed():
+ case !G.Opts.WarnExtra,
// Well-known variables are probably defined by the infrastructure.
- return
-
- case ck.MkLines.vars.DefinedSimilar(varname):
- return
-
- case ck.MkLines.forVars[varname]:
- return
-
- case ck.MkLines.vars.Mentioned(varname) != nil:
- return
-
- case G.Pkg != nil && G.Pkg.vars.DefinedSimilar(varname):
- return
-
- case containsVarRef(varname):
- return
-
- case G.Pkgsrc.vartypes.DefinedCanon(varname):
- return
-
- case !ck.MkLines.once.FirstTimeSlice("used but not defined: ", varname):
+ vartype != nil && !vartype.Guessed(),
+ ck.MkLines.vars.DefinedSimilar(varname),
+ ck.MkLines.forVars[varname],
+ ck.MkLines.vars.Mentioned(varname) != nil,
+ G.Pkg != nil && G.Pkg.vars.DefinedSimilar(varname),
+ containsVarRef(varname),
+ G.Pkgsrc.vartypes.DefinedCanon(varname),
+ varname == "":
return
}
- ck.MkLine.Warnf("%s is used but not defined.", varname)
+ if ck.MkLines.once.FirstTimeSlice("used but not defined", varname) {
+ ck.MkLine.Warnf("%s is used but not defined.", varname)
+ }
}
func (ck MkLineChecker) checkVaruseModifiers(varuse *MkVarUse, vartype *Vartype) {
@@ -1600,27 +1602,35 @@ func (ck MkLineChecker) simplifyCondition(varuse *MkVarUse, fromEmpty bool, notE
modifiers := varuse.modifiers
for _, modifier := range modifiers {
- if m, positive, pattern, exact := modifier.MatchMatch(); m && (positive || len(modifiers) == 1) {
- ck.checkVartype(varname, opUseMatch, pattern, "")
+ m, positive, pattern, exact := modifier.MatchMatch()
+ if !m || !positive && len(modifiers) != 1 {
+ continue
+ }
- vartype := G.Pkgsrc.VariableType(ck.MkLines, varname)
- if exact && matches(pattern, `^[\w-/]+$`) && vartype != nil && !vartype.List() {
+ ck.checkVartype(varname, opUseMatch, pattern, "")
- fix := ck.MkLine.Autofix()
- fix.Notef("%s should be compared using %s instead of matching against %q.",
- varname, condStr(positive == notEmpty, "==", "!="), ":"+modifier.Text)
- fix.Explain(
- "This variable has a single value, not a list of values.",
- "Therefore it feels strange to apply list operators like :M and :N onto it.",
- "A more direct approach is to use the == and != operators.",
- "",
- "An entirely different case is when the pattern contains wildcards like ^, *, $.",
- "In such a case, using the :M or :N modifiers is useful and preferred.")
- fix.Replace(replace(varname, positive, pattern))
- fix.Anyway()
- fix.Apply()
- }
+ vartype := G.Pkgsrc.VariableType(ck.MkLines, varname)
+ switch {
+ case !exact,
+ vartype == nil,
+ vartype.List(),
+ textproc.NewLexer(pattern).NextBytesSet(mkCondLiteralChars) != pattern:
+ continue
}
+
+ fix := ck.MkLine.Autofix()
+ fix.Notef("%s should be compared using %s instead of matching against %q.",
+ varname, condStr(positive == notEmpty, "==", "!="), ":"+modifier.Text)
+ fix.Explain(
+ "This variable has a single value, not a list of values.",
+ "Therefore it feels strange to apply list operators like :M and :N onto it.",
+ "A more direct approach is to use the == and != operators.",
+ "",
+ "An entirely different case is when the pattern contains wildcards like ^, *, $.",
+ "In such a case, using the :M or :N modifiers is useful and preferred.")
+ fix.Replace(replace(varname, positive, pattern))
+ fix.Anyway()
+ fix.Apply()
}
}
diff --git a/pkgtools/pkglint/files/mklinechecker_test.go b/pkgtools/pkglint/files/mklinechecker_test.go
index e2f258ffd06..e891ba78edf 100644
--- a/pkgtools/pkglint/files/mklinechecker_test.go
+++ b/pkgtools/pkglint/files/mklinechecker_test.go
@@ -5,6 +5,26 @@ import (
"runtime"
)
+func (s *Suite) Test_MkLineChecker_checkEmptyContinuation(c *check.C) {
+ t := s.Init(c)
+
+ mklines := t.SetUpFileMkLines("filename.mk",
+ MkCvsID,
+ "# line 1 \\",
+ "",
+ "# line 2")
+
+ // Don't check this when loading a file, since otherwise the infrastructure
+ // files could possibly get this warning. Sure, they should be fixed, but
+ // it's not in the focus of the package maintainer.
+ t.CheckOutputEmpty()
+
+ mklines.Check()
+
+ t.CheckOutputLines(
+ "WARN: ~/filename.mk:3: This line looks empty but continues the previous line.")
+}
+
func (s *Suite) Test_MkLineChecker_checkVarassignLeft(c *check.C) {
t := s.Init(c)
diff --git a/pkgtools/pkglint/files/mklines.go b/pkgtools/pkglint/files/mklines.go
index 9fedaf309c3..0bbb14ed785 100644
--- a/pkgtools/pkglint/files/mklines.go
+++ b/pkgtools/pkglint/files/mklines.go
@@ -118,6 +118,7 @@ func (mklines *MkLines) checkAll() {
substContext := NewSubstContext()
var varalign VaralignBlock
+ vargroupsChecker := NewVargroupsChecker(mklines)
isHacksMk := mklines.lines.BaseName == "hacks.mk"
lineAction := func(mkline *MkLine) bool {
@@ -128,6 +129,7 @@ func (mklines *MkLines) checkAll() {
ck := MkLineChecker{mklines, mkline}
ck.Check()
+ vargroupsChecker.Check(mkline)
varalign.Process(mkline)
mklines.Tools.ParseToolLine(mklines, mkline, false, false)
@@ -163,6 +165,7 @@ func (mklines *MkLines) checkAll() {
atEnd := func(mkline *MkLine) {
mklines.indentation.CheckFinish(mklines.lines.Filename)
+ vargroupsChecker.Finish(mkline)
}
if trace.Tracing {
diff --git a/pkgtools/pkglint/files/mkparser.go b/pkgtools/pkglint/files/mkparser.go
index daf0ac6f41a..9c3032d2d3c 100644
--- a/pkgtools/pkglint/files/mkparser.go
+++ b/pkgtools/pkglint/files/mkparser.go
@@ -458,6 +458,10 @@ func (p *MkParser) mkCondAnd() *MkCond {
return &MkCond{And: atoms}
}
+// mkCondLiteralChars contains the characters that may be used outside
+// quotes in a comparison condition such as ${PKGPATH} == category/package.
+var mkCondLiteralChars = textproc.NewByteSet("+---./0-9A-Z_a-z")
+
func (p *MkParser) mkCondCompare() *MkCond {
if trace.Tracing {
defer trace.Call1(p.Rest())()
@@ -522,7 +526,7 @@ func (p *MkParser) mkCondCompare() *MkCond {
return &MkCond{Compare: &MkCondCompare{*lhs, op, *rhs}}
}
- if str := lexer.NextBytesSet(textproc.AlnumU); str != "" {
+ if str := lexer.NextBytesSet(mkCondLiteralChars); str != "" {
return &MkCond{Compare: &MkCondCompare{*lhs, op, MkCondTerm{Str: str}}}
}
}
diff --git a/pkgtools/pkglint/files/mkparser_test.go b/pkgtools/pkglint/files/mkparser_test.go
index 6dbb5e65100..0c5f60a713f 100644
--- a/pkgtools/pkglint/files/mkparser_test.go
+++ b/pkgtools/pkglint/files/mkparser_test.go
@@ -548,7 +548,7 @@ func (s *Suite) Test_MkParser_MkCond(c *check.C) {
testRest := func(input string, expectedTree *MkCond, expectedRest string) {
// As of July 2019 p.MkCond does not emit warnings;
- // this is left to MkLineChecker) checkDirectiveCond.
+ // this is left to MkLineChecker.checkDirectiveCond.
line := t.NewLine("filename.mk", 1, ".if "+input)
p := NewMkParser(line, input)
actualTree := p.MkCond()
@@ -786,6 +786,25 @@ func (s *Suite) Test_MkParser_MkCond(c *check.C) {
"empty{USE_CROSS_COMPILE:M[yY][eE][sS]}")
}
+func (s *Suite) Test_MkParser_mkCondCompare(c *check.C) {
+ t := s.Init(c)
+
+ mkline := t.NewMkLine("Makefile", 123, ".if ${PKGPATH} == category/pack.age-3+")
+ p := NewMkParser(mkline.Line, mkline.Args())
+ cond := p.MkCond()
+
+ t.CheckEquals(p.Rest(), "")
+ t.CheckDeepEquals(
+ cond,
+ &MkCond{
+ Compare: &MkCondCompare{
+ Left: MkCondTerm{Var: NewMkVarUse("PKGPATH")},
+ Op: "==",
+ Right: MkCondTerm{Str: "category/pack.age-3+"}}})
+
+ t.CheckOutputEmpty()
+}
+
func (s *Suite) Test_MkParser_Varname(c *check.C) {
t := s.Init(c)
diff --git a/pkgtools/pkglint/files/package.go b/pkgtools/pkglint/files/package.go
index 9270846ed81..fc18ca9f8a5 100644
--- a/pkgtools/pkglint/files/package.go
+++ b/pkgtools/pkglint/files/package.go
@@ -695,7 +695,10 @@ func (pkg *Package) checkfilePackageMakefile(filename string, mklines *MkLines,
mklines.Tools = allLines.Tools // TODO: also copy the other collected data
mklines.Check()
- if false && pkg.EffectivePkgname != "" && pkg.Includes("../../lang/python/extension.mk") {
+ // This check is experimental because it's not yet clear how to
+ // classify the various Python packages and whether all Python
+ // packages really need the prefix.
+ if G.Experimental && pkg.EffectivePkgname != "" && pkg.Includes("../../lang/python/extension.mk") {
pkg.EffectivePkgnameLine.Warnf("The PKGNAME of Python extensions should start with ${PYPKGPREFIX}.")
}
diff --git a/pkgtools/pkglint/files/package_test.go b/pkgtools/pkglint/files/package_test.go
index 38b9e25fd16..199ada23d93 100644
--- a/pkgtools/pkglint/files/package_test.go
+++ b/pkgtools/pkglint/files/package_test.go
@@ -719,6 +719,7 @@ func (s *Suite) Test_Package_determineEffectivePkgVars__ineffective_C_modifier(c
func (s *Suite) Test_Package_determineEffectivePkgVars__Python_prefix(c *check.C) {
t := s.Init(c)
+ G.Experimental = true
t.SetUpPackage("category/package",
"PKGNAME=\tpackage-2.0",
".include \"../../lang/python/extension.mk\"")
@@ -728,16 +729,14 @@ func (s *Suite) Test_Package_determineEffectivePkgVars__Python_prefix(c *check.C
t.Main("-Wall", "category/package")
t.CheckOutputLines(
- "Looks fine.")
- // TODO: Wait for joerg's answer before enabling this check.
- //t.CheckOutputLines(
- // "WARN: ~/category/package/Makefile:4: The PKGNAME of Python extensions should start with ${PYPKGPREFIX}.",
- // "1 warning found.")
+ "WARN: ~/category/package/Makefile:4: The PKGNAME of Python extensions should start with ${PYPKGPREFIX}.",
+ "1 warning found.")
}
func (s *Suite) Test_Package_determineEffectivePkgVars__Python_prefix_PKGNAME_variable(c *check.C) {
t := s.Init(c)
+ G.Experimental = true
t.SetUpPackage("category/package",
"PKGNAME=\t${VAR}-package-2.0",
".include \"../../lang/python/extension.mk\"")
@@ -765,6 +764,7 @@ func (s *Suite) Test_Package_determineEffectivePkgVars__Python_prefix_PKGNAME_va
func (s *Suite) Test_Package_determineEffectivePkgVars__Python_prefix_late(c *check.C) {
t := s.Init(c)
+ G.Experimental = true
t.SetUpPackage("category/package",
"PKGNAME=\tpackage-2.0",
".include \"common.mk\"")
@@ -776,13 +776,10 @@ func (s *Suite) Test_Package_determineEffectivePkgVars__Python_prefix_late(c *ch
t.Main("-Wall", "category/package")
- // TODO: Wait for joerg's answer before enabling this check.
t.CheckOutputLines(
- "Looks fine.")
- //t.CheckOutputLines(
- // "WARN: ~/category/package/Makefile:4: "+
- // "The PKGNAME of Python extensions should start with ${PYPKGPREFIX}.",
- // "1 warning found.")
+ "WARN: ~/category/package/Makefile:4: "+
+ "The PKGNAME of Python extensions should start with ${PYPKGPREFIX}.",
+ "1 warning found.")
}
func (s *Suite) Test_Package_checkPossibleDowngrade(c *check.C) {
@@ -2944,3 +2941,23 @@ func (s *Suite) Test_Package_Includes(c *check.C) {
// Indentation.IsConditional for the current implementation.
t.CheckEquals(pkg.conditionalIncludes["never.mk"], (*MkLine)(nil))
}
+
+func (s *Suite) Test_Package__case_insensitive(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpPkgsrc()
+ t.SetUpPackage("net/p5-Net-DNS")
+ t.SetUpPackage("category/package",
+ "DEPENDS+=\tp5-Net-DNS>=0:../../net/p5-net-dns")
+ t.FinishSetUp()
+
+ // this test is only interesting on a case-insensitive filesystem
+ if !fileExists(t.File("mk/BSD.PKG.MK")) {
+ return
+ }
+
+ G.Check(t.File("category/package"))
+
+ // FIXME: On a case-sensitive filesystem, p5-net-dns would not be found.
+ t.CheckOutputEmpty()
+}
diff --git a/pkgtools/pkglint/files/pkglint.go b/pkgtools/pkglint/files/pkglint.go
index f1a016521af..646c4d785d4 100644
--- a/pkgtools/pkglint/files/pkglint.go
+++ b/pkgtools/pkglint/files/pkglint.go
@@ -30,6 +30,7 @@ type Pkglint struct {
Wip bool // Is the currently checked file or package from pkgsrc-wip?
Infrastructure bool // Is the currently checked file from the pkgsrc infrastructure?
Testing bool // Is pkglint in self-testing mode (only during development)?
+ Experimental bool // For experimental features, only enabled individually in tests
Username string // For checking against OWNER and MAINTAINER
cvsEntriesDir string // Cached to avoid I/O
diff --git a/pkgtools/pkglint/files/pkglint_test.go b/pkgtools/pkglint/files/pkglint_test.go
index adf8c9ecaa0..15d0f625cef 100644
--- a/pkgtools/pkglint/files/pkglint_test.go
+++ b/pkgtools/pkglint/files/pkglint_test.go
@@ -726,7 +726,7 @@ func (s *Suite) Test_Pkglint_Tool__lookup_by_name_fallback(c *check.C) {
loadTimeTool, loadTimeUsable := G.Tool(mklines, "tool", LoadTime)
runTimeTool, runTimeUsable := G.Tool(mklines, "tool", RunTime)
- // The tool is returned even though it may not be used at the moment.
+ // The tool is returned even though it cannot be used at the moment.
// The calling code must explicitly check for usability.
t.CheckEquals(loadTimeTool.String(), "tool:::Nowhere")
diff --git a/pkgtools/pkglint/files/pkgsrc.go b/pkgtools/pkglint/files/pkgsrc.go
index c94deeaf22c..0aa6fbbb672 100644
--- a/pkgtools/pkglint/files/pkgsrc.go
+++ b/pkgtools/pkglint/files/pkgsrc.go
@@ -819,6 +819,15 @@ func (src *Pkgsrc) LoadMk(filename string, options LoadOptions) *MkLines {
return LoadMk(src.File(filename), options)
}
+func (src *Pkgsrc) LoadMkInfra(filename string, options LoadOptions) *MkLines {
+ if G.Testing {
+ // During testing, the infrastructure files don't have to exist.
+ // They are often emulated by setting their data structures manually.
+ options &^= MustSucceed
+ }
+ return src.LoadMk(filename, options)
+}
+
// ReadDir lists the files and subdirectories from the given directory
// (relative to the pkgsrc root), filtering out any ignored files (CVS/*)
// and empty directories.
diff --git a/pkgtools/pkglint/files/shell.go b/pkgtools/pkglint/files/shell.go
index b2210752db6..19da783e293 100644
--- a/pkgtools/pkglint/files/shell.go
+++ b/pkgtools/pkglint/files/shell.go
@@ -422,6 +422,7 @@ func (ck *ShellLineChecker) checkHiddenAndSuppress(hiddenAndSuppress, rest strin
"${DO_NADA}",
"${ECHO}", "${ECHO_MSG}", "${ECHO_N}", "${ERROR_CAT}", "${ERROR_MSG}",
"${FAIL_MSG}",
+ "${INFO_MSG}",
"${PHASE_MSG}", "${PRINTF}",
"${SHCOMMENT}", "${STEP_MSG}",
"${WARNING_CAT}", "${WARNING_MSG}":
diff --git a/pkgtools/pkglint/files/tools.go b/pkgtools/pkglint/files/tools.go
index e54a2771a85..6eb01c21bbc 100644
--- a/pkgtools/pkglint/files/tools.go
+++ b/pkgtools/pkglint/files/tools.go
@@ -415,6 +415,8 @@ func (tr *Tools) adjustValidity(tool *Tool) {
}
}
+func (tr *Tools) ExistsVar(varname string) bool { return tr.byVarname[varname] != nil }
+
type Validity uint8
const (
diff --git a/pkgtools/pkglint/files/util.go b/pkgtools/pkglint/files/util.go
index 45f89524390..3e9c20648f6 100644
--- a/pkgtools/pkglint/files/util.go
+++ b/pkgtools/pkglint/files/util.go
@@ -149,6 +149,25 @@ func keysJoined(m map[string]bool) string {
return strings.Join(keys, " ")
}
+func copyStringMkLine(m map[string]*MkLine) map[string]*MkLine {
+ c := make(map[string]*MkLine, len(m))
+ for k, v := range m {
+ c[k] = v
+ }
+ return c
+}
+
+func forEachStringMkLine(m map[string]*MkLine, action func(s string, mkline *MkLine)) {
+ var keys []string
+ for key := range m {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+ for _, key := range keys {
+ action(key, m[key])
+ }
+}
+
func imax(a, b int) int {
if a > b {
return a
@@ -245,7 +264,7 @@ func getSubdirs(filename string) []string {
func isIgnoredFilename(filename string) bool {
switch filename {
- case ".", "..", "CVS", ".svn", ".git", ".hg":
+ case ".", "..", "CVS", ".svn", ".git", ".hg", ".idea":
return true
}
return false
@@ -312,16 +331,18 @@ type CvsEntry struct {
// Returns the number of columns that a string occupies when printed with
// a tabulator size of 8.
-func tabWidth(s string) int {
- length := 0
+func tabWidth(s string) int { return tabWidthAppend(0, s) }
+
+func tabWidthAppend(width int, s string) int {
for _, r := range s {
+ assert(r != '\n')
if r == '\t' {
- length = length&-8 + 8
+ width = width&-8 + 8
} else {
- length++
+ width++
}
}
- return length
+ return width
}
func detab(s string) string {
@@ -1331,3 +1352,8 @@ func (s *StringSet) AddAll(elements []string) {
s.Add(element)
}
}
+
+func (s *StringSet) Contains(needle string) bool {
+ _, found := s.seen[needle]
+ return found
+}
diff --git a/pkgtools/pkglint/files/util_test.go b/pkgtools/pkglint/files/util_test.go
index fc3937baceb..a3f08f7b741 100644
--- a/pkgtools/pkglint/files/util_test.go
+++ b/pkgtools/pkglint/files/util_test.go
@@ -105,6 +105,15 @@ func (s *Suite) Test_tabWidth(c *check.C) {
t.CheckEquals(tabWidth("12345678\t"), 16)
}
+// Since tabWidthAppend is used with logical lines (Line.Text) as well as with
+// raw lines (RawLine.textnl or RawLine.orignl), and since the width only
+// makes sense for a single line, better panic.
+func (s *Suite) Test_tabWidthAppend__panic(c *check.C) {
+ t := s.Init(c)
+
+ t.ExpectAssert(func() { tabWidthAppend(0, "\n") })
+}
+
func (s *Suite) Test_cleanpath(c *check.C) {
t := s.Init(c)
diff --git a/pkgtools/pkglint/files/varalignblock.go b/pkgtools/pkglint/files/varalignblock.go
index 2656ab43c65..ce020b30253 100644
--- a/pkgtools/pkglint/files/varalignblock.go
+++ b/pkgtools/pkglint/files/varalignblock.go
@@ -2,6 +2,7 @@ package pkglint
import (
"netbsd.org/pkglint/textproc"
+ "sort"
"strings"
)
@@ -16,29 +17,31 @@ import (
// between HOMEPAGE and DISTFILES.
//
// Continuation lines are also aligned to the single-line assignments.
-// There are two types of continuation lines. The first type is an
-// empty multiline:
+// There are two types of continuation lines. The first type has just
+// the continuation backslash in the first line:
//
-// MULTI_EMPTY_LINE= \
+// MULTI_LINE= \
// The value starts in the second line.
//
// The backslash in the first line is usually aligned to the other variables
-// in the same paragraph. If the variable name is so long that it is an
-// outlier, it may be indented with a single space, just like a single-line
-// variable. In multi-line shell commands or AWK programs, the backslash is
+// in the same paragraph. If the variable name is longer than the indentation
+// of the paragraph, it may be indented with a single space.
+// In multi-line shell commands or AWK programs, the backslash is
// often indented to column 73, as are the backslashes from the follow-up
// lines, to act as a visual guideline.
//
-// Since this type is often used for URLs or other long values, the first
-// follow-up line may be indented with a single tab, even if the other
+// The indentation of the first value of the variable determines the minimum
+// indentation for the remaining continuation lines. To allow long variable
+// values to be indented as little as possible, the follow-up lines only need
+// to be indented by a single tab, even if the other
// variables in the paragraph are aligned further to the right. If the
// indentation is not a single tab, it must match the indentation of the
// other lines in the paragraph.
//
-// INITIAL_LINE= The value starts in the first line \
+// MULTI_LINE= The value starts in the first line \
// and continues in the second line.
//
-// In lists or plain text, like in the INITIAL_LINE above, all values are
+// In lists or plain text, like in the example above, all values are
// aligned in the same column. Some variables also contain code, and in
// these variables, the line containing the first word defines how deep
// the follow-up lines must be indented at least.
@@ -55,31 +58,17 @@ import (
// especially true for CONFIGURE_ENV, since the environment variables are
// typically uppercase as well.
//
-// TODO: An initial line has this form:
-// comment? varname+op space? value? space? comment? space? backslash?
+// For the purpose of aligning the variable assignments, each raw line is
+// split into several parts, as described by the varalignParts type.
//
-// TODO: A follow-up line has the form:
-// comment? space? value? space? comment? space? backslash?
-//
-// TODO: The alignment checks are performed on the raw lines instead of
-// the logical lines, since this check is about the visual appearance
-// as opposed to the meaning of the variable assignment.
+// The alignment checks are performed on the raw lines instead of
+// the logical lines, since this check is about the visual appearance
+// as opposed to the meaning of the variable assignment.
//
// FIXME: Implement each requirement from the above documentation.
type VaralignBlock struct {
infos []*varalignLine
skip bool
-
- // When the indentation of the initial line of a multiline is
- // changed, all its follow-up lines are shifted by the same
- // amount and in the same direction. Typical examples are
- // SUBST_SED, shell programs and AWK programs like in
- // GENERATE_PLIST.
- indentDiffSet bool
- // The amount by which the follow-up lines are shifted.
- // Positive values mean shifting to the right, negative values
- // mean shifting to the left.
- indentDiff int
}
type varalignLine struct {
@@ -98,18 +87,7 @@ type varalignLine struct {
// of the first value found.
multiEmpty bool
- parts varalignSplitResult
-}
-
-type varalignSplitResult struct {
- leadingComment string // either the # or some rarely used U+0020 spaces
- varnameOp string // empty iff it is a follow-up line
- spaceBeforeValue string // for follow-up lines, this is the indentation
- value string
- spaceAfterValue string
- trailingComment string
- spaceAfterComment string
- continuation string
+ varalignParts
}
func (va *VaralignBlock) Process(mkline *MkLine) {
@@ -154,9 +132,10 @@ func (va *VaralignBlock) processVarassign(mkline *MkLine) {
follow := false
for i, raw := range mkline.raw {
- info := varalignLine{mkline, i, follow, va.split(raw.textnl, i == 0)}
+ parts := NewVaralignSplitter().split(strings.TrimSuffix(raw.textnl, "\n"), i == 0)
+ info := varalignLine{mkline, i, follow, parts}
- if i == 0 && info.parts.value == "" && info.continuation() {
+ if i == 0 && info.isEmptyContinuation() {
follow = true
info.multiEmpty = true
}
@@ -165,135 +144,84 @@ func (va *VaralignBlock) processVarassign(mkline *MkLine) {
}
}
-func (*VaralignBlock) split(textnl string, initial bool) varalignSplitResult {
+func (va *VaralignBlock) Finish() {
+ infos := va.infos
+ skip := va.skip
+ *va = VaralignBlock{} // overwrites infos and skip
- // See MkLineParser.unescapeComment for very similar code.
+ if len(infos) == 0 || skip {
+ return
+ }
- p := NewMkParser(nil, textnl)
- lexer := p.lexer
+ if trace.Tracing {
+ defer trace.Call(infos[0].mkline.Line)()
+ }
- parseLeadingComment := func() string {
- if hasPrefix(lexer.Rest(), "# ") {
- return ""
- }
+ newWidth := va.optimalWidth(infos)
+ rightMargin := 0
- mark := lexer.Mark()
+ // When the indentation of the initial line of a multiline is
+ // changed, all its follow-up lines are shifted by the same
+ // amount and in the same direction. Typical examples are
+ // SUBST_SED, shell programs and AWK programs like in
+ // GENERATE_PLIST.
+ indentDiffSet := false
+ // The amount by which the follow-up lines are shifted.
+ // Positive values mean shifting to the right, negative values
+ // mean shifting to the left.
+ indentDiff := 0
- if !lexer.SkipByte('#') && initial && lexer.SkipByte(' ') {
- lexer.SkipHspace()
+ for i, info := range infos {
+ if info.rawIndex == 0 {
+ indentDiffSet = false
+ indentDiff = 0
+ restIndex := i + condInt(info.value != "", 0, 1)
+ rightMargin = va.rightMargin(infos[restIndex:])
}
- return lexer.Since(mark)
- }
+ va.checkContinuationIndentation(info, newWidth, rightMargin)
- parseVarnameOp := func() string {
- if !initial {
- return ""
+ if newWidth > 0 || info.rawIndex > 0 {
+ va.realign(info, newWidth, &indentDiffSet, &indentDiff)
}
-
- mark := lexer.Mark()
- _ = p.Varname()
- lexer.SkipHspace()
- ok, _ := p.Op()
- assert(ok)
- return lexer.Since(mark)
}
+}
- parseValue := func() (string, string) {
- mark := lexer.Mark()
-
- for !lexer.EOF() &&
- lexer.PeekByte() != '#' &&
- lexer.PeekByte() != '\n' &&
- !hasPrefix(lexer.Rest(), "\\\n") {
-
- if lexer.NextBytesSet(unescapeMkCommentSafeChars) != "" ||
- lexer.SkipString("[#") ||
- lexer.SkipByte('[') {
- continue
- }
-
- if len(lexer.Rest()) < 2 {
- break
+// rightMargin calculates the column in which the continuation backslashes
+// should be placed.
+func (*VaralignBlock) rightMargin(infos []*varalignLine) int {
+ var columns []int
+ for _, info := range infos {
+ if info.isContinuation() {
+ space := info.spaceBeforeContinuation()
+ if space != "" && space != " " {
+ columns = append(columns, info.continuationColumn())
}
-
- assert(lexer.SkipByte('\\'))
- lexer.Skip(1)
}
-
- valueSpace := lexer.Since(mark)
- value := rtrimHspace(valueSpace)
- space := valueSpace[len(value):]
- return value, space
}
- parseComment := func() (string, string, string) {
- rest := lexer.Rest()
-
- newline := len(rest)
- for newline > 0 && rest[newline-1] == '\n' {
- newline--
- }
-
- backslash := newline
- for backslash > 0 && rest[backslash-1] == '\\' {
- backslash--
- }
+ sort.Ints(columns)
- if (newline-backslash)%2 == 1 {
- continuation := rest[backslash:]
- commentSpace := rest[:backslash]
- comment := rtrimHspace(commentSpace)
- space := commentSpace[len(comment):]
- return comment, space, continuation
+ for i := len(columns) - 2; i >= 0; i-- {
+ if columns[i] == columns[i+1] {
+ return columns[i]
}
-
- return rest[:newline], "", rest[newline:]
- }
-
- leadingComment := parseLeadingComment()
- varnameOp := parseVarnameOp()
- spaceBeforeValue := lexer.NextHspace()
- value, spaceAfterValue := parseValue()
- trailingComment, spaceAfterComment, continuation := parseComment()
-
- return varalignSplitResult{
- leadingComment,
- varnameOp,
- spaceBeforeValue,
- value,
- spaceAfterValue,
- trailingComment,
- spaceAfterComment,
- continuation,
}
-}
-func (va *VaralignBlock) Finish() {
- infos := va.infos
- skip := va.skip
- *va = VaralignBlock{} // overwrites infos and skip
-
- if len(infos) == 0 || skip {
- return
- }
-
- if trace.Tracing {
- defer trace.Call(infos[0].mkline.Line)()
+ if len(columns) <= 1 {
+ return 0
}
- newWidth := va.optimalWidth(infos)
-
+ var min int
for _, info := range infos {
- if info.rawIndex == 0 {
- va.indentDiffSet = false
- va.indentDiff = 0
- }
-
- if newWidth > 0 || info.rawIndex > 0 {
- va.realign(info, newWidth)
+ if info.isContinuation() {
+ mainWidth := tabWidth(info.beforeContinuation())
+ if mainWidth > min {
+ min = mainWidth
+ }
}
}
+ return (min & -8) + 8
}
// optimalWidth computes the desired screen width for the variable assignment
@@ -303,47 +231,39 @@ func (va *VaralignBlock) Finish() {
// This is to prevent a single SITES.* variable from forcing the rest of the
// paragraph to be indented too far to the right.
func (*VaralignBlock) optimalWidth(infos []*varalignLine) int {
- longest := 0 // The longest seen varnameOpWidth
- var longestLine *MkLine
- secondLongest := 0 // The second-longest seen varnameOpWidth
+ var widths mklineInts
for _, info := range infos {
- if info.multiEmpty || info.rawIndex > 0 {
- continue
- }
-
- width := info.varnameOpWidth()
- if width >= longest {
- secondLongest = longest
- longest = width
- longestLine = info.mkline
- } else if width > secondLongest {
- secondLongest = width
+ if !info.multiEmpty && info.rawIndex == 0 {
+ widths.append(info.mkline, info.varnameOpWidth())
}
}
+ widths.sortDesc()
+
+ longest := widths.opt(0)
+ longestLine := widths.optLine(0)
+ secondLongest := widths.opt(1)
haveOutlier := secondLongest != 0 &&
- longest/8 >= secondLongest/8+2 &&
- !longestLine.IsMultiline()
+ secondLongest/8+1 < longest/8 &&
+ !longestLine.IsMultiline() // TODO: may be too imprecise
// Minimum required width of varnameOp, without the trailing whitespace.
minVarnameOpWidth := condInt(haveOutlier, secondLongest, longest)
outlier := condInt(haveOutlier, longest, 0)
// Widths of the current indentation (including whitespace)
- minTotalWidth := 0
- maxTotalWidth := 0
+ var spaceWidths mklineInts
for _, info := range infos {
if info.multiEmpty || info.rawIndex > 0 || (outlier > 0 && info.varnameOpWidth() == outlier) {
continue
}
-
- width := info.varnameOpSpaceWidth()
- if minTotalWidth == 0 || width < minTotalWidth {
- minTotalWidth = width
- }
- maxTotalWidth = imax(maxTotalWidth, width)
+ spaceWidths.append(info.mkline, info.varnameOpSpaceWidth())
}
+ spaceWidths.sortDesc()
+
+ minTotalWidth := spaceWidths.min()
+ maxTotalWidth := spaceWidths.max()
if trace.Tracing {
trace.Stepf("Indentation including whitespace is between %d and %d.",
@@ -367,26 +287,58 @@ func (*VaralignBlock) optimalWidth(infos []*varalignLine) int {
return (minVarnameOpWidth & -8) + 8
}
-func (va *VaralignBlock) realign(info *varalignLine, newWidth int) {
+func (va *VaralignBlock) checkContinuationIndentation(info *varalignLine, newWidth int, rightMargin int) {
+ if !info.isContinuation() {
+ return
+ }
+
+ oldSpace := info.spaceBeforeContinuation()
+ if oldSpace == " " || oldSpace == "\t" {
+ return
+ }
+
+ column := info.continuationColumn()
+ if column == 72 || column == rightMargin || column <= newWidth {
+ return
+ }
+
+ newSpace := " "
+ fix := info.mkline.Autofix()
+ if oldSpace == "" || rightMargin == 0 || tabWidth(info.beforeContinuation()) >= rightMargin {
+ fix.Notef("The continuation backslash should be preceded by a single space or tab.")
+ } else {
+ newSpace = alignmentAfter(info.beforeContinuation(), rightMargin)
+ fix.Notef(
+ "The continuation backslash should be preceded by a single space or tab, "+
+ "or be in column %d, not %d.",
+ rightMargin+1, column+1)
+ }
+ fix.ReplaceAt(info.rawIndex, info.continuationIndex()-len(oldSpace), oldSpace, newSpace)
+ fix.Apply()
+}
+
+func (va *VaralignBlock) realign(info *varalignLine, newWidth int, indentDiffSet *bool, indentDiff *int) {
if info.multiEmpty {
if info.rawIndex == 0 {
va.realignMultiEmptyInitial(info, newWidth)
} else {
- va.realignMultiEmptyFollow(info, newWidth)
+ va.realignMultiEmptyFollow(info, newWidth, indentDiffSet, indentDiff)
}
- } else if info.rawIndex == 0 && info.continuation() {
- va.realignMultiInitial(info, newWidth)
+ } else if info.rawIndex == 0 && info.isContinuation() {
+ va.realignMultiInitial(info, newWidth, indentDiffSet, indentDiff)
} else if info.rawIndex > 0 {
- va.realignMultiFollow(info, newWidth)
+ assert(*indentDiffSet)
+ va.realignMultiFollow(info, newWidth, *indentDiff)
} else {
+ assert(!*indentDiffSet)
va.realignSingle(info, newWidth)
}
}
func (*VaralignBlock) realignMultiEmptyInitial(info *varalignLine, newWidth int) {
- leadingComment := info.parts.leadingComment
- varnameOp := info.parts.varnameOp
- oldSpace := info.parts.spaceBeforeValue
+ leadingComment := info.leadingComment
+ varnameOp := info.varnameOp
+ oldSpace := info.spaceBeforeValue
// Indent the outlier and any other lines that stick out
// with a space instead of a tab to keep the line short.
@@ -399,20 +351,16 @@ func (*VaralignBlock) realignMultiEmptyInitial(info *varalignLine, newWidth int)
return
}
- if newSpace == " " && oldSpace != "" && oldSpace == strings.Repeat("\t", len(oldSpace)) {
- return
+ if newSpace == " " {
+ return // This case is handled by checkContinuationIndentation.
}
hasSpace := strings.IndexByte(oldSpace, ' ') != -1
oldColumn := info.varnameOpSpaceWidth()
column := tabWidth(leadingComment + varnameOp + newSpace)
- // TODO: explicitly mention "single space", "tabs to the newWidth", "tabs to column 72"
-
fix := info.mkline.Autofix()
- if newSpace == " " {
- fix.Notef("This outlier variable value should be aligned with a single space.")
- } else if hasSpace && column != oldColumn {
+ if hasSpace && column != oldColumn {
fix.Notef("This variable value should be aligned with tabs, not spaces, to column %d.", column+1)
} else if column != oldColumn {
fix.Notef("This variable value should be aligned to column %d.", column+1)
@@ -423,26 +371,26 @@ func (*VaralignBlock) realignMultiEmptyInitial(info *varalignLine, newWidth int)
fix.Apply()
}
-func (va *VaralignBlock) realignMultiEmptyFollow(info *varalignLine, newWidth int) {
- oldSpace := info.parts.spaceBeforeValue
+func (va *VaralignBlock) realignMultiEmptyFollow(info *varalignLine, newWidth int, indentDiffSet *bool, indentDiff *int) {
+ oldSpace := info.spaceBeforeValue
oldWidth := tabWidth(oldSpace)
- if !va.indentDiffSet {
- va.indentDiffSet = true
- va.indentDiff = condInt(newWidth != 0, newWidth-oldWidth, 0)
- if va.indentDiff > 0 && !info.commentedOut() {
- va.indentDiff = 0
+ if !*indentDiffSet {
+ *indentDiffSet = true
+ *indentDiff = condInt(newWidth != 0, newWidth-oldWidth, 0)
+ if *indentDiff > 0 && !info.commentedOut() {
+ *indentDiff = 0
}
}
- newSpace := indent(imax(oldWidth+va.indentDiff, 8))
+ newSpace := indent(imax(oldWidth+*indentDiff, 8))
if newSpace == oldSpace {
return
}
// Below a continuation marker, there may be a completely empty line.
// This is confusing to the human readers, but technically allowed.
- if info.parts.value == "" && info.parts.trailingComment == "" && !info.continuation() {
+ if info.varalignParts.isEmpty() {
return
}
@@ -452,14 +400,14 @@ func (va *VaralignBlock) realignMultiEmptyFollow(info *varalignLine, newWidth in
fix.Apply()
}
-func (va *VaralignBlock) realignMultiInitial(info *varalignLine, newWidth int) {
- leadingComment := info.parts.leadingComment
- varnameOp := info.parts.varnameOp
- oldSpace := info.parts.spaceBeforeValue
+func (va *VaralignBlock) realignMultiInitial(info *varalignLine, newWidth int, indentDiffSet *bool, indentDiff *int) {
+ leadingComment := info.leadingComment
+ varnameOp := info.varnameOp
+ oldSpace := info.spaceBeforeValue
- va.indentDiffSet = true
+ *indentDiffSet = true
oldWidth := info.varnameOpSpaceWidth()
- va.indentDiff = newWidth - oldWidth
+ *indentDiff = newWidth - oldWidth
newSpace := alignmentAfter(leadingComment+varnameOp, newWidth)
if newSpace == oldSpace {
@@ -481,15 +429,13 @@ func (va *VaralignBlock) realignMultiInitial(info *varalignLine, newWidth int) {
fix.Apply()
}
-func (va *VaralignBlock) realignMultiFollow(info *varalignLine, newWidth int) {
- assert(va.indentDiffSet)
-
- oldSpace := info.parts.spaceBeforeValue
- newSpace := indent(tabWidth(oldSpace) + va.indentDiff)
+func (va *VaralignBlock) realignMultiFollow(info *varalignLine, newWidth int, indentDiff int) {
+ oldSpace := info.spaceBeforeValue
+ newSpace := indent(tabWidth(oldSpace) + indentDiff)
if tabWidth(newSpace) < newWidth {
newSpace = indent(newWidth)
}
- if newSpace == oldSpace || oldSpace == "\t" {
+ if newSpace == oldSpace || (oldSpace == "\t" && info.widthAlignedAt(newWidth) > 72) {
return
}
@@ -500,11 +446,9 @@ func (va *VaralignBlock) realignMultiFollow(info *varalignLine, newWidth int) {
}
func (va *VaralignBlock) realignSingle(info *varalignLine, newWidth int) {
- assert(!va.indentDiffSet)
-
- leadingComment := info.parts.leadingComment
- varnameOp := info.parts.varnameOp
- oldSpace := info.parts.spaceBeforeValue
+ leadingComment := info.leadingComment
+ varnameOp := info.varnameOp
+ oldSpace := info.spaceBeforeValue
newSpace := ""
for tabWidth(leadingComment+varnameOp+newSpace) < newWidth {
@@ -562,41 +506,196 @@ func (va *VaralignBlock) explainWrongColumn(fix *Autofix) {
"and directives like .if or .for, it is not checked at all.")
}
-func (l *varalignLine) varnameOpWidth() int {
- return tabWidth(l.parts.leadingComment + l.parts.varnameOp)
+// VaralignSplitter parses the text of a raw line into those parts that
+// are relevant for aligning the variable values, and the backslash
+// continuation markers.
+//
+// See MkLineParser.unescapeComment for very similar code.
+type VaralignSplitter struct {
}
-func (l *varalignLine) varnameOpSpaceWidth() int {
- return tabWidth(l.parts.leadingComment + l.parts.varnameOp + l.parts.spaceBeforeValue)
+func NewVaralignSplitter() VaralignSplitter {
+ return VaralignSplitter{}
+}
+
+func (s VaralignSplitter) split(rawText string, initial bool) varalignParts {
+ assert(!hasSuffix(rawText, "\n"))
+ parser := NewMkParser(nil, rawText)
+ lexer := parser.lexer
+
+ leadingComment := s.parseLeadingComment(lexer, initial)
+ varnameOp, spaceBeforeValue := s.parseVarnameOp(parser, initial)
+ value, spaceAfterValue := s.parseValue(lexer)
+ trailingComment, spaceAfterComment, continuation := s.parseComment(lexer.Rest())
+
+ return varalignParts{
+ leadingComment,
+ varnameOp,
+ spaceBeforeValue,
+ value,
+ spaceAfterValue,
+ trailingComment,
+ spaceAfterComment,
+ continuation,
+ }
+}
+
+func (VaralignSplitter) parseLeadingComment(lexer *textproc.Lexer, initial bool) string {
+ if hasPrefix(lexer.Rest(), "# ") {
+ return ""
+ }
+
+ comment := lexer.NextString("#")
+ if comment != "" {
+ return comment
+ }
+
+ mark := lexer.Mark()
+ if initial && lexer.SkipByte(' ') {
+ lexer.SkipHspace()
+ }
+ return lexer.Since(mark)
+}
+
+func (VaralignSplitter) parseVarnameOp(parser *MkParser, initial bool) (string, string) {
+ lexer := parser.lexer
+ if !initial {
+ return "", lexer.NextHspace()
+ }
+
+ mark := lexer.Mark()
+ _ = parser.Varname()
+ lexer.SkipHspace()
+ ok, _ := parser.Op()
+ assert(ok)
+ return lexer.Since(mark), lexer.NextHspace()
+}
+
+func (VaralignSplitter) parseValue(lexer *textproc.Lexer) (string, string) {
+ mark := lexer.Mark()
+
+ for !lexer.EOF() && lexer.PeekByte() != '#' && lexer.Rest() != "\\" {
+
+ if lexer.NextBytesSet(unescapeMkCommentSafeChars) != "" ||
+ lexer.SkipString("[#") ||
+ lexer.SkipByte('[') {
+ continue
+ }
+
+ assert(lexer.SkipByte('\\'))
+ lexer.Skip(1)
+ }
+
+ valueSpace := lexer.Since(mark)
+ value := rtrimHspace(valueSpace)
+ space := valueSpace[len(value):]
+ return value, space
+}
+
+func (VaralignSplitter) parseComment(rest string) (string, string, string) {
+ end := len(rest)
+
+ backslash := end
+ for backslash > 0 && rest[backslash-1] == '\\' {
+ backslash--
+ }
+
+ if (end-backslash)&1 == 0 { // see https://github.com/golang/go/issues/34166
+ return rest[:end], "", ""
+ }
+
+ continuation := rest[backslash:]
+ commentSpace := rest[:backslash]
+ comment := rtrimHspace(commentSpace)
+ space := commentSpace[len(comment):]
+ return comment, space, continuation
+}
+
+type varalignParts struct {
+ leadingComment string // either the # or some rarely used U+0020 spaces
+ varnameOp string // empty iff it is a follow-up line
+ spaceBeforeValue string // for follow-up lines, this is the indentation
+ value string
+ spaceAfterValue string // only set if there is a value
+ trailingComment string
+ spaceAfterComment string // only set if there is a trailing comment
+ continuation string // either a single backslash or empty
+}
+
+// continuation returns whether this line ends with a backslash.
+func (p *varalignParts) isContinuation() bool {
+ return p.continuation != ""
+}
+
+func (p *varalignParts) isEmptyContinuation() bool {
+ return p.value == "" && p.trailingComment == "" && p.isContinuation()
+}
+
+func (p *varalignParts) isEmpty() bool {
+ return p.value == "" && p.trailingComment == "" && !p.isContinuation()
+}
+
+func (p *varalignParts) varnameOpWidth() int {
+ return tabWidth(p.leadingComment + p.varnameOp)
+}
+
+func (p *varalignParts) varnameOpSpaceWidth() int {
+ return tabWidth(p.leadingComment + p.varnameOp + p.spaceBeforeValue)
}
// spaceBeforeValueIndex returns the string index at which the space before the value starts.
// It's the same as the end of the assignment operator. Example:
// #VAR= value
// The index is 5.
-func (l *varalignLine) spaceBeforeValueIndex() int {
- return len(l.parts.leadingComment) + len(l.parts.varnameOp)
+func (p *varalignParts) spaceBeforeValueIndex() int {
+ return len(p.leadingComment) + len(p.varnameOp)
}
-// continuation returns whether this line ends with a backslash.
-func (l *varalignLine) continuation() bool {
- return hasPrefix(l.parts.continuation, "\\")
+func (p *varalignParts) spaceBeforeContinuation() string {
+ if p.trailingComment == "" {
+ if p.value == "" {
+ return p.spaceBeforeValue
+ }
+ return p.spaceAfterValue
+ }
+ return p.spaceAfterComment
+}
+
+func (p *varalignParts) beforeContinuation() string {
+ return rtrimHspace(p.leadingComment +
+ p.varnameOp + p.spaceBeforeValue +
+ p.value + p.spaceAfterValue +
+ p.trailingComment + p.spaceAfterComment)
}
-func (l *varalignLine) commentedOut() bool {
- return hasPrefix(l.parts.leadingComment, "#")
+func (p *varalignParts) continuationColumn() int {
+ return tabWidth(p.leadingComment +
+ p.varnameOp + p.spaceBeforeValue +
+ p.value + p.spaceAfterValue +
+ p.trailingComment + p.spaceAfterComment)
+}
+
+func (p *varalignParts) continuationIndex() int {
+ return len(p.leadingComment) +
+ len(p.varnameOp) + len(p.spaceBeforeValue) +
+ len(p.value) + len(p.spaceAfterValue) +
+ len(p.trailingComment) + len(p.spaceAfterComment)
+}
+
+func (p *varalignParts) commentedOut() bool {
+ return hasPrefix(p.leadingComment, "#")
}
// canonicalInitial returns whether the space between the assignment
// operator and the value has its canonical form, which is either
// at least one tab, or a single space, but only for lines that stick out.
-func (l *varalignLine) canonicalInitial(width int) bool {
- space := l.parts.spaceBeforeValue
+func (p *varalignParts) canonicalInitial(width int) bool {
+ space := p.spaceBeforeValue
if space == "" {
return false
}
- if space == " " && l.varnameOpSpaceWidth() > width {
+ if space == " " && p.varnameOpSpaceWidth() > width {
return true
}
@@ -605,8 +704,8 @@ func (l *varalignLine) canonicalInitial(width int) bool {
// canonicalFollow returns whether the space before the value has its
// canonical form, which is at least one tab, followed by up to 7 spaces.
-func (l *varalignLine) canonicalFollow() bool {
- lexer := textproc.NewLexer(l.parts.spaceBeforeValue)
+func (p *varalignParts) canonicalFollow() bool {
+ lexer := textproc.NewLexer(p.spaceBeforeValue)
tabs := 0
for lexer.SkipByte('\t') {
@@ -620,3 +719,46 @@ func (l *varalignLine) canonicalFollow() bool {
return tabs >= 1 && spaces <= 7
}
+
+func (p *varalignParts) widthAlignedAt(valueAlign int) int {
+ return tabWidthAppend(
+ valueAlign,
+ p.value+p.spaceAfterValue+p.trailingComment+p.spaceAfterComment+p.continuation)
+}
+
+type mklineInts struct {
+ slice []struct {
+ mkline *MkLine
+ value int
+ }
+}
+
+func (mi mklineInts) sortDesc() {
+ less := func(i, j int) bool { return mi.slice[j].value < mi.slice[i].value }
+ sort.SliceStable(mi.slice, less)
+}
+
+func (mi mklineInts) opt(index int) int {
+ if uint(index) < uint(len(mi.slice)) {
+ return mi.slice[index].value
+ }
+ return 0
+}
+
+func (mi mklineInts) optLine(index int) *MkLine {
+ if uint(index) < uint(len(mi.slice)) {
+ return mi.slice[index].mkline
+ }
+ return nil
+}
+
+func (mi *mklineInts) append(mkline *MkLine, value int) {
+ mi.slice = append(mi.slice, struct {
+ mkline *MkLine
+ value int
+ }{mkline, value})
+}
+
+func (mi mklineInts) min() int { return mi.opt(0) }
+
+func (mi mklineInts) max() int { return mi.opt(len(mi.slice) - 1) }
diff --git a/pkgtools/pkglint/files/varalignblock_test.go b/pkgtools/pkglint/files/varalignblock_test.go
index 46c29942cc7..b0bbf0d0fb4 100644
--- a/pkgtools/pkglint/files/varalignblock_test.go
+++ b/pkgtools/pkglint/files/varalignblock_test.go
@@ -1,6 +1,10 @@
package pkglint
-import "gopkg.in/check.v1"
+import (
+ "gopkg.in/check.v1"
+ "strconv"
+ "strings"
+)
// VaralignTester reduces the amount of test code for aligning variable
// assignments in Makefiles.
@@ -48,8 +52,10 @@ func (vt *VaralignTester) Fixed(lines ...string) { vt.fixed = lines }
// Run is called after setting up the data and runs the varalign checks twice.
// Once for getting the diagnostics and once for automatically fixing them.
func (vt *VaralignTester) Run() {
+ vt.tester.Chdir(".")
vt.run(false)
vt.run(true)
+ vt.checkTestName()
}
func (vt *VaralignTester) run(autofix bool) {
@@ -78,13 +84,19 @@ func (vt *VaralignTester) run(autofix bool) {
infos := varalign.infos // since they are overwritten by Finish
varalign.Finish()
- var actual []string
- for _, info := range infos {
- minWidth := condStr(info.rawIndex == 0, sprintf("%02d", info.varnameOpWidth()), " ")
- infoStr := sprintf("%s %02d", minWidth, info.varnameOpSpaceWidth())
- actual = append(actual, infoStr)
+ if len(vt.internals) > 0 {
+ var actual []string
+ for _, info := range infos {
+ var minWidth, curWidth, continuation string
+ minWidth = condStr(info.rawIndex == 0, sprintf("%02d", info.varnameOpWidth()), " ")
+ curWidth = sprintf(" %02d", info.varnameOpSpaceWidth())
+ if info.isContinuation() {
+ continuation = sprintf(" %02d", info.continuationColumn())
+ }
+ actual = append(actual, minWidth+curWidth+continuation)
+ }
+ t.CheckDeepEquals(actual, vt.internals)
}
- t.CheckDeepEquals(actual, vt.internals)
if autofix {
t.CheckOutput(vt.autofixes)
@@ -96,37 +108,185 @@ func (vt *VaralignTester) run(autofix bool) {
}
}
-func (s *Suite) Test_VaralignBlock__one_line_simple_none(c *check.C) {
+func (vt *VaralignTester) checkTestName() {
+ testName := vt.tester.c.TestName()
+ if !matches(testName, `__(lead|var)\d*_`) {
+ return
+ }
+
+ type descriptor struct {
+ name string
+ width int
+ }
+
+ var actual []descriptor
+ width := 0
+ describe := func(str string, descr string) {
+ width = tabWidthAppend(width, str)
+ actual = append(actual, descriptor{descr, width})
+ }
+ describeHspace := func(hspace string) {
+ width = tabWidthAppend(width, hspace)
+ var descr string
+ switch {
+ case hspace == "":
+ descr = "none"
+ case hspace == "\t":
+ descr = "tab"
+ case hspace == " ":
+ descr = "space"
+ case matches(hspace, `^\t+$`):
+ descr = "tabs"
+ case matches(hspace, `^ +$`):
+ descr = "spaces"
+ case matches(hspace, `^\t+ {0,7}$`):
+ descr = "indent"
+ default:
+ descr = strings.Replace(strings.Replace(hspace, " ", "s", -1), "\t", "t", -1)
+ }
+ actual = append(actual, descriptor{descr, width})
+ }
+
+ vt.tester.NewMkLines("Makefile", vt.input...).ForEach(func(mkline *MkLine) {
+ if !mkline.IsVarassignMaybeCommented() {
+ return
+ }
+
+ for i, input := range mkline.raw {
+ parts := NewVaralignSplitter().split(strings.TrimSuffix(input.orignl, "\n"), i == 0)
+ width = 0
+ if parts.leadingComment != "" {
+ describe(parts.leadingComment, "lead")
+ }
+ if parts.varnameOp != "" {
+ describe(parts.varnameOp, "var")
+ }
+ describeHspace(parts.spaceBeforeValue)
+ if parts.value != "" {
+ describe(parts.value, "value")
+ if parts.trailingComment != "" || parts.continuation != "" {
+ describeHspace(parts.spaceAfterValue)
+ }
+ }
+ if parts.trailingComment != "" {
+ describe(parts.trailingComment, "comment")
+ if parts.continuation != "" {
+ describeHspace(parts.spaceAfterComment)
+ }
+ }
+ if parts.continuation != "" {
+ describe(parts.continuation, "cont")
+ }
+ }
+ })
+
+ var expected []descriptor
+ testDescr := strings.SplitN(testName, "__", 2)[1]
+ for i, part := range strings.Split(testDescr, "_") {
+ m, name, widthStr := match2(part, `^([a-z]+)(\d*)$`)
+ assert(m)
+ width, err := strconv.Atoi(widthStr)
+ if err != nil || width == 0 {
+ width = 0
+ if i < len(actual) && name == actual[i].name {
+ width = actual[i].width
+ }
+ }
+ expected = append(expected, descriptor{name, width})
+ }
+
+ vt.tester.CheckDeepEquals(actual, expected)
+}
+
+func (s *Suite) Test_VaralignBlock__var_none_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=value")
vt.Internals(
"20 20")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\" with \"\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_simple_space(c *check.C) {
+// Indentations with a single space are only allowed in some very few
+// places, such as continuation lines or very long variable names.
+// In a single paragraph of its own, indentation with a single space
+// doesn't make sense, therefore it is replaced with a tab.
+func (s *Suite) Test_VaralignBlock__var_space_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= value")
vt.Internals(
"20 21")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_simple_tab(c *check.C) {
+// While indentation with a single space is allowed in a few cases,
+// indentation with several spaces is never allowed and is replaced
+// with tabs.
+func (s *Suite) Test_VaralignBlock__var_spaces7_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= several spaces")
+ vt.Internals(
+ "04 07")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
+ vt.Fixed(
+ "VAR= several spaces")
+ vt.Run()
+}
+
+// Inconsistently aligned lines for variables of the same length are
+// replaced with tabs, so that they align nicely.
+func (s *Suite) Test_VaralignBlock__var4_space5_value_var4_spaces6_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= indented with one space",
+ "VAR= indented with two spaces")
+ vt.Internals(
+ "04 05",
+ "04 06")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.",
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 9.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".")
+ vt.Fixed(
+ "VAR= indented with one space",
+ "VAR= indented with two spaces")
+ vt.Run()
+}
+
+// Generally, the value in variable assignments is aligned at the next tab.
+func (s *Suite) Test_VaralignBlock__var_tab8_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR=\tone tab")
+ vt.Internals(
+ "04 08")
+ vt.Diagnostics()
+ vt.Autofixes()
+ vt.Fixed(
+ "VAR= one tab")
+ vt.Run()
+}
+
+func (s *Suite) Test_VaralignBlock__var_tab24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue")
@@ -139,22 +299,24 @@ func (s *Suite) Test_VaralignBlock__one_line_simple_tab(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_simple_sss(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_spaces_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= value")
vt.Internals(
"20 23")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_simple_ttt(c *check.C) {
+// Having more tabs than necessary is allowed. This can be for aesthetic
+// reasons to align this paragraph with the others in the same file.
+func (s *Suite) Test_VaralignBlock__var_tabs_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\t\t\tvalue")
@@ -167,47 +329,46 @@ func (s *Suite) Test_VaralignBlock__one_line_simple_ttt(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_simple_tsts(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tsts_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\t \t value")
vt.Internals(
"20 33")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t \\t \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t \\t \" with \"\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_none(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_none_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\\",
"\tvalue")
vt.Internals(
- "20 20",
+ "20 20 20",
" 08")
vt.Diagnostics(
- // TODO: There should be a space to the left of the backslash.
- nil...)
+ "NOTE: Makefile:1: The continuation backslash should be preceded by a single space or tab.")
vt.Autofixes(
- nil...)
+ "AUTOFIX: Makefile:1: Replacing \"\" with \" \".")
vt.Fixed(
- "VVVVVVVVVVVVVVVVVVV=\\",
+ "VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_space(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_space_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
"\tvalue")
vt.Internals(
- "20 21",
+ "20 21 21",
" 08")
vt.Diagnostics()
vt.Autofixes()
@@ -217,13 +378,13 @@ func (s *Suite) Test_VaralignBlock__one_line_follow_space(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_tab(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\t\\",
"\tvalue")
vt.Internals(
- "20 24",
+ "20 24 24",
" 08")
vt.Diagnostics()
vt.Autofixes()
@@ -233,45 +394,49 @@ func (s *Suite) Test_VaralignBlock__one_line_follow_tab(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_sss(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_spaces_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
"\tvalue")
vt.Internals(
- "20 23",
+ "20 23 23",
" 08")
- vt.Diagnostics()
- vt.Autofixes()
+ vt.Diagnostics(
+ "NOTE: Makefile:1: The continuation backslash should be preceded by a single space or tab.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \" \".")
vt.Fixed(
- "VVVVVVVVVVVVVVVVVVV= \\",
+ "VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_ttt(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tabs_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\t\t\t\\",
"\tvalue")
vt.Internals(
- "20 40",
+ "20 40 40",
" 08")
- vt.Diagnostics()
- vt.Autofixes()
+ vt.Diagnostics(
+ "NOTE: Makefile:1: The continuation backslash should be preceded by a single space or tab.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \"\\t\\t\\t\" with \" \".")
vt.Fixed(
- "VVVVVVVVVVVVVVVVVVV= \\",
+ "VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_tab72(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tabs72_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\t\t\t\t\t\t\t\\",
"\tvalue")
vt.Internals(
- "20 72",
+ "20 72 72",
" 08")
vt.Diagnostics()
vt.Autofixes()
@@ -281,83 +446,67 @@ func (s *Suite) Test_VaralignBlock__one_line_follow_tab72(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_indent_none(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_space_cont_none_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
"value")
vt.Internals(
- "20 21",
+ "20 21 21",
" 00")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\" with \"\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \"\" with \"\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_indent_space(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_space_cont_space_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Internals(
- "20 21",
+ "20 21 21",
" 01")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".")
- vt.Fixed(
- "VVVVVVVVVVVVVVVVVVV= \\",
- " value")
- vt.Run()
-}
-
-func (s *Suite) Test_VaralignBlock__one_line_follow_indent_tab(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VVVVVVVVVVVVVVVVVVV= \\",
- "\tvalue")
- vt.Internals(
- "20 21",
- " 08")
- vt.Diagnostics()
- vt.Autofixes()
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_indent_sss(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_space_cont_spaces3_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Internals(
- "20 21",
+ "20 21 21",
" 03")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_indent_tt(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var20_space_cont_tabs16_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
"\t\tvalue")
vt.Internals(
- "20 21",
+ "20 21 21",
" 16")
vt.Diagnostics()
vt.Autofixes()
@@ -367,13 +516,13 @@ func (s *Suite) Test_VaralignBlock__one_line_follow_indent_tt(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_indent_ttt(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var20_space_cont_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
"\t\t\tvalue")
vt.Internals(
- "20 21",
+ "20 21 21",
" 24")
vt.Diagnostics()
vt.Autofixes()
@@ -383,71 +532,71 @@ func (s *Suite) Test_VaralignBlock__one_line_follow_indent_ttt(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_follow_indent_tsts(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var20_space_cont_tsts_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= \\",
"\t \t value")
vt.Internals(
- "20 21",
+ "20 21 21",
" 17")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t \".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t \".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t \\t \" with \"\\t\\t \".")
+ "AUTOFIX: Makefile:2: Replacing \"\\t \\t \" with \"\\t\\t \".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_none(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var20_none_value_space_cont_indent20_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=value \\",
"\t\t value")
vt.Internals(
- "20 20",
+ "20 20 26",
" 20")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 25.",
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:1: This variable value should be aligned to column 25.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\\t \" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\\t \" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_space(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var20_space_value_space_cont_spaces21_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Internals(
- "20 21",
+ "20 21 27",
" 21")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_tab(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
"\t\t\tvalue")
vt.Internals(
- "20 24",
+ "20 24 30",
" 24")
vt.Diagnostics()
vt.Autofixes()
@@ -457,33 +606,37 @@ func (s *Suite) Test_VaralignBlock__one_line_initial_tab(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_sss(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_spaces_value_space_cont_spaces_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Internals(
- "20 23",
+ "20 23 29",
" 23")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_ttt(c *check.C) {
+// The indentation is deeper than necessary, but that's ok.
+//
+// In most cases, this deeper indentation matches the other
+// paragraphs in the same file.
+func (s *Suite) Test_VaralignBlock__var_tabs40_value_space_cont_tabs40_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\t\t\tvalue \\",
"\t\t\t\t\tvalue")
vt.Internals(
- "20 40",
+ "20 40 46",
" 40")
vt.Diagnostics()
vt.Autofixes()
@@ -493,34 +646,32 @@ func (s *Suite) Test_VaralignBlock__one_line_initial_ttt(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_tab64(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_tabs64_cont_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue\t\t\t\t\t\\",
"\t\t\tvalue")
vt.Internals(
- "20 24",
+ "20 24 64",
" 24")
vt.Diagnostics(
- // FIXME: backslash indentation must be space, tab or at column 73.
- nil...)
+ // TODO: Or the continuation may be in column 73.
+ "NOTE: Makefile:1: The continuation backslash should be preceded by a single space or tab.")
vt.Autofixes(
- // FIXME: replace many tabs with a single space, since there are
- // no more backslashes in this logical line.
- nil...)
+ "AUTOFIX: Makefile:1: Replacing \"\\t\\t\\t\\t\\t\" with \" \".")
vt.Fixed(
- "VVVVVVVVVVVVVVVVVVV= value \\",
+ "VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_tab72(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_tabs72_cont_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue\t\t\t\t\t\t\\",
"\t\t\tvalue")
vt.Internals(
- "20 24",
+ "20 24 72",
" 24")
vt.Diagnostics()
vt.Autofixes()
@@ -530,137 +681,122 @@ func (s *Suite) Test_VaralignBlock__one_line_initial_tab72(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_none(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab_value_space_cont_none_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
"value")
vt.Internals(
- "20 24",
+ "20 24 30",
" 00")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \"\" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_space(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab_value_space_cont_space_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
" value")
vt.Internals(
- "20 24",
+ "20 24 30",
" 01")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_tab(c *check.C) {
+// The follow-up values should have at least the same indentation as the initial value.
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
"\tvalue")
vt.Internals(
- "20 24",
+ "20 24 30",
" 08")
vt.Diagnostics(
- nil...)
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- nil...)
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
- " value")
+ " value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_sss(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_spaces3_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
" value")
vt.Internals(
- "20 24",
+ "20 24 30",
" 03")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_tt(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_tabs16_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
"\t\tvalue")
vt.Internals(
- "20 24",
+ "20 24 30",
" 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\\t\" with \"\\t\\t\\t\".")
- vt.Fixed(
- "VVVVVVVVVVVVVVVVVVV= value \\",
- " value")
- vt.Run()
-}
-
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_ttt(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VVVVVVVVVVVVVVVVVVV=\tvalue \\",
- "\t\t\tvalue")
- vt.Internals(
- "20 24",
- " 24")
- vt.Diagnostics()
- vt.Autofixes()
+ "AUTOFIX: Makefile:2: Replacing \"\\t\\t\" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_tsts(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_tsts_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
"\t \t value")
vt.Internals(
- "20 24",
+ "20 24 30",
" 17")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t \\t \" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \"\\t \\t \" with \"\\t\\t\\t\".")
vt.Fixed(
"VVVVVVVVVVVVVVVVVVV= value \\",
" value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_plus_sss(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_indent27_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
"\t\t\t value")
vt.Internals(
- "20 24",
+ "20 24 30",
" 27")
vt.Diagnostics()
vt.Autofixes()
@@ -670,13 +806,13 @@ func (s *Suite) Test_VaralignBlock__one_line_initial_indent_plus_sss(c *check.C)
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__one_line_initial_indent_plus_tab(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_tabs32_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VVVVVVVVVVVVVVVVVVV=\tvalue \\",
"\t\t\t\tvalue")
vt.Internals(
- "20 24",
+ "20 24 30",
" 32")
vt.Diagnostics()
vt.Autofixes()
@@ -695,97 +831,8 @@ func (s *Suite) Test_VaralignBlock__one_line_initial_indent_plus_tab(c *check.C)
// TODO: add systematic tests for commented lines
-// Generally, the value in variable assignments is aligned
-// at the next tab.
-func (s *Suite) Test_VaralignBlock__one_var_tab(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VAR=\tone tab")
- vt.Internals(
- "04 08")
- vt.Diagnostics()
- vt.Autofixes()
- vt.Fixed(
- "VAR= one tab")
- vt.Run()
-}
-
-// Having more tabs than necessary is allowed. This can be for aesthetic
-// reasons to align this paragraph with the others in the same file.
-func (s *Suite) Test_VaralignBlock__one_var_tabs(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VAR=\t\t\tseveral tabs")
- vt.Internals(
- "04 24")
- vt.Diagnostics()
- vt.Autofixes()
- vt.Fixed(
- "VAR= several tabs")
- vt.Run()
-}
-
-// Indentations with a single space are only allowed in some very few
-// places, such as continuation lines or very long variable names.
-// In a single paragraph of its own, indentation with a single space
-// doesn't make sense, therefore it is replaced with a tab.
-func (s *Suite) Test_VaralignBlock__one_var_space(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VAR= indented with one space")
- vt.Internals(
- "04 05")
- vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.")
- vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
- vt.Fixed(
- "VAR= indented with one space")
- vt.Run()
-}
-
-// While indentation with a single space is allowed in a few cases,
-// indentation with several spaces is never allowed and is replaced
-// with tabs.
-func (s *Suite) Test_VaralignBlock__one_var_spaces(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VAR= several spaces")
- vt.Internals(
- "04 07")
- vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.")
- vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
- vt.Fixed(
- "VAR= several spaces")
- vt.Run()
-}
-
-// Inconsistently aligned lines for variables of the same length are
-// replaced with tabs, so that they align nicely.
-func (s *Suite) Test_VaralignBlock__two_vars__spaces(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VAR= indented with one space",
- "VAR= indented with two spaces")
- vt.Internals(
- "04 05",
- "04 06")
- vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.",
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 9.")
- vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".")
- vt.Fixed(
- "VAR= indented with one space",
- "VAR= indented with two spaces")
- vt.Run()
-}
-
// All variables in a block are aligned to the same depth.
-func (s *Suite) Test_VaralignBlock__several_vars__spaces(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var6_space_value_var7_space_value_var8_space_value_var9_space_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"GRP_A= value",
@@ -798,15 +845,15 @@ func (s *Suite) Test_VaralignBlock__several_vars__spaces(c *check.C) {
"08 09",
"09 10")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:4: This variable value should be aligned with tabs, not spaces, to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:4: This variable value should be aligned with tabs, not spaces, to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:4: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \" \" with \"\\t\".")
vt.Fixed(
"GRP_A= value",
"GRP_AA= value",
@@ -815,18 +862,21 @@ func (s *Suite) Test_VaralignBlock__several_vars__spaces(c *check.C) {
vt.Run()
}
-// Lines that are continued may be indented with a single space
-// if the first line of the variable definition has no value.
-func (s *Suite) Test_VaralignBlock__continuation(c *check.C) {
+// In lines that are continued, the continuation backslash may be separated
+// with a single space if the first line contains only the backslash, but
+// not a variable value.
+func (s *Suite) Test_VaralignBlock__var4_space_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VAR= \\",
"\tvalue")
vt.Internals(
- "04 05",
+ "04 05 05",
" 08")
- vt.Diagnostics()
- vt.Autofixes()
+ vt.Diagnostics(
+ nil...)
+ vt.Autofixes(
+ nil...)
vt.Fixed(
"VAR= \\",
" value")
@@ -837,7 +887,7 @@ func (s *Suite) Test_VaralignBlock__continuation(c *check.C) {
// The second line is further to the right but doesn't count as
// an outlier since it is not far enough.
// Adding one more tab to the indentation is generally considered ok.
-func (s *Suite) Test_VaralignBlock__short_tab__long_space(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var_space15_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"BLOCK=\tindented with tab",
@@ -846,11 +896,11 @@ func (s *Suite) Test_VaralignBlock__short_tab__long_space(c *check.C) {
"06 08",
"14 15")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".")
vt.Fixed(
"BLOCK= indented with tab",
"BLOCK_LONGVAR= indented with space")
@@ -859,7 +909,7 @@ func (s *Suite) Test_VaralignBlock__short_tab__long_space(c *check.C) {
// When the indentation differs, the indentation is adjusted to the
// minimum necessary.
-func (s *Suite) Test_VaralignBlock__short_long__tab(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var14_tabs40_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"BLOCK=\tshort",
@@ -868,11 +918,11 @@ func (s *Suite) Test_VaralignBlock__short_long__tab(c *check.C) {
"06 08",
"14 40")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:2: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\\t\\t\\t\" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\\t\\t\\t\" with \"\\t\".")
vt.Fixed(
"BLOCK= short",
"BLOCK_LONGVAR= long")
@@ -882,7 +932,7 @@ func (s *Suite) Test_VaralignBlock__short_long__tab(c *check.C) {
// For differing indentation, it doesn't matter whether the indentation
// is done with tabs or with spaces. It is aligned to the minimum
// necessary depth.
-func (s *Suite) Test_VaralignBlock__space_and_tab(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_spaces8_value_var_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VAR= space",
@@ -891,69 +941,55 @@ func (s *Suite) Test_VaralignBlock__space_and_tab(c *check.C) {
"04 08",
"04 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.")
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
vt.Fixed(
"VAR= space",
"VAR= tab ${VAR}")
vt.Run()
}
-// There must always be a visible space between the assignment operator
+// There should always be a visible space between the assignment operator
// and the value.
-func (s *Suite) Test_VaralignBlock__no_space_at_all(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var17_none_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"PKG_FAIL_REASON+=\"Message\"")
vt.Internals(
"17 17")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\" with \"\\t\".")
vt.Fixed(
"PKG_FAIL_REASON+= \"Message\"")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__empty_continuation_in_column_1(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var4_space_cont_none_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VAR= \\",
"no indentation")
vt.Internals(
- "04 05",
+ "04 05 05",
" 00")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\" with \"\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \"\" with \"\\t\".")
vt.Fixed(
"VAR= \\",
" no indentation")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__empty_continuation_in_column_9(c *check.C) {
- vt := NewVaralignTester(s, c)
- vt.Input(
- "VAR= \\",
- "\tminimum indentation")
- vt.Internals(
- "04 05",
- " 08")
- vt.Diagnostics(
- nil...)
- vt.Autofixes(
- nil...)
- vt.Fixed(
- "VAR= \\",
- " minimum indentation")
- vt.Run()
-}
-
-func (s *Suite) Test_VaralignBlock__empty_continuation_space(c *check.C) {
+// The simple variable assignment in line 1 determines the main alignment
+// column, to which line 2 is then aligned.
+//
+// If the variable name in line 2 were longer, the space would be ok.
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var4_space_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"REF=\tvalue",
@@ -961,12 +997,12 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_space(c *check.C) {
"\tminimum indentation")
vt.Internals(
"04 08",
- "04 05",
+ "04 05 05",
" 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 9.")
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 9.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".")
vt.Fixed(
"REF= value",
"VAR= \\",
@@ -974,7 +1010,7 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_space(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__empty_continuation_properly_indented(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var_tab8_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"REF=\tvalue",
@@ -982,7 +1018,7 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_properly_indented(c *chec
"\tminimum indentation")
vt.Internals(
"04 08",
- "04 08",
+ "04 08 08",
" 08")
vt.Diagnostics()
vt.Autofixes()
@@ -993,7 +1029,10 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_properly_indented(c *chec
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__empty_continuation_too_narrow(c *check.C) {
+// The difference in the variable names is 10 characters, but when aligned
+// with tabs, their values only differ by a single tab. This is not enough
+// to count as an outlier.
+func (s *Suite) Test_VaralignBlock__var14_tab_value_var4_tab_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"LONG_VARIABLE=\tvalue",
@@ -1001,12 +1040,12 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_too_narrow(c *check.C) {
"\tminimum indentation")
vt.Internals(
"14 16",
- "04 08",
+ "04 08 08",
" 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:2: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"LONG_VARIABLE= value",
"VAR= \\",
@@ -1016,7 +1055,7 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_too_narrow(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__empty_continuation_too_wide(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var14_tab_value_var21_tab_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"LONG_VARIABLE=\tvalue",
@@ -1024,7 +1063,7 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_too_wide(c *check.C) {
"\tminimum indentation")
vt.Internals(
"14 16",
- "21 24",
+ "21 24 24",
" 08")
vt.Diagnostics(
nil...)
@@ -1044,7 +1083,7 @@ func (s *Suite) Test_VaralignBlock__empty_continuation_too_wide(c *check.C) {
// It only contains a backslash, without the usual space to the left.
// This space should be inserted to the left of the backslash.
// Everything else is fine.
-func (s *Suite) Test_VaralignBlock__outlier_in_follow_continuation(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var12_tabs24_value_var38_none_cont_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"PATCHFILES+=\t\temacs20-jumbo-patch-20170723.gz",
@@ -1052,12 +1091,12 @@ func (s *Suite) Test_VaralignBlock__outlier_in_follow_continuation(c *check.C) {
"\t\t\thttp://www.NetBSD.org/~dholland/patchkits/emacs20/")
vt.Internals(
"12 24",
- "38 38",
+ "38 38 38",
" 24")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This outlier variable value should be aligned with a single space.")
+ "NOTE: Makefile:2: The continuation backslash should be preceded by a single space or tab.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\" with \" \".")
+ "AUTOFIX: Makefile:2: Replacing \"\" with \" \".")
vt.Fixed(
"PATCHFILES+= emacs20-jumbo-patch-20170723.gz",
"SITES.emacs20-jumbo-patch-20170723.gz= \\",
@@ -1065,7 +1104,7 @@ func (s *Suite) Test_VaralignBlock__outlier_in_follow_continuation(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__continuation_lines(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab16_value_var_space_cont_tabs24_value_var_tabs32_value_var11_space_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"DISTFILES+=\tvalue",
@@ -1075,20 +1114,20 @@ func (s *Suite) Test_VaralignBlock__continuation_lines(c *check.C) {
"DISTFILES+= value")
vt.Internals(
"11 16",
- "11 12",
+ "11 12 12",
" 24",
"11 32",
"11 12")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:3: This continuation line should be indented with \"\\t\\t\".",
- "NOTE: ~/Makefile:4: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:5: This variable value should be aligned with tabs, not spaces, to column 17.")
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:3: This continuation line should be indented with \"\\t\\t\".",
+ "NOTE: Makefile:4: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:5: This variable value should be aligned with tabs, not spaces, to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \"\\t\\t\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:4: Replacing \"\\t\\t\\t\" with \"\\t\".",
- "AUTOFIX: ~/Makefile:5: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \"\\t\\t\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \"\\t\\t\\t\" with \"\\t\".",
+ "AUTOFIX: Makefile:5: Replacing \" \" with \"\\t\".")
vt.Fixed(
"DISTFILES+= value",
"DISTFILES+= \\",
@@ -1115,7 +1154,7 @@ func (s *Suite) Test_VaralignBlock__continuation_lines(c *check.C) {
// indentation is the same, this logical line doesn't count as an outlier.
//
// Because line 2--3 is not an outlier, line 1 is realigned to column 25.
-func (s *Suite) Test_VaralignBlock__continuation_line_one_tab_ahead(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tabs16_value_var_tab24_value_space_cont_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"VAR=\t\tvalue",
@@ -1123,12 +1162,12 @@ func (s *Suite) Test_VaralignBlock__continuation_line_one_tab_ahead(c *check.C)
"\t\t\thttps://example.org")
vt.Internals(
"04 16",
- "18 24",
+ "18 24 44",
" 24")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\\t\" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\\t\" with \"\\t\\t\\t\".")
vt.Fixed(
"VAR= value",
"MASTER_SITE_NEDIT= https://example.org \\",
@@ -1144,7 +1183,7 @@ func (s *Suite) Test_VaralignBlock__continuation_line_one_tab_ahead(c *check.C)
// As soon as the V2 value would be properly indented with a tab, the
// visual difference would not be as much, therefore the current
// behavior is appropriate.
-func (s *Suite) Test_VaralignBlock__outlier_more_than_8_spaces(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var_tab16_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V2=\tvalue",
@@ -1153,9 +1192,9 @@ func (s *Suite) Test_VaralignBlock__outlier_more_than_8_spaces(c *check.C) {
"03 08",
"15 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"V2= value",
"V0000000000014= value")
@@ -1165,13 +1204,13 @@ func (s *Suite) Test_VaralignBlock__outlier_more_than_8_spaces(c *check.C) {
// Ensures that a wrong warning introduced in ccb56a5 is not logged.
// The warning was about continuation lines that should be reindented.
// In this case though, everything is already perfectly aligned.
-func (s *Suite) Test_VaralignBlock__aligned_continuation(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab16_value_space_cont_tabs16_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"USE_TOOLS+=\t[ awk \\",
"\t\tsed")
vt.Internals(
- "11 16",
+ "11 16 22",
" 16")
vt.Diagnostics()
vt.Autofixes()
@@ -1184,7 +1223,7 @@ func (s *Suite) Test_VaralignBlock__aligned_continuation(c *check.C) {
// Shell commands in continuation lines are assumed to be already nicely indented.
// This particular example is not, but pkglint cannot decide this as of
// version 5.7.14 (July 2019).
-func (s *Suite) Test_VaralignBlock__shell_command(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_var20_tabs72_cont_tab_value_tabs72_cont_tabs16_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"USE_BUILTIN.Xfixes=\tyes",
@@ -1193,8 +1232,8 @@ func (s *Suite) Test_VaralignBlock__shell_command(c *check.C) {
"\t\t:; else :; fi")
vt.Internals(
"19 24",
- "20 72",
- " 08",
+ "20 72 72",
+ " 08 72",
" 16")
vt.Diagnostics(
nil...)
@@ -1208,18 +1247,21 @@ func (s *Suite) Test_VaralignBlock__shell_command(c *check.C) {
vt.Run()
}
+// For escaped variable names, the number of actual characters in the
+// Makefile is relevant for indenting the source code. Therefore, the
+// parsed an unescaped mkline.Varname cannot be used here.
func (s *Suite) Test_VaralignBlock__escaped_varname(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.${v:S,\\#,,g}=\tvalue",
"V2345678123456781234=\tvalue")
vt.Internals(
- "15 16", // 15, since the number sign is not escaped when computing the indentation
+ "15 16", // 15, since the number sign is still escaped when computing the indentation
"21 24")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"V.${v:S,\\#,,g}= value", // looks misaligned because of the backslash
"V2345678123456781234= value")
@@ -1230,7 +1272,7 @@ func (s *Suite) Test_VaralignBlock__escaped_varname(c *check.C) {
// values in the continuation lines, one value per line, all indented to the same depth.
// The depth is either a single tab (see the test below) or aligns with the other
// variables in the paragraph (this test).
-func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_second_line(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var_tab16_value_var28_space_cont_tabs24_value_space_cont_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"WRKSRC=\t${WRKDIR}",
@@ -1241,17 +1283,17 @@ func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_second_line(c *
vt.Internals(
"07 08",
"10 16",
- "28 29",
- " 24",
+ "28 29 29",
+ " 24 52",
" 24")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:4: This continuation line should be indented with \"\\t\\t\".",
- "NOTE: ~/Makefile:5: This continuation line should be indented with \"\\t\\t\".")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:4: This continuation line should be indented with \"\\t\\t\".",
+ "NOTE: Makefile:5: This continuation line should be indented with \"\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:4: Replacing \"\\t\\t\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:5: Replacing \"\\t\\t\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \"\\t\\t\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:5: Replacing \"\\t\\t\\t\" with \"\\t\\t\".")
vt.Fixed(
"WRKSRC= ${WRKDIR}",
"DISTFILES= distfile-1.0.0.tar.gz",
@@ -1265,7 +1307,7 @@ func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_second_line(c *
// values in the continuation lines, one value per line, all indented to the same depth.
// The depth is either a single tab (this test) or aligns with the other
// variables in the paragraph (see the test above).
-func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_second_line_with_single_tab(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var_tab16_value_var28_space_cont_tab_value_space_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"WRKSRC=\t${WRKDIR}",
@@ -1276,13 +1318,13 @@ func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_second_line_wit
vt.Internals(
"07 08",
"10 16",
- "28 29",
- " 08",
+ "28 29 29",
+ " 08 36",
" 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"WRKSRC= ${WRKDIR}",
"DISTFILES= distfile-1.0.0.tar.gz",
@@ -1303,7 +1345,7 @@ func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_second_line_wit
// The whole paragraph could be indented less by making the SITES line a
// pure continuation line, having only a backslash in its first line. Then
// everything could be aligned to column 17.
-func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_first_line(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var_tab16_value_var28_tab_value_space_cont_tabs32_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"WRKSRC=\t${WRKDIR}",
@@ -1313,14 +1355,14 @@ func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_first_line(c *c
vt.Internals(
"07 08",
"10 16",
- "28 32",
+ "28 32 60",
" 32")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 33.",
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 33.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 33.",
+ "NOTE: Makefile:2: This variable value should be aligned to column 33.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\\t\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\" with \"\\t\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\\t\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\\t\".")
vt.Fixed(
"WRKSRC= ${WRKDIR}",
"DISTFILES= distfile-1.0.0.tar.gz",
@@ -1334,7 +1376,7 @@ func (s *Suite) Test_VaralignBlock__continuation_value_starts_in_first_line(c *c
// other lines. The lines that are indented further should keep their
// relative indentation depth, no matter if that is done with spaces or
// with tabs.
-func (s *Suite) Test_VaralignBlock__continuation_mixed_indentation_in_second_line(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var_tab16_value_var13_space_cont_indent34_value_space_cont_indent36_value_space_cont_indent34_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"WRKSRC=\t${WRKDIR}",
@@ -1346,22 +1388,22 @@ func (s *Suite) Test_VaralignBlock__continuation_mixed_indentation_in_second_lin
vt.Internals(
"07 08",
"10 16",
- "13 14",
- " 34",
- " 36",
+ "13 14 14",
+ " 34 45",
+ " 36 46",
" 34")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:4: This continuation line should be indented with \"\\t\\t\".",
- "NOTE: ~/Makefile:5: This continuation line should be indented with \"\\t\\t \".",
- "NOTE: ~/Makefile:6: This continuation line should be indented with \"\\t\\t\".")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:4: This continuation line should be indented with \"\\t\\t\".",
+ "NOTE: Makefile:5: This continuation line should be indented with \"\\t\\t \".",
+ "NOTE: Makefile:6: This continuation line should be indented with \"\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:4: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:5: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t \".",
- "AUTOFIX: ~/Makefile:6: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:5: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t \".",
+ "AUTOFIX: Makefile:6: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t\".")
vt.Fixed(
"WRKSRC= ${WRKDIR}",
"DISTFILES= distfile-1.0.0.tar.gz",
@@ -1387,19 +1429,19 @@ func (s *Suite) Test_VaralignBlock__continuation_mixed_indentation_in_first_line
vt.Internals(
"07 08",
"10 16",
- "13 34",
- " 36",
+ "13 34 45",
+ " 36 46",
" 34")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:4: This continuation line should be indented with \"\\t\\t\".",
- "NOTE: ~/Makefile:5: This continuation line should be indented with \"\\t\\t\".")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:4: This continuation line should be indented with \"\\t\\t\".",
+ "NOTE: Makefile:5: This continuation line should be indented with \"\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \"\\t\\t\\t \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:4: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t \".",
- "AUTOFIX: ~/Makefile:5: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \"\\t\\t\\t \" with \"\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t \".",
+ "AUTOFIX: Makefile:5: Replacing \"\\t\\t\\t\\t \" with \"\\t\\t\".")
vt.Fixed(
"WRKSRC= ${WRKDIR}",
"DISTFILES= distfile-1.0.0.tar.gz",
@@ -1410,9 +1452,6 @@ func (s *Suite) Test_VaralignBlock__continuation_mixed_indentation_in_first_line
}
func (s *Suite) Test_VaralignBlock__follow_up_indented_with_spaces(c *check.C) {
- // FIXME: warn about the misleading empty line 6,
- // but not in this test
-
vt := NewVaralignTester(s, c)
vt.Input(
"DISTFILES= \\",
@@ -1422,20 +1461,20 @@ func (s *Suite) Test_VaralignBlock__follow_up_indented_with_spaces(c *check.C) {
"\tand a tab \\",
" ") // trailing whitespace
vt.Internals(
- "10 11",
- " 01",
- " 03",
- " 08",
- " 08",
+ "10 11 11",
+ " 01 11",
+ " 03 16",
+ " 08 21",
+ " 08 18",
" 03")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".",
- "NOTE: ~/Makefile:3: This continuation line should be indented with \"\\t\".",
- "NOTE: ~/Makefile:4: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\".",
+ "NOTE: Makefile:3: This continuation line should be indented with \"\\t\".",
+ "NOTE: Makefile:4: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:4: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \" \" with \"\\t\".")
vt.Fixed(
"DISTFILES= \\",
" one space \\",
@@ -1450,8 +1489,9 @@ func (s *Suite) Test_VaralignBlock__follow_up_indented_with_spaces(c *check.C) {
// fix the whole block to use the indentation of the second-longest line.
// In this case, all of the remaining lines have the same indentation
// (as there is only 1 line at all).
-// Therefore this existing indentation is used instead of the minimum necessary, which would only be a single tab.
-func (s *Suite) Test_VaralignBlock__tab_outlier(c *check.C) {
+// Therefore this existing indentation is used instead of the minimum
+// necessary, which would only be a single tab.
+func (s *Suite) Test_VaralignBlock__var_tabs24_value_var45_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"DISTFILES=\t\tvery-very-very-very-long-distfile-name",
@@ -1469,10 +1509,9 @@ func (s *Suite) Test_VaralignBlock__tab_outlier(c *check.C) {
// The SITES.* definition is indented less than the other lines,
// therefore the whole paragraph will be realigned to that depth.
-func (s *Suite) Test_VaralignBlock__multiline(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_spaces24_value_space_cont_spaces24_value_var_spaces16_value_var_spaces24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
- "DIST_SUBDIR= asc",
"DISTFILES= ${DISTNAME}${EXTRACT_SUFX} frontiers.mp3 \\",
" machine_wars.mp3 time_to_strike.mp3",
".for file in frontiers.mp3 machine_wars.mp3 time_to_strike.mp3",
@@ -1480,25 +1519,21 @@ func (s *Suite) Test_VaralignBlock__multiline(c *check.C) {
".endfor",
"WRKSRC= ${WRKDIR}/${PKGNAME_NOREV}")
vt.Internals(
- "12 24",
- "10 24",
+ "10 24 65",
" 24",
"14 16",
"07 24")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:3: This continuation line should be indented with \"\\t\\t\".",
- "NOTE: ~/Makefile:5: Variable values should be aligned with tabs, not spaces.",
- "NOTE: ~/Makefile:7: This variable value should be aligned with tabs, not spaces, to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\".",
+ "NOTE: Makefile:4: Variable values should be aligned with tabs, not spaces.",
+ "NOTE: Makefile:6: This variable value should be aligned with tabs, not spaces, to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:5: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:7: Replacing \" \" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:6: Replacing \" \" with \"\\t\\t\".")
vt.Fixed(
- "DIST_SUBDIR= asc",
"DISTFILES= ${DISTNAME}${EXTRACT_SUFX} frontiers.mp3 \\",
" machine_wars.mp3 time_to_strike.mp3",
".for file in frontiers.mp3 machine_wars.mp3 time_to_strike.mp3",
@@ -1508,41 +1543,31 @@ func (s *Suite) Test_VaralignBlock__multiline(c *check.C) {
vt.Run()
}
-// The CDROM variables align exactly at a tab position, therefore they must
+// The CDROM variable aligns exactly at a tab position, therefore it must
// be indented by at least one more space. Since that one space is not
-// enough to count as an outlier, everything is indented by one more tab.
-func (s *Suite) Test_VaralignBlock__single_space(c *check.C) {
+// enough to count as an outlier, each line is indented by one more tab.
+func (s *Suite) Test_VaralignBlock__var_tab16_value_var16_space_value_var14_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"RESTRICTED=\tDo not sell, do not rent",
"NO_BIN_ON_CDROM= ${RESTRICTED}",
- "NO_BIN_ON_FTP=\t${RESTRICTED}",
- "NO_SRC_ON_CDROM= ${RESTRICTED}",
- "NO_SRC_ON_FTP=\t${RESTRICTED}")
+ "NO_BIN_ON_FTP=\t${RESTRICTED}")
vt.Internals(
"11 16",
"16 17",
- "14 16",
- "16 17",
"14 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 25.",
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 25.",
- "NOTE: ~/Makefile:3: This variable value should be aligned to column 25.",
- "NOTE: ~/Makefile:4: This variable value should be aligned with tabs, not spaces, to column 25.",
- "NOTE: ~/Makefile:5: This variable value should be aligned to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 25.",
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 25.",
+ "NOTE: Makefile:3: This variable value should be aligned to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:4: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:5: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"RESTRICTED= Do not sell, do not rent",
"NO_BIN_ON_CDROM= ${RESTRICTED}",
- "NO_BIN_ON_FTP= ${RESTRICTED}",
- "NO_SRC_ON_CDROM= ${RESTRICTED}",
- "NO_SRC_ON_FTP= ${RESTRICTED}")
+ "NO_BIN_ON_FTP= ${RESTRICTED}")
vt.Run()
}
@@ -1550,7 +1575,7 @@ func (s *Suite) Test_VaralignBlock__single_space(c *check.C) {
// The spaces are replaced with tabs, which makes the indentation 4 spaces deeper in the first paragraph.
// In the second paragraph it's even 7 additional spaces.
// This is ok though since it is the prevailing indentation style in pkgsrc.
-func (s *Suite) Test_VaralignBlock__only_space(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var16_space_value_var16_space_value_var16_space_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"REPLACE_PYTHON+= *.py",
@@ -1561,13 +1586,13 @@ func (s *Suite) Test_VaralignBlock__only_space(c *check.C) {
"16 17",
"16 17")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
- "NOTE: ~/Makefile:2: This variable value should be aligned with tabs, not spaces, to column 25.",
- "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 25.",
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\".")
vt.Fixed(
"REPLACE_PYTHON+= *.py",
"REPLACE_PYTHON+= lib/*.py",
@@ -1581,7 +1606,7 @@ func (s *Suite) Test_VaralignBlock__only_space(c *check.C) {
//
// As of December 2018, pkglint only looks at a single paragraph at a time,
// therefore it cannot reliably decide whether this deep indentation is necessary.
-func (s *Suite) Test_VaralignBlock__mixed_tabs_and_spaces_same_column(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_spaces24_value_var_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"DISTFILES+= space",
@@ -1590,9 +1615,9 @@ func (s *Suite) Test_VaralignBlock__mixed_tabs_and_spaces_same_column(c *check.C
"11 24",
"11 24")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.")
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\\t\".")
vt.Fixed(
"DISTFILES+= space",
"DISTFILES+= tab")
@@ -1600,7 +1625,7 @@ func (s *Suite) Test_VaralignBlock__mixed_tabs_and_spaces_same_column(c *check.C
}
// Both lines are indented to the same column. Therefore none of them is considered an outlier.
-func (s *Suite) Test_VaralignBlock__outlier_1(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var2_space_value_var2_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V= value",
@@ -1609,9 +1634,9 @@ func (s *Suite) Test_VaralignBlock__outlier_1(c *check.C) {
"02 03",
"02 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
vt.Fixed(
"V= value",
"V= value")
@@ -1619,7 +1644,7 @@ func (s *Suite) Test_VaralignBlock__outlier_1(c *check.C) {
}
// A single space that ends at the same depth as a tab is replaced with a tab, for consistency.
-func (s *Suite) Test_VaralignBlock__outlier_2(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var7_space_value_var2_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.0008= value",
@@ -1628,9 +1653,9 @@ func (s *Suite) Test_VaralignBlock__outlier_2(c *check.C) {
"07 08",
"02 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.")
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
vt.Fixed(
"V.0008= value",
"V= value")
@@ -1641,7 +1666,7 @@ func (s *Suite) Test_VaralignBlock__outlier_2(c *check.C) {
// that is indented with a space. This is because space-indented lines are
// only allowed when their indentation is much deeper than the tab-indented
// ones (so-called outliers), or as the first line of a continuation line.
-func (s *Suite) Test_VaralignBlock__outlier_3(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var8_space_value_var2_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.00009= value",
@@ -1650,11 +1675,11 @@ func (s *Suite) Test_VaralignBlock__outlier_3(c *check.C) {
"08 09",
"02 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:2: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"V.00009= value",
"V= value")
@@ -1664,7 +1689,7 @@ func (s *Suite) Test_VaralignBlock__outlier_3(c *check.C) {
// This space-indented line doesn't count as an outlier yet because it
// is only a single tab away. The limit is two tabs.
// Therefore both lines are indented with tabs.
-func (s *Suite) Test_VaralignBlock__outlier_4(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var15_space_value_var2_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.000000000016= value",
@@ -1673,11 +1698,11 @@ func (s *Suite) Test_VaralignBlock__outlier_4(c *check.C) {
"15 16",
"02 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.",
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.",
+ "NOTE: Makefile:2: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"V.000000000016= value",
"V= value")
@@ -1688,7 +1713,7 @@ func (s *Suite) Test_VaralignBlock__outlier_4(c *check.C) {
// tab-indented line. The latter would require 2 tabs to align to the former.
// Therefore the short line is not indented to the long line, in order to
// keep the indentation reasonably short for a large amount of the lines.
-func (s *Suite) Test_VaralignBlock__outlier_5(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var16_space_value_var2_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.0000000000017= value",
@@ -1705,7 +1730,7 @@ func (s *Suite) Test_VaralignBlock__outlier_5(c *check.C) {
}
// Short space-indented lines do not count as outliers. They are are aligned to the longer tab-indented line.
-func (s *Suite) Test_VaralignBlock__outlier_6(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var2_space_value_var9_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V= value",
@@ -1714,9 +1739,9 @@ func (s *Suite) Test_VaralignBlock__outlier_6(c *check.C) {
"02 03",
"09 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\\t\".")
vt.Fixed(
"V= value",
"V.000010= value")
@@ -1724,7 +1749,7 @@ func (s *Suite) Test_VaralignBlock__outlier_6(c *check.C) {
}
// The long line is not an outlier but very close. One more space, and it would count.
-func (s *Suite) Test_VaralignBlock__outlier_10(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var22_space_value_var9_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.0000000000000000023= value", // Adjust from 23 to 24 (+ 1 tab)
@@ -1733,18 +1758,18 @@ func (s *Suite) Test_VaralignBlock__outlier_10(c *check.C) {
"22 23",
"09 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
+ "NOTE: Makefile:2: This variable value should be aligned to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"V.0000000000000000023= value",
"V.000010= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__outlier_11(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var23_space_value_var9_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.00000000000000000024= value", // Keep at 24 (space to tab)
@@ -1753,18 +1778,18 @@ func (s *Suite) Test_VaralignBlock__outlier_11(c *check.C) {
"23 24",
"09 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.",
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 25.")
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.",
+ "NOTE: Makefile:2: This variable value should be aligned to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"V.00000000000000000024= value",
"V.000010= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__outlier_12(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var24_space_value_var9_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.000000000000000000025= value", // Keep at 25 (outlier)
@@ -1782,7 +1807,7 @@ func (s *Suite) Test_VaralignBlock__outlier_12(c *check.C) {
// When the lines are indented inconsistently, the indentation is reduced
// to the required minimum.
-func (s *Suite) Test_VaralignBlock__outlier_14(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tabs24_value_var_tabs40_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"V.00008=\t\tvalue", // Adjust from 24 to 16 (removes 1 tab)
@@ -1791,18 +1816,18 @@ func (s *Suite) Test_VaralignBlock__outlier_14(c *check.C) {
"08 24",
"08 40")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:2: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:2: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\\t\" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \"\\t\\t\\t\\t\" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\\t\" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\\t\\t\\t\" with \"\\t\".")
vt.Fixed(
"V.00008= value",
"V.00008= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__outlier_with_several_spaces(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab8_value_var29_spaces32_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"SHORT=\tvalue",
@@ -1811,16 +1836,16 @@ func (s *Suite) Test_VaralignBlock__outlier_with_several_spaces(c *check.C) {
"06 08",
"29 32")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This outlier variable value should be aligned with a single space.")
+ "NOTE: Makefile:2: This outlier variable value should be aligned with a single space.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \" \".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \" \".")
vt.Fixed(
"SHORT= value",
"VERY_VERY_LONG_VARIABLE_NAME= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__single_space_in_short_line(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var6_space_value_var10_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"SHORT= value",
@@ -1829,16 +1854,16 @@ func (s *Suite) Test_VaralignBlock__single_space_in_short_line(c *check.C) {
"06 07",
"10 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\\t\".")
vt.Fixed(
"SHORT= value",
"LONG_NAME= value")
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__outlier_without_space(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var6_tab_value_var21_none_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"SHORT=\tvalue",
@@ -1847,9 +1872,9 @@ func (s *Suite) Test_VaralignBlock__outlier_without_space(c *check.C) {
"06 08",
"21 21")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This outlier variable value should be aligned with a single space.")
+ "NOTE: Makefile:2: This outlier variable value should be aligned with a single space.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \"\" with \" \".")
+ "AUTOFIX: Makefile:2: Replacing \"\" with \" \".")
vt.Fixed(
"SHORT= value",
"LONG.678901234567890= value")
@@ -1858,9 +1883,10 @@ func (s *Suite) Test_VaralignBlock__outlier_without_space(c *check.C) {
// The INSTALLATION_DIRS line is so long that it is considered an outlier,
// since compared to the DIST line, it is at least two tabs away.
+//
// Pkglint before 2018-01-26 suggested that it "should be aligned to column 9",
// which is not possible since the variable name is already longer.
-func (s *Suite) Test_VaralignBlock__long_short(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_var_tab8_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"INSTALLATION_DIRS=\tbin",
@@ -1885,14 +1911,12 @@ func (s *Suite) Test_VaralignBlock__long_short(c *check.C) {
// FIXME: The definition of an outlier should be based on the actual indentation,
// not on the minimum indentation. Or maybe even better on the corrected indentation.
// In the below paragraph, the outlier is not indented enough to qualify as a visual outlier.
-func (s *Suite) Test_VaralignBlock__tabbed_outlier(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab32_value_var_tabs24_value_var_tabs24_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
- ".if !empty(PKG_OPTIONS:Minspircd-sqloper)",
- "INSPIRCD_STORAGE_DRIVER?=\tmysql",
- "MODULES+=\t\tm_sqloper.cpp m_sqlutils.cpp",
- "HEADERS+=\t\tm_sqlutils.h",
- ".endif")
+ "INSPIRCD_STORAGE_DRIVER?=\tvalue",
+ "MODULES+=\t\tvalue",
+ "HEADERS+=\t\tvalue")
vt.Internals(
"25 32",
"09 24",
@@ -1900,11 +1924,9 @@ func (s *Suite) Test_VaralignBlock__tabbed_outlier(c *check.C) {
vt.Diagnostics()
vt.Autofixes()
vt.Fixed(
- ".if !empty(PKG_OPTIONS:Minspircd-sqloper)",
- "INSPIRCD_STORAGE_DRIVER?= mysql",
- "MODULES+= m_sqloper.cpp m_sqlutils.cpp",
- "HEADERS+= m_sqlutils.h",
- ".endif")
+ "INSPIRCD_STORAGE_DRIVER?= value",
+ "MODULES+= value",
+ "HEADERS+= value")
vt.Run()
}
@@ -1913,15 +1935,15 @@ func (s *Suite) Test_VaralignBlock__tabbed_outlier(c *check.C) {
//
// TODO: Make this rule more general: if the indentation of the continuation
// lines is more than the initial line, it is intentional.
-func (s *Suite) Test_VaralignBlock__indented_continuation_line(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tab24_value_space_cont_tabs32_value_space_cont_tabs32_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"CONF_FILES_PERMS=\tsource \\",
"\t\t\t\tdestination \\",
"\t\t\t\tuser group 0644")
vt.Internals(
- "17 24",
- " 32",
+ "17 24 31",
+ " 32 44",
" 32")
vt.Diagnostics()
vt.Autofixes()
@@ -1932,7 +1954,7 @@ func (s *Suite) Test_VaralignBlock__indented_continuation_line(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock__indented_continuation_line_in_paragraph(c *check.C) {
+func (s *Suite) Test_VaralignBlock__var_tabs24_value_var_tab24_value_var_space_cont_tab_value_space_cont_tab_value_space_cont_tab_value(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
"SUBST_CLASSES+=\t\tfix",
@@ -1944,14 +1966,14 @@ func (s *Suite) Test_VaralignBlock__indented_continuation_line_in_paragraph(c *c
vt.Internals(
"15 24",
"16 24",
- "14 15",
- " 08",
- " 08",
+ "14 15 15",
+ " 08 23",
+ " 08 23",
" 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 25.")
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 25.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\\t\".")
vt.Fixed(
"SUBST_CLASSES+= fix",
"SUBST_STAGE.fix= post-patch",
@@ -1962,6 +1984,24 @@ func (s *Suite) Test_VaralignBlock__indented_continuation_line_in_paragraph(c *c
vt.Run()
}
+func (s *Suite) Test_VaralignBlock__lead_var_tab8_value_lead_var_tab16_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "#VAR=\tvalue",
+ "#VAR.param=\tvalue")
+ vt.Internals(
+ "05 08",
+ "11 16")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".")
+ vt.Fixed(
+ "#VAR= value",
+ "#VAR.param= value")
+ vt.Run()
+}
+
// Up to 2018-01-27, it could happen that some source code was logged
// without a corresponding diagnostic. This was unintended and confusing.
func (s *Suite) Test_VaralignBlock__fix_without_diagnostic(c *check.C) {
@@ -1974,9 +2014,9 @@ func (s *Suite) Test_VaralignBlock__fix_without_diagnostic(c *check.C) {
"\t\t\tRUBY_NAME=${RUBY_NAME:Q}")
vt.Internals(
"15 24",
- "13 24",
- " 24",
- " 24",
+ "13 24 57",
+ " 24 61",
+ " 24 63",
" 24")
vt.Diagnostics()
vt.Autofixes()
@@ -1996,7 +2036,6 @@ func (s *Suite) Test_VaralignBlock__fix_without_diagnostic(c *check.C) {
func (s *Suite) Test_VaralignBlock__continuation_line_last_empty(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
- // FIXME: Add a test for MkParser to warn about this apparently empty line.
"DISTFILES= \\",
"\ta \\",
"\tb \\",
@@ -2004,16 +2043,16 @@ func (s *Suite) Test_VaralignBlock__continuation_line_last_empty(c *check.C) {
"", // This is the final line of the variable assignment.
"NEXT_VAR=\tsecond line")
vt.Internals(
- "10 11",
- " 08",
- " 08",
- " 08",
+ "10 11 11",
+ " 08 10",
+ " 08 10",
+ " 08 10",
" 00",
"09 16")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
vt.Fixed(
"DISTFILES= \\",
" a \\",
@@ -2042,21 +2081,21 @@ func (s *Suite) Test_VaralignBlock__realign_commented_single_lines(c *check.C) {
vt.Internals(
"06 08",
"11 16",
- "14 15",
+ "14 15 15",
" 16",
- "16 17",
+ "16 17 17",
" 01",
"06 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned to column 17.",
- "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
- "NOTE: ~/Makefile:6: This continuation line should be indented with \"\\t\\t\".",
- "NOTE: ~/Makefile:7: This variable value should be aligned to column 17.")
+ "NOTE: Makefile:1: This variable value should be aligned to column 17.",
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:6: This continuation line should be indented with \"\\t\\t\".",
+ "NOTE: Makefile:7: This variable value should be aligned to column 17.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:6: Replacing \"\" with \"\\t\\t\".",
- "AUTOFIX: ~/Makefile:7: Replacing \"\\t\" with \"\\t\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \"\\t\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:6: Replacing \"\" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:7: Replacing \"\\t\" with \"\\t\\t\".")
vt.Fixed(
"SHORT= value",
"#DISTFILES= distfile-1.0.0.tar.gz",
@@ -2083,15 +2122,15 @@ func (s *Suite) Test_VaralignBlock__realign_commented_continuation_line(c *check
"AFTER=\tafter")
vt.Internals(
"07 08",
- "11 12",
- " 08",
- " 08",
- " 08",
+ "11 12 12",
+ " 08 15",
+ " 08 15",
+ " 08 15",
" 00")
vt.Diagnostics(
- "NOTE: ~/Makefile:6: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:6: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:6: Replacing \"\" with \"\\t\".")
+ "AUTOFIX: Makefile:6: Replacing \"\" with \"\\t\".")
vt.Fixed(
"BEFORE= value",
"#COMMENTED= \\",
@@ -2135,7 +2174,7 @@ func (s *Suite) Test_VaralignBlock__realign_commented_multiline(c *check.C) {
"#CONF_FILES+=\t\tfile1 \\",
"#\t\t\tfile2")
vt.Internals(
- "13 24",
+ "13 24 30",
" 24")
vt.Diagnostics()
vt.Autofixes()
@@ -2163,12 +2202,12 @@ func (s *Suite) Test_VaralignBlock__mixed_indentation(c *check.C) {
" \t \t value2 continued")
vt.Internals(
"05 08",
- "05 08",
+ "05 08 15",
" 17")
vt.Diagnostics(
- "NOTE: ~/Makefile:3: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:3: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:3: Replacing \" \\t \\t \" with \"\\t\\t \".")
+ "AUTOFIX: Makefile:3: Replacing \" \\t \\t \" with \"\\t\\t \".")
vt.Fixed(
"VAR1= value1",
"VAR2= value2 \\",
@@ -2211,7 +2250,7 @@ func (s *Suite) Test_VaralignBlock__follow_up_indentation(c *check.C) {
"EGDIR=\t\t\t${PREFIX}/share/examples/rtunes")
vt.Internals(
"06 24",
- "11 24",
+ "11 24 45",
" 32",
"06 24")
vt.Diagnostics(
@@ -2235,10 +2274,10 @@ func (s *Suite) Test_VaralignBlock__staircase(c *check.C) {
"\t\t\t${PREFIX}/bin/my-cmd\t\t\t\t\\",
"\t\t\t\t-options arg...")
vt.Internals(
- "12 16",
- " 08",
- " 16",
- " 24",
+ "12 16 16",
+ " 08 72",
+ " 16 72",
+ " 24 72",
" 32")
vt.Diagnostics(
nil...)
@@ -2253,9 +2292,9 @@ func (s *Suite) Test_VaralignBlock__staircase(c *check.C) {
vt.Run()
}
-// The follow-up lines may always start in column 9.
-// This is used for long variable values, to prevent wrapping them
-// into multiple lines.
+// The follow-up lines may only start in column 9 if they are longer than
+// 72 characters. Since this is not the case in this test, they are realigned
+// to match the initial line.
func (s *Suite) Test_VaralignBlock__command_with_arguments(c *check.C) {
vt := NewVaralignTester(s, c)
vt.Input(
@@ -2264,19 +2303,25 @@ func (s *Suite) Test_VaralignBlock__command_with_arguments(c *check.C) {
"\t-e s,a,b, \\",
"\t-e s,a,b,")
vt.Internals(
- "20 21",
- " 08",
- " 08",
+ "20 21 31",
+ " 08 18",
+ " 08 18",
" 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.")
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 25.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\".",
+ "NOTE: Makefile:3: This continuation line should be indented with \"\\t\\t\\t\".",
+ "NOTE: Makefile:4: This continuation line should be indented with \"\\t\\t\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \"\\t\" with \"\\t\\t\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \"\\t\" with \"\\t\\t\\t\".")
vt.Fixed(
"SED_REPLACEMENT_CMD= ${SED} -n \\",
- " -e s,a,b, \\",
- " -e s,a,b, \\",
- " -e s,a,b,")
+ " -e s,a,b, \\",
+ " -e s,a,b, \\",
+ " -e s,a,b,")
vt.Run()
}
@@ -2299,45 +2344,84 @@ func (s *Suite) Test_VaralignBlock__empty_value(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock_Process__autofix(c *check.C) {
- t := s.Init(c)
+func (s *Suite) Test_VaralignBlock_Process__var_spaces7_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= value")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
+ vt.Fixed(
+ "VAR= value")
+ vt.Run()
+}
- t.SetUpCommandLine("-Wspace", "--show-autofix")
+func (s *Suite) Test_VaralignBlock_Process__var_spaces8_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= value")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
+ vt.Fixed(
+ "VAR= value")
+ vt.Run()
+}
- mklines := t.NewMkLines("file.mk",
- "VAR= value", // Indentation 7, fixed to 8.
- "", //
- "VAR= value", // Indentation 8, fixed to 8.
- "", //
- "VAR= value", // Indentation 9, fixed to 8.
- "", //
- "VAR= \tvalue", // Mixed indentation 8, fixed to 8.
- "", //
- "VAR= \tvalue", // Mixed indentation 8, fixed to 8.
- "", //
- "VAR= \tvalue", // Mixed indentation 16, fixed to 16.
- "", //
- "VAR=\tvalue") // Already aligned with tabs only, left unchanged.
+func (s *Suite) Test_VaralignBlock_Process__var_spaces9_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= value")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 9.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".")
+ vt.Fixed(
+ "VAR= value")
+ vt.Run()
+}
- var varalign VaralignBlock
- for _, line := range mklines.mklines {
- varalign.Process(line)
- }
- varalign.Finish()
+func (s *Suite) Test_VaralignBlock_Process__var_st8_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= \tvalue")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \\t\" with \"\\t\".")
+ vt.Fixed(
+ "VAR= value")
+ vt.Run()
+}
- t.CheckOutputLines(
- "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 9.",
- "AUTOFIX: file.mk:1: Replacing \" \" with \"\\t\".",
- "NOTE: file.mk:3: Variable values should be aligned with tabs, not spaces.",
- "AUTOFIX: file.mk:3: Replacing \" \" with \"\\t\".",
- "NOTE: file.mk:5: This variable value should be aligned with tabs, not spaces, to column 9.",
- "AUTOFIX: file.mk:5: Replacing \" \" with \"\\t\".",
- "NOTE: file.mk:7: Variable values should be aligned with tabs, not spaces.",
- "AUTOFIX: file.mk:7: Replacing \" \\t\" with \"\\t\".",
- "NOTE: file.mk:9: Variable values should be aligned with tabs, not spaces.",
- "AUTOFIX: file.mk:9: Replacing \" \\t\" with \"\\t\".",
- "NOTE: file.mk:11: Variable values should be aligned with tabs, not spaces.",
- "AUTOFIX: file.mk:11: Replacing \" \\t\" with \"\\t\\t\".")
+func (s *Suite) Test_VaralignBlock_Process__var_ssst8_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= \tvalue")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \\t\" with \"\\t\".")
+ vt.Fixed(
+ "VAR= value")
+ vt.Run()
+}
+
+// Since the variable is visually aligned at column 17, the alignment
+// is kept at that depth, although a smaller indentation were possible.
+func (s *Suite) Test_VaralignBlock_Process__var_sssst16_value(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR= \tvalue")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \\t\" with \"\\t\\t\".")
+ vt.Fixed(
+ "VAR= value")
+ vt.Run()
}
// When the lines of a paragraph are inconsistently aligned,
@@ -2370,26 +2454,28 @@ func (s *Suite) Test_VaralignBlock_Process__reduce_indentation(c *check.C) {
// name and the value. Even if it is the longest line, and even if the value would start
// exactly at a tab stop.
func (s *Suite) Test_VaralignBlock_Process__longest_line_no_space(c *check.C) {
- t := s.Init(c)
-
- t.SetUpCommandLine("-Wspace")
- mklines := t.NewMkLines("file.mk",
- "SUBST_CLASSES+= aaaaaaaa",
- "SUBST_STAGE.aaaaaaaa= pre-configure",
- "SUBST_FILES.aaaaaaaa= *.pl",
- "SUBST_FILTER_CMD.aaaaaa=cat")
-
- var varalign VaralignBlock
- for _, mkline := range mklines.mklines {
- varalign.Process(mkline)
- }
- varalign.Finish()
-
- t.CheckOutputLines(
- "NOTE: file.mk:1: This variable value should be aligned with tabs, not spaces, to column 33.",
- "NOTE: file.mk:2: This variable value should be aligned with tabs, not spaces, to column 33.",
- "NOTE: file.mk:3: This variable value should be aligned with tabs, not spaces, to column 33.",
- "NOTE: file.mk:4: This variable value should be aligned to column 33.")
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "SUBST_CLASSES+= 123456",
+ "SUBST_STAGE.123456= pre-configure",
+ "SUBST_FILES.123456= *.pl",
+ "SUBST_FILTER_CMD.123456=cat")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 33.",
+ "NOTE: Makefile:2: This variable value should be aligned with tabs, not spaces, to column 33.",
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 33.",
+ "NOTE: Makefile:4: This variable value should be aligned to column 33.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\\t\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\\t\".",
+ "AUTOFIX: Makefile:4: Replacing \"\" with \"\\t\".")
+ vt.Fixed(
+ "SUBST_CLASSES+= 123456",
+ "SUBST_STAGE.123456= pre-configure",
+ "SUBST_FILES.123456= *.pl",
+ "SUBST_FILTER_CMD.123456= cat")
+ vt.Run()
}
func (s *Suite) Test_VaralignBlock_Process__only_spaces(c *check.C) {
@@ -2414,6 +2500,23 @@ func (s *Suite) Test_VaralignBlock_Process__only_spaces(c *check.C) {
"NOTE: file.mk:4: This variable value should be aligned with tabs, not spaces, to column 33.")
}
+func (s *Suite) Test_VaralignBlock_processVarassign__comment_with_continuation(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR.param= # comment \\",
+ "#\tthe comment continues")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: This variable value should be aligned with tabs, not spaces, to column 17.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\".")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\".")
+ vt.Fixed(
+ "VAR.param= # comment \\",
+ "# the comment continues")
+ vt.Run()
+}
+
func (s *Suite) Test_VaralignBlock_realignMultiEmptyInitial(c *check.C) {
t := s.Init(c)
@@ -2426,7 +2529,7 @@ func (s *Suite) Test_VaralignBlock_realignMultiEmptyInitial(c *check.C) {
mklines.Check()
t.CheckOutputLines(
- "NOTE: filename.mk:3: This outlier variable value should be aligned with a single space.")
+ "NOTE: filename.mk:3: The continuation backslash should be preceded by a single space or tab.")
}
func (s *Suite) Test_VaralignBlock_realignMultiEmptyInitial__spaces(c *check.C) {
@@ -2437,15 +2540,15 @@ func (s *Suite) Test_VaralignBlock_realignMultiEmptyInitial__spaces(c *check.C)
// This line is necessary to trigger the realignment; see VaralignBlock.Finish.
"VAR= value")
vt.Internals(
- "04 08",
+ "04 08 08",
" 08",
"04 05")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.",
- "NOTE: ~/Makefile:3: This variable value should be aligned with tabs, not spaces, to column 9.")
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.",
+ "NOTE: Makefile:3: This variable value should be aligned with tabs, not spaces, to column 9.")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t\".")
vt.Fixed(
"VAR= \\",
" value",
@@ -2459,14 +2562,14 @@ func (s *Suite) Test_VaralignBlock_realignMultiInitial__spaces(c *check.C) {
"VAR= value1 \\",
" value2")
vt.Internals(
- "04 08",
+ "04 08 15",
" 08")
vt.Diagnostics(
- "NOTE: ~/Makefile:1: Variable values should be aligned with tabs, not spaces.",
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:1: Variable values should be aligned with tabs, not spaces.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:1: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".")
+ "AUTOFIX: Makefile:1: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".")
vt.Fixed(
"VAR= value1 \\",
" value2")
@@ -2489,27 +2592,27 @@ func (s *Suite) Test_VaralignBlock_realignMultiEmptyFollow(c *check.C) {
"\\",
"# comment")
vt.Internals(
- "04 05",
- " 08",
- " 10",
- " 06",
- " 00",
- " 00",
+ "04 05 05",
+ " 08 15",
+ " 10 17",
+ " 06 13",
+ " 00 07",
+ " 00 00",
" 00")
vt.Diagnostics(
- "NOTE: ~/Makefile:2: This continuation line should be indented with \"\\t\".",
- "NOTE: ~/Makefile:3: This continuation line should be indented with \"\\t \".",
- "NOTE: ~/Makefile:4: This continuation line should be indented with \"\\t\".",
- "NOTE: ~/Makefile:5: This continuation line should be indented with \"\\t\".",
- "NOTE: ~/Makefile:6: This continuation line should be indented with \"\\t\".",
- "NOTE: ~/Makefile:7: This continuation line should be indented with \"\\t\".")
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\".",
+ "NOTE: Makefile:3: This continuation line should be indented with \"\\t \".",
+ "NOTE: Makefile:4: This continuation line should be indented with \"\\t\".",
+ "NOTE: Makefile:5: This continuation line should be indented with \"\\t\".",
+ "NOTE: Makefile:6: This continuation line should be indented with \"\\t\".",
+ "NOTE: Makefile:7: This continuation line should be indented with \"\\t\".")
vt.Autofixes(
- "AUTOFIX: ~/Makefile:2: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:3: Replacing \" \" with \"\\t \".",
- "AUTOFIX: ~/Makefile:4: Replacing \" \" with \"\\t\".",
- "AUTOFIX: ~/Makefile:5: Replacing \"\" with \"\\t\".",
- "AUTOFIX: ~/Makefile:6: Replacing \"\" with \"\\t\".",
- "AUTOFIX: ~/Makefile:7: Replacing \"\" with \"\\t\".")
+ "AUTOFIX: Makefile:2: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \" \" with \"\\t \".",
+ "AUTOFIX: Makefile:4: Replacing \" \" with \"\\t\".",
+ "AUTOFIX: Makefile:5: Replacing \"\" with \"\\t\".",
+ "AUTOFIX: Makefile:6: Replacing \"\" with \"\\t\".",
+ "AUTOFIX: Makefile:7: Replacing \"\" with \"\\t\".")
vt.Fixed(
"VAR= \\",
" value1 \\",
@@ -2521,22 +2624,220 @@ func (s *Suite) Test_VaralignBlock_realignMultiEmptyFollow(c *check.C) {
vt.Run()
}
-func (s *Suite) Test_VaralignBlock_split(c *check.C) {
+// It's ok to have all backslashes in the same column, even if that column
+// is not 73.
+func (s *Suite) Test_VaralignBlock__continuation_backslashes_aligned(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR=\tvalue value value\t\\",
+ "\tvalue\t\t\t\\",
+ "\tvalue\t\t\t\\",
+ "\tvalue")
+ vt.Internals(
+ "04 08 32",
+ " 08 32",
+ " 08 32",
+ " 08")
+ vt.Diagnostics(
+ nil...)
+ vt.Autofixes(
+ nil...)
+ vt.Fixed(
+ "VAR= value value value \\",
+ " value \\",
+ " value \\",
+ " value")
+ vt.Run()
+}
+
+// The first line is indented with a single tab. This looks strange but
+// pkglint considers it acceptable since there is a simple rule saying
+// "a single tab is always ok". Any rule that would replace this simple
+// rule would have to be similarly simple and intuitive.
+func (s *Suite) Test_VaralignBlock__continuation_backslashes_aligned_except_initial(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR=\tvalue value value\t\\",
+ "\tvalue\t\t\t\t\\",
+ "\tvalue\t\t\t\t\\",
+ "\tvalue")
+ vt.Internals(
+ "04 08 32",
+ " 08 40",
+ " 08 40",
+ " 08")
+ vt.Diagnostics(
+ nil...)
+ vt.Autofixes(
+ nil...)
+ // TODO: The backslashes should be aligned by a _simple_ rule.
+ vt.Fixed(
+ "VAR= value value value \\",
+ " value \\",
+ " value \\",
+ " value")
+ vt.Run()
+}
+
+// Lines whose continuation backslash is indented by a single space are
+// usually those that stick out further than column 73. These are not
+// touched by the realignment.
+func (s *Suite) Test_VaralignBlock__continuation_backslashes_one_sticks_out(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR=\tvalue\t\\",
+ "\tvalue value value \\",
+ "\tvalue\t\\",
+ "\tvalue")
+ vt.Internals(
+ "04 08 16",
+ " 08 26",
+ " 08 16",
+ " 08")
+ vt.Diagnostics(
+ nil...)
+ vt.Autofixes(
+ nil...)
+ vt.Fixed(
+ "VAR= value \\",
+ " value value value \\",
+ " value \\",
+ " value")
+ vt.Run()
+}
+
+func (s *Suite) Test_VaralignBlock__initial_value_tab80(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VVVVVVVVVVVVVVVVVVV=\tvalue\t\t\t\t\t\t\t\\",
+ "\t\t\tvalue\t\t\t\t\t\t\\",
+ "\t\t\tvalue\t\t\t\t\t\t\\",
+ "\t\t\tvalue")
+ vt.Internals(
+ "20 24 80",
+ " 24 72",
+ " 24 72",
+ " 24")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: The continuation backslash should be preceded " +
+ "by a single space or tab, or be in column 73, not 81.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \"\\t\\t\\t\\t\\t\\t\\t\" with \"\\t\\t\\t\\t\\t\\t\".")
+ vt.Fixed(
+ "VVVVVVVVVVVVVVVVVVV= value \\",
+ " value \\",
+ " value \\",
+ " value")
+ vt.Run()
+}
+
+func (s *Suite) Test_VaralignBlock__long_lines(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR=\t\t\t\t\t\tvalue\t\t \\",
+ "\tvalue \t \\",
+ "\tvalue")
+ vt.Internals(
+ "04 48 65",
+ " 08 17",
+ " 08")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: The continuation backslash should be preceded "+
+ "by a single space or tab, or be in column 57, not 66.",
+ "NOTE: Makefile:2: This continuation line should be indented with \"\\t\\t\\t\\t\\t\\t\".",
+ "NOTE: Makefile:3: This continuation line should be indented with \"\\t\\t\\t\\t\\t\\t\".")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \"\\t\\t \" with \"\\t\".",
+ "AUTOFIX: Makefile:2: Replacing \"\\t\" with \"\\t\\t\\t\\t\\t\\t\".",
+ "AUTOFIX: Makefile:3: Replacing \"\\t\" with \"\\t\\t\\t\\t\\t\\t\".")
+ vt.Fixed(
+ "VAR= value \\",
+ // FIXME: The backslash should be aligned properly.
+ " value \\",
+ " value")
+ vt.Run()
+}
+
+// A practical chaotic test case, derived from wip/compat32_mit-krb5/Makefile.
+// It made pkglint before 2019-09-03 panic.
+func (s *Suite) Test_VaralignBlock__long_lines_2(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "INSTALLATION_DIRS=\t_____________________________________________________________________ \\"+
+ "\t\t\t\t __________________________________________________________\t\t \\",
+ "\t\t\t__________________________________________________________\t\t \t\\",
+ "\t\t\t__________________________________________________________\t\t \t\\",
+ "\t\t\t_________________________")
+ vt.Internals(
+ "18 24 201",
+ " 24 104",
+ " 24 104",
+ " 24")
+ vt.Diagnostics(
+ "NOTE: Makefile:1: The continuation backslash should be preceded by a single space or tab.")
+ vt.Autofixes(
+ "AUTOFIX: Makefile:1: Replacing \"\\t\\t \" with \" \".")
+ vt.Fixed(
+ "INSTALLATION_DIRS= _____________________________________________________________________ \\"+
+ " __________________________________________________________ \\",
+ " __________________________________________________________ \\",
+ " __________________________________________________________ \\",
+ " _________________________")
+ vt.Run()
+}
+
+// I've never seen an intentionally continued comment in practice,
+// but pkglint needs to be able to handle this situation anyway.
+func (s *Suite) Test_varalignParts_spaceBeforeContinuation__continued_comment(c *check.C) {
+ vt := NewVaralignTester(s, c)
+ vt.Input(
+ "VAR=\tvalue # comment \\",
+ "\tstill comment \\",
+ "\tand still")
+ vt.Internals(
+ "04 08 24",
+ " 08 22",
+ " 08")
+ vt.Diagnostics(
+ nil...)
+ vt.Autofixes(
+ nil...)
+ vt.Fixed(
+ "VAR= value # comment \\",
+ " still comment \\",
+ " and still")
+ vt.Run()
+}
+
+func (s *Suite) Test_VaralignSplitter_split(c *check.C) {
t := s.Init(c)
- test := func(textnl string, initial bool, expected varalignSplitResult) {
- actual := (&VaralignBlock{}).split(textnl, initial)
+ test := func(rawText string, initial bool, expected varalignParts) {
+ actual := NewVaralignSplitter().split(rawText, initial)
t.CheckEquals(actual, expected)
t.CheckEquals(
actual.leadingComment+actual.varnameOp+
actual.spaceBeforeValue+actual.value+actual.spaceAfterValue+
actual.trailingComment+actual.spaceAfterComment+actual.continuation,
- textnl)
+ rawText)
}
- test("VAR=value\n", true,
- varalignSplitResult{
+ // This case is prevented from occurring in practice by the guard
+ // code in VaralignBlock.processVarassign, see INCLUSION_GUARD_MK.
+ test("VAR=", true,
+ varalignParts{
+ leadingComment: "",
+ varnameOp: "VAR=",
+ spaceBeforeValue: "",
+ value: "",
+ spaceAfterValue: "",
+ trailingComment: "",
+ spaceAfterComment: "",
+ continuation: ""})
+
+ test("VAR=value", true,
+ varalignParts{
leadingComment: "",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2544,11 +2845,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "\n",
- })
+ continuation: ""})
- test("#VAR=value\n", true,
- varalignSplitResult{
+ test("#VAR=value", true,
+ varalignParts{
leadingComment: "#",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2556,11 +2856,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "\n",
- })
+ continuation: ""})
- test("#VAR = value # comment \\\n", true,
- varalignSplitResult{
+ test("#VAR = value # comment \\", true,
+ varalignParts{
leadingComment: "#",
varnameOp: "VAR =",
spaceBeforeValue: " ",
@@ -2568,11 +2867,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: " ",
trailingComment: "# comment",
spaceAfterComment: " ",
- continuation: "\\\n",
- })
+ continuation: "\\"})
- test("VAR=value \\\n", true,
- varalignSplitResult{
+ test("VAR=value \\", true,
+ varalignParts{
leadingComment: "",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2580,11 +2878,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: " ",
trailingComment: "",
spaceAfterComment: "",
- continuation: "\\\n",
- })
+ continuation: "\\"})
- test("VAR=value # comment \\\n", true,
- varalignSplitResult{
+ test("VAR=value # comment \\", true,
+ varalignParts{
leadingComment: "",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2592,11 +2889,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: " ",
trailingComment: "# comment",
spaceAfterComment: " ",
- continuation: "\\\n",
- })
+ continuation: "\\"})
- test("VAR=value # comment \\\\\n", true,
- varalignSplitResult{
+ test("VAR=value # comment \\\\", true,
+ varalignParts{
leadingComment: "",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2604,11 +2900,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: " ",
trailingComment: "# comment \\\\",
spaceAfterComment: "",
- continuation: "\n",
- })
+ continuation: ""})
- test("VAR=\\# a [#] b # comment \\\\\n", true,
- varalignSplitResult{
+ test("VAR=\\# a [#] b # comment \\\\", true,
+ varalignParts{
leadingComment: "",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2616,11 +2911,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: " ",
trailingComment: "# comment \\\\",
spaceAfterComment: "",
- continuation: "\n",
- })
+ continuation: ""})
- test("VAR.${param:[#]}=\tvalue\n", true,
- varalignSplitResult{
+ test("VAR.${param:[#]}=\tvalue", true,
+ varalignParts{
leadingComment: "",
varnameOp: "VAR.${param:[#]}=",
spaceBeforeValue: "\t",
@@ -2628,11 +2922,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "\n",
- })
+ continuation: ""})
test("VAR=value", true,
- varalignSplitResult{
+ varalignParts{
leadingComment: "",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2640,13 +2933,12 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "",
- })
+ continuation: ""})
// Since this is a follow-up line, the text ends up in the variable
// value, and varnameOp is necessarily empty.
test("VAR=value", false,
- varalignSplitResult{
+ varalignParts{
leadingComment: "",
varnameOp: "",
spaceBeforeValue: "",
@@ -2654,13 +2946,12 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "",
- })
+ continuation: ""})
// In some edge cases the variable name is indented with ordinary spaces.
// This must not lead to a panic.
test(" VAR=value", true,
- varalignSplitResult{
+ varalignParts{
leadingComment: " ",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2668,13 +2959,12 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "",
- })
+ continuation: ""})
// And in really edgy cases, the leading space may even be followed by tabs.
// This should not happen in practice since it is really confusing.
test(" \t VAR=value", true,
- varalignSplitResult{
+ varalignParts{
leadingComment: " \t ",
varnameOp: "VAR=",
spaceBeforeValue: "",
@@ -2682,11 +2972,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "",
- })
+ continuation: ""})
test(" value", false,
- varalignSplitResult{
+ varalignParts{
leadingComment: "",
varnameOp: "",
spaceBeforeValue: " ",
@@ -2694,14 +2983,13 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "",
- })
+ continuation: ""})
// In practice it doesn't really happen that the last line of a file
// ends in a backslash and at the same time it doesn't have the usual
// newline ending.
test(" value \\", false,
- varalignSplitResult{
+ varalignParts{
leadingComment: "",
varnameOp: "",
spaceBeforeValue: " ",
@@ -2709,8 +2997,7 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: " ",
trailingComment: "",
spaceAfterComment: "",
- continuation: "\\",
- })
+ continuation: "\\"})
// A follow-up line may start with a comment character. There are
// two possible interpretations:
@@ -2727,7 +3014,7 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
// Any other character makes it a leading comment.
test("#\tcomment", false,
- varalignSplitResult{
+ varalignParts{
leadingComment: "#",
varnameOp: "",
spaceBeforeValue: "\t",
@@ -2735,11 +3022,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "",
spaceAfterComment: "",
- continuation: "",
- })
+ continuation: ""})
test("#\tcomment \\", false,
- varalignSplitResult{
+ varalignParts{
leadingComment: "#",
varnameOp: "",
spaceBeforeValue: "\t",
@@ -2747,11 +3033,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: " ",
trailingComment: "",
spaceAfterComment: "",
- continuation: "\\",
- })
+ continuation: "\\"})
test("# comment", false,
- varalignSplitResult{
+ varalignParts{
leadingComment: "",
varnameOp: "",
spaceBeforeValue: "",
@@ -2759,11 +3044,10 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "# comment",
spaceAfterComment: "",
- continuation: "",
- })
+ continuation: ""})
test("# comment \\", false,
- varalignSplitResult{
+ varalignParts{
leadingComment: "",
varnameOp: "",
spaceBeforeValue: "",
@@ -2771,8 +3055,7 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
spaceAfterValue: "",
trailingComment: "# comment",
spaceAfterComment: " ",
- continuation: "\\",
- })
+ continuation: "\\"})
// Commented variable assignments are only valid if they
// directly follow the comment sign.
@@ -2780,35 +3063,38 @@ func (s *Suite) Test_VaralignBlock_split(c *check.C) {
// It is a programming error if such a line is ever added to
// the VaralignBlock.
t.ExpectAssert(
- func() { test("# VAR= value", true, varalignSplitResult{}) })
+ func() { test("# VAR= value", true, varalignParts{}) })
+
+ t.ExpectAssert(
+ func() { test("VAR=\tvalue\n", true, varalignParts{}) })
}
// This test runs canonicalInitial directly since as of August 2019
// that function is only used in a single place, and from this place
// varnameOpSpaceWidth is always bigger than width.
-func (s *Suite) Test_varalignLine_canonicalInitial(c *check.C) {
+func (s *Suite) Test_varalignParts_canonicalInitial(c *check.C) {
t := s.Init(c)
var v varalignLine
- v.parts.varnameOp = "LONG.123456789="
- v.parts.spaceBeforeValue = " "
+ v.varnameOp = "LONG.123456789="
+ v.spaceBeforeValue = " "
t.CheckEquals(v.canonicalInitial(16), false)
- v.parts.varnameOp = "LONG.1234567890="
+ v.varnameOp = "LONG.1234567890="
t.CheckEquals(v.canonicalInitial(16), true)
- v.parts.spaceBeforeValue = ""
+ v.spaceBeforeValue = ""
t.CheckEquals(v.canonicalInitial(16), false)
}
-func (s *Suite) Test_varalignLine_canonicalFollow(c *check.C) {
+func (s *Suite) Test_varalignParts_canonicalFollow(c *check.C) {
t := s.Init(c)
test := func(comment, space string, expected bool) {
l := varalignLine{
- parts: varalignSplitResult{
+ varalignParts: varalignParts{
leadingComment: comment,
spaceBeforeValue: space}}
@@ -2842,3 +3128,13 @@ func (s *Suite) Test_varalignLine_canonicalFollow(c *check.C) {
test("#", " ", false)
test("#", "\t", true)
}
+
+// This constellation doesn't occur in practice because the code in
+// VaralignBlock.processVarassign skips it, see INCLUSION_GUARD_MK.
+func (s *Suite) Test_varalignParts_isEmptyContinuation__edge_case(c *check.C) {
+ t := s.Init(c)
+
+ parts := NewVaralignSplitter().split("VAR=", true)
+
+ t.CheckEquals(parts.isEmptyContinuation(), false)
+}
diff --git a/pkgtools/pkglint/files/vardefs.go b/pkgtools/pkglint/files/vardefs.go
index 0f04f34758e..a7e71f923e4 100644
--- a/pkgtools/pkglint/files/vardefs.go
+++ b/pkgtools/pkglint/files/vardefs.go
@@ -311,11 +311,7 @@ func (reg *VarTypeRegistry) infralist(varname string, basicType *BasicType) {
// compilerLanguages reads the available languages that are typically
// bundled in a single compiler framework, such as GCC or Clang.
func (reg *VarTypeRegistry) compilerLanguages(src *Pkgsrc) *BasicType {
- options := NotEmpty
- if !G.Testing {
- options = NotEmpty | MustSucceed
- }
- mklines := LoadMk(src.File("mk/compiler.mk"), options)
+ mklines := src.LoadMkInfra("mk/compiler.mk", NotEmpty|MustSucceed)
languages := make(map[string]bool)
if mklines != nil {
@@ -1141,6 +1137,7 @@ func (reg *VarTypeRegistry) Init(src *Pkgsrc) {
reg.pkglistbl3rat("INCOMPAT_CURSES", BtMachinePlatformPattern)
reg.sys("INFO_DIR", BtPathname) // relative to PREFIX
reg.pkg("INFO_FILES", BtYes)
+ reg.sys("INFO_MSG", BtShellCommand)
reg.sys("INSTALL", BtShellCommand)
reg.pkglist("INSTALLATION_DIRS", BtPrefixPathname)
reg.pkg("INSTALLATION_DIRS_FROM_PLIST", BtYes)
@@ -1233,11 +1230,7 @@ func (reg *VarTypeRegistry) Init(src *Pkgsrc) {
reg.pkglist("MASTER_SITES", BtFetchURL)
for _, filename := range []string{"mk/fetch/sites.mk", "mk/fetch/fetch.mk"} {
- loadOptions := NotEmpty | MustSucceed
- if G.Testing {
- loadOptions = NotEmpty
- }
- sitesMk := LoadMk(src.File(filename), loadOptions)
+ sitesMk := src.LoadMkInfra(filename, NotEmpty|MustSucceed)
if sitesMk != nil {
sitesMk.ForEach(func(mkline *MkLine) {
if mkline.IsVarassign() && hasPrefix(mkline.Varname(), "MASTER_SITE_") {
@@ -1682,6 +1675,7 @@ func (reg *VarTypeRegistry) Init(src *Pkgsrc) {
reg.infralist("_SYS_VARS.*", BtVariableName)
reg.infralist("_DEF_VARS.*", BtVariableName)
reg.infralist("_USE_VARS.*", BtVariableName)
+ reg.infralist("_IGN_VARS.*", BtVariableName)
reg.infralist("_SORTED_VARS.*", BtVariableNamePattern)
reg.infralist("_LISTED_VARS.*", BtVariableNamePattern)
}
diff --git a/pkgtools/pkglint/files/vargroups.go b/pkgtools/pkglint/files/vargroups.go
new file mode 100644
index 00000000000..8fc17242d0b
--- /dev/null
+++ b/pkgtools/pkglint/files/vargroups.go
@@ -0,0 +1,246 @@
+package pkglint
+
+import "strings"
+
+// VargroupsChecker checks that the _VARGROUPS section of an infrastructure
+// file matches the rest of the file content:
+//
+// - All variables that are used in the file should also be declared as used.
+//
+// - All variables that are declared to be used should actually be used.
+//
+// See mk/misc/show.mk, keyword _VARGROUPS.
+type VargroupsChecker struct {
+ mklines *MkLines
+ skip bool
+
+ registered map[string]*MkLine
+ userVars map[string]*MkLine
+ pkgVars map[string]*MkLine
+ sysVars map[string]*MkLine
+ defVars map[string]*MkLine
+ useVars map[string]*MkLine
+ ignVars map[string]*MkLine
+ sortedVars map[string]*MkLine
+ listedVars map[string]*MkLine
+
+ undefinedVars map[string]*MkLine
+ unusedVars map[string]*MkLine
+}
+
+func NewVargroupsChecker(mklines *MkLines) *VargroupsChecker {
+ ck := VargroupsChecker{mklines: mklines}
+ ck.init()
+ return &ck
+}
+
+func (ck *VargroupsChecker) init() {
+ mklines := ck.mklines
+ scope := mklines.vars
+ if !scope.Defined("_VARGROUPS") {
+ ck.skip = true
+ return
+ }
+
+ ck.registered = make(map[string]*MkLine)
+ ck.userVars = make(map[string]*MkLine)
+ ck.pkgVars = make(map[string]*MkLine)
+ ck.sysVars = make(map[string]*MkLine)
+ ck.defVars = make(map[string]*MkLine)
+ ck.useVars = make(map[string]*MkLine)
+ ck.ignVars = make(map[string]*MkLine)
+ ck.sortedVars = make(map[string]*MkLine)
+ ck.listedVars = make(map[string]*MkLine)
+
+ group := ""
+
+ checkGroupName := func(mkline *MkLine) {
+ varname := mkline.Varname()
+ if varnameParam(varname) != group {
+ mkline.Warnf("Expected %s.%s, but found %q.",
+ varnameBase(varname), group, varnameParam(varname))
+ }
+ }
+
+ appendTo := func(vars map[string]*MkLine, mkline *MkLine, public bool) {
+ checkGroupName(mkline)
+
+ for _, varname := range mkline.ValueFields(mkline.Value()) {
+ if containsVarRef(varname) {
+ continue
+ }
+
+ if public && hasPrefix(varname, "_") {
+ mkline.Warnf("%s should list only variables that start with a letter, not %q.",
+ mkline.Varname(), varname)
+ }
+
+ if ck.registered[varname] != nil {
+ mkline.Warnf("Duplicate variable name %s, already appeared in %s.",
+ varname, mkline.RefTo(ck.registered[varname]))
+ } else {
+ ck.registered[varname] = mkline
+ }
+
+ vars[varname] = mkline
+ }
+ }
+
+ appendToStyle := func(vars map[string]*MkLine, mkline *MkLine) {
+ checkGroupName(mkline)
+
+ for _, varname := range mkline.ValueFields(mkline.Value()) {
+ if !containsVarRef(varname) {
+ vars[varname] = mkline
+ }
+ }
+ }
+
+ mklines.ForEach(func(mkline *MkLine) {
+ if mkline.IsVarassign() {
+ switch varnameCanon(mkline.Varname()) {
+ case "_VARGROUPS":
+ group = mkline.Value()
+ case "_USER_VARS.*":
+ appendTo(ck.userVars, mkline, true)
+ case "_PKG_VARS.*":
+ appendTo(ck.pkgVars, mkline, true)
+ case "_SYS_VARS.*":
+ appendTo(ck.sysVars, mkline, true)
+ case "_DEF_VARS.*":
+ appendTo(ck.defVars, mkline, false)
+ case "_USE_VARS.*":
+ appendTo(ck.useVars, mkline, false)
+ case "_IGN_VARS.*":
+ appendTo(ck.ignVars, mkline, false)
+ case "_SORTED_VARS.*":
+ appendToStyle(ck.sortedVars, mkline)
+ case "_LISTED_VARS.*":
+ appendToStyle(ck.listedVars, mkline)
+ }
+ }
+ })
+
+ ck.undefinedVars = copyStringMkLine(ck.defVars)
+ ck.unusedVars = copyStringMkLine(ck.useVars)
+}
+
+// CheckVargroups checks that each variable that is used or defined
+// somewhere in the file is also registered in the _VARGROUPS section,
+// in order to make it discoverable by "bmake show-all".
+//
+// This check is intended mainly for infrastructure files and similar
+// support files, such as lang/*/module.mk.
+func (ck *VargroupsChecker) Check(mkline *MkLine) {
+ if ck.skip {
+ return
+ }
+
+ ck.checkDef(mkline)
+ ck.checkUse(mkline)
+}
+
+func (ck *VargroupsChecker) checkDef(mkline *MkLine) {
+ if !mkline.IsVarassignMaybeCommented() {
+ return
+ }
+
+ varname := mkline.Varname()
+ delete(ck.undefinedVars, varname)
+ if ck.ignoreDef(varname) {
+ return
+ }
+
+ if ck.mklines.once.FirstTimeSlice("_VARGROUPS", "def", varname) {
+ mkline.Warnf("Variable %s is defined but not mentioned in the _VARGROUPS section.", varname)
+ }
+}
+
+func (ck *VargroupsChecker) ignoreDef(varname string) bool {
+ switch {
+ case containsVarRef(varname),
+ ck.registered[varname] != nil,
+ hasSuffix(varname, "_MK"),
+ ck.isVargroups(varname),
+ varname == "PKG_FAIL_REASON":
+ return true
+ }
+
+ return false
+}
+
+func (ck *VargroupsChecker) checkUse(mkline *MkLine) {
+ mkline.ForEachUsed(func(varUse *MkVarUse, _ VucTime) { ck.checkUseVar(mkline, varUse) })
+}
+
+func (ck *VargroupsChecker) checkUseVar(mkline *MkLine, varUse *MkVarUse) {
+ varname := varUse.varname
+ delete(ck.unusedVars, varname)
+
+ if ck.ignoreUse(varname) {
+ return
+ }
+
+ if ck.mklines.once.FirstTimeSlice("_VARGROUPS", "use", varname) {
+ mkline.Warnf("Variable %s is used but not mentioned in the _VARGROUPS section.", varname)
+ }
+}
+
+func (ck *VargroupsChecker) ignoreUse(varname string) bool {
+ switch {
+ case containsVarRef(varname),
+ hasSuffix(varname, "_MK"),
+ varname == ".TARGET",
+ varname == "TOOLS_SHELL",
+ varname == "TOUCH_FLAGS",
+ varname == strings.ToLower(varname),
+ G.Pkgsrc.Tools.ExistsVar(varname),
+ ck.isShellCommand(varname),
+ ck.registered[varname] != nil,
+ ck.isVargroups(varname):
+ return true
+ }
+
+ return false
+}
+
+func (ck *VargroupsChecker) isShellCommand(varname string) bool {
+ vartype := G.Pkgsrc.VariableType(ck.mklines, varname)
+ if vartype != nil && vartype.basicType == BtShellCommand {
+ return true
+ }
+ return false
+}
+
+func (ck *VargroupsChecker) isVargroups(varname string) bool {
+ if !hasPrefix(varname, "_") {
+ return false
+ }
+
+ switch varnameCanon(varname) {
+ case "_VARGROUPS",
+ "_USER_VARS.*",
+ "_PKG_VARS.*",
+ "_SYS_VARS.*",
+ "_DEF_VARS.*",
+ "_USE_VARS.*",
+ "_IGN_VARS.*",
+ "_LISTED_VARS.*",
+ "_SORTED_VARS.*":
+ return true
+ }
+ return false
+}
+
+func (ck *VargroupsChecker) Finish(mkline *MkLine) {
+ if ck.skip {
+ return
+ }
+
+ forEachStringMkLine(ck.undefinedVars, func(varname string, mkline *MkLine) {
+ mkline.Warnf("The variable %s is not actually defined in this file.", varname)
+ })
+ forEachStringMkLine(ck.unusedVars, func(varname string, mkline *MkLine) {
+ mkline.Warnf("The variable %s is not actually used in this file.", varname)
+ })
+}
diff --git a/pkgtools/pkglint/files/vargroups_test.go b/pkgtools/pkglint/files/vargroups_test.go
new file mode 100644
index 00000000000..6978fe3d973
--- /dev/null
+++ b/pkgtools/pkglint/files/vargroups_test.go
@@ -0,0 +1,142 @@
+package pkglint
+
+import "gopkg.in/check.v1"
+
+func (s *Suite) Test_VargroupsChecker__typo_in_group_name(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpVartypes()
+
+ mklines := t.NewMkLines("Makefile",
+ MkCvsID,
+ "",
+ "_VARGROUPS+=\t\tgroup",
+ "_DEF_VARS.typo=\t\t# none",
+ "_LISTED_VARS.typo=\t# none")
+
+ mklines.Check()
+
+ t.CheckOutputLines(
+ "WARN: Makefile:4: Expected _DEF_VARS.group, but found \"typo\".",
+ "WARN: Makefile:5: Expected _LISTED_VARS.group, but found \"typo\".")
+}
+
+func (s *Suite) Test_VargroupsChecker__duplicate_variable_name(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpVartypes()
+
+ mklines := t.NewMkLines("Makefile",
+ MkCvsID,
+ "",
+ "_VARGROUPS+=\t\tgroup",
+ "_USER_VARS.group=\tVAR",
+ "_PKG_VARS.group=\tVAR",
+ "_SYS_VARS.group=\tVAR",
+ "_DEF_VARS.group=\tVAR",
+ "_USE_VARS.group=\tVAR",
+ "_IGN_VARS.group=\tVAR",
+ "_SORTED_VARS.group=\tVAR",
+ "_LISTED_VARS.group=\tVAR",
+ "",
+ "VAR=\t${VAR}")
+
+ mklines.Check()
+
+ t.CheckOutputLines(
+ "WARN: Makefile:5: Duplicate variable name VAR, already appeared in line 4.",
+ "WARN: Makefile:6: Duplicate variable name VAR, already appeared in line 4.",
+ "WARN: Makefile:7: Duplicate variable name VAR, already appeared in line 4.",
+ "WARN: Makefile:8: Duplicate variable name VAR, already appeared in line 4.",
+ "WARN: Makefile:9: Duplicate variable name VAR, already appeared in line 4.")
+}
+
+func (s *Suite) Test_VargroupsChecker__variable_reference(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpVartypes()
+
+ mklines := t.NewMkLines("Makefile",
+ MkCvsID,
+ "",
+ "_VARGROUPS+=\t\tgroup",
+ "_USER_VARS.group=\t${:Uparam:@param@VAR.${param}@}",
+ "",
+ "VAR.param=\tvalue")
+
+ mklines.Check()
+
+ t.CheckOutputLines(
+ "WARN: Makefile:6: VAR.param is defined but not used.",
+ // FIXME: Hmmm, that's going to be complicated to get right.
+ "WARN: Makefile:6: Variable VAR.param is defined but not mentioned in the _VARGROUPS section.")
+}
+
+func (s *Suite) Test_VargroupsChecker__public_underscore(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpVartypes()
+
+ mklines := t.NewMkLines("Makefile",
+ MkCvsID,
+ "",
+ "_VARGROUPS+=\t\tgroup",
+ "_USER_VARS.group=\t_VARGROUPS")
+
+ mklines.Check()
+
+ t.CheckOutputLines(
+ "WARN: Makefile:4: _USER_VARS.group should list only variables that " +
+ "start with a letter, not \"_VARGROUPS\".")
+}
+
+func (s *Suite) Test_VargroupsChecker__declared_but_undefined(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpVartypes()
+
+ mklines := t.NewMkLines("Makefile",
+ MkCvsID,
+ "",
+ "# UNDECLARED", // Documented to avoid a warning about being defined but unused.
+ "#\tDocumentation",
+ "#\tDocumentation",
+ "",
+ "_VARGROUPS+=\t\tgroup",
+ "_DEF_VARS.group=\tUNDEFINED",
+ "",
+ "UNDECLARED=\tvalue",
+ "UNDECLARED=\tvalue")
+
+ mklines.Check()
+
+ t.CheckOutputLines(
+ "WARN: Makefile:10: Variable UNDECLARED is defined but not mentioned in the _VARGROUPS section.",
+ "WARN: Makefile:8: The variable UNDEFINED is not actually defined in this file.")
+}
+
+func (s *Suite) Test_VargroupsChecker__declared_but_unused(c *check.C) {
+ t := s.Init(c)
+
+ t.SetUpVartypes()
+
+ mklines := t.NewMkLines("Makefile",
+ MkCvsID,
+ "",
+ "# UNDECLARED", // Documented to avoid a "used but not defined" warning.
+ "#\tDocumentation",
+ "#\tDocumentation",
+ "",
+ "_VARGROUPS+=\t\tgroup",
+ "_USE_VARS.group=\tUNUSED",
+ "",
+ "target: .PHONY",
+ "\t: ${UNDECLARED}",
+ "\t: ${UNDECLARED}")
+
+ mklines.Check()
+
+ t.CheckOutputLines(
+ "WARN: Makefile:11: Variable UNDECLARED is used but not mentioned in the _VARGROUPS section.",
+ "WARN: Makefile:8: The variable UNUSED is not actually used in this file.")
+}