nim-lang / RFCs

A repository for your Nim proposals.
135 stars 26 forks source link

Structured compilation errors #324

Closed haxscramper closed 3 years ago

haxscramper commented 3 years ago

Currently only file(line, column) message start can be reliably retrieved from compilation error - everything else is constructed based on string interpolation.

Providing json output for compilation errors would make it easier to build additional heuristics on top of compilation errors (for example hooking up type mismatch to documentation search to provide import suggestions), simplify IDE integration, and mitigate pointless debate for changing error formatting - if someone wants to have all-colorful arrows everywhere, they will have access to all information needed.

Introduce error index

Having centralized listing of all compilation errors would be helpful for beginners, makes it easier necessary to explain particular details for errors in the manual/tutorial (or worse - leaving users to figure out cause and solution themselves).

Single source of information for all compilation (and some runtime) errors would make it easier to spot bad errors, show and explain possible fixes.

related:

Introduce internal type for errors instead of using strings

Having intermediate representation for compilation errors will make particular output format most a rendering issue - converting to json, or pretty-printing compilation errors.

Additionall this would make writing compiler-based tools easier, as structuredErrorHook would still have access to all necessary information instead of opperating only on info: TLineInfo; msg: string; level: Severity.

haxscramper commented 3 years ago

Split into separate RFC from https://github.com/nim-lang/RFCs/issues/322

Araq commented 3 years ago

I know I know, nobody understands the "semi structured" aspect of XML/HTML but it is what it makes it useful for layout -- JSON isn't suitable.

shirleyquirk commented 3 years ago

nkError now exists, providing a starting point. how should nkError best be structured to support this RFC?

rust's error index has hundreds of entries, a single enum for errorKind might get unmaintainable/unreadable very quickly. perhaps errorKind should be subcategorized.

How can nkError avoid becoming an xkcd standard when it doesn't replace other forms of error handling (nil PNodes, localError, globalError, nkEmpty, tyError...) but just adds one more?

here's a dump of every proc that raises a localError, and the line the localError is on, organized by file. it's just thrown together with grep and awk, so its got some holes, but it's the beginnings of an overview.

the procs returning a PNode (sem* mostly) are clear candidates for nkError

several others are "fooCheck" or "expectFoo" sorts of things that look like they could be refactored

it's less clear to me how the category of procs that return a PSym,PType,or deal with the PNode within a PType could work within a more structured error-handling system. use a similar scheme for tyError? should there be an skError?

Araq commented 3 years ago

Well the way I structured it should work well.

rust's error index has hundreds of entries, a single enum for errorKind might get unmaintainable/unreadable very quickly. perhaps errorKind should be subcategorized.

I'm not worried. There is nothing unreadable about an enum with many cases or a lookup table with many cases or a case statement with many cases -- the alternatives are usually much worse where there is some arbitrary splitup just because somebody read some book about how method bodies shouldn't have more than 3 lines.

How can nkError avoid becoming an xkcd standard when it doesn't replace other forms of error handling (nil PNodes, localError, globalError, nkEmpty, tyError...) but just adds one more?

Well ... for a start the backend ideally shouldn't report errors but in practice it needs to, and the backend doesn't produce a new AST so it's stuck with localError. That's ok. nkError should replace returning nil PNodes and nkEmpty.

haxscramper commented 3 years ago

how should nkError best be structured to support this RFC?

I'm not exactly sure about concrete implementation details (to be discussed (in https://github.com/nim-lang/Nim/pull/16915/files there is no additional information about error cause in TNode)), but my original idea was to add a branch to TNodeKind, and put some ErrorDescription field in it, and store all relevant information there. I'm mostly concerned with supporting diagnostics mentioned in https://github.com/nim-lang/RFCs/issues/323 [1] and https://github.com/nim-lang/RFCs/issues/325

