Skip to content

Go implementation of Piranha#112

Open
crekIron wants to merge 11 commits into
uber:masterfrom
crekIron:master
Open

Go implementation of Piranha#112
crekIron wants to merge 11 commits into
uber:masterfrom
crekIron:master

Conversation

@crekIron

Copy link
Copy Markdown

Piranha implementation for Golang. See README.md for more info.

@CLAassistant

CLAassistant commented Mar 30, 2021

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@mkr-plse mkr-plse mentioned this pull request Mar 30, 2021
@mkr-plse mkr-plse linked an issue Mar 30, 2021 that may be closed by this pull request
@mkr-plse

Copy link
Copy Markdown
Contributor

@crekIron Thanks for the PR and for extending Piranha for Go. Can you please sign the CLA so that the PR can be reviewed? Thanks!

@crekIron

crekIron commented Apr 1, 2021

Copy link
Copy Markdown
Author

I did it.

@mkr-plse

mkr-plse commented Apr 1, 2021

Copy link
Copy Markdown
Contributor

I did it.

Seems signed now. Thanks!

@lazaroclapp lazaroclapp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @crekIron, thanks for the PR!

This is really cool, and I appreciate the test suite included (let's hook it to CI before landing this!). I did have a number of comments on the PR, and a few things that aren't quite clear to me. See below. It's just a first pass, but hopefully it's pretty detailed!

I also think there are parts that would benefit from someone with more experience with Go parsing took a look too (cc: @awelc )

Comment thread go/README.md Outdated
Optional arguments:
-h: Show the options and exit.
-treated: If this is given, then flag is treated,
otherwise it is control.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want control to be implicit? I believe most other Piranha versions take an explicit string, which is either treatment or control (and, for some languages, and probably for Go in the future, they can also take a specific named treatment group).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make it different. But I thought making it implicit is still fine. I think it's generally less pain to first-time users as they have to set fewer attributes initially when they are trying out. (I will answer other comments slowly and steadily:)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking here is that, since we are deleting code in place, we do want the user to be somewhat explicit about which logic ought to be deleted, even if it's a bit more friction. It's also not clear that one default would be more common than the other.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I agree with you. I suggest we can ask for as like:
-mode treated or -mode control. Is it fine?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. That makes sense to me :)

