Skip to content

Commit

Permalink
feat(go): implement Compiler.Errors that returns the compilation er…
Browse files Browse the repository at this point in the history
…rors.
  • Loading branch information
plusvic committed Aug 27, 2024
1 parent c7e32fe commit ed818ac
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 6 deletions.
35 changes: 33 additions & 2 deletions capi/include/yara_x.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,39 @@ enum YRX_RESULT yrx_compiler_define_global_float(struct YRX_COMPILER *compiler,
//
// In the address indicated by the `buf` pointer, the function will copy a
// `YRX_BUFFER*` pointer. The `YRX_BUFFER` structure represents a buffer
// that contains the JSON representation of the compilation errors. The
// [`YRX_BUFFER`] must be destroyed with [`yrx_buffer_destroy`].
// that contains the JSON representation of the compilation errors.
//
// The JSON consists on an array of objects, each object representing a
// compilation error. The object has the following fields:
//
// * type: A string that describes the type of error.
// * code: Error code (e.g: "E009").
// * title: Error title (e.g: ""unknown identifier `foo`").
// * labels: Array of labels.
// * text: The full text of the error report, as shown by the command-line tool.
//
// Here is an example:
//
// ```json
// [
// {
// "type": "UnknownIdentifier",
// "code": "E009",
// "title": "unknown identifier `foo`",
// "labels": [
// {
// "level": "error",
// "code_origin": null,
// "span": {"start":25,"end":28},
// "text": "this identifier has not been declared"
// }
// ],
// "text": "... <full report here> ..."
// }
// ]
// ```
//
// The [`YRX_BUFFER`] must be destroyed with [`yrx_buffer_destroy`].
enum YRX_RESULT yrx_compiler_errors_json(struct YRX_COMPILER *compiler,
struct YRX_BUFFER **buf);

Expand Down
35 changes: 33 additions & 2 deletions capi/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,39 @@ pub unsafe extern "C" fn yrx_compiler_define_global_float(
///
/// In the address indicated by the `buf` pointer, the function will copy a
/// `YRX_BUFFER*` pointer. The `YRX_BUFFER` structure represents a buffer
/// that contains the JSON representation of the compilation errors. The
/// [`YRX_BUFFER`] must be destroyed with [`yrx_buffer_destroy`].
/// that contains the JSON representation of the compilation errors.
///
/// The JSON consists on an array of objects, each object representing a
/// compilation error. The object has the following fields:
///
/// * type: A string that describes the type of error.
/// * code: Error code (e.g: "E009").
/// * title: Error title (e.g: ""unknown identifier `foo`").
/// * labels: Array of labels.
/// * text: The full text of the error report, as shown by the command-line tool.
///
/// Here is an example:
///
/// ```json
/// [
/// {
/// "type": "UnknownIdentifier",
/// "code": "E009",
/// "title": "unknown identifier `foo`",
/// "labels": [
/// {
/// "level": "error",
/// "code_origin": null,
/// "span": {"start":25,"end":28},
/// "text": "this identifier has not been declared"
/// }
/// ],
/// "text": "... <full report here> ..."
/// }
/// ]
/// ```
///
/// The [`YRX_BUFFER`] must be destroyed with [`yrx_buffer_destroy`].
#[no_mangle]
pub unsafe extern "C" fn yrx_compiler_errors_json(
compiler: *mut YRX_COMPILER,
Expand Down
59 changes: 59 additions & 0 deletions go/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package yara_x
// #include <yara_x.h>
import "C"
import (
"encoding/json"
"errors"
"fmt"
"runtime"
Expand Down Expand Up @@ -80,6 +81,41 @@ func ErrorOnSlowPattern(yes bool) CompileOption {
}
}

// CompileError represents each of the errors returned by [Compiler.Errors].
type CompileError struct {
// Error code (e.g: "E001").
Code string
// Error title (e.g: "unknown identifier `foo`").
Title string
// Each of the labels in the error report.
Labels []Label
// The error's full report, as shown by the command-line tool.
Text string
}

// Label represents a label in a [CompileError].
type Label struct {
// Label's level (e.g: "error", "warning", "info", "note", "help").
Level string
CodeOrigin string
// The code span covered by the label.
Span Span
// Text associated to the label.
Text string
}

// Span represents the starting and ending point of some piece of source
// code.
type Span struct {
Start int
End int
}

// Error returns the error's full report.
func (c CompileError) Error() string {
return c.Text
}

// Compiler represent a YARA compiler.
type Compiler struct {
cCompiler *C.YRX_COMPILER
Expand Down Expand Up @@ -254,6 +290,29 @@ func (c *Compiler) DefineGlobal(ident string, value interface{}) error {
return nil
}


// Errors that occurred during the compilation, across multiple calls to
// [Compiler.AddSource].
func (c *Compiler) Errors() []CompileError {
var buf *C.YRX_BUFFER
if C.yrx_compiler_errors_json(c.cCompiler, &buf) != C.SUCCESS {
panic("yrx_compiler_errors_json failed")
}

defer C.yrx_buffer_destroy(buf)
runtime.KeepAlive(c)

jsonErrors := C.GoBytes(unsafe.Pointer(buf.data), C.int(buf.length))

var result []CompileError

if err := json.Unmarshal(jsonErrors, &result); err != nil {
panic(err)
}

return result
}

// Build creates a [Rules] object containing a compiled version of all the
// YARA rules previously added to the compiler.
//
Expand Down
36 changes: 34 additions & 2 deletions go/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,42 @@ func TestVariables(t *testing.T) {

func TestError(t *testing.T) {
_, err := Compile("rule test { condition: foo }")
assert.EqualError(t, err, `error[E009]: unknown identifier `+"`foo`"+`
expected := `error[E009]: unknown identifier `+"`foo`"+`
--> line:1:24
|
1 | rule test { condition: foo }
| ^^^ this identifier has not been declared
|`)
|`
assert.EqualError(t, err, expected)
}


func TestErrors(t *testing.T) {
c, err := NewCompiler()
assert.NoError(t, err)

c.AddSource("rule test_1 { condition: true }")
assert.Equal(t, []CompileError{}, c.Errors())

c.AddSource("rule test_2 { condition: foo }")
assert.Equal(t, []CompileError{
{
Code: "E009",
Title: "unknown identifier `foo`",
Labels: []Label{
{
Level: "error",
CodeOrigin: "",
Span: Span { Start: 25, End: 28 },
Text: "this identifier has not been declared",
},
},
Text: `error[E009]: unknown identifier `+"`foo`"+`
--> line:1:26
|
1 | rule test_2 { condition: foo }
| ^^^ this identifier has not been declared
|`,
},
}, c.Errors())
}

0 comments on commit ed818ac

Please sign in to comment.