I'm still not sure why json is not considered suitable, but that is a presentation issue, so I guess anything works. I'm fine with XML, but I know some people would still prefer json over it any time. I don't think it would create any major implementation constraints, although having ErrorDescription as a separate type would make it easier to write couple of default error formatters (and make it possible for anyone else to use compiler API and output to required format (or use error description as-is))

proc printPrettyError(err: ErrorDescription)
proc printXmlError(err: ErrorDescription)
[1]_ Despite "accepted RFC" tag I think this has to be discussed more, because it is not just about "rewording some messages", it is about reconsidering how we retrieve and present information about malformed code to the user. As an example of what I'm talking about: > I've looked into current implementation of semcheck/error handling and I think general approach of storing original AST and running semcheck on it again should work as a starting position. This would require some minor modifications to things like trackCase - right now it just sets appropriate flags for PEffects, and in the end mismatches are just reported using on toplevel with any details. > I think that running same semcheck steps again with additional diagnostics enabled would allow creating good enough error messages for effects/exceptions etc. > But in 'top-down' mode, where effect annotations are not inferred based on body, but instead each statement (I'm not sure if side effect analysis is cached though) is compared with expected list of annotations, and if any differences found - they will be reported. >This is for side effect analysis. For type mismatches storing original list of candidates and original expression would also work for the most cases I guess. I.e. current implementation as I see it is **fundamentally unable** to provide good error messages. Just not possible. Maybe I misunderstand something (I sure hope I'm an idiot and missed some obvious part and it is not so dramatic as I paint it.)

rust's error index has hundreds of entries, a single enum for errorKind might get unmaintainable/unreadable very quickly. perhaps errorKind should be subcategorized.

If you are talking purely about implementation (i.e. how error kind should be represented in compiler) I don't think it makes much difference implementation-wise to have

type
  ErrorKind* = enum ## expand as you need.
    RawTypeMismatchError
    ExpressionCannotBeCalled
    CustomError
    WrongNumberOfArguments
    AmbiguousCall

vs

type

  SyntaxError = object
    case kind*: SyntaxErrorKind
      # ..

  ErrorCategory = enum
    ErrSyntax
    ErrTypes
    ErrSomethingElse

  ErrorDescription* = enum ## expand as you need.
    case category*: ErrorCategory
      of ErrSyntax:
        syntax: SyntaxError

      of ... # (you get the idea)

I would even say I'm in favor of second approach, because there really is a lot of different scenarios where error can be created, but again - it does not make much of a difference, and it might be more annoying to work with.

How can nkError avoid becoming an xkcd standard when it doesn't replace other forms of error handling (nil PNodes, localError, globalError, nkEmpty, tyError...) but just adds one more?

From what I understand @Araq wanted to largely replace all currently implemented error handling with nkError (where possible). In addition to that, I think using nil PNodes and/or nkEmpty for error signaling is not the best idea, as they are fundamentally incapable of storing any information about error cause.

here's a dump of every proc that raises a localError, and the line the localError is on, organized by file. it's just thrown together with grep and awk, so its got some holes, but it's the beginnings of an overview.

After doing similar filtering (using grep -R localError | grep -Eohe '".*?"' | grep -v "internal error:" | uniq) I've found ~300 different error messages that are not internal (i.e. candidates for addition to error enum), so it should give a very rough estimate for the enum size.

