diff --git a/.gitignore b/.gitignore index 1988a5c..9b8cdca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ golinter revive vendor - +*.swp diff --git a/README.md b/README.md index da72206..c64c678 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a | `bool-literal-in-expr`| n/a | Suggests removing Boolean literals from logic expressions | no | no | | `redefines-builtin-id`| n/a | Warns on redefinitions of builtin identifiers | no | no | | `function-result-limit` | int | Specifies the maximum number of results a function can return | no | no | +| `imports-blacklist` | []string | Disallows importing the specified packages | no | no | ## Configurable rules diff --git a/config.go b/config.go index 52493b3..8e7c163 100644 --- a/config.go +++ b/config.go @@ -64,6 +64,7 @@ var allRules = append([]lint.Rule{ &rule.ConstantLogicalExprRule{}, &rule.BoolLiteralRule{}, &rule.RedefinesBuiltinIDRule{}, + &rule.ImportsBlacklistRule{}, &rule.FunctionResultsLimitRule{}, }, defaultRules...) diff --git a/fixtures/imports-blacklist.go b/fixtures/imports-blacklist.go new file mode 100644 index 0000000..cdd5ef4 --- /dev/null +++ b/fixtures/imports-blacklist.go @@ -0,0 +1,7 @@ +package fixtures + +import ( + "crypto/md5" // MATCH /should not use the following blacklisted import: "crypto/md5"/ + "crypto/sha1" // MATCH /should not use the following blacklisted import: "crypto/sha1"/ + "strings" +) diff --git a/rule/imports-blacklist.go b/rule/imports-blacklist.go new file mode 100644 index 0000000..ecc0595 --- /dev/null +++ b/rule/imports-blacklist.go @@ -0,0 +1,69 @@ +package rule + +import ( + "fmt" + "go/ast" + + "github.com/mgechev/revive/lint" +) + +// ImportsBlacklistRule lints given else constructs. +type ImportsBlacklistRule struct{} + +// Apply applies the rule to given file. +func (r *ImportsBlacklistRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + var failures []lint.Failure + blacklist := make(map[string]bool, len(arguments)) + + for _, arg := range arguments { + argStr, ok := arg.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the imports-blacklist rule. Expecting a string, got %T", arg)) + } + // we add quotes if nt present, because when parsed, the value of the AST node, will be quoted + if len(argStr) > 2 && argStr[0] != '"' && argStr[len(argStr)-1] != '"' { + argStr = fmt.Sprintf(`"%s"`, argStr) + } + blacklist[argStr] = true + } + + fileAst := file.AST + walker := blacklistedImports{ + file: file, + fileAst: fileAst, + onFailure: func(failure lint.Failure) { + failures = append(failures, failure) + }, + blacklist: blacklist, + } + + ast.Walk(walker, fileAst) + + return failures +} + +// Name returns the rule name. +func (r *ImportsBlacklistRule) Name() string { + return "imports-blacklist" +} + +type blacklistedImports struct { + file *lint.File + fileAst *ast.File + onFailure func(lint.Failure) + blacklist map[string]bool +} + +func (w blacklistedImports) Visit(n ast.Node) ast.Visitor { + for _, is := range w.fileAst.Imports { + if is.Path != nil && !w.file.IsTest() && w.blacklist[is.Path.Value] { + w.onFailure(lint.Failure{ + Confidence: 1, + Failure: fmt.Sprintf("should not use the following blacklisted import: %s", is.Path.Value), + Node: is, + Category: "imports", + }) + } + } + return nil +} diff --git a/test/import-blacklist_test.go b/test/import-blacklist_test.go new file mode 100644 index 0000000..6132b7b --- /dev/null +++ b/test/import-blacklist_test.go @@ -0,0 +1,16 @@ +package test + +import ( + "testing" + + "github.com/mgechev/revive/lint" + "github.com/mgechev/revive/rule" +) + +func TestImportsBlacklist(t *testing.T) { + args := []interface{}{"crypto/md5", "crypto/sha1"} + + testRule(t, "imports-blacklist", &rule.ImportsBlacklistRule{}, &lint.RuleConfig{ + Arguments: args, + }) +}