forked from quasilyte/go-ruleguard
/
dsl.go
338 lines (281 loc) · 12.8 KB
/
dsl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
package dsl
// Matcher is a main API group-level entry point.
// It's used to define and configure the group rules.
// It also represents a map of all rule-local variables.
type Matcher map[string]Var
// Import loads given package path into a rule group imports table.
//
// That table is used during the rules compilation.
//
// The table has the following effect on the rules:
// * For type expressions, it's used to resolve the
// full package paths of qualified types, like `foo.Bar`.
// If Import(`a/b/foo`) is called, `foo.Bar` will match
// `a/b/foo.Bar` type during the pattern execution.
func (m Matcher) Import(pkgPath string) {}
// Match specifies a set of patterns that match a rule being defined.
// Pattern matching succeeds if at least 1 pattern matches.
//
// If none of the given patterns matched, rule execution stops.
func (m Matcher) Match(pattern string, alternatives ...string) Matcher {
return m
}
// MatchComment is like Match, but handles only comments and uses regexp patterns.
//
// Multi-line /**/ comments are passed as a single string.
// Single-line // comments are passed line-by-line.
//
// Hint: if you want to match a plain text and don't want to do meta char escaping,
// prepend `\Q` to your pattern. `\Qf(x)` will match `f(x)` as a plain text
// and there is no need to escape the `(` and `)` chars.
//
// Named regexp capture groups can be accessed using the usual indexing notation.
//
// Given this pattern:
//
// `(?P<first>\d+)\.(\d+).(?P<second>\d+)`
//
// And this input comment: `// 14.6.600`
//
// We'll get these submatches:
//
// m["$$"] => `14.6.600`
// m["first"] => `14`
// m["second"] => `600`
//
// All usual filters can be applied:
//
// Where(!m["first"].Text.Matches(`foo`))
//
// You can use this to reject some matches (allow-list behavior).
func (m Matcher) MatchComment(pattern string, alternatives ...string) Matcher {
return m
}
// Where applies additional constraint to a match.
// If a given cond is not satisfied, a match is rejected and
// rule execution stops.
func (m Matcher) Where(cond bool) Matcher {
return m
}
// Report prints a message if associated rule match is successful.
//
// A message is a string that can contain interpolated expressions.
// For every matched variable it's possible to interpolate
// their printed representation into the message text with $<name>.
// An entire match can be addressed with $$.
func (m Matcher) Report(message string) Matcher {
return m
}
// Suggest assigns a quickfix suggestion for the matched code.
func (m Matcher) Suggest(suggestion string) Matcher {
return m
}
// At binds the reported node to a named submatch.
// If no explicit location is given, the outermost node ($$) is used.
func (m Matcher) At(v Var) Matcher {
return m
}
// File returns the current file context.
func (m Matcher) File() File { return File{} }
// GoVersion returns the analyzer associated target Go language version.
func (m Matcher) GoVersion() GoVersion { return GoVersion{} }
// Deadcode reports whether this match is contained inside a dead code path.
func (m Matcher) Deadcode() bool { return boolResult }
// Var is a pattern variable that describes a named submatch.
type Var struct {
// Pure reports whether expr matched by var is side-effect-free.
Pure bool
// Const reports whether expr matched by var is a constant value.
Const bool
// ConstSlice reports whether expr matched by var is a slice literal
// consisting of contant elements.
//
// We need a separate Const-like predicate here because Go doesn't
// treat slices of const elements as constants, so including
// them in Const would be incorrect.
// Use `m["x"].Const || m["x"].ConstSlice` when you need
// to have extended definition of "constant value".
//
// Some examples:
// []byte("foo") -- constant byte slice
// []byte{'f', 'o', 'o'} -- same constant byte slice
// []int{1, 2} -- constant int slice
ConstSlice bool
// Value is a compile-time computable value of the expression.
Value ExprValue
// Addressable reports whether the corresponding expression is addressable.
// See https://golang.org/ref/spec#Address_operators.
Addressable bool
// Type is a type of a matched expr.
//
// For function call expressions, a type is a function result type,
// but for a function expression itself it's a *types.Signature.
//
// Suppose we have a `a.b()` expression:
// `$x()` m["x"].Type is `a.b` function type
// `$x` m["x"].Type is `a.b()` function call result type
Type ExprType
// Object is an associated "go/types" Object.
Object TypesObject
// Text is a captured node text as in the source code.
Text MatchedText
// Node is a captured AST node.
Node MatchedNode
// Line is a source code line number that contains this match.
// If this match is multi-line, this is the first line number.
Line int
}
// Filter applies a custom predicate function on a submatch.
//
// The callback function should use VarFilterContext to access the
// information that is usually accessed through Var.
// For example, `VarFilterContext.Type` is mapped to `Var.Type`.
func (Var) Filter(pred func(*VarFilterContext) bool) bool { return boolResult }
// Contains runs a sub-search from a given pattern using the captured
// vars from the original pattern match.
//
// For example, given the Match(`$lhs = append($lhs, $x)`) pattern,
// we can do m["lhs"].Contains(`$x`) and learn whether $lhs contains
// $x as its sub-expression.
//
// Experimental: this function is not part of the stable API.
func (Var) Contains(pattern string) bool { return boolResult }
// MatchedNode represents an AST node associated with a named submatch.
type MatchedNode struct{}
// Is reports whether a matched node AST type is compatible with the specified type.
// A valid argument is a ast.Node implementing type name from the "go/ast" package.
// Examples: "BasicLit", "Expr", "Stmt", "Ident", "ParenExpr".
// See https://golang.org/pkg/go/ast/.
func (MatchedNode) Is(typ string) bool { return boolResult }
// Parent returns a matched node parent.
func (MatchedNode) Parent() Node { return Node{} }
// Node represents an AST node somewhere inside a match.
// Unlike MatchedNode, it doesn't have to be associated with a named submatch.
type Node struct{}
// Is reports whether a node AST type is compatible with the specified type.
// See `MatchedNode.Is` for the full reference.
func (Node) Is(typ string) bool { return boolResult }
// ExprValue describes a compile-time computable value of a matched expr.
type ExprValue struct{}
// Int returns compile-time computable int value of the expression.
// If value can't be computed, condition will fail.
func (ExprValue) Int() int { return intResult }
// TypesObject is a types.Object mapping.
type TypesObject struct{}
// Is reports whether an associated types.Object is compatible with the specified type.
// A valid argument is a types.Object type name from the "go/types" package.
// Examples: "Func", "Var", "Const", "TypeName", "Label", "PkgName", "Builtin", "Nil"
// See https://golang.org/pkg/go/types/.
func (TypesObject) Is(typ string) bool { return boolResult }
// IsGlobal reports whether an associated types.Object is defined in global scope.
func (TypesObject) IsGlobal() bool { return boolResult }
// ExprType describes a type of a matcher expr.
type ExprType struct {
// Size represents expression type size in bytes.
Size int
}
// IdenticalTo applies types.Identical(this, v.Type) operation.
// See https://golang.org/pkg/go/types/#Identical function documentation.
//
// Experimental: this function is not part of the stable API.
func (ExprType) IdenticalTo(v Var) bool { return boolResult }
// Underlying returns expression type underlying type.
// See https://golang.org/pkg/go/types/#Type Underlying() method documentation.
// Read https://golang.org/ref/spec#Types section to learn more about underlying types.
func (ExprType) Underlying() ExprType { return underlyingType }
// AssignableTo reports whether a type is assign-compatible with a given type.
// See https://golang.org/pkg/go/types/#AssignableTo.
func (ExprType) AssignableTo(typ string) bool { return boolResult }
// ConvertibleTo reports whether a type is conversible to a given type.
// See https://golang.org/pkg/go/types/#ConvertibleTo.
func (ExprType) ConvertibleTo(typ string) bool { return boolResult }
// Implements reports whether a type implements a given interface.
// See https://golang.org/pkg/go/types/#Implements.
func (ExprType) Implements(typ typeName) bool { return boolResult }
// HasMethod reports whether a type has a given method.
// Unlike Implements(), it will work for both value and pointer types.
//
// fn argument is a function signature, like `WriteString(string) (int, error)`.
// It can also be in form of a method reference for importable types: `io.StringWriter.WriteString`.
//
// To avoid confusion with Implements() method, here is a hint when to use which:
//
// - To check if it's possible to call F on x, use HasMethod(F)
// - To check if x can be passed as I interface, use Implements(I)
func (ExprType) HasMethod(fn string) bool { return boolResult }
// Is reports whether a type is identical to a given type.
func (ExprType) Is(typ string) bool { return boolResult }
// HasPointers reports whether a type contains at least one pointer.
//
// We try to be as close to the Go sense of pointer-free objects as possible,
// therefore string type is not considered to be a pointer-free type.
//
// This function may return "true" for some complicated cases as a
// conservative result. It never returns "false" for a type that
// actually contains a pointer.
//
// So this function is mostly useful for !HasPointers() form.
func (ExprType) HasPointers() bool { return boolResult }
// OfKind reports whether a matched expr type is compatible with the specified kind.
//
// Only a few "kinds" are recognized, the list is provided below.
//
// "integer" -- typ is *types.Basic, where typ.Info()&types.Integer != 0
// "unsigned" -- typ is *types.Basic, where typ.Info()&types.Unsigned != 0
// "float" -- typ is *types.Basic, where typ.Info()&types.Float != 0
// "complex" -- typ is *types.Basic, where typ.Info()&types.Complex != 0
// "untyped" -- typ is *types.Basic, where typ.Info()&types.Untyped != 0
// "numeric" -- typ is *types.Basic, where typ.Info()&types.Numeric != 0
// "signed" -- identical to `OfKind("integer") && !OfKind("unsigned")`
// "int" -- int, int8, int16, int32, int64
// "uint" -- uint, uint8, uint16, uint32, uint64
//
// Note: "int" will include "rune" as well, as it's an alias.
// In the same manner, "uint" includes the "byte" type.
//
// Using OfKind("unsigned") is more efficient (and concise) than using a set
// of or-conditions with Is("uint8"), Is("uint16") and so on.
func (ExprType) OfKind(kind string) bool { return boolResult }
// MatchedText represents a source text associated with a matched node.
type MatchedText string
// Matches reports whether the text matches the given regexp pattern.
func (MatchedText) Matches(pattern string) bool { return boolResult }
// String represents an arbitrary string-typed data.
type String string
// Matches reports whether a string matches the given regexp pattern.
func (String) Matches(pattern string) bool { return boolResult }
// File represents the current Go source file.
type File struct {
// Name is a file base name.
Name String
// PkgPath is a file package path.
// Examples: "io/ioutil", "strings", "github.com/quasilyte/go-ruleguard/dsl".
PkgPath String
}
// Imports reports whether the current file imports the given path.
func (File) Imports(path string) bool { return boolResult }
// GoVersion is an analysis target go language version.
// It can be compared to Go versions like "1.10", "1.16" using
// the associated methods.
type GoVersion struct{}
// Eq asserts that target Go version is equal to (==) specified version.
func (GoVersion) Eq(version string) bool { return boolResult }
// GreaterEqThan asserts that target Go version is greater or equal than (>=) specified version.
func (GoVersion) GreaterEqThan(version string) bool { return boolResult }
// GreaterThan asserts that target Go version is greater than (>) specified version.
func (GoVersion) GreaterThan(version string) bool { return boolResult }
// LessThan asserts that target Go version is less than (<) specified version.
func (GoVersion) LessThan(version string) bool { return boolResult }
// LessEqThan asserts that target Go version is less or equal than (<=) specified version.
func (GoVersion) LessEqThan(version string) bool { return boolResult }
// typeName is a helper type used to document function params better.
//
// A type name can be:
// - builtin type name: `error`, `string`, etc.
// - qualified name from a standard library: `io.Reader`, etc.
// - fully-qualified type name, like `github.com/username/pkgname.TypeName`
//
// typeName is also affected by a local import table, which can override
// how qualified names are interpreted.
// See `Matcher.Import` for more info.
type typeName = string