```txt "illegal capture '$1' because '$2' has the calling convention: <$3>" "could not find up reference for closure iter" "ignored invalid for loop" "system needs: 'true'" "no object or tuple type" "'goto' target must be a literal value" "range notation not available for computed goto" "no case statement found for computed goto" "range notation not available for computed goto" "unexpected construct in the new-styled concept " "incorrect object construction syntax; use a space after the colon" "incorrect object construction syntax" "possible values " "branch initialization " "object constructor needs an object type" "'$1' is not a parameter of '$2'" "parameter '$1' is not a pointer to a partial object" "type 'var var' is not allowed" "illegal recursion in type '" & typeToString(t) & "'" "cannot instantiate: '" & typeToString(t) & "'" "; maybe use 'unsafeAddr'" "typeof: cannot evaluate 'mode' parameter at compile-time" "could not resolve: " "expected generic type, got: type $2 of kind $1" "unknown trait: " "'of' takes object types" "'$1' cannot be of this subtype" "'of' takes 2 arguments" " 'in' expected" "'old' takes a parameter name" " does not belong to " "cannot find plugin " "finalizer must be a direct reference to a proc" "expression cannot be isolated: " "system module needs: " "'" & $result[0] & "' lacks a dispatcher" "method is not a base" "invalid declaration order; cannot attach '" "recursive dependency: '$1'" "invalid usage of counter after increment" "cannot prove ($#)..($#) disjoint from ($#)..($#)" "invalid control flow for 'parallel'" "invalid context for 'spawn'" "'parallel' section without 'spawn'" "cannot determine how to produce code for string literal" "runnableExamples must appear before the first non-comment statement" "expression has no type: " "cannot use symbol of kind '$1' as a '$2'" "expression has no type: " "illegal discard proc, did you mean: " & $n[0] & "()" "'continue' cannot have a label" "Mix of imported and native exception types is not allowed in one except branch" "Only one finally is allowed after all other branches" "Only one general except clause is allowed after more specific exceptions" "using" "'using' section must have a type" "'using' sections cannot contain assignments" "implicit object field construction " "nkDotNode requires 2 children" "inconsistent typing for reintroduced symbol '" "tuple" "iterator within for loop context expected" "not all cases are covered; missing: $1" "not all cases are covered" "raised object of type $1 does not inherit from Exception" "unknown package name: " " is not a type that can be forwarded" "only top level types in a package can be 'package'" "cannot complete type '" & s.name.s & "' twice; " "{.exportc.} not allowed for type aliases" "only a 'distinct' type can borrow `.`" "'$1' is not a concrete type; " "incorrect result proc symbol" "cannot infer type of parameter: " "'method' needs a parameter that has an object type" "the overloaded " "return type 'auto' cannot be used in forward declarations" "'.closure' calling convention for top level routines is invalid" "iterator needs a return type" "method" "converter" "a converter takes exactly one argument" "Cannot use '" & it[0].ident.s & "' in 'include'." "concept predicate failed" "expected " "locks pragma without argument" "invalid lock level: " "invalid nested locking" "unguarded access: " "invalid guard field: " "unguarded access: " "'$1' cannot be passed to a procvar" " is not GC safe" "routine has no return type, but .requires contains 'result'" " " "base method is GC-safe, but '$1' is not" "for method '" "'$1' is not GC-safe" "'$1' can have side effects" " ": localError(conf, info, errInvalidCmdLineOption % "-" "argument for command line option expected: '$1'" "invalid argument for command line option: '$1'" "unknown hint: " "unknown warning: " "invalid path: " "FILE,LINE,COLUMN expected" "invalid backend: '$1'" "expected native|gdb|on|off but found " "unknown OS: '$1'. Available options are: $2" "unknown CPU: '$1'. Available options are: $2" "invalid verbosity level: '$1'" "invalid option for --incremental: " "unknown experimental feature" "unknown obsolete feature" "unknown Nim version; currently supported values are: `1.0`, `1.2`" "overloaded '$1' leads to ambiguous calls" "illformed AST: " "inheritance only works with an enum" "; given: " "set" "varargs" "type 'var var' is not allowed" "range is empty" "type mismatch" "ordinal or float type expected" "enum '$1' has holes" "NaN is not a valid start or end for a range" "range is empty" "range types need to be constructed with '..', '..<' is not supported" "expected range" "range" "enum '$1' has holes" "ordinal" "identifier expected" "attempt to redefine: '" & field.name.s & "'" "export" "range is empty" "selector must be of an ordinal type, float" "low(" "len($1) must be less than 32768" "not all cases are covered; missing: $1" "not all cases are covered" "attempt to redefine: '" & f.name.s & "'" "attempt to redefine: '" & n.sym.name.s & "'" "cannot inherit from a type that is not an object type" "inheritance only works with non-final objects; " "type '$1 void' is not allowed" "'" & typ.sym.name.s & "' is only allowed in templates and macros or magic procs" "cannot infer the type of parameter '" & $a[0] & "'" "':' expected" "typeless parameters are obsolete" "attempt to redefine: '" & arg.name.s & "'" "return type '" "object" "cannot instantiate the '$1' $2" "illegal recursion in type '$1'" "expected type, but got: " "typeof: cannot evaluate 'mode' parameter at compile-time" "invalid type" "type 'var var' is not allowed" "object constructor needs an object type;" "type expected, but symbol '$1' has no type." "type expected, but got symbol '$1' of kind '$2'" "type expected, but got: " "unknown C compiler: '$1'. Available options are: $2" "cannot open file: " "usage of '$1' is a user-defined error" "unresolved generic parameter" "cstring doesn't support `[]=` operator" "request to generate code for .compileTime proc: " "wrong number of parameters emitted; expected: " "'repr' doesn't support 'void' type" "unsupported control flow: 'finally: ... raise' duplicated because of 'break'" "" "constant of type '" & typeToString(s.typ) & "' has no value" "a type conversion takes exactly one argument" "object construction uses ':', not '='" "illegal conversion from '$1' to '$2'" " can't be converted to " "cannot cast to a non concrete type: '$1'" "expression cannot be cast to " "invalid argument for: " "'is' operator takes 2 arguments" "cannot convert " "expected ordinal value for array " "size of array exceeds range of index " "invalid order in array constructor" "system needs: echo" "illegal context for 'nimvm' magic" "nil dereference is not allowed" "invalid index value for tuple subscript" "'$1' escapes its stack frame; context: '$2'; see $3" "'$1' is not the first parameter; context: '$2'; see $3" "cannot return an owned pointer as an unowned pointer; " "expression has no type" "'return' not allowed here" "tuple constructor" "yield statement must yield a value" "identifier expected, but got: " "'$1' is not a macro or template" "expected a template that takes " & $(macroCall.len-1) & " arguments" "ambiguous symbol in 'getAst' context: " "getAst takes a call, but got " "'quote' expects 1 or 2 arguments" "block" "system needs: nimCreateFlowVar" "sizeof" "use the {.experimental.} pragma to enable 'parallel'" "compiler was built without 'spawn' support" "typedesc not allowed as tuple field." "The export/except syntax expects a module name" "cannot export: " "Mixing types and values in tuples is not allowed." "import" "from" "include" "export" "defer statement not supported at top level" "'defer' takes a 'void' expression" "'defer' not allowed in this context" "invalid context for 'bind' statement: " "invalid expression: " "illformed AST: " "cannot convert " "field not found: " "over- or underflow" "division by zero" "nil dereference is not allowed" "invalid filter: " "'end' does not close a control flow construct" "expected closing '}'" "invalid expression" "Illegal use of ``case`` in union type." "union type may not have an object header" "can't compute offsetof on this ast" "$1usage of '$2' is an {.error.} defined at $3" "invalid pragma: " "cannot attach a custom pragma to '" & s.name.s & "'" "proposition expected" "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)" "'on' or 'off' expected" "calling convention expected" "unknown experimental feature" "'none', 'speed' or 'size' expected" "option expected" "'push' cannot have arguments" "{.pop.} without a corresponding {.push.}" "'.compile' pragma takes up 2 arguments" "empty 'asm' statement" "tuple expected" "invalid type for raises/tags list" "locks pragma takes a list of expressions" "invalid string literal for locks pragma (only allowed string is \"unknown\")" "integer must be within 0.." "a type can only borrow `.` for now" "list of key:value pairs expected" "the .deprecated pragma is unreliable for routines" "key:value pair expected" "a 'cast' pragma cannot be pushed" "'cast' pragma only allowed in a statement context" "exportcpp requires `cpp` backend, got " "`importjs` pragma requires the JavaScript target" "`importjs` for routines requires a pattern" "size may only be 1, 2, 4 or 8" "power of two expected" "annotation to deprecated not supported here" "`{.union.}` is not implemented for js backend." "bitsize needs to be positive" "expression expected" "'experimental' pragma only valid as toplevel statement or in a 'push' environment" "'this' pragma is allowed to have zero or one arguments" ".dynlib requires .exportc" "cannot create a flowVar of type: " "'spawn'ed function cannot have a 'var' parameter" "'spawn'ed function cannot refer to 'ref'/closure" "'spawn' must not be discarded" "'spawn' takes a call expression; got " "'spawn' takes a GC safe call expression" "closure in spawn environment is not allowed" "iterator in spawn environment is not allowed" "invalid package name: " "expression '$1' cannot be called" "\nexpression: " "could not resolve: " "expression '$1' cannot be called" "cannot instantiate: '" " too nested for type matching" "cannot infer the value of the static param '" & name & "'" "localError(f.n.info, errTypeExpected)" "named parameter has to be an identifier" "cannot convert $1 to $2" "cannot instantiate: '" & dc.name.s & "'" "error" "request for RTTI generation for incomplete object: " "invalid bindSym usage" "cannot call method " "implementation of '$1' expected" "invalid expression" "a pattern cannot be empty" "implementation of '$1' expected" "identifier expected, but got: " "wrong argument count" "call expression expected for C++ pattern" """ "cannot create an implicit openArray copy to be passed to a sink parameter" "first argument needs to be an iterator" "second argument needs to be a type" "third argument needs to be an identifier" "cannot borrow from " & $src & ", it is not a path expression; " "'result' must borrow from the first parameter" "attempt to mutate an immutable view" "cannot determine the target of the borrow" "attempt to mutate a borrowed location from an immutable view" "'" & v.name.s & "' borrows from location '" "'" & v.name.s & "' borrows from the immutable location '" "'" "system module needs: " "package name must be an identifier or string literal" "invalid path: " "only '/' supported with $package notation" "using '.' instead of '/' in import paths is deprecated" "invalid module name: '$1'" "cannot open file: " "'export except' not implemented" "module alias must be an identifier" "A module cannot import itself" "'repr' is not available for --newruntime" "'repr' doesn't support 'void' type" "invalid context for 'toOpenArray'; " "request to generate code for .compileTime proc: " "cannot create null element for: " "found no symbol at this position " ```

