├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── LICENSE ├── README.md ├── astcopy.go ├── example_test.go ├── go.mod └── go.sum /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | commit-message: 5 | prefix: "deps:" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | day: "sunday" 10 | time: "09:00" 11 | - package-ecosystem: "github-actions" 12 | commit-message: 13 | prefix: "ci:" 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | day: "sunday" 18 | time: "09:00" 19 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: 'stable' 20 | 21 | - name: Go Format 22 | run: gofmt -s -w . && git diff --exit-code 23 | 24 | - name: Go Tidy 25 | run: go mod tidy && git diff --exit-code 26 | 27 | - name: Go Mod 28 | run: go mod download 29 | 30 | - name: Build 31 | run: go build ./... 32 | 33 | - name: Test 34 | run: go test -v -race -shuffle=on ./... 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 go-toolsmith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # astcopy 2 | 3 | [![build-img]][build-url] 4 | [![pkg-img]][pkg-url] 5 | [![reportcard-img]][reportcard-url] 6 | [![version-img]][version-url] 7 | 8 | Package `astcopy` implements Go AST reflection-free deep copy operations. 9 | 10 | ## Installation: 11 | 12 | Go version 1.16+ 13 | 14 | ```bash 15 | go get github.com/go-toolsmith/astcopy 16 | ``` 17 | 18 | ## Example 19 | 20 | ```go 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "go/ast" 26 | "go/token" 27 | 28 | "github.com/go-toolsmith/astcopy" 29 | "github.com/go-toolsmith/astequal" 30 | "github.com/go-toolsmith/strparse" 31 | ) 32 | 33 | func main() { 34 | x := strparse.Expr(`1 + 2`).(*ast.BinaryExpr) 35 | y := astcopy.BinaryExpr(x) 36 | fmt.Println(astequal.Expr(x, y)) // => true 37 | 38 | // Now modify x and make sure y is not modified. 39 | z := astcopy.BinaryExpr(y) 40 | x.Op = token.SUB 41 | fmt.Println(astequal.Expr(y, z)) // => true 42 | fmt.Println(astequal.Expr(x, y)) // => false 43 | } 44 | ``` 45 | 46 | ## License 47 | 48 | [MIT License](LICENSE). 49 | 50 | [build-img]: https://github.com/go-toolsmith/astp/workflows/build/badge.svg 51 | [build-url]: https://github.com/go-toolsmith/astp/actions 52 | [pkg-img]: https://pkg.go.dev/badge/go-toolsmith/astp 53 | [pkg-url]: https://pkg.go.dev/github.com/go-toolsmith/astp 54 | [reportcard-img]: https://goreportcard.com/badge/go-toolsmith/astp 55 | [reportcard-url]: https://goreportcard.com/report/go-toolsmith/astp 56 | [version-img]: https://img.shields.io/github/v/release/go-toolsmith/astp 57 | [version-url]: https://github.com/go-toolsmith/astp/releases 58 | -------------------------------------------------------------------------------- /astcopy.go: -------------------------------------------------------------------------------- 1 | // Package astcopy implements Go AST reflection-free deep copy operations. 2 | package astcopy 3 | 4 | import ( 5 | "go/ast" 6 | ) 7 | 8 | // Node returns x node deep copy. 9 | // Copy of nil argument is nil. 10 | func Node(x ast.Node) ast.Node { 11 | return copyNode(x) 12 | } 13 | 14 | // NodeList returns xs node slice deep copy. 15 | // Copy of nil argument is nil. 16 | func NodeList(xs []ast.Node) []ast.Node { 17 | if xs == nil { 18 | return nil 19 | } 20 | cp := make([]ast.Node, len(xs)) 21 | for i := range xs { 22 | cp[i] = copyNode(xs[i]) 23 | } 24 | return cp 25 | } 26 | 27 | // Expr returns x expression deep copy. 28 | // Copy of nil argument is nil. 29 | func Expr(x ast.Expr) ast.Expr { 30 | return copyExpr(x) 31 | } 32 | 33 | // ExprList returns xs expression slice deep copy. 34 | // Copy of nil argument is nil. 35 | func ExprList(xs []ast.Expr) []ast.Expr { 36 | if xs == nil { 37 | return nil 38 | } 39 | cp := make([]ast.Expr, len(xs)) 40 | for i := range xs { 41 | cp[i] = copyExpr(xs[i]) 42 | } 43 | return cp 44 | } 45 | 46 | // Stmt returns x statement deep copy. 47 | // Copy of nil argument is nil. 48 | func Stmt(x ast.Stmt) ast.Stmt { 49 | return copyStmt(x) 50 | } 51 | 52 | // StmtList returns xs statement slice deep copy. 53 | // Copy of nil argument is nil. 54 | func StmtList(xs []ast.Stmt) []ast.Stmt { 55 | if xs == nil { 56 | return nil 57 | } 58 | cp := make([]ast.Stmt, len(xs)) 59 | for i := range xs { 60 | cp[i] = copyStmt(xs[i]) 61 | } 62 | return cp 63 | } 64 | 65 | // Decl returns x declaration deep copy. 66 | // Copy of nil argument is nil. 67 | func Decl(x ast.Decl) ast.Decl { 68 | return copyDecl(x) 69 | } 70 | 71 | // DeclList returns xs declaration slice deep copy. 72 | // Copy of nil argument is nil. 73 | func DeclList(xs []ast.Decl) []ast.Decl { 74 | if xs == nil { 75 | return nil 76 | } 77 | cp := make([]ast.Decl, len(xs)) 78 | for i := range xs { 79 | cp[i] = copyDecl(xs[i]) 80 | } 81 | return cp 82 | } 83 | 84 | // BadExpr returns x deep copy. 85 | // Copy of nil argument is nil. 86 | func BadExpr(x *ast.BadExpr) *ast.BadExpr { 87 | if x == nil { 88 | return nil 89 | } 90 | cp := *x 91 | return &cp 92 | } 93 | 94 | // Ident returns x deep copy. 95 | // Copy of nil argument is nil. 96 | func Ident(x *ast.Ident) *ast.Ident { 97 | if x == nil { 98 | return nil 99 | } 100 | cp := *x 101 | return &cp 102 | } 103 | 104 | // IdentList returns xs identifier slice deep copy. 105 | // Copy of nil argument is nil. 106 | func IdentList(xs []*ast.Ident) []*ast.Ident { 107 | if xs == nil { 108 | return nil 109 | } 110 | cp := make([]*ast.Ident, len(xs)) 111 | for i := range xs { 112 | cp[i] = Ident(xs[i]) 113 | } 114 | return cp 115 | } 116 | 117 | // Ellipsis returns x deep copy. 118 | // Copy of nil argument is nil. 119 | func Ellipsis(x *ast.Ellipsis) *ast.Ellipsis { 120 | if x == nil { 121 | return nil 122 | } 123 | cp := *x 124 | cp.Elt = copyExpr(x.Elt) 125 | return &cp 126 | } 127 | 128 | // BasicLit returns x deep copy. 129 | // Copy of nil argument is nil. 130 | func BasicLit(x *ast.BasicLit) *ast.BasicLit { 131 | if x == nil { 132 | return nil 133 | } 134 | cp := *x 135 | return &cp 136 | } 137 | 138 | // FuncLit returns x deep copy. 139 | // Copy of nil argument is nil. 140 | func FuncLit(x *ast.FuncLit) *ast.FuncLit { 141 | if x == nil { 142 | return nil 143 | } 144 | cp := *x 145 | cp.Type = FuncType(x.Type) 146 | cp.Body = BlockStmt(x.Body) 147 | return &cp 148 | } 149 | 150 | // CompositeLit returns x deep copy. 151 | // Copy of nil argument is nil. 152 | func CompositeLit(x *ast.CompositeLit) *ast.CompositeLit { 153 | if x == nil { 154 | return nil 155 | } 156 | cp := *x 157 | cp.Type = copyExpr(x.Type) 158 | cp.Elts = ExprList(x.Elts) 159 | return &cp 160 | } 161 | 162 | // ParenExpr returns x deep copy. 163 | // Copy of nil argument is nil. 164 | func ParenExpr(x *ast.ParenExpr) *ast.ParenExpr { 165 | if x == nil { 166 | return nil 167 | } 168 | cp := *x 169 | cp.X = copyExpr(x.X) 170 | return &cp 171 | } 172 | 173 | // SelectorExpr returns x deep copy. 174 | // Copy of nil argument is nil. 175 | func SelectorExpr(x *ast.SelectorExpr) *ast.SelectorExpr { 176 | if x == nil { 177 | return nil 178 | } 179 | cp := *x 180 | cp.X = copyExpr(x.X) 181 | cp.Sel = Ident(x.Sel) 182 | return &cp 183 | } 184 | 185 | // IndexExpr returns x deep copy. 186 | // Copy of nil argument is nil. 187 | func IndexExpr(x *ast.IndexExpr) *ast.IndexExpr { 188 | if x == nil { 189 | return nil 190 | } 191 | cp := *x 192 | cp.X = copyExpr(x.X) 193 | cp.Index = copyExpr(x.Index) 194 | return &cp 195 | } 196 | 197 | // IndexListExpr returns x deep copy. 198 | // Copy of nil argument is nil. 199 | func IndexListExpr(x *ast.IndexListExpr) *ast.IndexListExpr { 200 | if x == nil { 201 | return nil 202 | } 203 | cp := *x 204 | cp.X = copyExpr(x.X) 205 | cp.Indices = ExprList(x.Indices) 206 | return &cp 207 | } 208 | 209 | // SliceExpr returns x deep copy. 210 | // Copy of nil argument is nil. 211 | func SliceExpr(x *ast.SliceExpr) *ast.SliceExpr { 212 | if x == nil { 213 | return nil 214 | } 215 | cp := *x 216 | cp.X = copyExpr(x.X) 217 | cp.Low = copyExpr(x.Low) 218 | cp.High = copyExpr(x.High) 219 | cp.Max = copyExpr(x.Max) 220 | return &cp 221 | } 222 | 223 | // TypeAssertExpr returns x deep copy. 224 | // Copy of nil argument is nil. 225 | func TypeAssertExpr(x *ast.TypeAssertExpr) *ast.TypeAssertExpr { 226 | if x == nil { 227 | return nil 228 | } 229 | cp := *x 230 | cp.X = copyExpr(x.X) 231 | cp.Type = copyExpr(x.Type) 232 | return &cp 233 | } 234 | 235 | // CallExpr returns x deep copy. 236 | // Copy of nil argument is nil. 237 | func CallExpr(x *ast.CallExpr) *ast.CallExpr { 238 | if x == nil { 239 | return nil 240 | } 241 | cp := *x 242 | cp.Fun = copyExpr(x.Fun) 243 | cp.Args = ExprList(x.Args) 244 | return &cp 245 | } 246 | 247 | // StarExpr returns x deep copy. 248 | // Copy of nil argument is nil. 249 | func StarExpr(x *ast.StarExpr) *ast.StarExpr { 250 | if x == nil { 251 | return nil 252 | } 253 | cp := *x 254 | cp.X = copyExpr(x.X) 255 | return &cp 256 | } 257 | 258 | // UnaryExpr returns x deep copy. 259 | // Copy of nil argument is nil. 260 | func UnaryExpr(x *ast.UnaryExpr) *ast.UnaryExpr { 261 | if x == nil { 262 | return nil 263 | } 264 | cp := *x 265 | cp.X = copyExpr(x.X) 266 | return &cp 267 | } 268 | 269 | // BinaryExpr returns x deep copy. 270 | // Copy of nil argument is nil. 271 | func BinaryExpr(x *ast.BinaryExpr) *ast.BinaryExpr { 272 | if x == nil { 273 | return nil 274 | } 275 | cp := *x 276 | cp.X = copyExpr(x.X) 277 | cp.Y = copyExpr(x.Y) 278 | return &cp 279 | } 280 | 281 | // KeyValueExpr returns x deep copy. 282 | // Copy of nil argument is nil. 283 | func KeyValueExpr(x *ast.KeyValueExpr) *ast.KeyValueExpr { 284 | if x == nil { 285 | return nil 286 | } 287 | cp := *x 288 | cp.Key = copyExpr(x.Key) 289 | cp.Value = copyExpr(x.Value) 290 | return &cp 291 | } 292 | 293 | // ArrayType returns x deep copy. 294 | // Copy of nil argument is nil. 295 | func ArrayType(x *ast.ArrayType) *ast.ArrayType { 296 | if x == nil { 297 | return nil 298 | } 299 | cp := *x 300 | cp.Len = copyExpr(x.Len) 301 | cp.Elt = copyExpr(x.Elt) 302 | return &cp 303 | } 304 | 305 | // StructType returns x deep copy. 306 | // Copy of nil argument is nil. 307 | func StructType(x *ast.StructType) *ast.StructType { 308 | if x == nil { 309 | return nil 310 | } 311 | cp := *x 312 | cp.Fields = FieldList(x.Fields) 313 | return &cp 314 | } 315 | 316 | // Field returns x deep copy. 317 | // Copy of nil argument is nil. 318 | func Field(x *ast.Field) *ast.Field { 319 | if x == nil { 320 | return nil 321 | } 322 | cp := *x 323 | cp.Names = IdentList(x.Names) 324 | cp.Type = copyExpr(x.Type) 325 | cp.Tag = BasicLit(x.Tag) 326 | cp.Doc = CommentGroup(x.Doc) 327 | cp.Comment = CommentGroup(x.Comment) 328 | return &cp 329 | } 330 | 331 | // FieldList returns x deep copy. 332 | // Copy of nil argument is nil. 333 | func FieldList(x *ast.FieldList) *ast.FieldList { 334 | if x == nil { 335 | return nil 336 | } 337 | cp := *x 338 | if x.List != nil { 339 | cp.List = make([]*ast.Field, len(x.List)) 340 | for i := range x.List { 341 | cp.List[i] = Field(x.List[i]) 342 | } 343 | } 344 | return &cp 345 | } 346 | 347 | // InterfaceType returns x deep copy. 348 | // Copy of nil argument is nil. 349 | func InterfaceType(x *ast.InterfaceType) *ast.InterfaceType { 350 | if x == nil { 351 | return nil 352 | } 353 | cp := *x 354 | cp.Methods = FieldList(x.Methods) 355 | return &cp 356 | } 357 | 358 | // MapType returns x deep copy. 359 | // Copy of nil argument is nil. 360 | func MapType(x *ast.MapType) *ast.MapType { 361 | if x == nil { 362 | return nil 363 | } 364 | cp := *x 365 | cp.Key = copyExpr(x.Key) 366 | cp.Value = copyExpr(x.Value) 367 | return &cp 368 | } 369 | 370 | // ChanType returns x deep copy. 371 | // Copy of nil argument is nil. 372 | func ChanType(x *ast.ChanType) *ast.ChanType { 373 | if x == nil { 374 | return nil 375 | } 376 | cp := *x 377 | cp.Value = copyExpr(x.Value) 378 | return &cp 379 | } 380 | 381 | // BlockStmt returns x deep copy. 382 | // Copy of nil argument is nil. 383 | func BlockStmt(x *ast.BlockStmt) *ast.BlockStmt { 384 | if x == nil { 385 | return nil 386 | } 387 | cp := *x 388 | cp.List = StmtList(x.List) 389 | return &cp 390 | } 391 | 392 | // ImportSpec returns x deep copy. 393 | // Copy of nil argument is nil. 394 | func ImportSpec(x *ast.ImportSpec) *ast.ImportSpec { 395 | if x == nil { 396 | return nil 397 | } 398 | cp := *x 399 | cp.Name = Ident(x.Name) 400 | cp.Path = BasicLit(x.Path) 401 | cp.Doc = CommentGroup(x.Doc) 402 | cp.Comment = CommentGroup(x.Comment) 403 | return &cp 404 | } 405 | 406 | // ValueSpec returns x deep copy. 407 | // Copy of nil argument is nil. 408 | func ValueSpec(x *ast.ValueSpec) *ast.ValueSpec { 409 | if x == nil { 410 | return nil 411 | } 412 | cp := *x 413 | cp.Names = IdentList(x.Names) 414 | cp.Values = ExprList(x.Values) 415 | cp.Type = copyExpr(x.Type) 416 | cp.Doc = CommentGroup(x.Doc) 417 | cp.Comment = CommentGroup(x.Comment) 418 | return &cp 419 | } 420 | 421 | // Spec returns x deep copy. 422 | // Copy of nil argument is nil. 423 | func Spec(x ast.Spec) ast.Spec { 424 | if x == nil { 425 | return nil 426 | } 427 | 428 | switch x := x.(type) { 429 | case *ast.ImportSpec: 430 | return ImportSpec(x) 431 | case *ast.ValueSpec: 432 | return ValueSpec(x) 433 | case *ast.TypeSpec: 434 | return TypeSpec(x) 435 | default: 436 | panic("unhandled spec") 437 | } 438 | } 439 | 440 | // SpecList returns xs spec slice deep copy. 441 | // Copy of nil argument is nil. 442 | func SpecList(xs []ast.Spec) []ast.Spec { 443 | if xs == nil { 444 | return nil 445 | } 446 | cp := make([]ast.Spec, len(xs)) 447 | for i := range xs { 448 | cp[i] = Spec(xs[i]) 449 | } 450 | return cp 451 | } 452 | 453 | // BadStmt returns x deep copy. 454 | // Copy of nil argument is nil. 455 | func BadStmt(x *ast.BadStmt) *ast.BadStmt { 456 | if x == nil { 457 | return nil 458 | } 459 | cp := *x 460 | return &cp 461 | } 462 | 463 | // DeclStmt returns x deep copy. 464 | // Copy of nil argument is nil. 465 | func DeclStmt(x *ast.DeclStmt) *ast.DeclStmt { 466 | if x == nil { 467 | return nil 468 | } 469 | cp := *x 470 | cp.Decl = copyDecl(x.Decl) 471 | return &cp 472 | } 473 | 474 | // EmptyStmt returns x deep copy. 475 | // Copy of nil argument is nil. 476 | func EmptyStmt(x *ast.EmptyStmt) *ast.EmptyStmt { 477 | if x == nil { 478 | return nil 479 | } 480 | cp := *x 481 | return &cp 482 | } 483 | 484 | // LabeledStmt returns x deep copy. 485 | // Copy of nil argument is nil. 486 | func LabeledStmt(x *ast.LabeledStmt) *ast.LabeledStmt { 487 | if x == nil { 488 | return nil 489 | } 490 | cp := *x 491 | cp.Label = Ident(x.Label) 492 | cp.Stmt = copyStmt(x.Stmt) 493 | return &cp 494 | } 495 | 496 | // ExprStmt returns x deep copy. 497 | // Copy of nil argument is nil. 498 | func ExprStmt(x *ast.ExprStmt) *ast.ExprStmt { 499 | if x == nil { 500 | return nil 501 | } 502 | cp := *x 503 | cp.X = copyExpr(x.X) 504 | return &cp 505 | } 506 | 507 | // SendStmt returns x deep copy. 508 | // Copy of nil argument is nil. 509 | func SendStmt(x *ast.SendStmt) *ast.SendStmt { 510 | if x == nil { 511 | return nil 512 | } 513 | cp := *x 514 | cp.Chan = copyExpr(x.Chan) 515 | cp.Value = copyExpr(x.Value) 516 | return &cp 517 | } 518 | 519 | // IncDecStmt returns x deep copy. 520 | // Copy of nil argument is nil. 521 | func IncDecStmt(x *ast.IncDecStmt) *ast.IncDecStmt { 522 | if x == nil { 523 | return nil 524 | } 525 | cp := *x 526 | cp.X = copyExpr(x.X) 527 | return &cp 528 | } 529 | 530 | // AssignStmt returns x deep copy. 531 | // Copy of nil argument is nil. 532 | func AssignStmt(x *ast.AssignStmt) *ast.AssignStmt { 533 | if x == nil { 534 | return nil 535 | } 536 | cp := *x 537 | cp.Lhs = ExprList(x.Lhs) 538 | cp.Rhs = ExprList(x.Rhs) 539 | return &cp 540 | } 541 | 542 | // GoStmt returns x deep copy. 543 | // Copy of nil argument is nil. 544 | func GoStmt(x *ast.GoStmt) *ast.GoStmt { 545 | if x == nil { 546 | return nil 547 | } 548 | cp := *x 549 | cp.Call = CallExpr(x.Call) 550 | return &cp 551 | } 552 | 553 | // DeferStmt returns x deep copy. 554 | // Copy of nil argument is nil. 555 | func DeferStmt(x *ast.DeferStmt) *ast.DeferStmt { 556 | if x == nil { 557 | return nil 558 | } 559 | cp := *x 560 | cp.Call = CallExpr(x.Call) 561 | return &cp 562 | } 563 | 564 | // ReturnStmt returns x deep copy. 565 | // Copy of nil argument is nil. 566 | func ReturnStmt(x *ast.ReturnStmt) *ast.ReturnStmt { 567 | if x == nil { 568 | return nil 569 | } 570 | cp := *x 571 | cp.Results = ExprList(x.Results) 572 | return &cp 573 | } 574 | 575 | // BranchStmt returns x deep copy. 576 | // Copy of nil argument is nil. 577 | func BranchStmt(x *ast.BranchStmt) *ast.BranchStmt { 578 | if x == nil { 579 | return nil 580 | } 581 | cp := *x 582 | cp.Label = Ident(x.Label) 583 | return &cp 584 | } 585 | 586 | // IfStmt returns x deep copy. 587 | // Copy of nil argument is nil. 588 | func IfStmt(x *ast.IfStmt) *ast.IfStmt { 589 | if x == nil { 590 | return nil 591 | } 592 | cp := *x 593 | cp.Init = copyStmt(x.Init) 594 | cp.Cond = copyExpr(x.Cond) 595 | cp.Body = BlockStmt(x.Body) 596 | cp.Else = copyStmt(x.Else) 597 | return &cp 598 | } 599 | 600 | // CaseClause returns x deep copy. 601 | // Copy of nil argument is nil. 602 | func CaseClause(x *ast.CaseClause) *ast.CaseClause { 603 | if x == nil { 604 | return nil 605 | } 606 | cp := *x 607 | cp.List = ExprList(x.List) 608 | cp.Body = StmtList(x.Body) 609 | return &cp 610 | } 611 | 612 | // SwitchStmt returns x deep copy. 613 | // Copy of nil argument is nil. 614 | func SwitchStmt(x *ast.SwitchStmt) *ast.SwitchStmt { 615 | if x == nil { 616 | return nil 617 | } 618 | cp := *x 619 | cp.Init = copyStmt(x.Init) 620 | cp.Tag = copyExpr(x.Tag) 621 | cp.Body = BlockStmt(x.Body) 622 | return &cp 623 | } 624 | 625 | // TypeSwitchStmt returns x deep copy. 626 | // Copy of nil argument is nil. 627 | func TypeSwitchStmt(x *ast.TypeSwitchStmt) *ast.TypeSwitchStmt { 628 | if x == nil { 629 | return nil 630 | } 631 | cp := *x 632 | cp.Init = copyStmt(x.Init) 633 | cp.Assign = copyStmt(x.Assign) 634 | cp.Body = BlockStmt(x.Body) 635 | return &cp 636 | } 637 | 638 | // CommClause returns x deep copy. 639 | // Copy of nil argument is nil. 640 | func CommClause(x *ast.CommClause) *ast.CommClause { 641 | if x == nil { 642 | return nil 643 | } 644 | cp := *x 645 | cp.Comm = copyStmt(x.Comm) 646 | cp.Body = StmtList(x.Body) 647 | return &cp 648 | } 649 | 650 | // SelectStmt returns x deep copy. 651 | // Copy of nil argument is nil. 652 | func SelectStmt(x *ast.SelectStmt) *ast.SelectStmt { 653 | if x == nil { 654 | return nil 655 | } 656 | cp := *x 657 | cp.Body = BlockStmt(x.Body) 658 | return &cp 659 | } 660 | 661 | // ForStmt returns x deep copy. 662 | // Copy of nil argument is nil. 663 | func ForStmt(x *ast.ForStmt) *ast.ForStmt { 664 | if x == nil { 665 | return nil 666 | } 667 | cp := *x 668 | cp.Init = copyStmt(x.Init) 669 | cp.Cond = copyExpr(x.Cond) 670 | cp.Post = copyStmt(x.Post) 671 | cp.Body = BlockStmt(x.Body) 672 | return &cp 673 | } 674 | 675 | // RangeStmt returns x deep copy. 676 | // Copy of nil argument is nil. 677 | func RangeStmt(x *ast.RangeStmt) *ast.RangeStmt { 678 | if x == nil { 679 | return nil 680 | } 681 | cp := *x 682 | cp.Key = copyExpr(x.Key) 683 | cp.Value = copyExpr(x.Value) 684 | cp.X = copyExpr(x.X) 685 | cp.Body = BlockStmt(x.Body) 686 | return &cp 687 | } 688 | 689 | // Comment returns x deep copy. 690 | // Copy of nil argument is nil. 691 | func Comment(x *ast.Comment) *ast.Comment { 692 | if x == nil { 693 | return nil 694 | } 695 | cp := *x 696 | return &cp 697 | } 698 | 699 | // CommentGroup returns x deep copy. 700 | // Copy of nil argument is nil. 701 | func CommentGroup(x *ast.CommentGroup) *ast.CommentGroup { 702 | if x == nil { 703 | return nil 704 | } 705 | cp := *x 706 | if x.List != nil { 707 | cp.List = make([]*ast.Comment, len(x.List)) 708 | for i := range x.List { 709 | cp.List[i] = Comment(x.List[i]) 710 | } 711 | } 712 | return &cp 713 | } 714 | 715 | // File returns x deep copy. 716 | // Copy of nil argument is nil. 717 | func File(x *ast.File) *ast.File { 718 | if x == nil { 719 | return nil 720 | } 721 | cp := *x 722 | cp.Doc = CommentGroup(x.Doc) 723 | cp.Name = Ident(x.Name) 724 | cp.Decls = DeclList(x.Decls) 725 | cp.Imports = make([]*ast.ImportSpec, len(x.Imports)) 726 | for i := range x.Imports { 727 | cp.Imports[i] = ImportSpec(x.Imports[i]) 728 | } 729 | cp.Unresolved = IdentList(x.Unresolved) 730 | cp.Comments = make([]*ast.CommentGroup, len(x.Comments)) 731 | for i := range x.Comments { 732 | cp.Comments[i] = CommentGroup(x.Comments[i]) 733 | } 734 | return &cp 735 | } 736 | 737 | // Package returns x deep copy. 738 | // Copy of nil argument is nil. 739 | func Package(x *ast.Package) *ast.Package { 740 | if x == nil { 741 | return nil 742 | } 743 | cp := *x 744 | cp.Files = make(map[string]*ast.File) 745 | for filename, f := range x.Files { 746 | cp.Files[filename] = f 747 | } 748 | return &cp 749 | } 750 | 751 | // BadDecl returns x deep copy. 752 | // Copy of nil argument is nil. 753 | func BadDecl(x *ast.BadDecl) *ast.BadDecl { 754 | if x == nil { 755 | return nil 756 | } 757 | cp := *x 758 | return &cp 759 | } 760 | 761 | // GenDecl returns x deep copy. 762 | // Copy of nil argument is nil. 763 | func GenDecl(x *ast.GenDecl) *ast.GenDecl { 764 | if x == nil { 765 | return nil 766 | } 767 | cp := *x 768 | cp.Specs = SpecList(x.Specs) 769 | cp.Doc = CommentGroup(x.Doc) 770 | return &cp 771 | } 772 | 773 | // FuncDecl returns x deep copy. 774 | // Copy of nil argument is nil. 775 | func FuncDecl(x *ast.FuncDecl) *ast.FuncDecl { 776 | if x == nil { 777 | return nil 778 | } 779 | cp := *x 780 | cp.Recv = FieldList(x.Recv) 781 | cp.Name = Ident(x.Name) 782 | cp.Type = FuncType(x.Type) 783 | cp.Body = BlockStmt(x.Body) 784 | cp.Doc = CommentGroup(x.Doc) 785 | return &cp 786 | } 787 | 788 | // FuncType returns x deep copy. 789 | // Copy of nil argument is nil. 790 | func FuncType(x *ast.FuncType) *ast.FuncType { 791 | if x == nil { 792 | return nil 793 | } 794 | cp := *x 795 | cp.Params = FieldList(x.Params) 796 | cp.Results = FieldList(x.Results) 797 | cp.TypeParams = FieldList(x.TypeParams) 798 | return &cp 799 | } 800 | 801 | // TypeSpec returns x deep copy. 802 | // Copy of nil argument is nil. 803 | func TypeSpec(x *ast.TypeSpec) *ast.TypeSpec { 804 | if x == nil { 805 | return nil 806 | } 807 | cp := *x 808 | cp.Name = Ident(x.Name) 809 | cp.Type = copyExpr(x.Type) 810 | cp.Doc = CommentGroup(x.Doc) 811 | cp.Comment = CommentGroup(x.Comment) 812 | cp.TypeParams = FieldList(x.TypeParams) 813 | return &cp 814 | } 815 | 816 | func copyNode(x ast.Node) ast.Node { 817 | switch x := x.(type) { 818 | case ast.Expr: 819 | return copyExpr(x) 820 | case ast.Stmt: 821 | return copyStmt(x) 822 | case ast.Decl: 823 | return copyDecl(x) 824 | 825 | case ast.Spec: 826 | return Spec(x) 827 | case *ast.FieldList: 828 | return FieldList(x) 829 | case *ast.Comment: 830 | return Comment(x) 831 | case *ast.CommentGroup: 832 | return CommentGroup(x) 833 | case *ast.File: 834 | return File(x) 835 | case *ast.Package: 836 | return Package(x) 837 | 838 | default: 839 | panic("unhandled node") 840 | } 841 | } 842 | 843 | func copyExpr(x ast.Expr) ast.Expr { 844 | if x == nil { 845 | return nil 846 | } 847 | 848 | switch x := x.(type) { 849 | case *ast.BadExpr: 850 | return BadExpr(x) 851 | case *ast.Ident: 852 | return Ident(x) 853 | case *ast.Ellipsis: 854 | return Ellipsis(x) 855 | case *ast.BasicLit: 856 | return BasicLit(x) 857 | case *ast.FuncLit: 858 | return FuncLit(x) 859 | case *ast.CompositeLit: 860 | return CompositeLit(x) 861 | case *ast.ParenExpr: 862 | return ParenExpr(x) 863 | case *ast.SelectorExpr: 864 | return SelectorExpr(x) 865 | case *ast.IndexExpr: 866 | return IndexExpr(x) 867 | case *ast.IndexListExpr: 868 | return IndexListExpr(x) 869 | case *ast.SliceExpr: 870 | return SliceExpr(x) 871 | case *ast.TypeAssertExpr: 872 | return TypeAssertExpr(x) 873 | case *ast.CallExpr: 874 | return CallExpr(x) 875 | case *ast.StarExpr: 876 | return StarExpr(x) 877 | case *ast.UnaryExpr: 878 | return UnaryExpr(x) 879 | case *ast.BinaryExpr: 880 | return BinaryExpr(x) 881 | case *ast.KeyValueExpr: 882 | return KeyValueExpr(x) 883 | case *ast.ArrayType: 884 | return ArrayType(x) 885 | case *ast.StructType: 886 | return StructType(x) 887 | case *ast.FuncType: 888 | return FuncType(x) 889 | case *ast.InterfaceType: 890 | return InterfaceType(x) 891 | case *ast.MapType: 892 | return MapType(x) 893 | case *ast.ChanType: 894 | return ChanType(x) 895 | 896 | default: 897 | panic("unhandled expr") 898 | } 899 | } 900 | 901 | func copyStmt(x ast.Stmt) ast.Stmt { 902 | if x == nil { 903 | return nil 904 | } 905 | 906 | switch x := x.(type) { 907 | case *ast.BadStmt: 908 | return BadStmt(x) 909 | case *ast.DeclStmt: 910 | return DeclStmt(x) 911 | case *ast.EmptyStmt: 912 | return EmptyStmt(x) 913 | case *ast.LabeledStmt: 914 | return LabeledStmt(x) 915 | case *ast.ExprStmt: 916 | return ExprStmt(x) 917 | case *ast.SendStmt: 918 | return SendStmt(x) 919 | case *ast.IncDecStmt: 920 | return IncDecStmt(x) 921 | case *ast.AssignStmt: 922 | return AssignStmt(x) 923 | case *ast.GoStmt: 924 | return GoStmt(x) 925 | case *ast.DeferStmt: 926 | return DeferStmt(x) 927 | case *ast.ReturnStmt: 928 | return ReturnStmt(x) 929 | case *ast.BranchStmt: 930 | return BranchStmt(x) 931 | case *ast.BlockStmt: 932 | return BlockStmt(x) 933 | case *ast.IfStmt: 934 | return IfStmt(x) 935 | case *ast.CaseClause: 936 | return CaseClause(x) 937 | case *ast.SwitchStmt: 938 | return SwitchStmt(x) 939 | case *ast.TypeSwitchStmt: 940 | return TypeSwitchStmt(x) 941 | case *ast.CommClause: 942 | return CommClause(x) 943 | case *ast.SelectStmt: 944 | return SelectStmt(x) 945 | case *ast.ForStmt: 946 | return ForStmt(x) 947 | case *ast.RangeStmt: 948 | return RangeStmt(x) 949 | 950 | default: 951 | panic("unhandled stmt") 952 | } 953 | } 954 | 955 | func copyDecl(x ast.Decl) ast.Decl { 956 | if x == nil { 957 | return nil 958 | } 959 | 960 | switch x := x.(type) { 961 | case *ast.BadDecl: 962 | return BadDecl(x) 963 | case *ast.GenDecl: 964 | return GenDecl(x) 965 | case *ast.FuncDecl: 966 | return FuncDecl(x) 967 | 968 | default: 969 | panic("unhandled decl") 970 | } 971 | } 972 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package astcopy_test 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/token" 7 | 8 | "github.com/go-toolsmith/astcopy" 9 | "github.com/go-toolsmith/astequal" 10 | "github.com/go-toolsmith/strparse" 11 | ) 12 | 13 | func Example() { 14 | x := strparse.Expr(`1 + 2`).(*ast.BinaryExpr) 15 | y := astcopy.BinaryExpr(x) 16 | fmt.Println(astequal.Expr(x, y)) // => true 17 | 18 | // Now modify x and make sure y is not modified. 19 | z := astcopy.BinaryExpr(y) 20 | x.Op = token.SUB 21 | fmt.Println(astequal.Expr(y, z)) // => true 22 | fmt.Println(astequal.Expr(x, y)) // => false 23 | 24 | // Output: 25 | // true 26 | // true 27 | // false 28 | } 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-toolsmith/astcopy 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/go-toolsmith/astequal v1.2.0 7 | github.com/go-toolsmith/strparse v1.1.0 8 | ) 9 | 10 | require github.com/google/go-cmp v0.6.0 // indirect 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= 2 | github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= 3 | github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= 4 | github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= 5 | github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= 6 | github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= 7 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 8 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 9 | golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= 10 | --------------------------------------------------------------------------------