diff --git a/README.md b/README.md index ae0ce5f4a..4027d79f2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ This repository contains four independent versions of Piranha, one for each of t To use/build each version, look under the corresponding [lang]/ directory and follow instructions in the corresponding [lang]/README.md file. Make sure to cd into that directory to build any related code following the instructions in the README. +- [PiranhaGo](go/README.md) - [PiranhaJava](java/README.md) - [PiranhaJS](javascript/README.md) - [PiranhaObjC](objc/README.md) diff --git a/go/README.md b/go/README.md new file mode 100644 index 000000000..00f79a55f --- /dev/null +++ b/go/README.md @@ -0,0 +1,19 @@ +# PiranhaGo +PiranhaGo is runnable now. +Instructions:- +1. To build the package run `go build -o piranha`. Dependencies will install automatically and they are given in `go.mod` file. +2. Below are the instructions for running for the single file. +``` +Inputs from args +Usage: ./piranha [-h] -p PROPERTIES -s SOURCE_FILE -f STALE_FLAG -mode MODE_NAME [-o OUTPUT] +Required arguments: + -s SOURCE_FILE: Path of the file to be refactored. + -p PROPERTIES: Configuration file (json format) for Piranha. + -f STALE_FLAG: Name of the stale flag. + -mode MODE_NAME: If MODE_NAME=treated, then flag is treated, + otherwise MODE_NAME=control, it is control. +Optional arguments: + -h: Show the options and exit. + -o OUTPUT: Destination of the refactored output from piranha. If -o is not provided, then the source file is updated in place. +``` +3. To do a test run, run piranha on `example/testExample.go`. Run `./piranha -p properties.json -s ./example/testExample.go -o ./example/treatedExample.go -f staleFlag -mode control` command in root directory. You will get your refactored file as `/example/treatedExample.go`. diff --git a/go/example/testExample.go b/go/example/testExample.go new file mode 100644 index 000000000..ea7447660 --- /dev/null +++ b/go/example/testExample.go @@ -0,0 +1,69 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testExpressions(ge GoExamples) { + var x, y bool = false, false + + if ge.flagMthds.treatedBehaviour(staleFlag) || x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && (x || y) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && (x && y) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && y == x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) || y == x { + fmt.Println("then-braanch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) && y && x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) || y || x { + fmt.Println("then-braanch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } + +} diff --git a/go/example/treatedExample.go b/go/example/treatedExample.go new file mode 100644 index 000000000..6434aeb71 --- /dev/null +++ b/go/example/treatedExample.go @@ -0,0 +1,37 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testfiles + +import "fmt" + +func testExpressions(ge GoExamples) { + var x, y bool = false, false + if x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + fmt.Println("then-braanch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + if y && x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } + fmt.Println("then-braanch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") +} diff --git a/go/go.mod b/go/go.mod new file mode 100644 index 000000000..562f7b0c7 --- /dev/null +++ b/go/go.mod @@ -0,0 +1,5 @@ +module github.com/PiranhaGo + +go 1.16 + +require github.com/dave/dst v0.26.2 diff --git a/go/go.sum b/go/go.sum new file mode 100644 index 000000000..1de5b54be --- /dev/null +++ b/go/go.sum @@ -0,0 +1,37 @@ +github.com/dave/dst v0.26.2 h1:lnxLAKI3tx7MgLNVDirFCsDTlTG9nKTk7GcptKcWSwY= +github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU= +github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= +github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= +github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= +github.com/google/pprof v0.0.0-20181127221834-b4f47329b966/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/arch v0.0.0-20180920145803-b19384d3c130/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5 h1:MeC2gMlMdkd67dn17MEby3rGXRxZtWeiRXOnISfTQ74= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= diff --git a/go/main.go b/go/main.go new file mode 100644 index 000000000..4733d5187 --- /dev/null +++ b/go/main.go @@ -0,0 +1,44 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "flag" + + "github.com/PiranhaGo/src" +) + +func main() { + var sourceFile, configFile, flagName, outputFileName, modeName string + var isTreated = false + + // get configFile + flag.StringVar(&configFile, "p", "PROPERTIES", "Configuration file (json format) for Piranha.") + // get sourceFile + flag.StringVar(&sourceFile, "s", "SOURCE_FILE", "Path of the file to be refactored.") + // get flagName + flag.StringVar(&flagName, "f", "STALE_FLAG", "Name of the stale flag.") + // check treatedMode + flag.StringVar(&modeName, "mode", "MODE_NAME", "If MODE_NAME=treated, then flag is treated, otherwise MODE_NAME=control, it is control.") + // get outputFileName + flag.StringVar(&outputFileName, "o", "OUTPUT", "Destination of the refactored output from piranha. If -o is not provided, then the source file is updated in place.") + + flag.Parse() + + if modeName == "treated" { + isTreated = true + } + + src.RunPiranha(sourceFile, configFile, flagName, outputFileName, isTreated) +} diff --git a/go/properties.json b/go/properties.json new file mode 100644 index 000000000..8e2490a77 --- /dev/null +++ b/go/properties.json @@ -0,0 +1,20 @@ +{ + "methodProperties": [ + { + "methodName": "treatedBehaviour", + "flagType": "treated", + "argumentIndex": 0 + }, + { + "methodName": "controlBehaviour", + "flagType": "control", + "argumentIndex": 0 + }, + { + "methodName": "commonBehaviour", + "flagType": "treated", + "argumentIndex": 1 + } + ] + } + \ No newline at end of file diff --git a/go/src/piranha.go b/go/src/piranha.go new file mode 100644 index 000000000..1bda3b104 --- /dev/null +++ b/go/src/piranha.go @@ -0,0 +1,106 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package src + +import ( + "fmt" + "go/parser" + "go/token" + "log" + "os" + "strings" + + "github.com/dave/dst" + "github.com/dave/dst/decorator" +) + +/* +Inputs from args +Usage: ./piranha [-h] -p PROPERTIES -s SOURCE_FILE -f STALE_FLAG -mode MODE_NAME [-o OUTPUT] +Required arguments: + -s SOURCE_FILE: Path of the file to be refactored. + -p PROPERTIES: Configuration file (json format) for Piranha. + -f STALE_FLAG: Name of the stale flag. + -mode MODE_NAME: If MODE_NAME=treated, then flag is treated, + otherwise MODE_NAME=control, it is control. +Optional arguments: + -h: Show the options and exit. + -o OUTPUT: Destination of the refactored output from piranha. If -o is not provided, then the source file is updated in place. +*/ +func reportArgumentError(arg string) { + switch arg { + case "configFileSuffix": + fmt.Println("Please provide configuration file of json format.") + case "sourceFileSuffix": + fmt.Println("Please provide source file of go format.") + case "flagName": + fmt.Println("Please provide a flag.") + case "configFile": + fmt.Println("Please provide a config file. See README for more instructions.") + case "sourceFile": + fmt.Println("Please provide a source file that is to be refactored.") + } + fmt.Println("For more info, run ./piranha -h.") +} + +// RunPiranha : the main function for the piranha tool +func RunPiranha(sourceFile string, configFile string, flagName string, outputFileName string, isTreated bool) { + if flagName == "STALE_FLAG" { + reportArgumentError("flagName") + return + } + if sourceFile == "SOURCE_FILE" { + reportArgumentError("sourceFile") + return + } + if configFile == "PROPERTIES" { + reportArgumentError("configFile") + return + } + + if !strings.HasSuffix(configFile, ".json") { + reportArgumentError("sourceFileSuffix") + return + } + + if !strings.HasSuffix(sourceFile, ".go") { + reportArgumentError("sourceFileSuffix") + return + } + + fs := token.NewFileSet() + parsed, err := decorator.ParseFile(fs, sourceFile, nil, parser.ParseComments) + if err != nil { + log.Fatal(err) + } + + var cleaner staleFlagCleaner + err = cleaner.init(configFile, flagName, isTreated) + if err != nil { + log.Fatal(err) + } + + newRoot := cleaner.run(parsed) + + if outputFileName == "" { + outputFileName = sourceFile + } + outputFile, _ := os.Create(outputFileName) + /* + Here we are typecasting newRoot to dst.File. It is safe because the root of AST + always starts with the dst.File type. + */ + decorator.Fprint(outputFile, newRoot.(*dst.File)) + outputFile.Close() +} diff --git a/go/src/staleFlagCleaner.go b/go/src/staleFlagCleaner.go new file mode 100644 index 000000000..6dfcce04f --- /dev/null +++ b/go/src/staleFlagCleaner.go @@ -0,0 +1,619 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package src + +import ( + "encoding/json" + "fmt" + "go/token" + "io/ioutil" + "os" + + /* + We have used dst package instead of official go ast package because ast package + does not keep positions of comments, we use dst package which keeps the positions + of comment in place. It is similiar to the ast package and have same functions as ast. + So you can use documentation of ast to see the details of helper functions. + */ + "github.com/dave/dst" + "github.com/dave/dst/dstutil" +) + +// API describes the type of API. +type API int + +const ( + _isTreated API = iota + _isControl + _isTesting + _isUnknown +) + +// Methods describes the parent field of the json format. +type Methods struct { + Methods []FlagAPI `json:"methodProperties"` +} + +// FlagAPI denotes the field values in json format. +type FlagAPI struct { + MethodName string `json:"methodName"` + OfType string `json:"flagType"` + FlagIndex int `json:"argumentIndex"` +} + +// Value is made to not confuse with true and false in code. +type Value int + +const ( + isTrue Value = iota + isFalse + // This will be returned when you don't have to find true or false. + isUndefined +) + +// Some useful global variables +var ( + modeTreated = "treated" + modeControl = "control" +) +var ( + strTrue = "true" + strFalse = "false" +) + +// Defining a error class in here +type initError struct { + errStr string +} + +func (je *initError) Error() string { + return fmt.Sprintf("staleFlagCleaner initialization error: %s", je.errStr) +} + +// Implementing this as whole class +type staleFlagCleaner struct { + configFile string + methods Methods + + /* + Here valueMap contains those variable names whose value are affected by the stale function output. + It means that this will used for cleaning in Deep Clean Pass. + */ + valueMap map[string]Value + functype map[string]FlagAPI + + flagName string + isTreated bool +} + +func (sfc *staleFlagCleaner) init(configFile string, flagName string, isTreated bool) error { + sfc.configFile = configFile + sfc.flagName = flagName + sfc.isTreated = isTreated + sfc.valueMap = make(map[string]Value) + sfc.functype = make(map[string]FlagAPI) + err := sfc.ParseJSON() + return err +} + +// Parse json +func (sfc *staleFlagCleaner) ParseJSON() error { + + jsonFile, err := os.Open(sfc.configFile) + if err != nil { + return &initError{"Failed opening the config file."} + } + + byteValue, err := ioutil.ReadAll(jsonFile) + if err != nil { + return &initError{"Failed processing the config file, unable to read it."} + } + + // we unmarshal our byteArray which contains our + // jsonFile's content into 'methodProperties' which we defined above + err = json.Unmarshal(byteValue, &sfc.methods) + if err != nil { + return &initError{"Failed processing the config file, unable to unmarshal json file."} + } + // store it in a hashmap + for _, element := range sfc.methods.Methods { + sfc.functype[element.MethodName] = element + } + return nil +} + +// Gets the type of the flagAPI +func (sfc *staleFlagCleaner) flagTypeAPI(callExpr *dst.CallExpr) API { + var apiName string + + switch d := callExpr.Fun.(type) { + case *dst.Ident: + apiName = d.Name + case *dst.SelectorExpr: + apiName = d.Sel.Name + default: + // other cases will come in future + } + + if element, ok := sfc.functype[apiName]; ok { + if element.FlagIndex < len(callExpr.Args) { + switch arg := callExpr.Args[element.FlagIndex].(type) { + case *dst.Ident: + if arg.Name == sfc.flagName { + switch element.OfType { + case modeTreated: + return _isTreated + case modeControl: + return _isControl + default: + return _isUnknown + } + } + } + } + } + return _isUnknown +} + +/* This function will evaluate all expression nodes here */ +func (sfc *staleFlagCleaner) evaluateExprNode(exprNode dst.Expr) Value { + + switch exprUnderConsideration := exprNode.(type) { + case *dst.Ident: + if val, ok := sfc.valueMap[exprUnderConsideration.Name]; ok { + return val + } + switch exprUnderConsideration.Name { + case strTrue: + return isTrue + case strFalse: + return isFalse + } + + // Handling expressions related to calling a function + case *dst.CallExpr: + // Get the type of API + typeOfAPI := sfc.flagTypeAPI(exprUnderConsideration) + // Hardcoding the output of some API types + if typeOfAPI == _isTreated { + if sfc.isTreated { + return isTrue + } + return isFalse + } + if typeOfAPI == _isControl { + if sfc.isTreated { + return isFalse + } + return isTrue + } + //Binary expressions + case *dst.BinaryExpr: + valX := sfc.evaluateExprNode(exprUnderConsideration.X) + valY := sfc.evaluateExprNode(exprUnderConsideration.Y) + if valX != isUndefined || valY != isUndefined { + switch exprUnderConsideration.Op { + case token.LAND: //stands for && + if valX == isFalse || valY == isFalse { + return isFalse + } + if valX == isTrue && valY == isTrue { + return isTrue + } + case token.LOR: //stands for || + if valX == isTrue || valY == isTrue { + return isTrue + } + if valX == isFalse && valY == isFalse { + return isFalse + } + case token.EQL: + if valX != isUndefined && valY != isUndefined { + if valX == valY { + return isTrue + } + return isFalse + } + + case token.NEQ: + if valX == valY { + return isFalse + } + return isTrue + } + } + //Unary expressions + case *dst.UnaryExpr: + switch exprUnderConsideration.Op { + case token.NOT: + valX := sfc.evaluateExprNode(exprUnderConsideration.X) + switch valX { + case isTrue: + return isFalse + case isFalse: + return isTrue + } + case token.AND: // &expr + return sfc.evaluateExprNode(exprUnderConsideration.X) + default: + //do nothing for now. + } + //Star expression (*expr) + case *dst.StarExpr: + return sfc.evaluateExprNode(exprUnderConsideration.X) + //Paren Expr: exprs inside parenthesis + case *dst.ParenExpr: + return sfc.evaluateExprNode(exprUnderConsideration.X) + + //Selector Expr (expr.A, a.b, etc.) + case *dst.SelectorExpr: + switch ident := exprUnderConsideration.X.(type) { + case *dst.Ident: + if val, ok := sfc.valueMap[ident.Name+"."+exprUnderConsideration.Sel.Name]; ok { + return val + } + default: + //Do nothing for now + } + + default: + //Do nothing for now + } + + return isUndefined +} + +// This is the pre function of the dstutil.Apply +/* +From documentation: +If pre is not nil, it is called for each node before the node's children are +traversed (pre-order). If pre returns false, no children are traversed, and +not called for that node. +*/ +func (sfc *staleFlagCleaner) pre(n *dstutil.Cursor) bool { + return true +} + +func (sfc *staleFlagCleaner) checkForBoolLiterals(value dst.Expr) bool { + switch checkval := value.(type) { + case *dst.Ident: + if checkval.Name == strTrue || checkval.Name == strFalse { + //do nothing + return true + } + default: + return false + } + return false +} + +/* +Below function have this aim: +If Value is isUndefined or it is a bool literal, then we are not adding that to valueMap, +otherwise we will add it to valueMap. +This signifies whether variable has effect of flag or not. +*/ +func (sfc *staleFlagCleaner) updateValueMapPost(name dst.Expr, value dst.Expr, doNotDelLiterals bool) { + valOfExpr := sfc.evaluateExprNode(value) + switch d := name.(type) { + case *dst.Ident: + if valOfExpr == isUndefined || doNotDelLiterals { + if _, ok := sfc.valueMap[d.Name]; ok { + delete(sfc.valueMap, d.Name) + } + } else { + sfc.valueMap[d.Name] = valOfExpr + } + case *dst.SelectorExpr: + switch ident := d.X.(type) { + case *dst.Ident: + if valOfExpr == isUndefined || doNotDelLiterals { + if _, ok := sfc.valueMap[ident.Name+"."+d.Sel.Name]; ok { + delete(sfc.valueMap, ident.Name+"."+d.Sel.Name) + } + } else { + sfc.valueMap[ident.Name+"."+d.Sel.Name] = valOfExpr + } + default: + //Do nothing for now + } + default: + // other cases may come here in future + } +} + +/* +It will deal with *dst.ValueSpec. Values of variable stored in valueMap +will be deleted from node. +*/ +func (sfc *staleFlagCleaner) DelValStmt(names *[]*dst.Ident, values *[]dst.Expr) bool { + valOfExpr := isUndefined + delCallExpr := false + doNotDelLiterals := false + for ind := 0; ind < len((*names)); ind++ { + delCallExpr = false + doNotDelLiterals = false + if len((*values)) != 0 { + valOfExpr = sfc.evaluateExprNode((*values)[ind]) + doNotDelLiterals = sfc.checkForBoolLiterals((*values)[ind]) + sfc.updateValueMapPost((*names)[ind], (*values)[ind], doNotDelLiterals) + } + + if (valOfExpr != isUndefined && !doNotDelLiterals) || (*names)[ind].Name == sfc.flagName || delCallExpr { + if len((*values)) != 0 { + var trueIdent dst.Ident + if valOfExpr == isTrue { + trueIdent.Name = strTrue + } else { + trueIdent.Name = strFalse + } + (*values)[ind] = dst.Expr(&trueIdent) + } + if len((*names)) <= 1 { + return true + } + } + valOfExpr = isUndefined + } + return len((*names)) == 0 +} + +/* +It will deal with *dst.AssignStmt. Values of variable stored in valueMap +will be deleted from node. +*/ +func (sfc *staleFlagCleaner) DelAssStmt(names *[]dst.Expr, values *[]dst.Expr) bool { + valOfExpr := isUndefined + delCallExpr := false + doNotDelLiterals := false + for ind := 0; ind < len((*names)); ind++ { + //check if name is in valueName + valOfExpr = sfc.evaluateExprNode((*values)[ind]) + delCallExpr = false + + doNotDelLiterals = sfc.checkForBoolLiterals((*values)[ind]) + + // Need to see what to do with this thing + sfc.updateValueMapPost((*names)[ind], (*values)[ind], doNotDelLiterals) + + if (valOfExpr != isUndefined && !doNotDelLiterals) || delCallExpr { + var trueIdent dst.Ident + if valOfExpr == isTrue { + trueIdent.Name = strTrue + } else { + trueIdent.Name = strFalse + } + (*values)[ind] = dst.Expr(&trueIdent) + if len((*names)) <= 1 { + return true + } + } + } + return len((*names)) == 0 +} + +// This is the post function of the dstutil.Apply +/* +From documentation: +If post is not nil, and a prior call of pre didn't return false, post is +called for each node after its children are traversed (post-order). If +post returns false, traversal is terminated and Apply returns immediately. + +Working: +This wiil be used to delete the nodes and storing the values of expressions. +*/ +func (sfc *staleFlagCleaner) post(n *dstutil.Cursor) bool { + switch d := n.Node().(type) { + case *dst.ValueSpec: + delVal := sfc.DelValStmt(&d.Names, &d.Values) + if delVal { + n.Delete() + } + //GenDecl + case *dst.GenDecl: + if len(d.Specs) == 0 { + switch n.Parent().(type) { + case *dst.DeclStmt: + //wait Decl stmt + default: + n.Delete() + } + } + //declstmt + case *dst.DeclStmt: + if gd := d.Decl.(*dst.GenDecl); gd.Tok == token.VAR { + if len(gd.Specs) == 0 { + n.Delete() + } + } + + //Assignment statements + case *dst.AssignStmt: + //then delete from slice + sfc.DelAssStmt(&d.Lhs, &d.Rhs) + + //If cond statements + case *dst.IfStmt: + // Init of Ifstmt will get handled in assignStmt + // Now evaluate the cond. Cond is already refactored due to pre-order traversal. + cond := sfc.evaluateExprNode(d.Cond) + if cond != isUndefined { + if cond == isFalse { + if d.Else != nil { + for _, a := range d.Else.(*dst.BlockStmt).List { + n.InsertBefore(a) + } + } + } else { + for _, a := range d.Body.List { + n.InsertBefore(a) + } + } + n.Delete() + } + //Switch statements + case *dst.SwitchStmt: + // Init of switchstmt will get handled in assignStmt + if d.Tag != nil { + valExpr := sfc.evaluateExprNode(d.Tag) + if valExpr != isUndefined { + notfoundCase := false + for _, exprstmt := range d.Body.List { + if exprstmt.(*dst.CaseClause).List != nil { //nil means default case + valCaseClause := sfc.evaluateExprNode(exprstmt.(*dst.CaseClause).List[0]) + if valCaseClause == valExpr { + //this has to go out as switch is going to delete + for _, bodyEle := range exprstmt.(*dst.CaseClause).Body { + n.InsertBefore(bodyEle) + } + notfoundCase = false + break + } else { + notfoundCase = true + } + } else { + //default case + //here assuming defualt case is at last + if notfoundCase { + for _, bodyEle := range exprstmt.(*dst.CaseClause).Body { + n.InsertBefore(bodyEle) + } + } + } + } + n.Delete() + } + } else { + //now switch is like elseif statements so do accordingly + needDeletion := false + isUndefinedAbove := false + for ind := 0; ind < len(d.Body.List); ind++ { + exprstmt := d.Body.List[ind] + if exprstmt.(*dst.CaseClause).List != nil { + valCaseClause := sfc.evaluateExprNode(exprstmt.(*dst.CaseClause).List[0]) + if valCaseClause == isTrue { + //this has to go out as switch is going to delete + //On the condition that there was no expression above whose value is isUndefined. + if !isUndefinedAbove { + for _, bodyEle := range exprstmt.(*dst.CaseClause).Body { + n.InsertBefore(bodyEle) + } + needDeletion = true + break + } else { + d.Body.List = d.Body.List[:ind+1] + d.Body.List[ind].(*dst.CaseClause).List = nil + break + } + } else if valCaseClause == isFalse { + if len(d.Body.List) > 1 { + copy(d.Body.List[ind:], d.Body.List[ind+1:]) // Shift d.Body.List[ind+1:] left one index. + d.Body.List[len(d.Body.List)-1] = nil // Erase last element (write zero value). + d.Body.List = d.Body.List[:len(d.Body.List)-1] // Truncate slice. + + ind = ind - 1 + } else { + n.Delete() + break + } + } else { + // so now value is bot + isUndefinedAbove = true + } + } + } + if needDeletion { + n.Delete() + } + } + //handling for in general experssions + case *dst.BinaryExpr: + valX := sfc.evaluateExprNode(d.X) + valY := sfc.evaluateExprNode(d.Y) + if valX == isUndefined || valY == isUndefined { + if valX == isUndefined && valY == isUndefined { + //do nothing + } else { + //now one of them is not bot + //check for valX + if valX == isTrue && d.Op != token.LOR { + n.Replace(d.Y) + } + if valX == isFalse && d.Op != token.LAND { + n.Replace(d.Y) + } + if valY == isTrue && d.Op != token.LOR { + n.Replace(d.X) + } + if valY == isFalse && d.Op != token.LAND { + n.Replace(d.X) + } + if d.Op == token.EQL { + newNode := n.Node() + var trueIdent dst.Ident + // either valX is not bot or valY. both cannot be bot at the same time as we checked out above + if valX != isUndefined { + if valX == isTrue { + trueIdent.Name = strTrue + } else { + trueIdent.Name = strFalse + } + newNode.(*dst.BinaryExpr).X = dst.Expr(&trueIdent) + n.Replace(newNode) + } else { + if valY == isTrue { + trueIdent.Name = strTrue + } else { + trueIdent.Name = strFalse + } + newNode.(*dst.BinaryExpr).Y = dst.Expr(&trueIdent) + n.Replace(newNode) + } + } + } + } + default: + + } + return true +} + +// This is the Upper flow of the programme +/* +Input: Parsed File(dst.Node) +Output: New Parser File(dst.Node) + +FLOW OF THE FUNCTION: + +This will take parsed file which will contain all comments. +Walk the tree with apply. In Pre function get values of variable and fucntion +returns that are related to the flag. In Post funtion will delete the nodes +according to the conditions. Also, this will store elements for doing deep +clean pass. + +Additional thought: + +Thinking of adding deep clean functionality in here after completing the +First Phase of the code. +*/ +func (sfc *staleFlagCleaner) run(root dst.Node) dst.Node { + /* + From documentation: + Apply traverses a syntax tree recursively, starting with root, and + calling pre and post for each node as described below. Apply returns + the syntax tree, possibly modified. + */ + modifiedRoot := dstutil.Apply(root, sfc.pre, sfc.post) + return modifiedRoot +} diff --git a/go/test/README.md b/go/test/README.md new file mode 100644 index 000000000..ef748804b --- /dev/null +++ b/go/test/README.md @@ -0,0 +1,4 @@ +# Testing of PiranhaGo +1. Run `go test` in this directory to test the working of Piranha. +2. `input` directory contains test cases that are used for testing. +3. `output` directory contains two subdirectories `treated` and `control`. When `piranha` is run **with** flag `-treated` on a test file in `input`, it generates refactored code that will match with the corresponding file in `output/treated`. Similarly, if `piranha` is run **without** flag `-treated`, then it will generate output that matches the corresponding file in `output/control`. diff --git a/go/test/input/deepClean.go b/go/test/input/deepClean.go new file mode 100644 index 000000000..1904a20e9 --- /dev/null +++ b/go/test/input/deepClean.go @@ -0,0 +1,105 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +// A defer statement defers the execution of a function until the surrounding function returns. +func (ge GoExamples) useOfDefer() bool { + defer ge.storeuseInit() + return true +} + +func (ge GoExamples) storeuseBefore() { + /* + TODO: There is work going on to add the deep clean feature. + In this case the first if statment should be treated because + ge.basicFeature value may get affected by stale feature flag. + */ + if ge.basicFeature { + fmt.Println("then-branch of `ge.basicFeature`") + } + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } + + if !ge.localService { + fmt.Println("then-branch of `!ge.localService`") + } else { + fmt.Println("else-branch of `!ge.localService`") + } + + if ge.localService && ge.newFeatures { + fmt.Println("then-branch of `ge.localService && ge.newFeatures`") + } + + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } +} + +// This function is also acting as a pointer reciever +// Methods with pointer receivers can modify the value to which the receiver points +// This also using above two functions +func (ge *GoExamples) storeuseInit() { + ge.basicFeature = ge.flagMthds.treatedBehaviour(staleFlag) + ge.localService = ge.flagMthds.controlBehaviour(localFlag) + ge.newFeatures = ge.flagMthds.treatedBehaviour(newFlag) + + pointerfieldX := &ge.basicFeature + pointerfieldY := &ge.localService + + if ge.basicFeature { + fmt.Println("then-branch of `ge.basicFeature`") + } + + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + + //testing pointer. I think need more test cases like this + if *pointerfieldX || !*pointerfieldY { + fmt.Println("then-branch of `*pointerfieldX || !*pointerfieldY`") + } +} + +func (ge GoExamples) storeuseAfter() { + // I love basicFeature + if ge.basicFeature { + fmt.Println("then-branch of `ge.basicFeature`") + } + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } + + if !ge.localService { + fmt.Println("then-branch of `!ge.localService`") + } else { + fmt.Println("else-branch of `!ge.localService`") + } + + if ge.localService && ge.newFeatures { + fmt.Println("then-branch of `ge.localService && ge.newFeatures`") + } + + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } +} diff --git a/go/test/input/init.go b/go/test/input/init.go new file mode 100644 index 000000000..53d594114 --- /dev/null +++ b/go/test/input/init.go @@ -0,0 +1,57 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +// PropFlag :defining a type for flag +type PropFlag int + +// This contatins flags +// This works like enum in golang +// If we got any other implementation of flags then we will see on that in future +const ( + //comment0 + globalFlag PropFlag = iota //comment1 + //comment2 + staleFlag //comment3 + //comment4 + localFlag + newFlag +) + +// FlagMethods : This will contains treatment and control methods +type FlagMethods struct { +} + +func (flgMthd FlagMethods) treatedBehaviour(flag PropFlag) bool { + return true +} +func (flgMthd FlagMethods) controlBehaviour(flag PropFlag) bool { + return true +} +func (flgMthd FlagMethods) commonBehaviour(str string, flag2 PropFlag) bool { + return true +} + +// GoExamples : This will act as a class +type GoExamples struct { + flagMthds FlagMethods + localService, globalService, newFeatures, basicFeature bool +} + +func globalFeature(flag PropFlag) bool { + if flag == globalFlag { + return true + } + return false +} diff --git a/go/test/input/testConditional.go b/go/test/input/testConditional.go new file mode 100644 index 000000000..99843a939 --- /dev/null +++ b/go/test/input/testConditional.go @@ -0,0 +1,103 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testIfConditionalAndInitExpressions(ge GoExamples) { + // Treated Control behaviour will be governed by three or more flags + if ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag) && globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag) && globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag) && globalFeature(globalFlag)`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } + + if globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag) { + fmt.Println("then-branch of `globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) || ge.flagMthds.treatedBehaviour(localFlag) || globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || ge.flagMthds.treatedBehaviour(localFlag) || globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || ge.flagMthds.treatedBehaviour(localFlag) || globalFeature(globalFlag)`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag) || globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag) || globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag) || globalFeature(globalFlag)`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) || globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag) { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } + + if globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag) { + fmt.Println("then-branch of `globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) && ge.flagMthds.controlBehaviour(localFlag) && globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) && ge.flagMthds.controlBehaviour(localFlag) && globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && ge.flagMthds.controlBehaviour(localFlag) && globalFeature(globalFlag)`") + } + + //comment1 + a := ge.flagMthds.commonBehaviour("user", staleFlag) //comment2 + //comment3 + b := ge.flagMthds.treatedBehaviour(newFlag) //comment4 + //comment5 + if a && b { + fmt.Println("then-branch of `a && b`") + } else { + fmt.Println("else-branch of `a && b`") + } + + if a || b { + fmt.Println("then-branch of `a || b`") + } else { + fmt.Println("else-branch of `a || b`") + } + + if v := ge.flagMthds.treatedBehaviour(staleFlag); v { + fmt.Println("then-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v`") + } else { + fmt.Println("else-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v`") + } + + if v := ge.flagMthds.treatedBehaviour(staleFlag); v == true { + b = a && b && !b + fmt.Println("then-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v == true`") + } else { + fmt.Println("else-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v == true`") + } + + if v := ge.flagMthds.treatedBehaviour(staleFlag); v != true { + fmt.Println("then-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v != true`") + } else { + fmt.Println("else-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v != true`") + } +} diff --git a/go/test/input/testExpressions.go b/go/test/input/testExpressions.go new file mode 100644 index 000000000..d52cb9ee1 --- /dev/null +++ b/go/test/input/testExpressions.go @@ -0,0 +1,107 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testExpressions(ge GoExamples) { + if ge.flagMthds.treatedBehaviour(staleFlag) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag)`") + } + + //global feature is not in properties right now. So this should not get treated + if globalFeature(staleFlag) { + fmt.Println("global treated behaviour") + } else { + fmt.Println("global control behaviour") + } + + if ge.flagMthds.controlBehaviour(staleFlag) { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag)`") + } + var x, y bool = false, false + + if ge.flagMthds.treatedBehaviour(staleFlag) || x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && (x || y) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && (x && y) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + } + + if ge.flagMthds.treatedBehaviour(staleFlag) && y == x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) || y == x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) && y && x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } + + if ge.flagMthds.controlBehaviour(staleFlag) || y || x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } + + y = ge.flagMthds.treatedBehaviour(staleFlag) + y = ge.flagMthds.controlBehaviour(staleFlag) && x + y = ge.flagMthds.treatedBehaviour(staleFlag) || x + + if y { + fmt.Println("y cleaned, so then-branch of y") + } else { + fmt.Println("y cleaned, so else-branch of y") + } + + y = ge.flagMthds.treatedBehaviour(staleFlag) && y == x + // This is done on purpose to check deep clean work + y = ge.flagMthds.treatedBehaviour(staleFlag) + + if y { + fmt.Println("y not cleaned, so then-branch of y") + } else { + fmt.Println("y not cleaned, so else-branch of y") + } + +} diff --git a/go/test/input/testSwitch.go b/go/test/input/testSwitch.go new file mode 100644 index 000000000..d49c4d819 --- /dev/null +++ b/go/test/input/testSwitch.go @@ -0,0 +1,167 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testSwitch(ge GoExamples) { + switch os := ge.flagMthds.treatedBehaviour(staleFlag); os { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os`") + } + + switch os := ge.flagMthds.treatedBehaviour(newFlag); os { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(newFlag); os`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(newFlag); os`") + } + + switch !ge.flagMthds.treatedBehaviour(staleFlag) { + case true: + fmt.Println("1st case of `!ge.flagMthds.treatedBehaviour(staleFlag)`") + default: + fmt.Println("default case of `!ge.flagMthds.treatedBehaviour(staleFlag)`") + } + + switch os := ge.flagMthds.controlBehaviour(staleFlag); os { + case true: + fmt.Println("1st case of `os := ge.flagMthds.controlBehaviour(staleFlag); os`") + default: + fmt.Println("default case of `os := ge.flagMthds.controlBehaviour(staleFlag); os`") + } + + x := true + y := false + + switch os := ge.flagMthds.treatedBehaviour(staleFlag) && (x || y); os { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(staleFlag) && (x || y); os`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag) && (x || y); os`") + } + + switch os := ge.flagMthds.treatedBehaviour(staleFlag); os && (x || y) { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os && (x || y)`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os && (x || y)`") + } + + switch os := ge.flagMthds.controlBehaviour(staleFlag); os && (x || y) { + case true: + fmt.Println("1st case of `os := ge.flagMthds.controlBehaviour(staleFlag); os && (x || y)`") + default: + fmt.Println("default case of `os := ge.flagMthds.controlBehaviour(staleFlag); os && (x || y)`") + } + + switch ge.flagMthds.treatedBehaviour(staleFlag) || x || y { + case true: + print("1st case of `ge.flagMthds.treatedBehaviour(staleFlag) || x || y`") + x = !ge.flagMthds.treatedBehaviour(staleFlag) || y + case false: + print("default case of `ge.flagMthds.treatedBehaviour(staleFlag) || x || y`") + y = !ge.flagMthds.treatedBehaviour(staleFlag) || x + } + + switch ge.flagMthds.controlBehaviour(staleFlag) && x && y { + case true: + fmt.Println("1st case of `ge.flagMthds.controlBehaviour(staleFlag) && x && y`") + case false: + fmt.Println("default case of `ge.flagMthds.controlBehaviour(staleFlag) && x && y`") + } + + /* + If you are familiar with go progamming then you probably know that + this style of writing switch statments is like writing if-elseif-else + statements in C, C++, Java or Python. + + So, to refactor this code we will consider three cases: + (ith case statement means position of the case statement from first case) + + Case1: + If ith case statement evaluates to true and all case statements + above this statement evaluates to false. (This also includes if 1st case + statement evaluates to true). + Do: + Then we will remove whole switch statment and replace it with body + of ith case statment. + + Case2: + If ith case statement evaluates to true and all case statements + above this statement evaluates to isBot(it means cannot determine + whether it is true or false). + Do: + Then we will remove all the cases below the ith case statement and + rewrite the switch statement with ith case statment as default. (check + corresponding refactored output without -treated of this file in + 'output/control' directory.) + + Case3: + If ith case statment is false. + Do: + Then remove it from the switch statement in refactored output. + */ + + x = true + y = false + + // Switch 1 + switch { + case ge.flagMthds.treatedBehaviour(staleFlag) || x: + fmt.Println("switch 1 test `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + case ge.flagMthds.treatedBehaviour(staleFlag) && x: + fmt.Println("switch 1 test `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + case ge.flagMthds.treatedBehaviour(staleFlag) && (x || y): + fmt.Println("switch 1 test `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + case ge.flagMthds.treatedBehaviour(staleFlag) && (x && y): + fmt.Println("switch 1 test `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + case ge.flagMthds.treatedBehaviour(staleFlag) && y == x: + fmt.Println("switch 1 test `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + x = y || ge.flagMthds.treatedBehaviour(newFlag) + case ge.flagMthds.controlBehaviour(staleFlag) || y == x: + fmt.Println("switch 1 test `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + case ge.flagMthds.controlBehaviour(staleFlag) && y && x: + fmt.Println("switch 1 test `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + case ge.flagMthds.controlBehaviour(staleFlag) || y || x: + fmt.Println("switch 1 test `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } + + // Switch 2 + switch { + case ge.flagMthds.treatedBehaviour(staleFlag) && x: + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + case ge.flagMthds.treatedBehaviour(staleFlag) && (x || y): + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + case ge.flagMthds.treatedBehaviour(staleFlag) && (x && y): + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + case ge.flagMthds.treatedBehaviour(staleFlag) && y == x: + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + x = y || ge.flagMthds.treatedBehaviour(newFlag) + case ge.flagMthds.controlBehaviour(staleFlag) || y == x: + fmt.Println("switch 2 test `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + case ge.flagMthds.controlBehaviour(staleFlag) && y && x: + fmt.Println("switch 2 test `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + case ge.flagMthds.controlBehaviour(staleFlag) || y || x: + fmt.Println("switch 2 test `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } + + // Switch 3 + switch { + case ge.flagMthds.controlBehaviour(staleFlag) && y && x: + fmt.Println("switch 3 `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } +} diff --git a/go/test/output/control/deepClean.go b/go/test/output/control/deepClean.go new file mode 100644 index 000000000..b3900f26d --- /dev/null +++ b/go/test/output/control/deepClean.go @@ -0,0 +1,97 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +// A defer statement defers the execution of a function until the surrounding function returns. +func (ge GoExamples) useOfDefer() bool { + defer ge.storeuseInit() + return true +} + +func (ge GoExamples) storeuseBefore() { + /* + TODO: There is work going on to add the deep clean feature. + In this case the first if statment should be treated because + ge.basicFeature value may get affected by stale feature flag. + */ + if ge.basicFeature { + fmt.Println("then-branch of `ge.basicFeature`") + } + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } + + if !ge.localService { + fmt.Println("then-branch of `!ge.localService`") + } else { + fmt.Println("else-branch of `!ge.localService`") + } + + if ge.localService && ge.newFeatures { + fmt.Println("then-branch of `ge.localService && ge.newFeatures`") + } + + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } +} + +// This function is also acting as a pointer reciever +// Methods with pointer receivers can modify the value to which the receiver points +// This also using above two functions +func (ge *GoExamples) storeuseInit() { + ge.basicFeature = false + ge.localService = ge.flagMthds.controlBehaviour(localFlag) + ge.newFeatures = ge.flagMthds.treatedBehaviour(newFlag) + + pointerfieldX := false + pointerfieldY := &ge.localService + + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + + //testing pointer. I think need more test cases like this + if !*pointerfieldY { + fmt.Println("then-branch of `*pointerfieldX || !*pointerfieldY`") + } +} + +func (ge GoExamples) storeuseAfter() { + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } + + if !ge.localService { + fmt.Println("then-branch of `!ge.localService`") + } else { + fmt.Println("else-branch of `!ge.localService`") + } + + if ge.localService && ge.newFeatures { + fmt.Println("then-branch of `ge.localService && ge.newFeatures`") + } + + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } +} diff --git a/go/test/output/control/init.go b/go/test/output/control/init.go new file mode 100644 index 000000000..0a3cafef2 --- /dev/null +++ b/go/test/output/control/init.go @@ -0,0 +1,55 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +// PropFlag :defining a type for flag +type PropFlag int + +// This contatins flags +// This works like enum in golang +// If we got any other implementation of flags then we will see on that in future +const ( + //comment0 + globalFlag PropFlag = iota //comment1 + //comment4 + localFlag + newFlag +) + +// FlagMethods : This will contains treatment and control methods +type FlagMethods struct { +} + +func (flgMthd FlagMethods) treatedBehaviour(flag PropFlag) bool { + return true +} +func (flgMthd FlagMethods) controlBehaviour(flag PropFlag) bool { + return true +} +func (flgMthd FlagMethods) commonBehaviour(str string, flag2 PropFlag) bool { + return true +} + +// GoExamples : This will act as a class +type GoExamples struct { + flagMthds FlagMethods + localService, globalService, newFeatures, basicFeature bool +} + +func globalFeature(flag PropFlag) bool { + if flag == globalFlag { + return true + } + return false +} diff --git a/go/test/output/control/testConditional.go b/go/test/output/control/testConditional.go new file mode 100644 index 000000000..29d537eec --- /dev/null +++ b/go/test/output/control/testConditional.go @@ -0,0 +1,54 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testIfConditionalAndInitExpressions(ge GoExamples) { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag) && globalFeature(globalFlag)`") + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + fmt.Println("else-branch of `globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + + if ge.flagMthds.treatedBehaviour(localFlag) || globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || ge.flagMthds.treatedBehaviour(localFlag) || globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || ge.flagMthds.treatedBehaviour(localFlag) || globalFeature(globalFlag)`") + } + + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag) || globalFeature(globalFlag)`") + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + fmt.Println("then-branch of `globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + + if ge.flagMthds.controlBehaviour(localFlag) && globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) && ge.flagMthds.controlBehaviour(localFlag) && globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && ge.flagMthds.controlBehaviour(localFlag) && globalFeature(globalFlag)`") + } + + //comment1 + a := false //comment2 + //comment3 + b := ge.flagMthds.treatedBehaviour(newFlag) //comment4 + fmt.Println("else-branch of `a && b`") + + if b { + fmt.Println("then-branch of `a || b`") + } else { + fmt.Println("else-branch of `a || b`") + } + + fmt.Println("else-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v`") + fmt.Println("else-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v == true`") + fmt.Println("then-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v != true`") +} diff --git a/go/test/output/control/testExpressions.go b/go/test/output/control/testExpressions.go new file mode 100644 index 000000000..d65fb9207 --- /dev/null +++ b/go/test/output/control/testExpressions.go @@ -0,0 +1,66 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testExpressions(ge GoExamples) { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag)`") + + //global feature is not in properties right now. So this should not get treated + if globalFeature(staleFlag) { + fmt.Println("global treated behaviour") + } else { + fmt.Println("global control behaviour") + } + + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag)`") + var x, y bool = false, false + + if x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + } + + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + + if y && x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } + + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + + y = false + y = x + y = x + + if y { + fmt.Println("y cleaned, so then-branch of y") + } else { + fmt.Println("y cleaned, so else-branch of y") + } + + y = false + // This is done on purpose to check deep clean work + y = false + + fmt.Println("y not cleaned, so else-branch of y") +} diff --git a/go/test/output/control/testSwitch.go b/go/test/output/control/testSwitch.go new file mode 100644 index 000000000..bb5589946 --- /dev/null +++ b/go/test/output/control/testSwitch.go @@ -0,0 +1,98 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testSwitch(ge GoExamples) { + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os`") + + switch os := ge.flagMthds.treatedBehaviour(newFlag); os { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(newFlag); os`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(newFlag); os`") + } + + fmt.Println("1st case of `!ge.flagMthds.treatedBehaviour(staleFlag)`") + fmt.Println("1st case of `os := ge.flagMthds.controlBehaviour(staleFlag); os`") + + x := true + y := false + + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag) && (x || y); os`") + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os && (x || y)`") + + switch os := true; x || y { + case true: + fmt.Println("1st case of `os := ge.flagMthds.controlBehaviour(staleFlag); os && (x || y)`") + default: + fmt.Println("default case of `os := ge.flagMthds.controlBehaviour(staleFlag); os && (x || y)`") + } + + print("1st case of `ge.flagMthds.treatedBehaviour(staleFlag) || x || y`") + x = true + fmt.Println("1st case of `ge.flagMthds.controlBehaviour(staleFlag) && x && y`") + + /* + If you are familiar with go progamming then you probably know that + this style of writing switch statments is like writing if-elseif-else + statements in C, C++, Java or Python. + + So, to refactor this code we will consider three cases: + (ith case statement means position of the case statement from first case) + + Case1: + If ith case statement evaluates to true and all case statements + above this statement evaluates to false. (This also includes if 1st case + statement evaluates to true). + Do: + Then we will remove whole switch statment and replace it with body + of ith case statment. + + Case2: + If ith case statement evaluates to true and all case statements + above this statement evaluates to isBot(it means cannot determine + whether it is true or false). + Do: + Then we will remove all the cases below the ith case statement and + rewrite the switch statement with ith case statment as default. (check + corresponding refactored output without -treated of this file in + 'output/control' directory.) + + Case3: + If ith case statment is false. + Do: + Then remove it from the switch statement in refactored output. + */ + + x = true + y = false + + // Switch 1 + switch { + case x: + fmt.Println("switch 1 test `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + default: + fmt.Println("switch 1 test `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + } + + fmt.Println("switch 2 test `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + + // Switch 3 + switch { + case y && x: + fmt.Println("switch 3 `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + } +} diff --git a/go/test/output/treated/deepClean.go b/go/test/output/treated/deepClean.go new file mode 100644 index 000000000..ca6b29a55 --- /dev/null +++ b/go/test/output/treated/deepClean.go @@ -0,0 +1,97 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +// A defer statement defers the execution of a function until the surrounding function returns. +func (ge GoExamples) useOfDefer() bool { + defer ge.storeuseInit() + return true +} + +func (ge GoExamples) storeuseBefore() { + /* + TODO: There is work going on to add the deep clean feature. + In this case the first if statment should be treated because + ge.basicFeature value may get affected by stale feature flag. + */ + if ge.basicFeature { + fmt.Println("then-branch of `ge.basicFeature`") + } + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } + + if !ge.localService { + fmt.Println("then-branch of `!ge.localService`") + } else { + fmt.Println("else-branch of `!ge.localService`") + } + + if ge.localService && ge.newFeatures { + fmt.Println("then-branch of `ge.localService && ge.newFeatures`") + } + + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } +} + +// This function is also acting as a pointer reciever +// Methods with pointer receivers can modify the value to which the receiver points +// This also using above two functions +func (ge *GoExamples) storeuseInit() { + ge.basicFeature = true + ge.localService = ge.flagMthds.controlBehaviour(localFlag) + ge.newFeatures = ge.flagMthds.treatedBehaviour(newFlag) + + pointerfieldX := true + pointerfieldY := &ge.localService + + fmt.Println("then-branch of `ge.basicFeature`") + + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + + fmt.Println("then-branch of `*pointerfieldX || !*pointerfieldY`") +} + +func (ge GoExamples) storeuseAfter() { + fmt.Println("then-branch of `ge.basicFeature`") + if ge.localService { + fmt.Println("then-branch of `ge.localService`") + } + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } + + if !ge.localService { + fmt.Println("then-branch of `!ge.localService`") + } else { + fmt.Println("else-branch of `!ge.localService`") + } + + if ge.localService && ge.newFeatures { + fmt.Println("then-branch of `ge.localService && ge.newFeatures`") + } + + if ge.newFeatures { + fmt.Println("then-branch of `ge.newFeatures`") + } +} diff --git a/go/test/output/treated/init.go b/go/test/output/treated/init.go new file mode 100644 index 000000000..0a3cafef2 --- /dev/null +++ b/go/test/output/treated/init.go @@ -0,0 +1,55 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +// PropFlag :defining a type for flag +type PropFlag int + +// This contatins flags +// This works like enum in golang +// If we got any other implementation of flags then we will see on that in future +const ( + //comment0 + globalFlag PropFlag = iota //comment1 + //comment4 + localFlag + newFlag +) + +// FlagMethods : This will contains treatment and control methods +type FlagMethods struct { +} + +func (flgMthd FlagMethods) treatedBehaviour(flag PropFlag) bool { + return true +} +func (flgMthd FlagMethods) controlBehaviour(flag PropFlag) bool { + return true +} +func (flgMthd FlagMethods) commonBehaviour(str string, flag2 PropFlag) bool { + return true +} + +// GoExamples : This will act as a class +type GoExamples struct { + flagMthds FlagMethods + localService, globalService, newFeatures, basicFeature bool +} + +func globalFeature(flag PropFlag) bool { + if flag == globalFlag { + return true + } + return false +} diff --git a/go/test/output/treated/testConditional.go b/go/test/output/treated/testConditional.go new file mode 100644 index 000000000..baac8312a --- /dev/null +++ b/go/test/output/treated/testConditional.go @@ -0,0 +1,76 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testIfConditionalAndInitExpressions(ge GoExamples) { + // Treated Control behaviour will be governed by three or more flags + if ge.flagMthds.treatedBehaviour(localFlag) && globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag) && globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag) && globalFeature(globalFlag)`") + } + + if globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } + + if globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(localFlag) { + fmt.Println("then-branch of `globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `globalFeature(globalFlag) && ge.flagMthds.treatedBehaviour(staleFlag) && ge.flagMthds.treatedBehaviour(localFlag)`") + } + + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || ge.flagMthds.treatedBehaviour(localFlag) || globalFeature(globalFlag)`") + + if ge.flagMthds.controlBehaviour(localFlag) || globalFeature(globalFlag) { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag) || globalFeature(globalFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag) || globalFeature(globalFlag)`") + } + + if globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag) { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } + + if globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(localFlag) { + fmt.Println("then-branch of `globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } else { + fmt.Println("else-branch of `globalFeature(globalFlag) || ge.flagMthds.controlBehaviour(staleFlag) || ge.flagMthds.controlBehaviour(localFlag)`") + } + + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && ge.flagMthds.controlBehaviour(localFlag) && globalFeature(globalFlag)`") + + //comment1 + a := true //comment2 + //comment3 + b := ge.flagMthds.treatedBehaviour(newFlag) //comment4 + //comment5 + if b { + fmt.Println("then-branch of `a && b`") + } else { + fmt.Println("else-branch of `a && b`") + } + + fmt.Println("then-branch of `a || b`") + fmt.Println("then-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v`") + b = b && !b + fmt.Println("then-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v == true`") + fmt.Println("else-branch of `v := ge.flagMthds.treatedBehaviour(staleFlag); v != true`") +} diff --git a/go/test/output/treated/testExpressions.go b/go/test/output/treated/testExpressions.go new file mode 100644 index 000000000..1e168302c --- /dev/null +++ b/go/test/output/treated/testExpressions.go @@ -0,0 +1,82 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testExpressions(ge GoExamples) { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag)`") + + //global feature is not in properties right now. So this should not get treated + if globalFeature(staleFlag) { + fmt.Println("global treated behaviour") + } else { + fmt.Println("global control behaviour") + } + + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag)`") + var x, y bool = false, false + + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + + if x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + } + + if x || y { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + } + + if x && y { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + } + + if y == x { + fmt.Println("then-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + } + + if y == x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + } + + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) && y && x`") + + if y || x { + fmt.Println("then-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } else { + fmt.Println("else-branch of `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } + + y = true + y = false + y = true + + fmt.Println("y cleaned, so then-branch of y") + + y = true == x + // This is done on purpose to check deep clean work + y = true + + fmt.Println("y not cleaned, so then-branch of y") +} diff --git a/go/test/output/treated/testSwitch.go b/go/test/output/treated/testSwitch.go new file mode 100644 index 000000000..1bb8a6f49 --- /dev/null +++ b/go/test/output/treated/testSwitch.go @@ -0,0 +1,107 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package testfiles + +import "fmt" + +func testSwitch(ge GoExamples) { + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os`") + + switch os := ge.flagMthds.treatedBehaviour(newFlag); os { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(newFlag); os`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(newFlag); os`") + } + + fmt.Println("default case of `!ge.flagMthds.treatedBehaviour(staleFlag)`") + fmt.Println("default case of `os := ge.flagMthds.controlBehaviour(staleFlag); os`") + + x := true + y := false + + switch os := (x || y); os { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(staleFlag) && (x || y); os`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag) && (x || y); os`") + } + + switch os := true; x || y { + case true: + fmt.Println("1st case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os && (x || y)`") + default: + fmt.Println("default case of `os := ge.flagMthds.treatedBehaviour(staleFlag); os && (x || y)`") + } + + fmt.Println("default case of `os := ge.flagMthds.controlBehaviour(staleFlag); os && (x || y)`") + print("1st case of `ge.flagMthds.treatedBehaviour(staleFlag) || x || y`") + x = y + fmt.Println("default case of `ge.flagMthds.controlBehaviour(staleFlag) && x && y`") + + /* + If you are familiar with go progamming then you probably know that + this style of writing switch statments is like writing if-elseif-else + statements in C, C++, Java or Python. + + So, to refactor this code we will consider three cases: + (ith case statement means position of the case statement from first case) + + Case1: + If ith case statement evaluates to true and all case statements + above this statement evaluates to false. (This also includes if 1st case + statement evaluates to true). + Do: + Then we will remove whole switch statment and replace it with body + of ith case statment. + + Case2: + If ith case statement evaluates to true and all case statements + above this statement evaluates to isBot(it means cannot determine + whether it is true or false). + Do: + Then we will remove all the cases below the ith case statement and + rewrite the switch statement with ith case statment as default. (check + corresponding refactored output without -treated of this file in + 'output/control' directory.) + + Case3: + If ith case statment is false. + Do: + Then remove it from the switch statement in refactored output. + */ + + x = true + y = false + + fmt.Println("switch 1 test `ge.flagMthds.treatedBehaviour(staleFlag) || x`") + + // Switch 2 + switch { + case x: + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && x`") + case (x || y): + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && (x || y)`") + case (x && y): + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && (x && y)`") + case y == x: + fmt.Println("switch 2 test `ge.flagMthds.treatedBehaviour(staleFlag) && y == x`") + x = y || ge.flagMthds.treatedBehaviour(newFlag) + case y == x: + fmt.Println("switch 2 test `ge.flagMthds.controlBehaviour(staleFlag) || y == x`") + case y || x: + fmt.Println("switch 2 test `ge.flagMthds.controlBehaviour(staleFlag) || y || x`") + } + +} diff --git a/go/test/piranha_test.go b/go/test/piranha_test.go new file mode 100644 index 000000000..49672234c --- /dev/null +++ b/go/test/piranha_test.go @@ -0,0 +1,150 @@ +/* +Copyright (c) 2021 Uber Technologies, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the + +License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "testing" + + "github.com/PiranhaGo/src" +) + +func compFiles(file1 string, file2 string) bool { + FileNotMatched := false + corrFile, err := os.Open(file2) + if err != nil { + fmt.Println("File reading error of correct file", err) + return false + } + treatFile, err := os.Open(file1) + if err != nil { + fmt.Println("File reading error of treat file", err) + return false + } + + corrScan := bufio.NewScanner(corrFile) + treatScan := bufio.NewScanner(treatFile) + + lineNumcorrFile := 0 + lineNumtreatFile := 0 + + endOfcorrFile := false + endOftreatFile := false + for corrScan.Scan() { + // Scanning + treatScan.Scan() + lineNumcorrFile++ + lineNumtreatFile++ + for corrScan.Text() == "" { + if !corrScan.Scan() { + endOfcorrFile = true + break + } + lineNumcorrFile++ + } + for treatScan.Text() == "" { + if !treatScan.Scan() { + endOftreatFile = true + break + } + lineNumtreatFile++ + } + if endOfcorrFile || endOftreatFile { + break + } + + // Matching + if corrScan.Text() != treatScan.Text() { + fmt.Print("\n") + fmt.Print("Line ", lineNumcorrFile, ": ", corrScan.Text()) + fmt.Print("\n") + fmt.Print("Line ", lineNumtreatFile, ": ", treatScan.Text()) + fmt.Print("\n") + FileNotMatched = true + break + } + if FileNotMatched { + break + } + } + corrFile.Close() + treatFile.Close() + return FileNotMatched +} + +// TestFiles :This will test the output from the correct output +func TestFiles(t *testing.T) { + tables := []struct { + input string + outputControl string + outputTreated string + }{ + {"./input/init.go", "./output/control/init.go", "./output/treated/init.go"}, + {"./input/testExpressions.go", "./output/control/testExpressions.go", "./output/treated/testExpressions.go"}, + {"./input/testConditional.go", "./output/control/testConditional.go", "./output/treated/testConditional.go"}, + {"./input/testSwitch.go", "./output/control/testSwitch.go", "./output/treated/testSwitch.go"}, + {"./input/deepClean.go", "./output/control/deepClean.go", "./output/treated/deepClean.go"}, + } + // running each file as if they are running with this command + // For one Pass + // ../piranha -p ../properties.json -s "{filename}" -f staleFlag -o ./treatedFiles/$(basename {filename}) -treated + var configFile = "../properties.json" + var flagName = "staleFlag" + var isTreated bool + + fmt.Print("Output format: \n") + fmt.Print("Line : \n") + fmt.Print("Line : \n") + fmt.Print("\n") + fmt.Print("Starting Tests \n") + + fmt.Print("Testing with -mode treated\n\n") + isTreated = true + var FileNotMatched bool + for _, table := range tables { + fmt.Print("Matching with: ") + fmt.Println(table.outputTreated) + src.RunPiranha(table.input, configFile, flagName, "temp.go", isTreated) + + FileNotMatched = compFiles("temp.go", table.outputTreated) + if FileNotMatched { + t.Errorf("Files didn't match, see above output") + } else { + fmt.Println("File Matched Sucessfully") + } + fmt.Println() + } + fmt.Print("Testing with -mode control\n") + isTreated = false + for _, table := range tables { + fmt.Print("Matching with: ") + fmt.Println(table.outputControl) + src.RunPiranha(table.input, configFile, flagName, "temp.go", isTreated) + + FileNotMatched = compFiles("temp.go", table.outputControl) + if FileNotMatched { + t.Errorf("Files didn't match, see above output") + } else { + fmt.Println("File Matched Sucessfully") + } + fmt.Println() + } + del := os.Remove("temp.go") + if del != nil { + log.Fatal(del) + } +}