it's less clear to me how the category of procs that return a PSym,PType, or deal with the PNode within a PType could work within a more structured error-handling system. use a similar scheme for tyError? should there be an skError?

I suppose adding skError is a possibility, but it is necessary to determine how often errors are created within PSym/PType-generatic procs and decide based on that.

Araq commented 3 years ago

For me it's pretty straight-forward:

haxscramper commented 3 years ago

nkError is the obvious way to do it, it's just another AST. The macro system can already handle ASTs

No objections here. But this RFC is about presenting error information in the structured way, and what kind of information should be presented to the user (though it is mostly addressed by the https://github.com/nim-lang/RFCs/issues/323)

Render error message ASTs as JSON/XML as you see fit for the external tooling.

The question is - how do we store "error messages"? Do we structure them using some enum, or introduce a separate type and store as much information as possible there? What exactly should be added to these messages?

You can argue that a more type-safe AST with subtyping etc would have been the better design, but the current design is not without its merits.

This is not about subtyping on the AST [1], it is mostly about storing more information about errors and presenting it to users. It might look like subtyping, but to me, it is just an upgrade from error: string to error: ErrorDescription

[1] I think current implementation is the best approach overall, until it comes to code generation, but that is another issue with its own set of requirements that has nothing to do with compiler development.

Araq commented 3 years ago

Do we structure them using some enum,

Yes, and we have the enum in errorhandling.ErrorKind.

error: string to error: ErrorDescription

It's an upgrade to nkError(childNodes). The translation step is enum value --> string format specifier. The children are translated via renderTree. Much like:


proc toAscii(e): string = 
  case errorKind
  of ExpressionCannotBeCalled:
    result = "expression '$1' cannot be called" % wrongNode.renderTree

proc toXml(e): string = 
  case errorKind
  of ExpressionCannotBeCalled:
    result = "<uncallableExpression>" & wrongNode.renderTree.escapeXml & "</uncallableExpression>"
shirleyquirk commented 3 years ago

The question is - how do we store "error messages"? Do we structure them using some enum, or introduce a separate type and store as much information as possible there? What exactly should be added to these messages?

there is a pattern (that i'd love to see more of) of "error: this went wrong got $1", and a special case of "error: this went wrong got $1, helpful suggestion?"

storing them both under the same enum, with it's corresponding format logic, and an optional help message as already implemented:

  result.add wrongNode
  result.add newIntNode(nkIntLit, ord(CustomError))
  result.add newStrNode(msg, wrongNode.info)
haxscramper commented 3 years ago

So the idea is to try and encode ~300 different errors together with their description in subnodes of TNode, correct? Basically what I got confused about in your implementation (and it is somewhat clearer now):

  1. Error kind is not stored as a separate field in the AST, instead it is represented using node[0] of kind nkIntLit
  2. All additional information about error is encoded in the [1.. ] nodes in the AST

Just as an example - generating error for type mismatch would involve something like

newError(ErrTypeMismatch, candidates)

And error renderer can kick in and employ all the sophisticated ordering and placement heuristics I mentioned in https://github.com/nim-lang/RFCs/issues/325

Araq commented 3 years ago

So the idea is to try and encode ~300 different errors together with their description in subnodes of TNode, correct?

Well yes, as I wrote the code in errorhandling.nim. However don't phrase it as "300 different errors", that's the worst case if we fail to unify some of these error messages.

And error renderer can kick in

It doesn't "kick in" as there is no callback mechanism involved. Instead error reporting is handled later in sempass2 and currently only there.

Araq commented 3 years ago

storing them both under the same enum, with it's corresponding format logic, and an optional help message as already implemented

Yeah, it's good idea.

haxscramper commented 3 years ago

It doesn't "kick in" as there is no callback mechanism involved. Instead error reporting is handled later in sempass2 and currently only there.

I mean we defer all pretty-printing logic until the rendering stage, not that there is some on-error callback

there is a pattern (that i'd love to see more of) of "error: this went wrong got $1", and a special case of "error: this went wrong got $1, helpful suggestion?"

It would be necessary to encode all useful context in subnodes somehow - my main concern, is that at some point we will encounter some error for which it is possible to generate some suggestion, but this suggestion cannot be easily encoded in the AST (or it would require very hackish implementation, like writing type -> AST (de)serialization).

For example noSideEffect annotation. When there is a side effect in the procedure implementation it would be really nice if it showed what part of the code generates a side effect instead of the current "you have an error" response. Something re-running semcheck in the top-down mode and add all offending subnodes to the generated error?

    when false:
      listGcUnsafety(s, onlyWarning=false, g.config)
    else:
-      localError(g.config, s.info, ("'$1' can have side effects" % s.name.s) & (g.config $ mutationInfo))
+      let offenders = findSideEffectedNodes(body)
+      return newError(ErrSideEfffect, offenders))

my main concern, is that at some point we will encounter some error for which it is possible to generate some suggestion, but this suggestion cannot be easily encoded in the AST

On the second thought - maybe it is not that big of a deal in the end, and trying to engineer around possibly non-existing issue is even worse.

haxscramper commented 3 years ago

Lexical errors have to be represented somehow. There are not a lot of them (indentation, invalid character literals, trying to use __ in identifier names and things like that). But this mostly a rewording issue (and maybe some additional check to get a better message).

Syntax errors can be represented using nkError - parser already has access to the AST and can generate them.

User-defined errors can be represented using ErrUser, but pretty-print and XML conversion should ideally be configurable too, somehow. Would it be possible to just put printer proc symbols in the errorNode[1] to make them called by xml/json/pretty-printer?

shirleyquirk commented 3 years ago

trying to engineer around...

if later you want to implement a better errormessage for X, and it needs extra info, you'll add the logic at the erroring site to pack the nkError with the right nodes, add a new ErrorKind, and a case to errorToString that unpacks them accordingly.

shirleyquirk commented 3 years ago

Lexical errors: ref #348

Araq commented 3 years ago

So ... is this RFC settled? I think the rest is all the required implementation effort.

haxscramper commented 3 years ago

I'm still not sure if a proposed implementation with subnode error encoding would be sufficient, but this can be addressed if we get to the point where this becomes an issue. Otherwise, I consider this settled, yes

timotheecour commented 3 years ago

if later you want to implement a better errormessage for X, and it needs extra info, you'll add the logic at the erroring site to pack the nkError with the right nodes, add a new ErrorKind, and a case to errorToString that unpacks them accordingly.

currently the error msg rendering happens where the error is generated, which allows writing error messages with runtime context information, eg: "expected $1, got $2, extra info = $3"

if you defer rendering to a later stage, rendering such messages would require adding fields to the error node (deep copying which is wasteful, or shallow copying which can cause issues if the nodes are modified), it adds significant complexity for no benefit.

Instead, we should keep the rendering as it currently is, at the place where error is generated, which is where we have all the runtime context available for writing informative error messages.

But adding an enum field/unique id which uniquely identifies the error message is a good idea, and can be done without any complex refactoring or loss of functionality. Furthermore, it can be done gradually, with a default enum value ErrorUnknown for error messages without an attached enum value.

PType already has tyError, PSym could have skError but I don't know if it's needed, it comes up so rarely that nil worked well enough.

we already have skError, although it's defined as skError* = skUnknown which is an IMO un-necessary micro-optimization that doesn't help performance. See also proc errorSym*(c: PContext, n: PNode): PSym=

haxscramper commented 3 years ago

Immediate pretty-printing of all newly generated errors (using the same interface as described above), and more compact error messages (just node kind + brief message) added to nkError after error has been printed?

There really is no need to drag around the fragile and potentially expensive instantiation context and all associated entries.

And if error message is generated immediately it might be possible to avoid this hack where all possible contexts should be first encoded into subnodes and then unpacked, and instead go directly for some internal error representation that can be printed in different ways? And after all context is used to generate appropriate error message it can be added to error as brief description?

Araq commented 3 years ago

"instantiated from here" information should be part of the nkError structure. The used nodes only need the TLineInfo field and so a cheap shallow copy suffices.

Araq commented 3 years ago

I consider this settled. Tooling can look for '\31\n' to determine precisely where one message ends. Error messages are inherently full of natural language so only a semi-structured approach can work.

juancarlospaco commented 3 years ago

XML but it is what it makes it useful for layout -- JSON isn't suitable.

JSON can be directly converted to XML easily, and vice-versa.

I do not care if JSON or XML, I like JSON more, but it is not a big problem whatever way.

Araq commented 3 years ago

JSON can be directly converted to XML easily, and vice-versa.

<b> is part of the HTML spec and not part of JSON, it's really as simple as that. These are different languages with different purposes. Yes you can translate one tree structure into the other tree structure, I understand that, I understand syntax trees, can you imagine?

juancarlospaco commented 3 years ago

We agree, ...then XML errors should be a thing 😏