func FindFirstLineOffset(re *regexp.Regexp, createStatement string) int {
	loc := re.FindStringIndex(createStatement)
	if loc == nil {
		return 0
	}
	return strings.Count(createStatement[0:loc[0]], "\n")
}
FindFirstLineOffset returns the line offset (i.e. line number starting at 0)
for the first match of re within createStatement. If no match occurs, 0 is
returned. This may happen often due to createStatement being arbitrarily
formatted.
This is useful for ObjectCheckers when populating Note.LineOffset.
package linter
import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	log "github.com/sirupsen/logrus"
	"github.com/skeema/skeema/internal/fs"
	"github.com/skeema/skeema/internal/workspace"
	"github.com/skeema/tengo"
)
type Note struct {
	LineOffset int
	Summary    string
	Message    string
}
type Annotation struct {
	RuleName  string
	Statement *fs.Statement
	Severity  Severity
	Note
}
func (a *Annotation) MessageWithLocation() string {
	if a.Statement.File == "" || a.Statement.LineNo == 0 {
		return fmt.Sprintf("%s [Full SQL: %s]", a.Message, a.Statement.Text)
	}
	return fmt.Sprintf("%s: %s", a.Location(), a.Message)
}
func (a *Annotation) LineNo() int {
	return a.Statement.LineNo + a.LineOffset
}
func (a *Annotation) Location() string {
	if a.LineOffset == 0 || a.Statement.File == "" {
		return a.Statement.Location()
	}
	return fmt.Sprintf("%s:%d", a.Statement.File, a.LineNo())
}
func (a *Annotation) Log() {
	message := a.MessageWithLocation()
	switch a.Severity {
	case SeverityError:
		log.Error(message)
	case SeverityWarning:
		log.Warning(message)
	default:
		log.Info(message)
	}
}