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
16 changes: 16 additions & 0 deletions .golangci.next.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ linters:
- prealloc
- predeclared
- promlinter
- propro
- protogetter
- reassign
- recvcheck
Expand Down Expand Up @@ -218,6 +219,7 @@ linters:
- prealloc
- predeclared
- promlinter
- propro
- protogetter
- reassign
- recvcheck
Expand Down Expand Up @@ -2322,6 +2324,20 @@ linters:
# UnitAbbreviations detects abbreviated units in the metric name.
- UnitAbbreviations

propro:
# Optional: Path to the file that contains the list of entities to be checked. Entities are expected to be specified in
# var EntityList []any = { &module.Entity1{}, &module.Entity2{}, ... } format.
# Default: ""
entity-list-file: "/your/project/path/to/entities.go"

# Optional: List of structs in the project to check.
# Default: []
structs:
- StructName1
- StructName2
# If no configuration section for propro is provided, it will check all structs in the project.


protogetter:
# Skip files generated by specified generators from the checking.
# Checks only the file's initial comment, which must follow the format: "// Code generated by <generator-name>".
Expand Down
9 changes: 9 additions & 0 deletions docs/data/linters_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,15 @@
"isSlow": false,
"since": "v1.40.0"
},
{
"name": "propro",
"desc": "public fields' write protection in structs (entities)",
"loadMode": 384,
"originalURL": "https://github.com/digitalstraw/propro",
"internal": false,
"isSlow": false,
"since": "v2.6.3"
},
{
"name": "protogetter",
"desc": "Reports direct reads from proto message fields when getters should be used",
Expand Down
8 changes: 8 additions & 0 deletions docs/data/thanks.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@
"profile": "https://github.com/sponsors/denis-tingaikin",
"avatar": "https://github.com/denis-tingaikin.png"
},
{
"name": "digitalstraw",
"linters": [
"propro"
],
"profile": "https://github.com/sponsors/digitalstraw",
"avatar": "https://github.com/digitalstraw.png"
},
{
"name": "dixonwille",
"linters": [
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ require (
github.com/curioswitch/go-reassign v0.3.0
github.com/daixiang0/gci v0.13.7
github.com/denis-tingaikin/go-header v0.5.0
github.com/digitalstraw/propro v1.0.0
github.com/fatih/color v1.18.0
github.com/firefart/nonamedreturns v1.0.6
github.com/fzipp/gocyclo v0.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions jsonschema/golangci.next.jsonschema.json
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,7 @@
"prealloc",
"predeclared",
"promlinter",
"propro",
"protogetter",
"reassign",
"recvcheck",
Expand Down Expand Up @@ -3089,6 +3090,24 @@
}
}
},
"proproSettings": {
"type": "object",
"additionalProperties": false,
"properties": {
"entity-list-file": {
"description": "Path to the Go source file defining the list of protected structs",
"type": "string",
"default": ""
},
"structs": {
"description": "List of struct names to protect",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"protogetterSettings": {
"type": "object",
"additionalProperties": false,
Expand Down Expand Up @@ -4837,6 +4856,9 @@
"promlinter": {
"$ref": "#/definitions/settings/definitions/promlinterSettings"
},
"propro": {
"$ref": "#/definitions/settings/definitions/proproSettings"
},
"protogetter": {
"$ref": "#/definitions/settings/definitions/protogetterSettings"
},
Expand Down
6 changes: 6 additions & 0 deletions pkg/config/linters_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ type LintersSettings struct {
Prealloc PreallocSettings `mapstructure:"prealloc"`
Predeclared PredeclaredSettings `mapstructure:"predeclared"`
Promlinter PromlinterSettings `mapstructure:"promlinter"`
ProPro ProProSettings `mapstructure:"propro"`
ProtoGetter ProtoGetterSettings `mapstructure:"protogetter"`
Reassign ReassignSettings `mapstructure:"reassign"`
Recvcheck RecvcheckSettings `mapstructure:"recvcheck"`
Expand Down Expand Up @@ -817,6 +818,11 @@ type PromlinterSettings struct {
DisabledLinters []string `mapstructure:"disabled-linters"`
}

type ProProSettings struct {
EntityListFile string `mapstructure:"entity-list-file"`
Structs []string `mapstructure:"structs"`
}

type ProtoGetterSettings struct {
SkipGeneratedBy []string `mapstructure:"skip-generated-by"`
SkipFiles []string `mapstructure:"skip-files"`
Expand Down
20 changes: 20 additions & 0 deletions pkg/golinters/propro/propro.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package propro

import (
"github.com/digitalstraw/propro/pkg/analyzer"

"github.com/golangci/golangci-lint/v2/pkg/config"
"github.com/golangci/golangci-lint/v2/pkg/goanalysis"
)

func New(settings *config.ProProSettings) *goanalysis.Linter {
cfg := map[string]any{}

cfg["entityListFile"] = settings.EntityListFile
cfg["structs"] = settings.Structs

return goanalysis.
NewLinterFromAnalyzer(analyzer.NewAnalyzer(cfg)).
WithLoadMode(goanalysis.LoadModeSyntax).
WithLoadMode(goanalysis.LoadModeTypesInfo)
}
11 changes: 11 additions & 0 deletions pkg/golinters/propro/propro_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package propro

import (
"testing"

"github.com/golangci/golangci-lint/v2/test/testshared/integration"
)

func TestFromTestdata(t *testing.T) {
integration.RunTestdata(t)
}
5 changes: 5 additions & 0 deletions pkg/golinters/propro/testdata/entities/entities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package entities

var EnetityList = []string{
"Entity",
}
116 changes: 116 additions & 0 deletions pkg/golinters/propro/testdata/propro.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//golangcitest:args -Epropro
package testdata

type Entity2 struct {
IntField int
IntPtrField *int
}

type Entity struct {
SubEntityViaProperty *SubEntity
ProtectedField string
}

func (e *Entity) SubEntity() *SubEntity {
return &SubEntity{}
}

func (e *Entity) SubEntityViaInterface() SubEntityInterface {
return &SubEntity{}
}

type SubEntityInterface interface {
SetProtectedField(value string)
}

type SubEntity struct {
ProtectedField string
}

func (s *SubEntity) SetProtectedField(value string) {
s.ProtectedField = value
}

type Repository interface {
Read() *Entity
}

type RepositoryImpl struct{}

func (r *RepositoryImpl) Read() *Entity {
return &Entity{}
}

var repo Repository = &RepositoryImpl{}

func (e *Entity) SetProtectedField(value string) {
e.ProtectedField = value
}

func SomeFunc1() {
e := &Entity{}
e.SetProtectedField("value")
}

func SomeFunc2() {
e := &Entity{}
e.ProtectedField = "value" // want "assignment to exported field Entity.ProtectedField is forbidden outside its methods"
}

func SomeFunc3() {
e := repo.Read()
e.ProtectedField = "value" // want "assignment to exported field Entity.ProtectedField is forbidden outside its methods"
}

func SomeFunc4() {
e := repo.Read()
e.SetProtectedField("value")
}

func SomeFunc5() {
e := &Entity{}
sub := e.SubEntity()
sub.SetProtectedField("value")
}
func SomeFunc6() {
e := &Entity{}
sub := e.SubEntity()
sub.ProtectedField = "value" // want "assignment to exported field SubEntity.ProtectedField is forbidden outside its methods"
}

func SomeFunc7() {
e := &Entity{}
sub := e.SubEntityViaInterface()
sub.SetProtectedField("value")
}

func SomeFunc8() {
e := &Entity{
SubEntityViaProperty: &SubEntity{},
}
e.SubEntityViaProperty.ProtectedField = "value" // want "assignment to exported field SubEntity.ProtectedField is forbidden outside its methods"
}

func SomeFunc9() {
e := &Entity{
SubEntityViaProperty: &SubEntity{},
}
e.SubEntityViaProperty.SetProtectedField("value")
if e.SubEntityViaProperty.ProtectedField != "value" {
}
}

func SomeFunc10() {
e := &Entity2{}
e.IntField++ // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
e.IntField-- // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
e.IntField += 10 // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
e.IntField -= 10 // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
e.IntField *= 10 // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
e.IntField /= 10 // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
e.IntField = 10 // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
*(&e.IntField)++ // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
*(&e.IntField)-- // want "assignment to exported field Entity2.IntField is forbidden outside its methods"
e.IntPtrField = new(int) // want "assignment to exported field Entity2.IntPtrField is forbidden outside its methods"
*e.IntPtrField = 20 // want "assignment to exported field Entity2.IntPtrField is forbidden outside its methods"
}
8 changes: 8 additions & 0 deletions pkg/golinters/propro/testdata/propro.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: "2"

linters:
settings:
propro:
entity-list-file: "testdata/entities/entities.go"
structs:
- "SubEntity"
36 changes: 36 additions & 0 deletions pkg/golinters/propro/testdata/propro_cgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//golangcitest:args -Epropro
package testdata

/*
#include <stdio.h>
#include <stdlib.h>

void myprint(char* s) {
printf("%d\n", s);
}
*/
import "C"

import (
"unsafe"
)

func _() {
cs := C.CString("Hello from stdio\n")
C.myprint(cs)
C.free(unsafe.Pointer(cs))
}

type CgoEntity struct {
IntField int
}

func (s *CgoEntity) SetProtectedField(value int) {
s.IntField = value
}

func CgoFunc1() {
e := &CgoEntity{}
e.SetProtectedField(1)
e.IntField = 10 // want "assignment to exported field CgoEntity.IntField is forbidden outside its methods"
}
6 changes: 6 additions & 0 deletions pkg/lint/lintersdb/builder_linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ import (
"github.com/golangci/golangci-lint/v2/pkg/golinters/prealloc"
"github.com/golangci/golangci-lint/v2/pkg/golinters/predeclared"
"github.com/golangci/golangci-lint/v2/pkg/golinters/promlinter"
"github.com/golangci/golangci-lint/v2/pkg/golinters/propro"
"github.com/golangci/golangci-lint/v2/pkg/golinters/protogetter"
"github.com/golangci/golangci-lint/v2/pkg/golinters/reassign"
"github.com/golangci/golangci-lint/v2/pkg/golinters/recvcheck"
Expand Down Expand Up @@ -566,6 +567,11 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) {
WithSince("v1.40.0").
WithURL("https://github.com/yeya24/promlinter"),

linter.NewConfig(propro.New(&cfg.Linters.Settings.ProPro)).
WithSince("v2.8.0").
WithLoadForGoAnalysis().
WithURL("https://github.com/digitalstraw/propro"),

linter.NewConfig(protogetter.New(&cfg.Linters.Settings.ProtoGetter)).
WithSince("v1.55.0").
WithLoadForGoAnalysis().
Expand Down