Skip to content

Commit

Permalink
feat: TODO linter (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
george-e-shaw-iv authored Jul 16, 2021
1 parent 347fd1a commit 010b800
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
3 changes: 2 additions & 1 deletion cmd/lintroller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/getoutreach/lintroller/internal/copyright"
"github.com/getoutreach/lintroller/internal/doculint"
"github.com/getoutreach/lintroller/internal/header"
"github.com/getoutreach/lintroller/internal/todo"
"golang.org/x/tools/go/analysis/unitchecker"
)

Expand All @@ -12,6 +13,6 @@ func main() {
&doculint.Analyzer,
&header.Analyzer,
&copyright.Analyzer,
// Add more *analysis.Analyzer's here.
&todo.Analyzer,
)
}
3 changes: 2 additions & 1 deletion internal/copyright/copyright.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package copyright contains the necessary logic for the copyright linter.
package copyright

import (
Expand All @@ -9,7 +10,7 @@ import (
)

// Here is an example regular expression that can be used to test this linter:
// ^Copyright 20[2-9][0-9] Outreach Corporation\. All Rights Reserved\.$
// ^Copyright 20[2-9][0-9] Outreach Corporation\. All Rights Reserved\.$

// doc defines the help text for the copyright linter.
const doc = `Ensures each .go file has a comment at the top of the file containing the
Expand Down
54 changes: 54 additions & 0 deletions internal/todo/todo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Package todo contains the necessary logic for the todo linter.
package todo

import (
"regexp"
"strings"

"golang.org/x/tools/go/analysis"
)

// doc defines the help text for the todo linter.
const doc = `Ensures that each TODO comment defined in the codebase conforms to one of the
following formats: TODO(<gh-user>)[<jira-ticket>]: <summary> or TODO[<jira-ticket>]: <summary>`

// Analyzer exports the todo analyzer (linter).
var Analyzer = analysis.Analyzer{
Name: "todo",
Doc: doc,
Run: todo,
}

// Regular expression variable block for the todo linter.
var (
// reTodo is the regular expression that matches the required TODO format by this
// linter.
//
// For examples, see: https://regex101.com/r/vsbdEm/1
reTodo = regexp.MustCompile(`^TODO(\((?P<ghUser>[\w-]+)\))?\[(?P<jiraTicket>[A-Z]+-\d+)\]: .+$`)

// These subexpression indexes are placeholders just incase they're ever needed to
// be used for whatever reason in the future.
_ = reTodo.SubexpIndex("ghUser")
_ = reTodo.SubexpIndex("jiraTicket")
)

// todo is the function that gets passed to the Analyzer which runs the actual
// analysis for the todo linter on a set of files.
func todo(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
for _, commentGroup := range file.Comments {
for _, comment := range commentGroup.List {
text := strings.TrimSpace(strings.TrimPrefix(comment.Text, "//"))

if strings.HasPrefix(text, "TODO") {
if !reTodo.MatchString(text) {
pass.Reportf(comment.Pos(), "TODO comment must match one of the required formats: TODO(<gh-user>)[<jira-ticket>]: <summary> or TODO[<jira-ticket>]: <summary>")
}
}
}
}
}

return nil, nil
}

0 comments on commit 010b800

Please sign in to comment.