expr.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /* Copyright 2018 The Bazel Authors. All rights reserved.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package rule
  13. import (
  14. "fmt"
  15. "log"
  16. "strings"
  17. "github.com/bazelbuild/bazel-gazelle/label"
  18. bzl "github.com/bazelbuild/buildtools/build"
  19. )
  20. // MapExprStrings applies a function to string sub-expressions within e.
  21. // An expression containing the results with the same structure as e is
  22. // returned.
  23. func MapExprStrings(e bzl.Expr, f func(string) string) bzl.Expr {
  24. if e == nil {
  25. return nil
  26. }
  27. switch expr := e.(type) {
  28. case *bzl.StringExpr:
  29. s := f(expr.Value)
  30. if s == "" {
  31. return nil
  32. }
  33. ret := *expr
  34. ret.Value = s
  35. return &ret
  36. case *bzl.ListExpr:
  37. var list []bzl.Expr
  38. for _, elem := range expr.List {
  39. elem = MapExprStrings(elem, f)
  40. if elem != nil {
  41. list = append(list, elem)
  42. }
  43. }
  44. if len(list) == 0 && len(expr.List) > 0 {
  45. return nil
  46. }
  47. ret := *expr
  48. ret.List = list
  49. return &ret
  50. case *bzl.DictExpr:
  51. var cases []bzl.Expr
  52. isEmpty := true
  53. for _, kv := range expr.List {
  54. keyval, ok := kv.(*bzl.KeyValueExpr)
  55. if !ok {
  56. log.Panicf("unexpected expression in generated imports dict: %#v", kv)
  57. }
  58. value := MapExprStrings(keyval.Value, f)
  59. if value != nil {
  60. cases = append(cases, &bzl.KeyValueExpr{Key: keyval.Key, Value: value})
  61. if key, ok := keyval.Key.(*bzl.StringExpr); !ok || key.Value != "//conditions:default" {
  62. isEmpty = false
  63. }
  64. }
  65. }
  66. if isEmpty {
  67. return nil
  68. }
  69. ret := *expr
  70. ret.List = cases
  71. return &ret
  72. case *bzl.CallExpr:
  73. if x, ok := expr.X.(*bzl.Ident); !ok || x.Name != "select" || len(expr.List) != 1 {
  74. log.Panicf("unexpected call expression in generated imports: %#v", e)
  75. }
  76. arg := MapExprStrings(expr.List[0], f)
  77. if arg == nil {
  78. return nil
  79. }
  80. call := *expr
  81. call.List[0] = arg
  82. return &call
  83. case *bzl.BinaryExpr:
  84. x := MapExprStrings(expr.X, f)
  85. y := MapExprStrings(expr.Y, f)
  86. if x == nil {
  87. return y
  88. }
  89. if y == nil {
  90. return x
  91. }
  92. binop := *expr
  93. binop.X = x
  94. binop.Y = y
  95. return &binop
  96. default:
  97. return nil
  98. }
  99. }
  100. // FlattenExpr takes an expression that may have been generated from
  101. // PlatformStrings and returns its values in a flat, sorted, de-duplicated
  102. // list. Comments are accumulated and de-duplicated across duplicate
  103. // expressions. If the expression could not have been generted by
  104. // PlatformStrings, the expression will be returned unmodified.
  105. func FlattenExpr(e bzl.Expr) bzl.Expr {
  106. ps, err := extractPlatformStringsExprs(e)
  107. if err != nil {
  108. return e
  109. }
  110. ls := makeListSquasher()
  111. addElem := func(e bzl.Expr) bool {
  112. s, ok := e.(*bzl.StringExpr)
  113. if !ok {
  114. return false
  115. }
  116. ls.add(s)
  117. return true
  118. }
  119. addList := func(e bzl.Expr) bool {
  120. l, ok := e.(*bzl.ListExpr)
  121. if !ok {
  122. return false
  123. }
  124. for _, elem := range l.List {
  125. if !addElem(elem) {
  126. return false
  127. }
  128. }
  129. return true
  130. }
  131. addDict := func(d *bzl.DictExpr) bool {
  132. for _, kv := range d.List {
  133. if !addList(kv.(*bzl.KeyValueExpr).Value) {
  134. return false
  135. }
  136. }
  137. return true
  138. }
  139. if ps.generic != nil {
  140. if !addList(ps.generic) {
  141. return e
  142. }
  143. }
  144. for _, d := range []*bzl.DictExpr{ps.os, ps.arch, ps.platform} {
  145. if d == nil {
  146. continue
  147. }
  148. if !addDict(d) {
  149. return e
  150. }
  151. }
  152. return ls.list()
  153. }
  154. func isScalar(e bzl.Expr) bool {
  155. switch e.(type) {
  156. case *bzl.StringExpr, *bzl.LiteralExpr, *bzl.Ident:
  157. return true
  158. default:
  159. return false
  160. }
  161. }
  162. func dictEntryKeyValue(e bzl.Expr) (string, *bzl.ListExpr, error) {
  163. kv, ok := e.(*bzl.KeyValueExpr)
  164. if !ok {
  165. return "", nil, fmt.Errorf("dict entry was not a key-value pair: %#v", e)
  166. }
  167. k, ok := kv.Key.(*bzl.StringExpr)
  168. if !ok {
  169. return "", nil, fmt.Errorf("dict key was not string: %#v", kv.Key)
  170. }
  171. v, ok := kv.Value.(*bzl.ListExpr)
  172. if !ok {
  173. return "", nil, fmt.Errorf("dict value was not list: %#v", kv.Value)
  174. }
  175. return k.Value, v, nil
  176. }
  177. func stringValue(e bzl.Expr) string {
  178. s, ok := e.(*bzl.StringExpr)
  179. if !ok {
  180. return ""
  181. }
  182. return s.Value
  183. }
  184. // platformStringsExprs is a set of sub-expressions that match the structure
  185. // of package.PlatformStrings. ExprFromValue produces expressions that
  186. // follow this structure for srcs, deps, and other attributes, so this matches
  187. // all non-scalar expressions generated by Gazelle.
  188. //
  189. // The matched expression has the form:
  190. //
  191. // [] + select({}) + select({}) + select({})
  192. //
  193. // The four collections may appear in any order, and some or all of them may
  194. // be omitted (all fields are nil for a nil expression).
  195. type platformStringsExprs struct {
  196. generic *bzl.ListExpr
  197. os, arch, platform *bzl.DictExpr
  198. }
  199. // extractPlatformStringsExprs matches an expression and attempts to extract
  200. // sub-expressions in platformStringsExprs. The sub-expressions can then be
  201. // merged with corresponding sub-expressions. Any field in the returned
  202. // structure may be nil. An error is returned if the given expression does
  203. // not follow the pattern described by platformStringsExprs.
  204. func extractPlatformStringsExprs(expr bzl.Expr) (platformStringsExprs, error) {
  205. var ps platformStringsExprs
  206. if expr == nil {
  207. return ps, nil
  208. }
  209. // Break the expression into a sequence of expressions combined with +.
  210. var parts []bzl.Expr
  211. for {
  212. binop, ok := expr.(*bzl.BinaryExpr)
  213. if !ok {
  214. parts = append(parts, expr)
  215. break
  216. }
  217. parts = append(parts, binop.Y)
  218. expr = binop.X
  219. }
  220. // Process each part. They may be in any order.
  221. for _, part := range parts {
  222. switch part := part.(type) {
  223. case *bzl.ListExpr:
  224. if ps.generic != nil {
  225. return platformStringsExprs{}, fmt.Errorf("expression could not be matched: multiple list expressions")
  226. }
  227. ps.generic = part
  228. case *bzl.CallExpr:
  229. x, ok := part.X.(*bzl.Ident)
  230. if !ok || x.Name != "select" || len(part.List) != 1 {
  231. return platformStringsExprs{}, fmt.Errorf("expression could not be matched: callee other than select or wrong number of args")
  232. }
  233. arg, ok := part.List[0].(*bzl.DictExpr)
  234. if !ok {
  235. return platformStringsExprs{}, fmt.Errorf("expression could not be matched: select argument not dict")
  236. }
  237. var dict **bzl.DictExpr
  238. for _, item := range arg.List {
  239. kv := item.(*bzl.KeyValueExpr) // parser guarantees this
  240. k, ok := kv.Key.(*bzl.StringExpr)
  241. if !ok {
  242. return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict keys are not all strings")
  243. }
  244. if k.Value == "//conditions:default" {
  245. continue
  246. }
  247. key, err := label.Parse(k.Value)
  248. if err != nil {
  249. return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict key is not label: %q", k.Value)
  250. }
  251. if KnownOSSet[key.Name] {
  252. dict = &ps.os
  253. break
  254. }
  255. if KnownArchSet[key.Name] {
  256. dict = &ps.arch
  257. break
  258. }
  259. osArch := strings.Split(key.Name, "_")
  260. if len(osArch) != 2 || !KnownOSSet[osArch[0]] || !KnownArchSet[osArch[1]] {
  261. return platformStringsExprs{}, fmt.Errorf("expression could not be matched: dict key contains unknown platform: %q", k.Value)
  262. }
  263. dict = &ps.platform
  264. break
  265. }
  266. if dict == nil {
  267. // We could not identify the dict because it's empty or only contains
  268. // //conditions:default. We'll call it the platform dict to avoid
  269. // dropping it.
  270. dict = &ps.platform
  271. }
  272. if *dict != nil {
  273. return platformStringsExprs{}, fmt.Errorf("expression could not be matched: multiple selects that are either os-specific, arch-specific, or platform-specific")
  274. }
  275. *dict = arg
  276. }
  277. }
  278. return ps, nil
  279. }
  280. // makePlatformStringsExpr constructs a single expression from the
  281. // sub-expressions in ps.
  282. func makePlatformStringsExpr(ps platformStringsExprs) bzl.Expr {
  283. makeSelect := func(dict *bzl.DictExpr) bzl.Expr {
  284. return &bzl.CallExpr{
  285. X: &bzl.Ident{Name: "select"},
  286. List: []bzl.Expr{dict},
  287. }
  288. }
  289. forceMultiline := func(e bzl.Expr) {
  290. switch e := e.(type) {
  291. case *bzl.ListExpr:
  292. e.ForceMultiLine = true
  293. case *bzl.CallExpr:
  294. e.List[0].(*bzl.DictExpr).ForceMultiLine = true
  295. }
  296. }
  297. var parts []bzl.Expr
  298. if ps.generic != nil {
  299. parts = append(parts, ps.generic)
  300. }
  301. if ps.os != nil {
  302. parts = append(parts, makeSelect(ps.os))
  303. }
  304. if ps.arch != nil {
  305. parts = append(parts, makeSelect(ps.arch))
  306. }
  307. if ps.platform != nil {
  308. parts = append(parts, makeSelect(ps.platform))
  309. }
  310. if len(parts) == 0 {
  311. return nil
  312. }
  313. if len(parts) == 1 {
  314. return parts[0]
  315. }
  316. expr := parts[0]
  317. forceMultiline(expr)
  318. for _, part := range parts[1:] {
  319. forceMultiline(part)
  320. expr = &bzl.BinaryExpr{
  321. Op: "+",
  322. X: expr,
  323. Y: part,
  324. }
  325. }
  326. return expr
  327. }