Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go implementation of Piranha #112

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open

Go implementation of Piranha #112

wants to merge 11 commits into from

Conversation

crekIron
Copy link

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

@CLAassistant
Copy link

CLAassistant commented Mar 30, 2021

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
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
Copy link
Author

crekIron commented Apr 1, 2021

I did it.

@mkr-plse
Copy link
Contributor

mkr-plse commented Apr 1, 2021

I did it.

Seems signed now. Thanks!

Copy link
Contributor

@lazaroclapp lazaroclapp left a comment

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 )

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
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
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
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
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
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 :)

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`.
Copy link
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
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.

"argumentIndex": 1
}
]
}
Copy link
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
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
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
Author

Choose a reason for hiding this comment

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

Doing.

}
}
if FileNotMatched {
t.Errorf("Files didn't matched, see above output")
Copy link
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
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.

@@ -0,0 +1,4 @@
# Testing of PiranhaGo
1. Run `go test` in this directory to test the working of Piranha.
Copy link
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
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
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
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?)

break
}
}
}
Copy link
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
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.

return isFalse
}
case token.EQL:
if valX != isBot && valY != isBot {
Copy link
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...

Copy link
Author

@crekIron crekIron Apr 4, 2022

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
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

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)?

Copy link
Author

@crekIron crekIron Aug 7, 2021

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
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
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
var sourceFile, configFile, flagName, outputFileName string = "", "", "", ""
var isTreated = false
sizeOfArgs := len(inArgs)
if len(inArgs) < 2 {
Copy link

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
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.

"github.com/dave/dst/dstutil"
)

// API : For the type of API
Copy link

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
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

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
Author

Choose a reason for hiding this comment

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

Ok

type Operator int

const (
and Operator = iota
Copy link

Choose a reason for hiding this comment

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

+1

)

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

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
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

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
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.

// 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

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

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

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

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

@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
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
Copy link
Author

crekIron commented Apr 19, 2022

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

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" {

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
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