Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 117 additions & 4 deletions checkers.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,26 @@ func newArgListParensChecker(ctxt *context) checker {
}

func (c *argListParensChecker) Visit(n ast.Node) bool {
call, ok := n.(*ast.CallExpr)
if !ok || len(call.Args) < 2 {
switch node := n.(type) {
case *ast.CallExpr:
return c.checkCallExpr(node)
case *ast.CompositeLit:
return c.checkCompositeLit(node)
case *ast.FuncDecl:
return c.checkFuncDecl(node)
case *ast.FuncLit:
return c.checkFuncLit(node)
case *ast.FuncType:
// Only check FuncType if it's not part of FuncDecl or FuncLit
// This handles interface method signatures
return c.checkFuncTypeIfStandalone(node)
}
return true
}

// checkCallExpr handles function calls (original logic)
func (c *argListParensChecker) checkCallExpr(call *ast.CallExpr) bool {
if len(call.Args) < 2 {
return true
}
lastArg := call.Args[len(call.Args)-1]
Expand All @@ -654,9 +672,104 @@ func (c *argListParensChecker) Visit(n ast.Node) bool {
rparenLine := c.ctxt.fset.Position(call.Rparen).Line
switch rparenLine {
case lastArgLine:
c.ctxt.mark(n, &c.sameLine)
c.ctxt.mark(call, &c.sameLine)
case lastArgLine + 1:
c.ctxt.mark(n, &c.nextLine)
c.ctxt.mark(call, &c.nextLine)
}
return true
}

// checkCompositeLit handles struct, slice, map literals
func (c *argListParensChecker) checkCompositeLit(lit *ast.CompositeLit) bool {
if len(lit.Elts) < 2 {
return true
}
lastElt := lit.Elts[len(lit.Elts)-1]
lastEltLine := c.ctxt.fset.Position(lastElt.Pos()).Line
firstEltLine := c.ctxt.fset.Position(lit.Elts[0].Pos()).Line
if firstEltLine == lastEltLine {
// Don't track single-line literals.
return true
}
rbracePos := lit.Rbrace
if rbracePos == 0 {
// No closing brace found
return true
}
rbraceLine := c.ctxt.fset.Position(rbracePos).Line
switch rbraceLine {
case lastEltLine:
c.ctxt.mark(lit, &c.sameLine)
case lastEltLine + 1:
c.ctxt.mark(lit, &c.nextLine)
}
return true
}

// checkFuncDecl handles function declarations
func (c *argListParensChecker) checkFuncDecl(decl *ast.FuncDecl) bool {
if decl.Type == nil || decl.Type.Params == nil {
return true
}
return c.checkFieldList(decl.Type.Params, decl)
}

// checkFuncLit handles function literals (anonymous functions)
func (c *argListParensChecker) checkFuncLit(lit *ast.FuncLit) bool {
if lit.Type == nil || lit.Type.Params == nil {
return true
}
return c.checkFieldList(lit.Type.Params, lit)
}

// checkFuncType handles function types (including interface methods)
func (c *argListParensChecker) checkFuncType(typ *ast.FuncType) bool {
if typ.Params == nil {
return true
}
return c.checkFieldList(typ.Params, typ)
}

// checkFuncTypeIfStandalone handles function types that are not part of FuncDecl/FuncLit
// This is mainly for interface method signatures
func (c *argListParensChecker) checkFuncTypeIfStandalone(typ *ast.FuncType) bool {
// Check if this FuncType has a parent that is FuncDecl or FuncLit
// If so, skip it to avoid duplication
if c.ctxt.astinfo.Parents != nil {
if parent := c.ctxt.astinfo.Parents[typ]; parent != nil {
switch parent.(type) {
case *ast.FuncDecl, *ast.FuncLit:
// Skip if it's part of a function declaration or literal
return true
}
}
}
return c.checkFuncType(typ)
}

// checkFieldList is a helper to check parameter lists
func (c *argListParensChecker) checkFieldList(params *ast.FieldList, node ast.Node) bool {
if len(params.List) < 2 {
return true
}
lastParam := params.List[len(params.List)-1]
lastParamLine := c.ctxt.fset.Position(lastParam.Pos()).Line
firstParamLine := c.ctxt.fset.Position(params.List[0].Pos()).Line
if firstParamLine == lastParamLine {
// Don't track single-line parameter lists.
return true
}
rparenPos := params.Closing
if rparenPos == 0 {
// No closing paren found
return true
}
rparenLine := c.ctxt.fset.Position(rparenPos).Line
switch rparenLine {
case lastParamLine:
c.ctxt.mark(node, &c.sameLine)
case lastParamLine + 1:
c.ctxt.mark(node, &c.nextLine)
}
return true
}
Expand Down
211 changes: 211 additions & 0 deletions testdata/negative_tests4.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package arglisttests

type Person struct {
Name string
Age int
}

func structLiterals() {
// All closing braces on same line as last element - consistent style
_ = Person{
Name: "John",
Age: 30}

_ = Person{Name: "Jane",
Age: 25}

_ = Person{
Name: "Bob",
Age: 40}
}

func sliceLiterals() {
// All closing braces on same line as last element
_ = []string{
"first",
"second",
"third"}

_ = []int{1,
2,
3}

_ = []string{
"alpha",
"beta",
"gamma"}

_ = []Person{
{Name: "Alice", Age: 28},
{Name: "Charlie", Age: 35}}
}

func mapLiterals() {
// All closing braces on same line as last element
_ = map[string]int{
"one": 1,
"two": 2,
"three": 3}

_ = map[int]string{1: "first",
2: "second",
3: "third"}

_ = map[string]Person{
"john": {Name: "John", Age: 30},
"jane": {Name: "Jane", Age: 25}}

_ = map[string]interface{}{
"name": "test",
"age": 42}
}

func functionWithMultipleParams(
name string,
age int,
active bool) {
}

func anotherFunction(param1 string,
param2 int,
param3 bool) {
}

func complexFunction(
param1 string,
param2 []int,
param3 map[string]interface{}) error {
return nil
}

type Calculator struct{}

func (c Calculator) Add(
x int,
y int) int {
return x + y
}

func (c Calculator) Multiply(a int,
b int,
z int) int {
return a * b * z
}

func (c Calculator) Divide(
dividend float64,
divisor float64) float64 {
return dividend / divisor
}

func (c *Calculator) ComplexOperation(
param1 string,
param2 []int,
param3 map[string]interface{}) (result interface{}, err error) {
return nil, nil
}

type MathInterface interface {
Calculate(
x int,
y int) int

Process(input string,
options map[string]interface{},
callback func(string) error) error

Transform(
data []byte,
format string) ([]byte, error)

Validate(
input interface{},
rules []string,
strict bool) bool
}

type AdvancedInterface interface {
ComplexMethod(
param1 string,
param2 func(int) error,
param3 chan string) <-chan result

SimpleMethod(
a int,
b string) error
}

type result struct {
Value interface{}
Error error
}

func functionLiterals() {
// All closing parens on same line as last parameter
fn1 := func(
x int,
y int) int {
return x + y
}

fn2 := func(a string,
b string,
c string) string {
return a + b + c
}

fn3 := func(
param1 string,
param2 int) error {
return nil
}

fn4 := func(
data []byte,
callback func(error),
options map[string]interface{}) {
// Implementation
}

_ = fn1
_ = fn2
_ = fn3
_ = fn4
}

func nestedLiterals() {
// All closing braces on same line as last element
_ = []map[string]Person{
{
"manager": {Name: "Alice", Age: 35},
"employee": {Name: "Bob", Age: 28}},
{
"lead": {Name: "Charlie", Age: 40},
"junior": {Name: "David", Age: 22}}}

_ = map[string][]Person{
"team1": {
{Name: "Eve", Age: 30},
{Name: "Frank", Age: 32}},
"team2": {
{Name: "Grace", Age: 29},
{Name: "Henry", Age: 31}}}
}

func singleLineCases() {
_ = Person{Name: "Test", Age: 25}
_ = []int{1, 2, 3}
_ = map[string]int{"a": 1, "b": 2}

fn := func(a, b int) int { return a + b }
_ = fn
}

func fewElementsCases() {
_ = Person{Name: "Single"}
_ = []int{42}
_ = map[string]int{"single": 1}
}

func singleParam(name string) {}
func noParams() {}
Loading