[BACK]Return to line.go CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files

File: [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files / Attic / line.go (download)

Revision 1.27, Wed Nov 7 20:58:23 2018 UTC (5 years, 5 months ago) by rillig
Branch: MAIN
Changes since 1.26: +43 -36 lines

pkgtools/pkglint: update to 5.6.6

Changes since 5.6.5:

- Removed plist-clash since it had crashed unconditionally whenever it
  was called. This means that in the last 3 years, nobody can have
  used it in the originally intended way.

- Fixed interactions between the --source, --explain, --show-autofix,
  --autofix and --only options.

- Fixed "defined but not used" and "used but not defined" for variables
  from the pkgsrc infrastructure.

- Lots of small fixes and improvements found by the large pkglint code
  review (12% done).

package main

// When files are read in by pkglint, they are interpreted in terms of
// lines. For Makefiles, line continuations are handled properly, allowing
// multiple raw lines to end in a single logical line. For other files
// there is a 1:1 translation.
//
// A difference between the raw and the logical lines is that the
// raw lines include the line end sequence, whereas the logical lines
// do not.
//
// Some methods allow modification of the raw lines contained in the
// logical line but leave the Text field untouched. These methods are
// used in the --autofix mode.

import (
	"fmt"
	"path"
	"strconv"
)

type RawLine struct {
	Lineno int
	// XXX: This is only needed for Autofix; probably should be moved there.
	orignl string
	textnl string
}

func (rline *RawLine) String() string {
	return strconv.Itoa(rline.Lineno) + ":" + rline.textnl
}

type Line = *LineImpl

type LineImpl struct {
	FileName  string
	Basename  string
	firstLine int32 // Zero means not applicable, -1 means EOF
	lastLine  int32 // Usually the same as firstLine, may differ in Makefiles
	Text      string
	raw       []*RawLine
	autofix   *Autofix
	Once
}

func NewLine(fileName string, lineno int, text string, rawLines []*RawLine) Line {
	return NewLineMulti(fileName, lineno, lineno, text, rawLines)
}

// NewLineMulti is for logical Makefile lines that end with backslash.
func NewLineMulti(fileName string, firstLine, lastLine int, text string, rawLines []*RawLine) Line {
	return &LineImpl{fileName, path.Base(fileName), int32(firstLine), int32(lastLine), text, rawLines, nil, Once{}}
}

// NewLineEOF creates a dummy line for logging, with the "line number" EOF.
func NewLineEOF(fileName string) Line {
	return NewLineMulti(fileName, -1, 0, "", nil)
}

// NewLineWhole creates a dummy line for logging messages that affect a file as a whole.
func NewLineWhole(fileName string) Line {
	return NewLine(fileName, 0, "", nil)
}

func (line *LineImpl) Linenos() string {
	switch {
	case line.firstLine == -1:
		return "EOF"
	case line.firstLine == 0:
		return ""
	case line.firstLine == line.lastLine:
		return strconv.Itoa(int(line.firstLine))
	default:
		return strconv.Itoa(int(line.firstLine)) + "--" + strconv.Itoa(int(line.lastLine))
	}
}

// RefTo returns a reference to another line,
// which can be in the same file or in a different file.
func (line *LineImpl) RefTo(other Line) string {
	if line.FileName != other.FileName {
		return cleanpath(relpath(path.Dir(line.FileName), other.FileName)) + ":" + other.Linenos()
	}
	return "line " + other.Linenos()
}

// PathToFile returns the relative path from this line to the given file path.
// This is typically used for arguments in diagnostics, which should always be
// relative to the line with which the diagnostic is associated.
func (line *LineImpl) PathToFile(filePath string) string {
	return relpath(path.Dir(line.FileName), filePath)
}

func (line *LineImpl) IsMultiline() bool {
	return line.firstLine > 0 && line.firstLine != line.lastLine
}

func (line *LineImpl) showSource(out *SeparatorWriter) {
	if !G.Opts.ShowSource {
		return
	}

	printDiff := func(rawLines []*RawLine) {
		for _, rawLine := range rawLines {
			if rawLine.textnl != rawLine.orignl {
				if rawLine.orignl != "" {
					out.Write("-\t" + rawLine.orignl)
				}
				if rawLine.textnl != "" {
					out.Write("+\t" + rawLine.textnl)
				}
			} else {
				out.Write(">\t" + rawLine.orignl)
			}
		}
	}

	if line.autofix != nil {
		for _, before := range line.autofix.linesBefore {
			out.Write("+\t" + before)
		}
		printDiff(line.raw)
		for _, after := range line.autofix.linesAfter {
			out.Write("+\t" + after)
		}
	} else {
		printDiff(line.raw)
	}
}

func (line *LineImpl) log(level *LogLevel, format string, args []interface{}) {
	if G.Opts.ShowAutofix || G.Opts.Autofix {
		// In these two cases, the only interesting diagnostics are
		// those that can be fixed automatically.
		// These are logged by Autofix.Apply.
		return
	}
	G.explainNext = shallBeLogged(format)
	if !G.explainNext {
		return
	}

	if G.Opts.ShowSource {
		line.showSource(G.logOut)
	}
	logf(level, line.FileName, line.Linenos(), format, fmt.Sprintf(format, args...))
	if G.Opts.ShowSource {
		G.logOut.Separate()
	}
}

func (line *LineImpl) Fatalf(format string, args ...interface{}) {
	line.log(Fatal, format, args)
}

func (line *LineImpl) Errorf(format string, args ...interface{}) {
	line.log(Error, format, args)
}

func (line *LineImpl) Warnf(format string, args ...interface{}) {
	line.log(Warn, format, args)
}

func (line *LineImpl) Notef(format string, args ...interface{}) {
	line.log(Note, format, args)
}

func (line *LineImpl) String() string {
	return line.FileName + ":" + line.Linenos() + ": " + line.Text
}

// Autofix returns the autofix instance belonging to the line.
//
// Usage:
//
//  fix := line.Autofix()
//
//  fix.Errorf("Must not be ...")
//  fix.Warnf("Should not be ...")
//  fix.Notef("It is also possible ...")
//
//  fix.Explain(
//      "Explanation ...",
//      "... end of explanation.")
//
//  fix.Replace("from", "to")
//  fix.ReplaceAfter("prefix", "from", "to")
//  fix.ReplaceRegex(`[\t ]+`, "space", -1)
//  fix.InsertBefore("new line")
//  fix.InsertAfter("new line")
//  fix.Delete()
//  fix.Custom(func(showAutofix, autofix bool) {})
//
//  fix.Apply()
func (line *LineImpl) Autofix() *Autofix {
	if line.autofix == nil {
		line.autofix = NewAutofix(line)
	}
	return line.autofix
}