Skip to content

Commit

Permalink
all: rename "dsl/fluent" to just "dsl" (#151)
Browse files Browse the repository at this point in the history
Previously, DSL was 99% about fluent.Matcher which sounds OK
because the matcher itself is a "Fluent API" (tm).

But it makes freestanding functions in fluent package awkward.
"dsl" package gives no restrictions in that regard.

Refs #149
  • Loading branch information
quasilyte authored Dec 22, 2020
1 parent 5d7e62a commit 8bb37f2
Show file tree
Hide file tree
Showing 25 changed files with 94 additions and 190 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,16 @@ Create a test `example.rules.go` file:

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func dupSubExpr(m fluent.Matcher) {
func dupSubExpr(m dsl.Matcher) {
m.Match(`$x || $x`,
`$x && $x`).
Where(m["x"].Pure).
Report(`suspicious identical LHS and RHS`)
}

func boolExprSimplify(m fluent.Matcher) {
func boolExprSimplify(m dsl.Matcher) {
m.Match(`!($x != $y)`).Suggest(`$x == $y`)
m.Match(`!($x == $y)`).Suggest(`$x != $y`)
}
Expand Down Expand Up @@ -122,11 +122,11 @@ The `-e` generated rule will have `e` name, so it can be debugged as well.
Loaded rules are then used to check the specified targets (Go files, packages).
The `rules.go` file itself is never compiled, nor executed.

A `rules.go` file, as interpreted by a [`dsl/fluent`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent) API, is a set of functions that serve as a rule groups. Every function accepts a single [`fluent.Matcher`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher) argument that is then used to define and configure rules inside the group.
A `rules.go` file, as interpreted by a [`dsl`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl) API, is a set of functions that serve as a rule groups. Every function accepts a single [`dsl.Matcher`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher) argument that is then used to define and configure rules inside the group.

A rule definition always starts from a [`Match(patterns...)`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Match) method call and ends with a [`Report(message)`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Report) method call.
A rule definition always starts from a [`Match(patterns...)`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Match) method call and ends with a [`Report(message)`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Report) method call.

There can be additional calls in between these two. For example, a [`Where(cond)`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Where) call applies constraints to a match to decide whether its accepted or rejected. So even if there is a match for a pattern, it won't produce a report message unless it satisfies a `Where()` condition.
There can be additional calls in between these two. For example, a [`Where(cond)`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Where) call applies constraints to a match to decide whether its accepted or rejected. So even if there is a match for a pattern, it won't produce a report message unless it satisfies a `Where()` condition.

To learn more, check out the documentation and/or the source code.

Expand All @@ -136,7 +136,7 @@ To learn more, check out the documentation and/or the source code.
* Example rule files: [rules.go](rules.go)
* Another great example: [github.com/dgryski/semgrep-go/ruleguard.rules.go](https://github.com/dgryski/semgrep-go/blob/master/ruleguard.rules.go)
* [gorules](docs/gorules.md) format documentation
* [dsl/fluent package](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent) reference
* [dsl package](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl) reference
* [ruleguard package](https://godoc.org/github.com/quasilyte/go-ruleguard/ruleguard) reference
* Introduction article: [EN](https://quasilyte.dev/blog/post/ruleguard/), [RU](https://habr.com/ru/post/481696/)

Expand Down
4 changes: 2 additions & 2 deletions analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ func readRules() (*parseRulesResult, error) {
case flagE != "":
ruleText := fmt.Sprintf(`
package gorules
import "github.com/quasilyte/go-ruleguard/dsl/fluent"
func e(m fluent.Matcher) {
import "github.com/quasilyte/go-ruleguard/dsl"
func e(m dsl.Matcher) {
%s.Report("$$")
}`,
flagE)
Expand Down
4 changes: 2 additions & 2 deletions analyzer/testdata/src/extra/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
// We don't want to suggest int64(x) if x is already int64,
// this is why 2 rules are needed.
// Maybe there will be a way to group these 2 together in
Expand Down
4 changes: 2 additions & 2 deletions analyzer/testdata/src/filtertest/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
m.Import(`github.com/quasilyte/go-ruleguard/analyzer/testdata/src/filtertest/foolib`)

m.Match(`typeTest($x, "contains time.Time")`).
Expand Down
8 changes: 4 additions & 4 deletions analyzer/testdata/src/gocritic/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
m.Match(`$x = $x`).Report(`suspicious self-assignment in $$`)

m.Match(`$tmp := $x; $x = $y; $y = $tmp`).
Expand Down Expand Up @@ -137,7 +137,7 @@ func testRules(m fluent.Matcher) {
Suggest(`&$x`)
}

func badCond(m fluent.Matcher) {
func badCond(m dsl.Matcher) {
m.Match(`$x < $a && $x > $b`).
Where(m["a"].Value.Int() <= m["b"].Value.Int()).
Report("the condition is always false because $a <= $b")
Expand All @@ -147,7 +147,7 @@ func badCond(m fluent.Matcher) {
Report("the condition is always false because $a >= $b")
}

func appendAssign(m fluent.Matcher) {
func appendAssign(m dsl.Matcher) {
m.Match(`$x = append($y, $_, $*_)`).
Where(m["x"].Text != m["y"].Text &&
m["x"].Text != "_" &&
Expand Down
4 changes: 2 additions & 2 deletions analyzer/testdata/src/golint/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
m.Match(`errors.New(fmt.Sprintf($*_))`).
Report(`should replace error.New(fmt.Sprintf(...)) with fmt.Errorf(...)`)
m.Match(`t.Error(fmt.Sprintf($*_))`).
Expand Down
4 changes: 2 additions & 2 deletions analyzer/testdata/src/namedtype/nested/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
m.Import(`namedtype/x/nested`)
m.Import(`extra`)

Expand Down
4 changes: 2 additions & 2 deletions analyzer/testdata/src/namedtype/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
m.Import(`namedtype/x/nested`)

m.Match(`sink = &$t`).
Expand Down
8 changes: 4 additions & 4 deletions analyzer/testdata/src/regression/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
package gorules

import (
"github.com/quasilyte/go-ruleguard/dsl/fluent"
"github.com/quasilyte/go-ruleguard/dsl"
)

func issue68(m fluent.Matcher) {
func issue68(m dsl.Matcher) {
m.Match(`func $_($_ *testing.T) { $p; $*_ }`).Where(m["p"].Text != "t.Parallel()").Report(`Not a parallel test`)
m.Match(`func $_($_ *testing.T) { $p; $*_ }`).Where(m["p"].Text == "t.Parallel()").Report(`Parallel test`)
}

func issue72(m fluent.Matcher) {
func issue72(m dsl.Matcher) {
m.Match("fmt.Sprintf(`\"%s\" <%s>`, $name, $email)",
"fmt.Sprintf(`\"%s\"<%s>`, $name, $email)",
`fmt.Sprintf("\"%s\" <%s>", $name, $email)`,
Expand All @@ -20,7 +20,7 @@ func issue72(m fluent.Matcher) {
Report("use net/mail Address.String() instead of fmt.Sprintf()")
}

func issue115(m fluent.Matcher) {
func issue115(m dsl.Matcher) {
m.Match(`println($x)`).
Where(!(m["x"].Const && m["x"].Type.Is("int"))).
Report("$x is not a constexpr int")
Expand Down
4 changes: 2 additions & 2 deletions analyzer/testdata/src/revive/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
m.Match(`runtime.GC()`).Report(`explicit call to GC`)

m.Match(`$x = atomic.AddInt32(&$x, $_)`,
Expand Down
4 changes: 2 additions & 2 deletions analyzer/testdata/src/suggest/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
m.Match(`($a) || ($b)`).Suggest(`$a || $b`)
}
4 changes: 2 additions & 2 deletions analyzer/testdata/src/testvendored/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func testRules(m fluent.Matcher) {
func testRules(m dsl.Matcher) {
// Test a vendored dependency can be imported successfully and used in a Where statement.
// Otherwise, the rules semantics are not important.

Expand Down
26 changes: 13 additions & 13 deletions docs/gorules.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ Every `gorules` file is a valid Go file.
We can describe a file structure like this:

1. It has a package clause (package name should be `gorules`).
2. An import clause (you need at least `github.com/quasilyte/go-ruleguard/dsl/fluent`).
2. An import clause (you need at least `github.com/quasilyte/go-ruleguard/dsl`).
3. Function declarations.

Functions play a special role: they serve as a **rule groups**.

Every function accepts exactly 1 argument, a [`fluent.Matcher`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher), and defines some **rules**.
Every function accepts exactly 1 argument, a [`dsl.Matcher`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher), and defines some **rules**.

Every **rule** definition starts with a [`Match`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Match) method call that specifies one or more [AST patterns](https://github.com/mvdan/gogrep) that should represent what kind of Go code rule supposed to match. Another mandatory method is [`Report`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Report) that describes a message template that is going to be printed when the rule match is accepted.
Every **rule** definition starts with a [`Match`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Match) method call that specifies one or more [AST patterns](https://github.com/mvdan/gogrep) that should represent what kind of Go code rule supposed to match. Another mandatory method is [`Report`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Report) that describes a message template that is going to be printed when the rule match is accepted.

Here is a small yet useful, example of `gorules` file:

Expand All @@ -37,9 +37,9 @@ Here is a small yet useful, example of `gorules` file:

package gorules

import "github.com/quasilyte/go-ruleguard/dsl/fluent"
import "github.com/quasilyte/go-ruleguard/dsl"

func regexpMust(m fluent.Matcher) {
func regexpMust(m dsl.Matcher) {
m.Match(`regexp.Compile($pat)`,
`regexp.CompilePOSIX($pat)`).
Where(m["pat"].Const).
Expand All @@ -54,12 +54,12 @@ There is a special case of `$$` which can be used to inject the entire pattern m

Apart from the rules, function can contain group statements.

As everything else, statements are `Matcher` methods. [`Import()`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Import) is one of these special methods.
As everything else, statements are `Matcher` methods. [`Import()`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Import) is one of these special methods.

Rule group statements only affect the current rule group and last from the line they were defined until the end of a function block.

```go
func testGroup(m fluent.Matcher) {
func testGroup(m dsl.Matcher) {
// <- Empty imports table.

m.Import(`github.com/some/pkg`)
Expand All @@ -75,7 +75,7 @@ func testGroup(m fluent.Matcher) {
## Filters

There are 2 types of filters that can be used in [`Where`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Where) call:
There are 2 types of filters that can be used in [`Where`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Where) call:
1. Submatch (named variable-based) filters
2. Context filters (current file, etc)

Expand All @@ -92,7 +92,7 @@ Here are some examples of supported filters:
* Submatch text matches provided regexp
* Current files imports package `P`

A match variable can be accessed with `fluent.Matcher` function argument indexing:
A match variable can be accessed with `dsl.Matcher` function argument indexing:

```go
Where(m["a"].Type.Is(`int`) && !m["b"].Type.AssignableTo(`[]string`))
Expand All @@ -109,7 +109,7 @@ Where(m.File().Imports("io/ioutil"))

The filter concept is crucial to avoid false-positives in rules.

Please refer to the godoc page of a [`dsl/fluent`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent) package to get an up-to-date list of supported filters.
Please refer to the godoc page of a [`dsl`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl) package to get an up-to-date list of supported filters.

## Named types and import tables

Expand All @@ -121,7 +121,7 @@ In `gorules`, unqualified type name is hard to interpret right. We could use the

Our resolution is to reject all the unqualified names. If you want a `Foo` type from `a/b/c` package, you need to:

1. Do an [`Import("a/b/c")`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#Matcher.Import) call, so the package is loaded into the current imports table.
1. Do an [`Import("a/b/c")`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#Matcher.Import) call, so the package is loaded into the current imports table.
2. Use `c.Foo` type name.

We need the `Import()` step to match `c` package name with its path, `a/b/c`.
Expand All @@ -135,7 +135,7 @@ m.Import(`html/template`)

## Type pattern matching

Methods like [`ExprType.Is()`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#ExprType.Is) accept a string argument that describes a Go type. It can be as simple as `"[]string"` that matches only a string slice, but it can also include a pattern-like variables:
Methods like [`ExprType.Is()`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#ExprType.Is) accept a string argument that describes a Go type. It can be as simple as `"[]string"` that matches only a string slice, but it can also include a pattern-like variables:

* `[]$T` matches any slice.
* `[$len]$T` matches any array.
Expand All @@ -147,7 +147,7 @@ Methods like [`ExprType.Is()`](https://godoc.org/github.com/quasilyte/go-rulegua
* `struct{$*_; $x}` struct that has $x-typed last field.

Note: when matching types, make sure to think whether you need to match a type or the **underlying type**.
To match the underlying type, use [`ExprType.Underlying()`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl/fluent#ExprType.Underlying) method.
To match the underlying type, use [`ExprType.Underlying()`](https://godoc.org/github.com/quasilyte/go-ruleguard/dsl#ExprType.Underlying) method.

You may recognize that it's the same pattern behavior as in AST patterns.

Expand Down
2 changes: 1 addition & 1 deletion dsl/fluent/bundle.go → dsl/bundle.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fluent
package dsl

// Bundle is a rules file export manifest.
type Bundle struct {
Expand Down
4 changes: 2 additions & 2 deletions dsl/fluent/dsl.go → dsl/dsl.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fluent
package dsl

// Matcher is a main API group-level entry point.
// It's used to define and configure the group rules.
Expand Down Expand Up @@ -147,7 +147,7 @@ type File struct {
Name String

// PkgPath is a file package path.
// Examples: "io/ioutil", "strings", "github.com/quasilyte/go-ruleguard/dsl/fluent".
// Examples: "io/ioutil", "strings", "github.com/quasilyte/go-ruleguard/dsl".
PkgPath String
}

Expand Down
3 changes: 0 additions & 3 deletions dsl/fluent/go.mod

This file was deleted.

3 changes: 3 additions & 0 deletions dsl/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/quasilyte/go-ruleguard/dsl

go 1.15
2 changes: 1 addition & 1 deletion dsl/fluent/internal.go → dsl/internal.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fluent
package dsl

var boolResult bool
var intResult int
Expand Down
Loading

0 comments on commit 8bb37f2

Please sign in to comment.