Semgrep: Match only function signatures or class header (in Go and Typescript)

Created on 30 Sep 2020  路  5Comments  路  Source: returntocorp/semgrep

Is your feature request related to a problem? Please describe.

I want to be able to find all functions that fulfil certain pattern in a fast manner and only it (without getting the function body). Normally, I would use grep for that (and parse the output) but that doesn't work well if a pattern (here: function signature) spans across multiple lines.

So, from a code like this (saved as a.go):

func (o *Type) Method(a *Args,
         y *Reply) error {
    longcode()
    longcode()
    longcode()
    longcode()
    longcode()
    return 1
}

I want to get the following result:

func (o *Type) Method(a *Args, y *Reply) error

Now, semgrep does almost allow for that now. This can be done with the following CLI command:

$ semgrep --lang go --pattern 'func ($O *Type) Method($A *$ARGTYPE, $R $REPLYTYPE) $RETTYPE { ... }'
a.go
1:func (o *Type) Method(a *Args,
2:         y *Reply) error {
3:    longcode()
4:    longcode()
5:    longcode()
6:    longcode()
7:    longcode()
8:    return 1
9:}

However, there are three issues with that:
1) It displays the function body, which I am not interested in
2) If I wanted to run this command line with different patterns, most likely the same parsing occurs many times instead of just once and being cached (I haven't confirmed it but I think semgrep does not cache anything?), which might make this slower than it could be?
3) If I use the semgrep --lang go --pattern 'func ($O *Type) Method($A *$ARGTYPE, $R $REPLYTYPE) $RETTYPE' pattern (note the missing { ... }), nothing is found and no error is there. Is it expected?

This can be worked around by creating a rule with the message key to specify the output and then grepping out the results, as shown below. However, this is not very convenient.

$ cat rule.yml
rules:
- id: some-rule
  pattern: func ($O *Type) Method($A *$ARGTYPE, $R $REPLYTYPE) $RETTYPE { ... }
  message: func ($O *Type) Method($A *$ARGTYPE, $R $REPLYTYPE) $RETTYPE
  severity: WARNING
  languages: [go]
$ semgrep --config rule.yml | grep severity
running 1 rules...
severity:warning rule:some-rule: func (o *Type) Method(a *Args, y *Reply) error

Describe the solution you'd like

I'd like to either be able to match just the function header/signature or to get an output of just it. With no function body.

Ideas for solving this

This has been discussed on the slack and there are two ideas for solving this:

  1. There has been suggestion of adding a display-pattern key which would allow to re-define what is displayed as the matched code. If that is added, it should probably have some special metavariables for displaying the ... matches from the pattern[s].
  1. Collapse the ... from the pattern and display it as shown below. I think this is a great feature that needs to be added anyway, but it doesn't solve the described issue.
  class ThunderBoltClass {
    [... 7 matched lines ...]
  }
enhancement

Most helpful comment

That way you can omit the trailing {...} in the pattern and we will not display the body

All 5 comments

I think i ll add the ability to just have pattern for function signature (or a clas header)

That way you can omit the trailing {...} in the pattern and we will not display the body

Just a bit more input on this. Consider this rule where I want to report any classes where the ngOnDestroy function exists but does not contain this.isDestroyed.next(). The output is voluminous--it prints out the entire class. Both of the two options that @disconnect3d mentions above would fix this, but I am not sure how the third option you mention @aryx , would do that.

- id: ngOnDestroy-missing-next
    patterns:
    - pattern: |
        class $CLASS {
          ...
          ngOnDestroy() {
            ...
            this.isDestroyed.complete(...);
            ...
          }
        ...
        }
    - pattern-not: |
        class $CLASS {
          ...
          ngOnDestroy() {
            this.isDestroyed.next(...);
            this.isDestroyed.complete(...);
          }
          ...
        }
    message: ngOnDestroy missing this.isDestroyed.next()
    languages: [ts]
    severity: ERROR

With the ability to omit trailing {...} you could potentially use pattern-in and pattern-not-in:

- id: ngOnDestroy-missing-next
    patterns:
    - pattern-in: |
        class $CLASS {
          ...
          ngOnDestroy() {
            ...
            this.isDestroyed.complete(...);
            ...
          }
        ...
        }
    - pattern-not-in: |
        class $CLASS {
          ...
          ngOnDestroy() {
            this.isDestroyed.next(...);
            this.isDestroyed.complete(...);
          }
          ...
        }
    - pattern: class $CLASS
    message: ngOnDestroy missing this.isDestroyed.next()
    languages: [ts]
    severity: ERROR

Should work now for function signature and class header in Go and Javascript/Typescript

Was this page helpful?
0 / 5 - 0 ratings