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

Core skeleton #1

Merged
merged 34 commits into from
Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6ca6176
Created a skeleton of the core library.
Mar 28, 2019
d13d54f
Added rule in-line disabling.
Mar 29, 2019
faa542f
Changed `lint.ID` to `lint.RuleID`.
mingzhi Mar 30, 2019
ded05fe
Fixed wrong check function.
mingzhi Mar 30, 2019
fa921cc
Added `RulesConfig` and implemented rules running.
mingzhi Mar 30, 2019
5cb0a2d
Created cmd `proto-gen-api_lint` and fixed descriptor source lookups.
mingzhi Mar 31, 2019
298b7cb
Used `go mod`
mingzhi Mar 31, 2019
685e712
Rewrote `protovisit`.
Apr 1, 2019
0842f9d
Added a visitor for general descriptors.
mingzhi Apr 2, 2019
6e6c9d7
Added more tests and cleared names.
Apr 2, 2019
d510581
Moved rule disabling check to context source.
mingzhi Apr 2, 2019
5afe6da
Rewrote rule checkers.
mingzhi Apr 2, 2019
6a8bb19
Pushed reflection down to checkers.
mingzhi Apr 3, 2019
ffdc046
Added gitignore
robcapo Apr 11, 2019
9923cc7
Removed rules
robcapo Apr 11, 2019
cc20751
Removed protovisit
robcapo Apr 11, 2019
3246993
Changed StartLocation to a factory
robcapo Apr 11, 2019
bfe53b6
Merge pull request #5 from jgeewax/start_location_factory
mingzhi Apr 11, 2019
a0514f1
remove cmd, we will rewrite it later
Apr 11, 2019
8e01b24
Merge pull request #6 from jgeewax/remove-cmd
robcapo Apr 11, 2019
63b7abf
Removed context and modified tests for DescriptorSource.
Apr 11, 2019
7183941
Added getters for Request and creating a Request from file descriptor…
Apr 11, 2019
c16bc2b
Merge pull request #7 from jgeewax/update-request
robcapo Apr 12, 2019
4a0bba0
Made NewDescriptorSource a private function.
Apr 12, 2019
b88445c
Removed unused error
robcapo Apr 12, 2019
f3be115
Renamed error
robcapo Apr 12, 2019
21623cb
Added an error for invalid spans, returning it if a span is provided …
robcapo Apr 12, 2019
b0c0f95
Added more tests for source.
Apr 12, 2019
6a1a362
Made error package private
robcapo Apr 12, 2019
e4ccca5
Merge pull request #8 from jgeewax/remove_unused_error
mingzhi Apr 12, 2019
ee2280f
Improved error message
robcapo Apr 12, 2019
88b5f67
Merge pull request #9 from jgeewax/update-request
mingzhi Apr 12, 2019
90d7eb1
Moved span error inline
robcapo Apr 12, 2019
4eabf74
Merge pull request #10 from jgeewax/span_error
mingzhi Apr 12, 2019
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
.vscode
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/jgeewax/api-linter

go 1.12