Comment thread go/README.md Outdated
otherwise it is control.
-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` command in root directory. You will get your refracted file as `/example/treatedExample.go`. No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"refactored", not "refracted", I assume.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will change that.

Comment thread go/properties.json Outdated
"argumentIndex": 1
}
]
} No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets have files end in a newline unless we have a really good reason not to.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will do that. But like any specific reason for it? Just curious.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ending files with new line is considered best practice. One reason I know of is because command line tools might otherwise merge the last and first line of two files when processing a list of files. Also, some programs will fail to parse the last line entirely if it doesn't end in newline (not that bizarre behavior, actually, since POSIX does define a line as "A sequence of zero or more non- characters plus a terminating character.")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing.

Comment thread go/test/piranha_test.go Outdated
}
}
if FileNotMatched {
t.Errorf("Files didn't matched, see above output")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be: "Files didn't match,"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup will fix it.

Comment thread go/test/README.md
@@ -0,0 +1,4 @@
# Testing of PiranhaGo
1. Run `go test` in this directory to test the working of Piranha.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love that this has such a comprehensive test suite!

That said, let's get it hooked with CI from day one. Shouldn't be a ton more work, and would reassure us that this tests run correctly now and keep running correctly as changes to this PR and future PR are made :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to do that? Do you mean github cli?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean CI as in "Continuous Integration" (not CLI). GitHub's CI is indeed what I'd recommend (see https://github.com/features/actions ). I believe we are already using that for PiranhaJS.

Note: PiranhaJava and Swift are on Travis CI for now. But I would not recommend new projects to use Travis CI.

if valY == isFalse && d.Op != token.LAND {
n.Replace(d.X)
}
if d.Op == token.EQL {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the cases above, and EQL not being either LOR or LAND, isn't this unreachable? (I am also not sure the conditionals above are safe for !=. Do we have an exhaustive list of binary ops valid for at least one boolean operand for Go?)

Comment thread go/src/staleFlagCleaner.go Outdated
break
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? Wouldn't it be better to just check for the argument at element.FlagIndex below, right above line 152, to see if it matches sfc.flagName and skip otherwise?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we don't need it. Dunno, why I didn't seen it.

Comment thread go/src/staleFlagCleaner.go Outdated
return isFalse
}
case token.EQL:
if valX != isBot && valY != isBot {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically subsumed by the check for valX != isBot || valY != isBot above, I think...

@crekIron crekIron Apr 4, 2022

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, when one of them is undefined, then I need to return isUndefined for the token.EQL case. It is getting returned at the last.

return isTrue
}
case token.AND: // &expr
return sfc.evaluateExprNode(exprUnderConsideration.X)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh? What is this case? If this is actually for &expr, it doesn't seem correct to me, since taking the address of a boolean value (e.g. &true not a *bool) just gives you an error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how this works either. Since we simply return X here, don't we equate expr with &expr? What is the reasoning behind it (similar question for the StarExpr)?

@crekIron crekIron Aug 7, 2021

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take an example (I know, it's very rare).....

staleVariable := funcAffectedByStaleFlag(.....)  
......
pointerToVar := &staleVariable // getting pointer to staleVariable
a := *pointerToVar && false

To handle the below two expressions I did it. I tried extracting the value of the variable from 2nd expression and store it in the valueMap which can be used to handle the 3rd expression.

}
//Star expression (*expr)
case *dst.StarExpr:
return sfc.evaluateExprNode(exprUnderConsideration.X)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, *true is not a bool, it's an error. Am I missing something here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check above comment.

@lazaroclapp lazaroclapp requested a review from awelc April 5, 2021 22:13
Comment thread go/src/piranha.go Outdated
var sourceFile, configFile, flagName, outputFileName string = "", "", "", ""
var isTreated = false
sizeOfArgs := len(inArgs)
if len(inArgs) < 2 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why you parse the arguments by hand. Unless there is a really good reason, please use the flag package (or a different one) that standardizes it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will use flag one.

Comment thread go/src/staleFlagCleaner.go Outdated
"github.com/dave/dst/dstutil"
)

// API : For the type of API

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to our coding conventions, comments for exported entities should start with their name (as they currently are actually) and be full sentences ending with a dot. For example here it could be API describes type of the API.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

// API : For the type of API
type API int

const (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private constant names should start with _.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Comment thread go/src/staleFlagCleaner.go Outdated
type Operator int

const (
and Operator = iota

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment thread go/src/staleFlagCleaner.go Outdated
)

// Some useful global variable
var treated, control string = "treated", "control"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistically, it would look better as (grouping related variables and no need to specify the type, particularly that in this case it's obvious what it is):

var (
   treated = "treated"
   control = "control"
)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sure. This looks good.

}
}
//declstmt
case *dst.DeclStmt:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we handle it here instead of handling it when processing GenDecl above?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two reasons:

  1. DeclStmt can occur anywhere else too.
  2. Tree traversal is happening. So, children of GenDecl is going to get checked anyway.

Comment thread go/src/staleFlagCleaner.go Outdated
// 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 != isBot {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is exprstmt always of the CaseClause type? If so, then why isn't it of the CaseClause type in the first place? Do we need a type check here instead of type assertion?

//this has to go out as switch is going to delete
for _, bodyEle := range exprstmt.(*dst.CaseClause).Body {
n.InsertBefore(bodyEle)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It certainly is possible.

return true
}

// This is the Upper flow of the programme

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to our style guide, you order your file (roughly) in the calling order. It makes sense in general, as it improves readability - you start with the "main" part and look for the helper functions as you keep parsing the code, rather than the other way around, that is starting with looking at helper functions.

@mkr-plse mkr-plse requested a review from awelc April 16, 2021 14:36
@sandyskies

Copy link
Copy Markdown

@lazaroclapp @crekIron Can u guys flow up this pull request? We need this pull request to clean up Go feature-flag. Appreciate!

@mkr-plse

Copy link
Copy Markdown
Contributor

@lazaroclapp @crekIron Can u guys flow up this pull request? We need this pull request to clean up Go feature-flag. Appreciate!

@sandyskies We are working on a re-architected implementation of Piranha using TreeSitter where onboarding for different languages is made simpler and faster. We will be able to release a version in the next month or so, and enabling Piranha for Go will be much easier. cc @ketkarameya.

@crekIron

crekIron commented Apr 19, 2022

Copy link
Copy Markdown
Author

I would like to complete this pull request. I have nearly handled all the comments.

Comment thread go/main.go Outdated
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.")
if modeName == "treated" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be excuted after L40 flag.Parse().

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing it out. I changed it now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Golang support

7 participants