go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/errors.go (about)

     1  // Copyright 2018 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package lucicfg
    16  
    17  import (
    18  	"go.starlark.net/starlark"
    19  
    20  	"go.chromium.org/luci/lucicfg/graph"
    21  	"go.chromium.org/luci/starlark/builtins"
    22  )
    23  
    24  // BacktracableError is an error that has a starlark backtrace attached to it.
    25  //
    26  // Implemented by Error here, by starlark.EvalError and graph errors.
    27  type BacktracableError interface {
    28  	error
    29  
    30  	// Backtrace returns a user-friendly error message describing the stack
    31  	// of calls that led to this error, along with the error message itself.
    32  	Backtrace() string
    33  }
    34  
    35  var (
    36  	_ BacktracableError = (*starlark.EvalError)(nil)
    37  	_ BacktracableError = (*builtins.Failure)(nil)
    38  	_ BacktracableError = (*Error)(nil)
    39  	_ BacktracableError = (*graph.NodeRedeclarationError)(nil)
    40  	_ BacktracableError = (*graph.CycleError)(nil)
    41  	_ BacktracableError = (*graph.DanglingEdgeError)(nil)
    42  )
    43  
    44  // Error is a single error message emitted by the config generator.
    45  //
    46  // It holds a stack trace responsible for the error.
    47  type Error struct {
    48  	Msg   string
    49  	Stack *builtins.CapturedStacktrace
    50  }
    51  
    52  // Error is part of 'error' interface.
    53  func (e *Error) Error() string {
    54  	return e.Msg
    55  }
    56  
    57  // Backtrace is part of BacktracableError interface.
    58  func (e *Error) Backtrace() string {
    59  	if e.Stack == nil {
    60  		return e.Msg
    61  	}
    62  	return e.Stack.String() + "Error: " + e.Msg
    63  }
    64  
    65  func init() {
    66  	// emit_error(msg, stack) adds the given error to the list of errors in the
    67  	// state, to be returned at the end of generation (in the default mode), or
    68  	// immediately aborts the execution if 'fail_on_errors' has been called
    69  	// before.
    70  	declNative("emit_error", func(call nativeCall) (starlark.Value, error) {
    71  		var msg starlark.String
    72  		var stack *builtins.CapturedStacktrace
    73  		if err := call.unpack(2, &msg, &stack); err != nil {
    74  			return nil, err
    75  		}
    76  		err := &Error{
    77  			Msg:   msg.GoString(),
    78  			Stack: stack,
    79  		}
    80  		if call.State.failOnErrs {
    81  			return nil, err
    82  		}
    83  		call.State.err(err)
    84  		return starlark.None, nil
    85  	})
    86  
    87  	// fail_on_errors() enables a mode in which emit_error(...) immediately aborts
    88  	// the execution instead of just recording the error. This is useful in
    89  	// tests to check what errors are emitted (using assert.fails). A call to
    90  	// clear_state() resets this mode.
    91  	declNative("fail_on_errors", func(call nativeCall) (starlark.Value, error) {
    92  		if err := call.unpack(0); err != nil {
    93  			return nil, err
    94  		}
    95  		call.State.failOnErrs = true
    96  		return starlark.None, nil
    97  	})
    98  }