require (
github.com/golang/protobuf/v2 v2.0.0-20190115031900-66c365cf7239
github.com/google/go-cmp v0.2.1-0.20181101181452-745b8ec83783
github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7
github.com/stretchr/testify v1.3.0
)
24 changes: 24 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.1-0.20181127190454-8d0c54c12466/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.2.1-0.20181205191652-7e65e513332f h1:jEoef3K+ZQwZ7UB1iGu6KhX8hS9cYw1aXR7djS3Vn10=
github.com/golang/protobuf v1.2.1-0.20181205191652-7e65e513332f/go.mod h1:asK8yRb/+zxJTE0SbTESCku/4OjiDfbPwk4rEyIatUA=
github.com/golang/protobuf/v2 v2.0.0-20181127193627-d7e97bc71bcb/go.mod h1:MgUD+N3FwzDmj2CdMsT5ap7K7jx+c9cQDQ7fVhmH+Xw=
github.com/golang/protobuf/v2 v2.0.0-20190115031900-66c365cf7239 h1:gRJbl/8g6n9YmDWRAI93uRuIVnw/ESX2o/SMfLO3QOY=
github.com/golang/protobuf/v2 v2.0.0-20190115031900-66c365cf7239/go.mod h1:sjQt90Yu/7/I4QgfZq+CzFOly7327WbVZ1EDQjIlnMI=
github.com/google/go-cmp v0.2.1-0.20181101181452-745b8ec83783 h1:wVZ6laEGf86tNDTpR5mxFyFIclJJiXCxuJhcQKnsOHk=
github.com/google/go-cmp v0.2.1-0.20181101181452-745b8ec83783/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7 h1:ux/56T2xqZO/3cP1I2F86qpeoYPCOzk+KF/UH/Ar+lk=
github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/tools v0.0.0-20180904205237-0aa4b8830f48/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180928181343-b3c0be4c978b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
24 changes: 24 additions & 0 deletions lint/location.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package lint

// Location describes a location in a source code file.
type Location struct {
Start, End Position
}

// IsValid checks if the location struct is constructed properly and
// returns true if it's valid.
func (l Location) IsValid() bool {
return l.Start.IsValid() && l.End.IsValid()
}

// Position describes a zero-based position in a source code file.
// Typically you will want to add 1 to each before displaying to a user.
type Position struct {
Line, Column int
}

// IsValid checks if the position struct is constructed properly and
// returns true if it's valid.
func (p Position) IsValid() bool {
return p.Line >= 0 && p.Column >= 0
}
104 changes: 104 additions & 0 deletions lint/mocks/Rule.go

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

22 changes: 22 additions & 0 deletions lint/problem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package lint

import (
"github.com/golang/protobuf/v2/reflect/protoreflect"
)

// Problem contains information about a result produced by an API Linter.
type Problem struct {
// Message provides a short description of the problem.
Message string
// Suggestion provides a suggested fix, if applicable.
Suggestion string
robcapo marked this conversation as resolved.
Show resolved Hide resolved
// Location provides the location of the problem. If both
// `Location` and `Descriptor` are specified, the location
// is then used from `Location` instead of `Descriptor`.
Location Location
// Descriptor provides the descriptor related
// to the problem. If present and `Location` is not
// specified, then the starting location of the descriptor
robcapo marked this conversation as resolved.
Show resolved Hide resolved
// is used as the location of the problem.
Descriptor protoreflect.Descriptor
}
37 changes: 37 additions & 0 deletions lint/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package lint

import (
"github.com/golang/protobuf/v2/reflect/protodesc"
"github.com/golang/protobuf/v2/reflect/protoreflect"
descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
)

// Request defines input data for a rule to perform linting.
type Request struct {
fileDesc protoreflect.FileDescriptor
descSource DescriptorSource
}

// ProtoFile returns a FileDescriptor of the .proto file that will be linted.
func (r Request) ProtoFile() protoreflect.FileDescriptor {
return r.fileDesc
}

// DescriptorSource returns a DescriptorSource that contains additional source
// information for the .proto file that will be linted.
func (r Request) DescriptorSource() DescriptorSource {
return r.descSource
}

// NewProtoFileRequest creates a linting Request for a .proto file.
func NewProtoFileRequest(fd *descriptorpb.FileDescriptorProto) (Request, error) {
f, err := protodesc.NewFile(fd, nil)
if err != nil {
return Request{}, err
}
s, err := newDescriptorSource(fd)
return Request{
fileDesc: f,
descSource: s,
}, err
}
11 changes: 11 additions & 0 deletions lint/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package lint

// Response describes the result returned by a rule.
type Response struct {
Problems []Problem
}

// merge merges another response.
func (resp *Response) merge(other Response) {
resp.Problems = append(resp.Problems, other.Problems...)
}
50 changes: 50 additions & 0 deletions lint/rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package lint

