rewrite.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. /*
  2. Copyright 2016 Google Inc. All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // Rewriting of high-level (not purely syntactic) BUILD constructs.
  14. package build
  15. import (
  16. "path"
  17. "regexp"
  18. "sort"
  19. "strings"
  20. "github.com/bazelbuild/buildtools/tables"
  21. )
  22. // For debugging: flag to disable certain rewrites.
  23. var DisableRewrites []string
  24. // disabled reports whether the named rewrite is disabled.
  25. func disabled(name string) bool {
  26. for _, x := range DisableRewrites {
  27. if name == x {
  28. return true
  29. }
  30. }
  31. return false
  32. }
  33. // For debugging: allow sorting of these lists even with sorting otherwise disabled.
  34. var AllowSort []string
  35. // allowedSort reports whether sorting is allowed in the named context.
  36. func allowedSort(name string) bool {
  37. for _, x := range AllowSort {
  38. if name == x {
  39. return true
  40. }
  41. }
  42. return false
  43. }
  44. // Rewrite applies the high-level Buildifier rewrites to f, modifying it in place.
  45. // If info is non-nil, Rewrite updates it with information about the rewrite.
  46. func Rewrite(f *File, info *RewriteInfo) {
  47. // Allocate an info so that helpers can assume it's there.
  48. if info == nil {
  49. info = new(RewriteInfo)
  50. }
  51. for _, r := range rewrites {
  52. if !disabled(r.name) {
  53. r.fn(f, info)
  54. }
  55. }
  56. }
  57. // RewriteInfo collects information about what Rewrite did.
  58. type RewriteInfo struct {
  59. EditLabel int // number of label strings edited
  60. NameCall int // number of calls with argument names added
  61. SortCall int // number of call argument lists sorted
  62. SortStringList int // number of string lists sorted
  63. UnsafeSort int // number of unsafe string lists sorted
  64. Log []string // log entries - may change
  65. }
  66. func (info *RewriteInfo) String() string {
  67. s := ""
  68. if info.EditLabel > 0 {
  69. s += " label"
  70. }
  71. if info.NameCall > 0 {
  72. s += " callname"
  73. }
  74. if info.SortCall > 0 {
  75. s += " callsort"
  76. }
  77. if info.SortStringList > 0 {
  78. s += " listsort"
  79. }
  80. if info.UnsafeSort > 0 {
  81. s += " unsafesort"
  82. }
  83. if s != "" {
  84. s = s[1:]
  85. }
  86. return s
  87. }
  88. // rewrites is the list of all Buildifier rewrites, in the order in which they are applied.
  89. // The order here matters: for example, label canonicalization must happen
  90. // before sorting lists of strings.
  91. var rewrites = []struct {
  92. name string
  93. fn func(*File, *RewriteInfo)
  94. }{
  95. {"callsort", sortCallArgs},
  96. {"label", fixLabels},
  97. {"listsort", sortStringLists},
  98. {"multiplus", fixMultilinePlus},
  99. }
  100. // leaveAlone reports whether any of the nodes on the stack are marked
  101. // with a comment containing "buildifier: leave-alone".
  102. func leaveAlone(stk []Expr, final Expr) bool {
  103. for _, x := range stk {
  104. if leaveAlone1(x) {
  105. return true
  106. }
  107. }
  108. if final != nil && leaveAlone1(final) {
  109. return true
  110. }
  111. return false
  112. }
  113. // hasComment reports whether x is marked with a comment that
  114. // after being converted to lower case, contains the specified text.
  115. func hasComment(x Expr, text string) bool {
  116. for _, com := range x.Comment().Before {
  117. if strings.Contains(strings.ToLower(com.Token), text) {
  118. return true
  119. }
  120. }
  121. return false
  122. }
  123. // leaveAlone1 reports whether x is marked with a comment containing
  124. // "buildifier: leave-alone", case-insensitive.
  125. func leaveAlone1(x Expr) bool {
  126. return hasComment(x, "buildifier: leave-alone")
  127. }
  128. // doNotSort reports whether x is marked with a comment containing
  129. // "do not sort", case-insensitive.
  130. func doNotSort(x Expr) bool {
  131. return hasComment(x, "do not sort")
  132. }
  133. // keepSorted reports whether x is marked with a comment containing
  134. // "keep sorted", case-insensitive.
  135. func keepSorted(x Expr) bool {
  136. return hasComment(x, "keep sorted")
  137. }
  138. // fixLabels rewrites labels into a canonical form.
  139. //
  140. // First, it joins labels written as string addition, turning
  141. // "//x" + ":y" (usually split across multiple lines) into "//x:y".
  142. //
  143. // Second, it removes redundant target qualifiers, turning labels like
  144. // "//third_party/m4:m4" into "//third_party/m4" as well as ones like
  145. // "@foo//:foo" into "@foo".
  146. //
  147. func fixLabels(f *File, info *RewriteInfo) {
  148. joinLabel := func(p *Expr) {
  149. add, ok := (*p).(*BinaryExpr)
  150. if !ok || add.Op != "+" {
  151. return
  152. }
  153. str1, ok := add.X.(*StringExpr)
  154. if !ok || !strings.HasPrefix(str1.Value, "//") || strings.Contains(str1.Value, " ") {
  155. return
  156. }
  157. str2, ok := add.Y.(*StringExpr)
  158. if !ok || strings.Contains(str2.Value, " ") {
  159. return
  160. }
  161. info.EditLabel++
  162. str1.Value += str2.Value
  163. // Deleting nodes add and str2.
  164. // Merge comments from add, str1, and str2 and save in str1.
  165. com1 := add.Comment()
  166. com2 := str1.Comment()
  167. com3 := str2.Comment()
  168. com1.Before = append(com1.Before, com2.Before...)
  169. com1.Before = append(com1.Before, com3.Before...)
  170. com1.Suffix = append(com1.Suffix, com2.Suffix...)
  171. com1.Suffix = append(com1.Suffix, com3.Suffix...)
  172. *str1.Comment() = *com1
  173. *p = str1
  174. }
  175. labelPrefix := "//"
  176. if tables.StripLabelLeadingSlashes {
  177. labelPrefix = ""
  178. }
  179. // labelRE matches label strings, e.g. @r//x/y/z:abc
  180. // where $1 is @r//x/y/z, $2 is @r//, $3 is r, $4 is z, $5 is abc.
  181. labelRE := regexp.MustCompile(`^(((?:@(\w+))?//|` + labelPrefix + `)(?:.+/)?([^:]*))(?::([^:]+))?$`)
  182. shortenLabel := func(v Expr) {
  183. str, ok := v.(*StringExpr)
  184. if !ok {
  185. return
  186. }
  187. editPerformed := false
  188. if tables.StripLabelLeadingSlashes && strings.HasPrefix(str.Value, "//") {
  189. if path.Dir(f.Path) == "." || !strings.HasPrefix(str.Value, "//:") {
  190. editPerformed = true
  191. str.Value = str.Value[2:]
  192. }
  193. }
  194. if tables.ShortenAbsoluteLabelsToRelative {
  195. thisPackage := labelPrefix + path.Dir(f.Path)
  196. if str.Value == thisPackage {
  197. editPerformed = true
  198. str.Value = ":" + path.Base(str.Value)
  199. } else if strings.HasPrefix(str.Value, thisPackage+":") {
  200. editPerformed = true
  201. str.Value = str.Value[len(thisPackage):]
  202. }
  203. }
  204. m := labelRE.FindStringSubmatch(str.Value)
  205. if m == nil {
  206. return
  207. }
  208. if m[4] != "" && m[4] == m[5] { // e.g. //foo:foo
  209. editPerformed = true
  210. str.Value = m[1]
  211. } else if m[3] != "" && m[4] == "" && m[3] == m[5] { // e.g. @foo//:foo
  212. editPerformed = true
  213. str.Value = "@" + m[3]
  214. }
  215. if editPerformed {
  216. info.EditLabel++
  217. }
  218. }
  219. Walk(f, func(v Expr, stk []Expr) {
  220. switch v := v.(type) {
  221. case *CallExpr:
  222. if leaveAlone(stk, v) {
  223. return
  224. }
  225. for i := range v.List {
  226. if leaveAlone1(v.List[i]) {
  227. continue
  228. }
  229. as, ok := v.List[i].(*BinaryExpr)
  230. if !ok || as.Op != "=" {
  231. continue
  232. }
  233. key, ok := as.X.(*LiteralExpr)
  234. if !ok || !tables.IsLabelArg[key.Token] || tables.LabelBlacklist[callName(v)+"."+key.Token] {
  235. continue
  236. }
  237. if leaveAlone1(as.Y) {
  238. continue
  239. }
  240. if list, ok := as.Y.(*ListExpr); ok {
  241. for i := range list.List {
  242. if leaveAlone1(list.List[i]) {
  243. continue
  244. }
  245. joinLabel(&list.List[i])
  246. shortenLabel(list.List[i])
  247. }
  248. }
  249. if set, ok := as.Y.(*SetExpr); ok {
  250. for i := range set.List {
  251. if leaveAlone1(set.List[i]) {
  252. continue
  253. }
  254. joinLabel(&set.List[i])
  255. shortenLabel(set.List[i])
  256. }
  257. } else {
  258. joinLabel(&as.Y)
  259. shortenLabel(as.Y)
  260. }
  261. }
  262. }
  263. })
  264. }
  265. // callName returns the name of the rule being called by call.
  266. // If the call is not to a literal rule name, callName returns "".
  267. func callName(call *CallExpr) string {
  268. rule, ok := call.X.(*LiteralExpr)
  269. if !ok {
  270. return ""
  271. }
  272. return rule.Token
  273. }
  274. // sortCallArgs sorts lists of named arguments to a call.
  275. func sortCallArgs(f *File, info *RewriteInfo) {
  276. Walk(f, func(v Expr, stk []Expr) {
  277. call, ok := v.(*CallExpr)
  278. if !ok {
  279. return
  280. }
  281. if leaveAlone(stk, call) {
  282. return
  283. }
  284. rule := callName(call)
  285. if rule == "" {
  286. return
  287. }
  288. // Find the tail of the argument list with named arguments.
  289. start := len(call.List)
  290. for start > 0 && argName(call.List[start-1]) != "" {
  291. start--
  292. }
  293. // Record information about each arg into a sortable list.
  294. var args namedArgs
  295. for i, x := range call.List[start:] {
  296. name := argName(x)
  297. args = append(args, namedArg{ruleNamePriority(rule, name), name, i, x})
  298. }
  299. // Sort the list and put the args back in the new order.
  300. if sort.IsSorted(args) {
  301. return
  302. }
  303. info.SortCall++
  304. sort.Sort(args)
  305. for i, x := range args {
  306. call.List[start+i] = x.expr
  307. }
  308. })
  309. }
  310. // ruleNamePriority maps a rule argument name to its sorting priority.
  311. // It could use the auto-generated per-rule tables but for now it just
  312. // falls back to the original list.
  313. func ruleNamePriority(rule, arg string) int {
  314. ruleArg := rule + "." + arg
  315. if val, ok := tables.NamePriority[ruleArg]; ok {
  316. return val
  317. }
  318. return tables.NamePriority[arg]
  319. /*
  320. list := ruleArgOrder[rule]
  321. if len(list) == 0 {
  322. return tables.NamePriority[arg]
  323. }
  324. for i, x := range list {
  325. if x == arg {
  326. return i
  327. }
  328. }
  329. return len(list)
  330. */
  331. }
  332. // If x is of the form key=value, argName returns the string key.
  333. // Otherwise argName returns "".
  334. func argName(x Expr) string {
  335. if as, ok := x.(*BinaryExpr); ok && as.Op == "=" {
  336. if id, ok := as.X.(*LiteralExpr); ok {
  337. return id.Token
  338. }
  339. }
  340. return ""
  341. }
  342. // A namedArg records information needed for sorting
  343. // a named call argument into its proper position.
  344. type namedArg struct {
  345. priority int // kind of name; first sort key
  346. name string // name; second sort key
  347. index int // original index; final sort key
  348. expr Expr // name=value argument
  349. }
  350. // namedArgs is a slice of namedArg that implements sort.Interface
  351. type namedArgs []namedArg
  352. func (x namedArgs) Len() int { return len(x) }
  353. func (x namedArgs) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
  354. func (x namedArgs) Less(i, j int) bool {
  355. p := x[i]
  356. q := x[j]
  357. if p.priority != q.priority {
  358. return p.priority < q.priority
  359. }
  360. if p.name != q.name {
  361. return p.name < q.name
  362. }
  363. return p.index < q.index
  364. }
  365. // sortStringLists sorts lists of string literals used as specific rule arguments.
  366. func sortStringLists(f *File, info *RewriteInfo) {
  367. Walk(f, func(v Expr, stk []Expr) {
  368. switch v := v.(type) {
  369. case *CallExpr:
  370. if leaveAlone(stk, v) {
  371. return
  372. }
  373. rule := callName(v)
  374. for _, arg := range v.List {
  375. if leaveAlone1(arg) {
  376. continue
  377. }
  378. as, ok := arg.(*BinaryExpr)
  379. if !ok || as.Op != "=" || leaveAlone1(as) || doNotSort(as) {
  380. continue
  381. }
  382. key, ok := as.X.(*LiteralExpr)
  383. if !ok {
  384. continue
  385. }
  386. context := rule + "." + key.Token
  387. if !tables.IsSortableListArg[key.Token] || tables.SortableBlacklist[context] {
  388. continue
  389. }
  390. if disabled("unsafesort") && !tables.SortableWhitelist[context] && !allowedSort(context) {
  391. continue
  392. }
  393. sortStringList(as.Y, info, context)
  394. }
  395. case *BinaryExpr:
  396. if disabled("unsafesort") {
  397. return
  398. }
  399. // "keep sorted" comment on x = list forces sorting of list.
  400. as := v
  401. if as.Op == "=" && keepSorted(as) {
  402. sortStringList(as.Y, info, "?")
  403. }
  404. case *KeyValueExpr:
  405. if disabled("unsafesort") {
  406. return
  407. }
  408. // "keep sorted" before key: list also forces sorting of list.
  409. if keepSorted(v) {
  410. sortStringList(v.Value, info, "?")
  411. }
  412. case *ListExpr:
  413. if disabled("unsafesort") {
  414. return
  415. }
  416. // "keep sorted" comment above first list element also forces sorting of list.
  417. if len(v.List) > 0 && keepSorted(v.List[0]) {
  418. sortStringList(v, info, "?")
  419. }
  420. }
  421. })
  422. }
  423. // SortStringList sorts x, a list of strings.
  424. func SortStringList(x Expr) {
  425. sortStringList(x, nil, "")
  426. }
  427. // sortStringList sorts x, a list of strings.
  428. // The list is broken by non-strings and by blank lines and comments into chunks.
  429. // Each chunk is sorted in place.
  430. func sortStringList(x Expr, info *RewriteInfo, context string) {
  431. list, ok := x.(*ListExpr)
  432. if !ok || len(list.List) < 2 || doNotSort(list.List[0]) {
  433. return
  434. }
  435. forceSort := keepSorted(list.List[0])
  436. // TODO(bazel-team): Decide how to recognize lists that cannot
  437. // be sorted. Avoiding all lists with comments avoids sorting
  438. // lists that say explicitly, in some form or another, why they
  439. // cannot be sorted. For example, many cc_test rules require
  440. // certain order in their deps attributes.
  441. if !forceSort {
  442. if line, _ := hasComments(list); line {
  443. return
  444. }
  445. }
  446. // Sort chunks of the list with no intervening blank lines or comments.
  447. for i := 0; i < len(list.List); {
  448. if _, ok := list.List[i].(*StringExpr); !ok {
  449. i++
  450. continue
  451. }
  452. j := i + 1
  453. for ; j < len(list.List); j++ {
  454. if str, ok := list.List[j].(*StringExpr); !ok || len(str.Before) > 0 {
  455. break
  456. }
  457. }
  458. var chunk []stringSortKey
  459. for index, x := range list.List[i:j] {
  460. chunk = append(chunk, makeSortKey(index, x.(*StringExpr)))
  461. }
  462. if !sort.IsSorted(byStringExpr(chunk)) || !isUniq(chunk) {
  463. if info != nil {
  464. info.SortStringList++
  465. if !tables.SortableWhitelist[context] {
  466. info.UnsafeSort++
  467. info.Log = append(info.Log, "sort:"+context)
  468. }
  469. }
  470. before := chunk[0].x.Comment().Before
  471. chunk[0].x.Comment().Before = nil
  472. sort.Sort(byStringExpr(chunk))
  473. chunk = uniq(chunk)
  474. chunk[0].x.Comment().Before = before
  475. for offset, key := range chunk {
  476. list.List[i+offset] = key.x
  477. }
  478. list.List = append(list.List[:(i+len(chunk))], list.List[j:]...)
  479. }
  480. i = j
  481. }
  482. }
  483. // uniq removes duplicates from a list, which must already be sorted.
  484. // It edits the list in place.
  485. func uniq(sortedList []stringSortKey) []stringSortKey {
  486. out := sortedList[:0]
  487. for _, sk := range sortedList {
  488. if len(out) == 0 || sk.value != out[len(out)-1].value {
  489. out = append(out, sk)
  490. }
  491. }
  492. return out
  493. }
  494. // isUniq reports whether the sorted list only contains unique elements.
  495. func isUniq(list []stringSortKey) bool {
  496. for i := range list {
  497. if i+1 < len(list) && list[i].value == list[i+1].value {
  498. return false
  499. }
  500. }
  501. return true
  502. }
  503. // If stk describes a call argument like rule(arg=...), callArgName
  504. // returns the name of that argument, formatted as "rule.arg".
  505. func callArgName(stk []Expr) string {
  506. n := len(stk)
  507. if n < 2 {
  508. return ""
  509. }
  510. arg := argName(stk[n-1])
  511. if arg == "" {
  512. return ""
  513. }
  514. call, ok := stk[n-2].(*CallExpr)
  515. if !ok {
  516. return ""
  517. }
  518. rule, ok := call.X.(*LiteralExpr)
  519. if !ok {
  520. return ""
  521. }
  522. return rule.Token + "." + arg
  523. }
  524. // A stringSortKey records information about a single string literal to be
  525. // sorted. The strings are first grouped into four phases: most strings,
  526. // strings beginning with ":", strings beginning with "//", and strings
  527. // beginning with "@". The next significant part of the comparison is the list
  528. // of elements in the value, where elements are split at `.' and `:'. Finally
  529. // we compare by value and break ties by original index.
  530. type stringSortKey struct {
  531. phase int
  532. split []string
  533. value string
  534. original int
  535. x Expr
  536. }
  537. func makeSortKey(index int, x *StringExpr) stringSortKey {
  538. key := stringSortKey{
  539. value: x.Value,
  540. original: index,
  541. x: x,
  542. }
  543. switch {
  544. case strings.HasPrefix(x.Value, ":"):
  545. key.phase = 1
  546. case strings.HasPrefix(x.Value, "//") || (tables.StripLabelLeadingSlashes && !strings.HasPrefix(x.Value, "@")):
  547. key.phase = 2
  548. case strings.HasPrefix(x.Value, "@"):
  549. key.phase = 3
  550. }
  551. key.split = strings.Split(strings.Replace(x.Value, ":", ".", -1), ".")
  552. return key
  553. }
  554. // byStringExpr implements sort.Interface for a list of stringSortKey.
  555. type byStringExpr []stringSortKey
  556. func (x byStringExpr) Len() int { return len(x) }
  557. func (x byStringExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
  558. func (x byStringExpr) Less(i, j int) bool {
  559. xi := x[i]
  560. xj := x[j]
  561. if xi.phase != xj.phase {
  562. return xi.phase < xj.phase
  563. }
  564. for k := 0; k < len(xi.split) && k < len(xj.split); k++ {
  565. if xi.split[k] != xj.split[k] {
  566. return xi.split[k] < xj.split[k]
  567. }
  568. }
  569. if len(xi.split) != len(xj.split) {
  570. return len(xi.split) < len(xj.split)
  571. }
  572. if xi.value != xj.value {
  573. return xi.value < xj.value
  574. }
  575. return xi.original < xj.original
  576. }
  577. // fixMultilinePlus turns
  578. //
  579. // ... +
  580. // [ ... ]
  581. //
  582. // ... +
  583. // call(...)
  584. //
  585. // into
  586. // ... + [
  587. // ...
  588. // ]
  589. //
  590. // ... + call(
  591. // ...
  592. // )
  593. //
  594. // which typically works better with our aggressively compact formatting.
  595. func fixMultilinePlus(f *File, info *RewriteInfo) {
  596. // List manipulation helpers.
  597. // As a special case, we treat f([...]) as a list, mainly
  598. // for glob.
  599. // isList reports whether x is a list.
  600. var isList func(x Expr) bool
  601. isList = func(x Expr) bool {
  602. switch x := x.(type) {
  603. case *ListExpr:
  604. return true
  605. case *CallExpr:
  606. if len(x.List) == 1 {
  607. return isList(x.List[0])
  608. }
  609. }
  610. return false
  611. }
  612. // isMultiLine reports whether x is a multiline list.
  613. var isMultiLine func(Expr) bool
  614. isMultiLine = func(x Expr) bool {
  615. switch x := x.(type) {
  616. case *ListExpr:
  617. return x.ForceMultiLine || len(x.List) > 1
  618. case *CallExpr:
  619. if x.ForceMultiLine || len(x.List) > 1 && !x.ForceCompact {
  620. return true
  621. }
  622. if len(x.List) == 1 {
  623. return isMultiLine(x.List[0])
  624. }
  625. }
  626. return false
  627. }
  628. // forceMultiLine tries to force the list x to use a multiline form.
  629. // It reports whether it was successful.
  630. var forceMultiLine func(Expr) bool
  631. forceMultiLine = func(x Expr) bool {
  632. switch x := x.(type) {
  633. case *ListExpr:
  634. // Already multi line?
  635. if x.ForceMultiLine {
  636. return true
  637. }
  638. // If this is a list containing a list, force the
  639. // inner list to be multiline instead.
  640. if len(x.List) == 1 && forceMultiLine(x.List[0]) {
  641. return true
  642. }
  643. x.ForceMultiLine = true
  644. return true
  645. case *CallExpr:
  646. if len(x.List) == 1 {
  647. return forceMultiLine(x.List[0])
  648. }
  649. }
  650. return false
  651. }
  652. skip := map[Expr]bool{}
  653. Walk(f, func(v Expr, stk []Expr) {
  654. if skip[v] {
  655. return
  656. }
  657. bin, ok := v.(*BinaryExpr)
  658. if !ok || bin.Op != "+" {
  659. return
  660. }
  661. // Found a +.
  662. // w + x + y + z parses as ((w + x) + y) + z,
  663. // so chase down the left side to make a list of
  664. // all the things being added together, separated
  665. // by the BinaryExprs that join them.
  666. // Mark them as "skip" so that when Walk recurses
  667. // into the subexpressions, we won't reprocess them.
  668. var all []Expr
  669. for {
  670. all = append(all, bin.Y, bin)
  671. bin1, ok := bin.X.(*BinaryExpr)
  672. if !ok || bin1.Op != "+" {
  673. break
  674. }
  675. bin = bin1
  676. skip[bin] = true
  677. }
  678. all = append(all, bin.X)
  679. // Because the outermost expression was the
  680. // rightmost one, the list is backward. Reverse it.
  681. for i, j := 0, len(all)-1; i < j; i, j = i+1, j-1 {
  682. all[i], all[j] = all[j], all[i]
  683. }
  684. // The 'all' slice is alternating addends and BinaryExpr +'s:
  685. // w, +, x, +, y, +, z
  686. // If there are no lists involved, don't rewrite anything.
  687. haveList := false
  688. for i := 0; i < len(all); i += 2 {
  689. if isList(all[i]) {
  690. haveList = true
  691. break
  692. }
  693. }
  694. if !haveList {
  695. return
  696. }
  697. // Okay, there are lists.
  698. // Consider each + next to a line break.
  699. for i := 1; i < len(all); i += 2 {
  700. bin := all[i].(*BinaryExpr)
  701. if !bin.LineBreak {
  702. continue
  703. }
  704. // We're going to break the line after the +.
  705. // If it is followed by a list, force that to be
  706. // multiline instead.
  707. if forceMultiLine(all[i+1]) {
  708. bin.LineBreak = false
  709. continue
  710. }
  711. // If the previous list was multiline already,
  712. // don't bother with the line break after
  713. // the +.
  714. if isMultiLine(all[i-1]) {
  715. bin.LineBreak = false
  716. continue
  717. }
  718. }
  719. })
  720. }
  721. // hasComments reports whether any comments are associated with
  722. // the list or its elements.
  723. func hasComments(list *ListExpr) (line, suffix bool) {
  724. com := list.Comment()
  725. if len(com.Before) > 0 || len(com.After) > 0 || len(list.End.Before) > 0 {
  726. line = true
  727. }
  728. if len(com.Suffix) > 0 {
  729. suffix = true
  730. }
  731. for _, elem := range list.List {
  732. com := elem.Comment()
  733. if len(com.Before) > 0 {
  734. line = true
  735. }
  736. if len(com.Suffix) > 0 {
  737. suffix = true
  738. }
  739. }
  740. return
  741. }