compiler/ast/errorhandling

  Source   Edit

This module contains support code for error handling via an nkError node kind.

An nkError node is used where an error occurs within the AST. Wrap the ast node with newError and typically take over the position of the wrapped node in whatever AST it was in.

Internally an nkError node stores these children:

  • 0 - wraps an AST node that has the error
  • 1 - nkIntLit with a value corresponding to ord(ErrorKind)
  • 2 - compiler instantiation location info
  • 3 - first argument position, assuming one was provided
  • _ - zero or more nodes with data for the error message

The rest of the compiler should watch for nkErrors and mostly no-op or wrap further errors as needed.

Future Considerations/Improvements:

  • accomodate for compiler related information like site of node creation to make it easier to debug the compiler itself, so we know where a node was created
  • rework internals to store actual error information in a lookup data structure on the side instead of directly in the node

Procs

func compilerInstInfo(e: PNode): InstantiationInfo {.inline, ...raises: [],
    tags: [].}
return where the error was instantiated in the compiler   Source   Edit
func errorKind(e: PNode): AstDiagKind {.inline, ...raises: [], tags: [].}
property to retrieve the error kind   Source   Edit
proc errorSubNode(n: PNode): PNode {....raises: [], tags: [].}
find the first error node, or nil, under n using a depth first traversal   Source   Edit
proc newError(conf: ConfigRef; wrongNode: PNode; diag: PAstDiag;
              inst: InstantiationInfo; posInfo: TLineInfo = unknownLineInfo): PNode {.
    ...raises: [], tags: [].}
Create nkError node with given diag, and sets the wrongNode on diag   Source   Edit
proc wrapIfErrorInSubTree(conf: ConfigRef; wrongNodeContainer: PNode): PNode {.
    ...deprecated: "transition proc, remove usage as soon as possible", raises: [],
    tags: [].}
Deprecated: transition proc, remove usage as soon as possible
wrongNodeContainer doesn't directly have an error but one may exist further down the tree. If an error does exist it will wrap wrongNodeContainer in an nkError node but no message will be reported for this wrapping. If there is no error, the wrongNodeContainer will be returned as is.   Source   Edit

Iterators

iterator anyErrorsWalk(config: ConfigRef; n: PNode): PNode {.
    ...deprecated: "only use for debugging purposes", raises: [], tags: [].}
Deprecated: only use for debugging purposes
for debugging, walk n yielding any errors found   Source   Edit
iterator ifErrorWalkErrors(config: ConfigRef; n: PNode): PNode {....raises: [],
    tags: [].}
traverse the ast like walkErrors, but will only do so if n is not nil or an error -- useful when guarding isn't beneficial.   Source   Edit
iterator walkErrors(config: ConfigRef; n: PNode): PNode {....raises: [], tags: [].}
traverses the ast and yields errors from innermost to outermost. this is a linear traversal and two, or more, sibling errors will result in only the first error (per PNode.kids) being yielded.   Source   Edit

Templates

template newError(conf: ConfigRef; wrongNode: PNode; diag: PAstDiag;
                  posInfo: TLineInfo = unknownLineInfo): untyped
  Source   Edit
template wrapError(conf: ConfigRef; wrongNodeContainer: PNode): PNode
wrongNodeContainer doesn't directly have an error but one exists further down the tree, this is used to wrap the wrongNodeContainer in an nkError node but no message will be reported for it.   Source   Edit