// Rule defines a lint rule that checks Google Protobuf APIs.
type Rule interface {
// ID returns the unique `RuleID` for this rule.
ID() RuleID
// Description returns a short summary about this rule.
Description() string
// URL returns a link at which the full documentation
// about this rule can be accessed.
URL() string
// FileTypes returns the types of files that this rule is targeting to.
// E.g., `ProtoFile` for protobuf files.
FileTypes() []FileType
// Category returns the category of findings produced
// by this rule, e.g. Problem, Suggestion, etc.
Category() Category
// Lint performs the linting process.
Lint(Request) (Response, error)
}

// RuleID describes a `Rule` ID.
type RuleID struct {
// Set is where a rule belongs to and usually is the scope
// of the contained rules should be applied to. For example,
// the `core` set contains all rules that should be applied to
// all Google APIs, while the set `google.corpeng` contains rules
// that should be applied only to Google Corp Eng APIs.
Set string
// Name is the unique name in the set.
Name string
}

// FileType defines a file type that a rule is targeting to.
type FileType string

const (
// ProtoFile indicates that the targeted file is a protobuf-definition file.
ProtoFile FileType = "proto-file"
robcapo marked this conversation as resolved.
Show resolved Hide resolved
)

// Category defines the category of the findings produced by a rule.
type Category string

const (
// CategoryError indicates that in the API, something will cause errors.
CategoryError Category = "API-Linter-Error"
// CategorySuggestion indicates that in the API, something can be do better.
CategorySuggestion Category = "API-Linter-Suggestion"
)
12 changes: 12 additions & 0 deletions lint/rule_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lint

// RulesConfig describes which rule sets to use.
type RulesConfig struct {
RuleSets []RuleSetConfig
}

// RuleSetConfig describes which rules to be excluded in a set.
type RuleSetConfig struct {
Set string
ExcludedRules []string
}
80 changes: 80 additions & 0 deletions lint/rules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package lint

import (
"errors"
)

var (
// ErrDuplicateID is the returned error when a duplicated rule ID is
// found in a rule registry.
ErrDuplicateID = errors.New("rule registry: found duplicate id")
// ErrNotFound is the returned error when a rule is not found in a
// rule registry
ErrNotFound = errors.New("rule registry: rule is not found")
)

// Rules is a registry for registering and looking up rules.
type Rules struct {
ruleMap map[RuleID]Rule
}

// Register registers the list of rules.
// It returns `ErrDuplicateID` if any of the rules is found duplicate
// in the registry.
func (r *Rules) Register(rules ...Rule) error {
for _, rl := range rules {
if _, found := r.ruleMap[rl.ID()]; found {
return ErrDuplicateID
}
r.ruleMap[rl.ID()] = rl
}
return nil
}

// Merge merges another rule registry.
// If any rule is found duplicate, returns `ErrDuplicateID`.
func (r *Rules) Merge(other Rules) error {
return r.Register(other.All()...)
}

// FindByConfig looks up a list of rules by a RulesConfig
func (r *Rules) FindByConfig(cfg RulesConfig) []Rule {
rules := []Rule{}
for _, setConfig := range cfg.RuleSets {
rules = append(rules, r.findBySet(setConfig)...)
}
return rules
}

// All returns all rules.
func (r Rules) All() []Rule {
rules := []Rule{}
for _, r1 := range r.ruleMap {
rules = append(rules, r1)
}
return rules
}

func (r *Rules) findBySet(s RuleSetConfig) []Rule {
excludedRules := make(map[string]bool)
for _, ruleName := range s.ExcludedRules {
excludedRules[ruleName] = true
}

rules := []Rule{}
for _, r1 := range r.ruleMap {
if r1.ID().Set == s.Set && !excludedRules[r1.ID().Name] {
rules = append(rules, r1)
}
}
return rules
}

// NewRules returns a rule registry initialized with the given set of rules.
func NewRules(rules ...Rule) (*Rules, error) {
r := Rules{
ruleMap: make(map[RuleID]Rule),
}
err := r.Register(rules...)
return &r, err
}
Loading