# Error Object

When we print error messages, we want to include as much useful information as possible, while also not giving anything that would just be noise. This requires at a `LogError` call to have this information available.

In the past, there was a lot of parameters passed around functions only for the sake of being able to print it out in the `LogError` message.  The `ErrorObject` was created as a way to improve this. With a easier way to pass information, it is less error prone to both forgot the information and/or provide bad information

## Single ErrorObject

The `chassis.cpp` holds the single `ErrorObject` reference which is passed to all `PreCallValidate` calls.

## Location

It is very important to know "where" in a function call the error occurred, this is where the `Location` object comes in.

inside `chassis.cpp` we generate the starting `Location` for `ErrorObject`. From here inside each function, we can append what we need

```cpp
// example

bool PreCallValidateQueueBindSparse(/*..*/ ErrorObject &error_obj) const {
    // automatically has Func::vkQueueBindSparse
    // note: error_obj.location is used if just want to print the function
    LogError("VUID-A", error_obj.location);

    for (uint32_t i = 0; i < bindInfoCount; ++i) {
        const Location loc = error_obj.location.dot(Field::pBindInfo, i);
        LogError("VUID-B", loc); // i == 3

        for (uint32_t j = 0; j < bufferBindCount; ++j) {
            const Location buffer_loc = loc.dot(Field::pBufferBinds, j);
            LogError("VUID-C", buffer_loc); // j == 2
        }

        if (pNext == VkTimelineSemaphoreSubmitInfo) {
            // use pNext() instead of dot() to print out it was part of a pNext chain
            const Location pnext_loc = loc.pNext(Struct::VkTimelineSemaphoreSubmitInfo, Field::waitSemaphoreValueCount);
            LogError("VUID-D", pnext_loc);
        }
    }
}
```

will produce the following location in the error message

```
[VUID-A] vkQueueBindSparse():
[VUID-B] vkQueueBindSparse(): pBindInfo[3]
[VUID-C] vkQueueBindSparse(): pBindInfo[3].pBufferBinds[2]
[VUID-D] vkQueueBindSparse(): pBindInfo[3].pNext<VkTimelineSemaphoreSubmitInfo>.waitSemaphoreValueCount
```

> We generate the `Func`/`Struct`/`Field` for all possible items from the XML

### Using fields in the  error messages

using the `Location::Fields()` you can print the location, minus the function, as a string

```cpp
const Location loc = error_obj.location.dot(Field::pBindInfo, i); // vkQueueBindSparse(): pBindInfo[3]

// prints "pBindInfo[3]"
LogError(/*..*/, "%s". loc.Fields().c_str());
```

### Limitations

When using the `.dot()` operation, it returns a new copy of `Location`. If you chain two `.dot().dot()` you need to be mindful.

The following will produce a `stack-use-after-scope` runtime error

```cpp
const Location layout_loc = loc.dot(Field::attachment).dot(Field::layout);
LogError(/*..*/, layout_loc, "error");
```

The 2 ways around the are:

```cpp
// Create 2nd variable
const Location attachment_loc = loc.dot(Field::attachment)
const Location layout_loc     = attachment_loc.dot(Field::layout);
LogError(/*..*/, layout_loc, "good");
```

or has been found to be more common

```cpp
// Pass an argument
LogError(/*..*/, loc.dot(Field::attachment).dot(Field::layout), "good");
```

### Using stack and making copies

The original design was to not make copies of `Location` and just have it modify the `ErrorObject::Location` but this would require you to remove items after a function call

```cpp
// example
error_obj.location.add(Struct::VkSomething);
ValidateItem(error_obj);
error_obj.location.remove(Struct::VkSomething);
```

which slowly leads to a LOT more code and becomes very error prone to forget to remove `Location` values. Instead the `dot` operator return a new copy of `Location`.

## LogObjectList

// TODO
