123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817 |
- package build
- import (
- "path"
- "regexp"
- "sort"
- "strings"
- "github.com/bazelbuild/buildtools/tables"
- )
- var DisableRewrites []string
- func disabled(name string) bool {
- for _, x := range DisableRewrites {
- if name == x {
- return true
- }
- }
- return false
- }
- var AllowSort []string
- func allowedSort(name string) bool {
- for _, x := range AllowSort {
- if name == x {
- return true
- }
- }
- return false
- }
- func Rewrite(f *File, info *RewriteInfo) {
-
- if info == nil {
- info = new(RewriteInfo)
- }
- for _, r := range rewrites {
- if !disabled(r.name) {
- r.fn(f, info)
- }
- }
- }
- type RewriteInfo struct {
- EditLabel int
- NameCall int
- SortCall int
- SortStringList int
- UnsafeSort int
- Log []string
- }
- func (info *RewriteInfo) String() string {
- s := ""
- if info.EditLabel > 0 {
- s += " label"
- }
- if info.NameCall > 0 {
- s += " callname"
- }
- if info.SortCall > 0 {
- s += " callsort"
- }
- if info.SortStringList > 0 {
- s += " listsort"
- }
- if info.UnsafeSort > 0 {
- s += " unsafesort"
- }
- if s != "" {
- s = s[1:]
- }
- return s
- }
- var rewrites = []struct {
- name string
- fn func(*File, *RewriteInfo)
- }{
- {"callsort", sortCallArgs},
- {"label", fixLabels},
- {"listsort", sortStringLists},
- {"multiplus", fixMultilinePlus},
- }
- func leaveAlone(stk []Expr, final Expr) bool {
- for _, x := range stk {
- if leaveAlone1(x) {
- return true
- }
- }
- if final != nil && leaveAlone1(final) {
- return true
- }
- return false
- }
- func hasComment(x Expr, text string) bool {
- for _, com := range x.Comment().Before {
- if strings.Contains(strings.ToLower(com.Token), text) {
- return true
- }
- }
- return false
- }
- func leaveAlone1(x Expr) bool {
- return hasComment(x, "buildifier: leave-alone")
- }
- func doNotSort(x Expr) bool {
- return hasComment(x, "do not sort")
- }
- func keepSorted(x Expr) bool {
- return hasComment(x, "keep sorted")
- }
- func fixLabels(f *File, info *RewriteInfo) {
- joinLabel := func(p *Expr) {
- add, ok := (*p).(*BinaryExpr)
- if !ok || add.Op != "+" {
- return
- }
- str1, ok := add.X.(*StringExpr)
- if !ok || !strings.HasPrefix(str1.Value, "//") || strings.Contains(str1.Value, " ") {
- return
- }
- str2, ok := add.Y.(*StringExpr)
- if !ok || strings.Contains(str2.Value, " ") {
- return
- }
- info.EditLabel++
- str1.Value += str2.Value
-
-
- com1 := add.Comment()
- com2 := str1.Comment()
- com3 := str2.Comment()
- com1.Before = append(com1.Before, com2.Before...)
- com1.Before = append(com1.Before, com3.Before...)
- com1.Suffix = append(com1.Suffix, com2.Suffix...)
- com1.Suffix = append(com1.Suffix, com3.Suffix...)
- *str1.Comment() = *com1
- *p = str1
- }
- labelPrefix := "//"
- if tables.StripLabelLeadingSlashes {
- labelPrefix = ""
- }
-
-
- labelRE := regexp.MustCompile(`^(((?:@(\w+))?//|` + labelPrefix + `)(?:.+/)?([^:]*))(?::([^:]+))?$`)
- shortenLabel := func(v Expr) {
- str, ok := v.(*StringExpr)
- if !ok {
- return
- }
- editPerformed := false
- if tables.StripLabelLeadingSlashes && strings.HasPrefix(str.Value, "//") {
- if path.Dir(f.Path) == "." || !strings.HasPrefix(str.Value, "//:") {
- editPerformed = true
- str.Value = str.Value[2:]
- }
- }
- if tables.ShortenAbsoluteLabelsToRelative {
- thisPackage := labelPrefix + path.Dir(f.Path)
- if str.Value == thisPackage {
- editPerformed = true
- str.Value = ":" + path.Base(str.Value)
- } else if strings.HasPrefix(str.Value, thisPackage+":") {
- editPerformed = true
- str.Value = str.Value[len(thisPackage):]
- }
- }
- m := labelRE.FindStringSubmatch(str.Value)
- if m == nil {
- return
- }
- if m[4] != "" && m[4] == m[5] {
- editPerformed = true
- str.Value = m[1]
- } else if m[3] != "" && m[4] == "" && m[3] == m[5] {
- editPerformed = true
- str.Value = "@" + m[3]
- }
- if editPerformed {
- info.EditLabel++
- }
- }
- Walk(f, func(v Expr, stk []Expr) {
- switch v := v.(type) {
- case *CallExpr:
- if leaveAlone(stk, v) {
- return
- }
- for i := range v.List {
- if leaveAlone1(v.List[i]) {
- continue
- }
- as, ok := v.List[i].(*BinaryExpr)
- if !ok || as.Op != "=" {
- continue
- }
- key, ok := as.X.(*LiteralExpr)
- if !ok || !tables.IsLabelArg[key.Token] || tables.LabelBlacklist[callName(v)+"."+key.Token] {
- continue
- }
- if leaveAlone1(as.Y) {
- continue
- }
- if list, ok := as.Y.(*ListExpr); ok {
- for i := range list.List {
- if leaveAlone1(list.List[i]) {
- continue
- }
- joinLabel(&list.List[i])
- shortenLabel(list.List[i])
- }
- }
- if set, ok := as.Y.(*SetExpr); ok {
- for i := range set.List {
- if leaveAlone1(set.List[i]) {
- continue
- }
- joinLabel(&set.List[i])
- shortenLabel(set.List[i])
- }
- } else {
- joinLabel(&as.Y)
- shortenLabel(as.Y)
- }
- }
- }
- })
- }
- func callName(call *CallExpr) string {
- rule, ok := call.X.(*LiteralExpr)
- if !ok {
- return ""
- }
- return rule.Token
- }
- func sortCallArgs(f *File, info *RewriteInfo) {
- Walk(f, func(v Expr, stk []Expr) {
- call, ok := v.(*CallExpr)
- if !ok {
- return
- }
- if leaveAlone(stk, call) {
- return
- }
- rule := callName(call)
- if rule == "" {
- return
- }
-
- start := len(call.List)
- for start > 0 && argName(call.List[start-1]) != "" {
- start--
- }
-
- var args namedArgs
- for i, x := range call.List[start:] {
- name := argName(x)
- args = append(args, namedArg{ruleNamePriority(rule, name), name, i, x})
- }
-
- if sort.IsSorted(args) {
- return
- }
- info.SortCall++
- sort.Sort(args)
- for i, x := range args {
- call.List[start+i] = x.expr
- }
- })
- }
- func ruleNamePriority(rule, arg string) int {
- ruleArg := rule + "." + arg
- if val, ok := tables.NamePriority[ruleArg]; ok {
- return val
- }
- return tables.NamePriority[arg]
-
- }
- func argName(x Expr) string {
- if as, ok := x.(*BinaryExpr); ok && as.Op == "=" {
- if id, ok := as.X.(*LiteralExpr); ok {
- return id.Token
- }
- }
- return ""
- }
- type namedArg struct {
- priority int
- name string
- index int
- expr Expr
- }
- type namedArgs []namedArg
- func (x namedArgs) Len() int { return len(x) }
- func (x namedArgs) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
- func (x namedArgs) Less(i, j int) bool {
- p := x[i]
- q := x[j]
- if p.priority != q.priority {
- return p.priority < q.priority
- }
- if p.name != q.name {
- return p.name < q.name
- }
- return p.index < q.index
- }
- func sortStringLists(f *File, info *RewriteInfo) {
- Walk(f, func(v Expr, stk []Expr) {
- switch v := v.(type) {
- case *CallExpr:
- if leaveAlone(stk, v) {
- return
- }
- rule := callName(v)
- for _, arg := range v.List {
- if leaveAlone1(arg) {
- continue
- }
- as, ok := arg.(*BinaryExpr)
- if !ok || as.Op != "=" || leaveAlone1(as) || doNotSort(as) {
- continue
- }
- key, ok := as.X.(*LiteralExpr)
- if !ok {
- continue
- }
- context := rule + "." + key.Token
- if !tables.IsSortableListArg[key.Token] || tables.SortableBlacklist[context] {
- continue
- }
- if disabled("unsafesort") && !tables.SortableWhitelist[context] && !allowedSort(context) {
- continue
- }
- sortStringList(as.Y, info, context)
- }
- case *BinaryExpr:
- if disabled("unsafesort") {
- return
- }
-
- as := v
- if as.Op == "=" && keepSorted(as) {
- sortStringList(as.Y, info, "?")
- }
- case *KeyValueExpr:
- if disabled("unsafesort") {
- return
- }
-
- if keepSorted(v) {
- sortStringList(v.Value, info, "?")
- }
- case *ListExpr:
- if disabled("unsafesort") {
- return
- }
-
- if len(v.List) > 0 && keepSorted(v.List[0]) {
- sortStringList(v, info, "?")
- }
- }
- })
- }
- func SortStringList(x Expr) {
- sortStringList(x, nil, "")
- }
- func sortStringList(x Expr, info *RewriteInfo, context string) {
- list, ok := x.(*ListExpr)
- if !ok || len(list.List) < 2 || doNotSort(list.List[0]) {
- return
- }
- forceSort := keepSorted(list.List[0])
-
-
-
-
-
- if !forceSort {
- if line, _ := hasComments(list); line {
- return
- }
- }
-
- for i := 0; i < len(list.List); {
- if _, ok := list.List[i].(*StringExpr); !ok {
- i++
- continue
- }
- j := i + 1
- for ; j < len(list.List); j++ {
- if str, ok := list.List[j].(*StringExpr); !ok || len(str.Before) > 0 {
- break
- }
- }
- var chunk []stringSortKey
- for index, x := range list.List[i:j] {
- chunk = append(chunk, makeSortKey(index, x.(*StringExpr)))
- }
- if !sort.IsSorted(byStringExpr(chunk)) || !isUniq(chunk) {
- if info != nil {
- info.SortStringList++
- if !tables.SortableWhitelist[context] {
- info.UnsafeSort++
- info.Log = append(info.Log, "sort:"+context)
- }
- }
- before := chunk[0].x.Comment().Before
- chunk[0].x.Comment().Before = nil
- sort.Sort(byStringExpr(chunk))
- chunk = uniq(chunk)
- chunk[0].x.Comment().Before = before
- for offset, key := range chunk {
- list.List[i+offset] = key.x
- }
- list.List = append(list.List[:(i+len(chunk))], list.List[j:]...)
- }
- i = j
- }
- }
- func uniq(sortedList []stringSortKey) []stringSortKey {
- out := sortedList[:0]
- for _, sk := range sortedList {
- if len(out) == 0 || sk.value != out[len(out)-1].value {
- out = append(out, sk)
- }
- }
- return out
- }
- func isUniq(list []stringSortKey) bool {
- for i := range list {
- if i+1 < len(list) && list[i].value == list[i+1].value {
- return false
- }
- }
- return true
- }
- func callArgName(stk []Expr) string {
- n := len(stk)
- if n < 2 {
- return ""
- }
- arg := argName(stk[n-1])
- if arg == "" {
- return ""
- }
- call, ok := stk[n-2].(*CallExpr)
- if !ok {
- return ""
- }
- rule, ok := call.X.(*LiteralExpr)
- if !ok {
- return ""
- }
- return rule.Token + "." + arg
- }
- type stringSortKey struct {
- phase int
- split []string
- value string
- original int
- x Expr
- }
- func makeSortKey(index int, x *StringExpr) stringSortKey {
- key := stringSortKey{
- value: x.Value,
- original: index,
- x: x,
- }
- switch {
- case strings.HasPrefix(x.Value, ":"):
- key.phase = 1
- case strings.HasPrefix(x.Value, "//") || (tables.StripLabelLeadingSlashes && !strings.HasPrefix(x.Value, "@")):
- key.phase = 2
- case strings.HasPrefix(x.Value, "@"):
- key.phase = 3
- }
- key.split = strings.Split(strings.Replace(x.Value, ":", ".", -1), ".")
- return key
- }
- type byStringExpr []stringSortKey
- func (x byStringExpr) Len() int { return len(x) }
- func (x byStringExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
- func (x byStringExpr) Less(i, j int) bool {
- xi := x[i]
- xj := x[j]
- if xi.phase != xj.phase {
- return xi.phase < xj.phase
- }
- for k := 0; k < len(xi.split) && k < len(xj.split); k++ {
- if xi.split[k] != xj.split[k] {
- return xi.split[k] < xj.split[k]
- }
- }
- if len(xi.split) != len(xj.split) {
- return len(xi.split) < len(xj.split)
- }
- if xi.value != xj.value {
- return xi.value < xj.value
- }
- return xi.original < xj.original
- }
- func fixMultilinePlus(f *File, info *RewriteInfo) {
-
-
-
-
- var isList func(x Expr) bool
- isList = func(x Expr) bool {
- switch x := x.(type) {
- case *ListExpr:
- return true
- case *CallExpr:
- if len(x.List) == 1 {
- return isList(x.List[0])
- }
- }
- return false
- }
-
- var isMultiLine func(Expr) bool
- isMultiLine = func(x Expr) bool {
- switch x := x.(type) {
- case *ListExpr:
- return x.ForceMultiLine || len(x.List) > 1
- case *CallExpr:
- if x.ForceMultiLine || len(x.List) > 1 && !x.ForceCompact {
- return true
- }
- if len(x.List) == 1 {
- return isMultiLine(x.List[0])
- }
- }
- return false
- }
-
-
- var forceMultiLine func(Expr) bool
- forceMultiLine = func(x Expr) bool {
- switch x := x.(type) {
- case *ListExpr:
-
- if x.ForceMultiLine {
- return true
- }
-
-
- if len(x.List) == 1 && forceMultiLine(x.List[0]) {
- return true
- }
- x.ForceMultiLine = true
- return true
- case *CallExpr:
- if len(x.List) == 1 {
- return forceMultiLine(x.List[0])
- }
- }
- return false
- }
- skip := map[Expr]bool{}
- Walk(f, func(v Expr, stk []Expr) {
- if skip[v] {
- return
- }
- bin, ok := v.(*BinaryExpr)
- if !ok || bin.Op != "+" {
- return
- }
-
-
-
-
-
-
-
- var all []Expr
- for {
- all = append(all, bin.Y, bin)
- bin1, ok := bin.X.(*BinaryExpr)
- if !ok || bin1.Op != "+" {
- break
- }
- bin = bin1
- skip[bin] = true
- }
- all = append(all, bin.X)
-
-
- for i, j := 0, len(all)-1; i < j; i, j = i+1, j-1 {
- all[i], all[j] = all[j], all[i]
- }
-
-
-
- haveList := false
- for i := 0; i < len(all); i += 2 {
- if isList(all[i]) {
- haveList = true
- break
- }
- }
- if !haveList {
- return
- }
-
-
- for i := 1; i < len(all); i += 2 {
- bin := all[i].(*BinaryExpr)
- if !bin.LineBreak {
- continue
- }
-
-
-
- if forceMultiLine(all[i+1]) {
- bin.LineBreak = false
- continue
- }
-
-
-
- if isMultiLine(all[i-1]) {
- bin.LineBreak = false
- continue
- }
- }
- })
- }
- func hasComments(list *ListExpr) (line, suffix bool) {
- com := list.Comment()
- if len(com.Before) > 0 || len(com.After) > 0 || len(list.End.Before) > 0 {
- line = true
- }
- if len(com.Suffix) > 0 {
- suffix = true
- }
- for _, elem := range list.List {
- com := elem.Comment()
- if len(com.Before) > 0 {
- line = true
- }
- if len(com.Suffix) > 0 {
- suffix = true
- }
- }
- return
- }
|