diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-03-03 02:13:16 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-03-03 02:13:16 +0100 |
commit | a9c1afd5fd9d0055c849c10bea5dfe7494398cfd (patch) | |
tree | cb7bbfea0a4df878ccc3d3770044c884a06e722d /tools/nimsuggest | |
parent | 3e7b04683c7912cc49d05444187ca3bde7bc18aa (diff) | |
download | Nim-a9c1afd5fd9d0055c849c10bea5dfe7494398cfd.tar.gz |
nimsuggest: structured error reporting; EPC mode still fails
Diffstat (limited to 'tools/nimsuggest')
-rw-r--r-- | tools/nimsuggest/nimsuggest.nim | 55 | ||||
-rw-r--r-- | tools/nimsuggest/tests/tchk1.nim | 27 | ||||
-rw-r--r-- | tools/nimsuggest/tests/tcursor_at_end.nim | 12 | ||||
-rw-r--r-- | tools/nimsuggest/tests/twithin_macro_prefix.nim | 209 |
4 files changed, 281 insertions, 22 deletions
diff --git a/tools/nimsuggest/nimsuggest.nim b/tools/nimsuggest/nimsuggest.nim index 5ed66ca36..97c46625c 100644 --- a/tools/nimsuggest/nimsuggest.nim +++ b/tools/nimsuggest/nimsuggest.nim @@ -17,7 +17,7 @@ import compiler / [options, commands, modules, sem, passes, passaux, msgs, nimconf, extccomp, condsyms, sigmatch, ast, scriptconfig, - idents, modulegraphs, compilerlog, vm] + idents, modulegraphs, vm] when defined(windows): import winlean @@ -60,6 +60,20 @@ var gLogging = false gRefresh: bool + requests: Channel[string] + results: Channel[Suggest] + +proc writelnToChannel(line: string) = + results.send(Suggest(section: ideMsg, doc: line)) + +proc sugResultHook(s: Suggest) = + results.send(s) + +proc errorHook(info: TLineInfo; msg: string; sev: Severity) = + results.send(Suggest(section: ideChk, filePath: toFullPath(info), + line: toLinenumber(info), column: toColumn(info), doc: msg, + forth: $sev)) + const seps = {':', ';', ' ', '\t'} Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known file.nim[;dirtyfile.nim]:line:col\n" & @@ -133,9 +147,12 @@ proc symFromInfo(graph: ModuleGraph; gTrackPos: TLineInfo): PSym = proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; graph: ModuleGraph; cache: IdentCache) = if gLogging: - logStr("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile & + log("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile & "[" & $line & ":" & $col & "]") gIdeCmd = cmd + if cmd == ideChk: + msgs.structuredErrorHook = errorHook + msgs.writelnHook = proc (s: string) = discard if cmd == ideUse and suggestVersion != 2: graph.resetAllModules() var isKnownFile = true @@ -194,7 +211,7 @@ template sendEpc(results: typed, tdef, hook: untyped) = executeEpc(gIdeCmd, args, graph, cache) let res = sexp(results) if gLogging: - logStr($res) + log($res) returnEpc(client, uid, res) template checkSanity(client, sizeHex, size, messageBuffer: typed) = @@ -205,16 +222,12 @@ template checkSanity(client, sizeHex, size, messageBuffer: typed) = if client.recv(messageBuffer, size) != size: raise newException(ValueError, "didn't get all the bytes") -var - requests: Channel[string] - results: Channel[Suggest] - proc toStdout() {.gcsafe.} = while true: let res = results.recv() case res.section of ideNone: break - of ideChk: echo res.doc + of ideMsg: echo res.doc of ideKnown: echo res.quality == 1 else: echo res @@ -223,7 +236,7 @@ proc toSocket(stdoutSocket: Socket) {.gcsafe.} = let res = results.recv() case res.section of ideNone: break - of ideChk: stdoutSocket.send(res.doc & "\c\L") + of ideMsg: stdoutSocket.send(res.doc & "\c\L") of ideKnown: stdoutSocket.send($(res.quality == 1) & "\c\L") else: stdoutSocket.send($res & "\c\L") @@ -233,7 +246,7 @@ proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} = let res = results.recv() case res.section of ideNone: break - of ideChk: + of ideMsg: list.add sexp(res.doc) of ideKnown: list.add sexp(res.quality == 1) @@ -241,12 +254,6 @@ proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} = list.add sexp(res) returnEpc(client, uid, list) -proc writelnToChannel(line: string) = - results.send(Suggest(section: ideChk, doc: line)) - -proc sugResultHook(s: Suggest) = - results.send(s) - template setVerbosity(level: typed) = gVerbosity = level gNotes = NotesVerbosity[gVerbosity] @@ -353,7 +360,7 @@ proc replEpc(x: ThreadParams) {.thread.} = else: discard let cmd = $gIdeCmd & " " & args.argsToStr if gLogging: - logStr "MSG CMD: " & cmd + log "MSG CMD: " & cmd requests.send(cmd) toEpc(client, uid) of "methods": @@ -437,11 +444,11 @@ proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) = proc mainThread(graph: ModuleGraph; cache: IdentCache) = if gLogging: for it in searchPaths: - logStr(it) + log(it) proc wrHook(line: string) {.closure.} = if gMode == mepc: - if gLogging: logStr(line) + if gLogging: log(line) else: writelnToChannel(line) @@ -454,7 +461,6 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) = if hasData: msgs.writelnHook = wrHook suggestionResultHook = sugResultHook - execCmd(req, graph, cache) idle = 0 else: @@ -464,6 +470,7 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) = # we use some nimsuggest activity to enable a lazy recompile: gIdeCmd = ideChk msgs.writelnHook = proc (s: string) = discard + msgs.structuredErrorHook = nil suggestionResultHook = proc (s: Suggest) = discard recompileFullProject(graph, cache) @@ -482,6 +489,10 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) = # do not stop after the first error: msgs.gErrorMax = high(int) + # do not print errors, but log them + msgs.writelnHook = proc (s: string) = log(s) + msgs.structuredErrorHook = nil + # compile the project before showing any input so that we already # can answer questions right away: compileProject(graph, cache) @@ -559,9 +570,9 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) = raise newException(IOError, "Cannot find Nim standard library: Nim compiler not in PATH") gPrefixDir = binaryPath.splitPath().head.parentDir() - #msgs.writelnHook = proc (line: string) = logStr(line) + #msgs.writelnHook = proc (line: string) = log(line) if gLogging: - logStr("START " & gProjectFull) + log("START " & gProjectFull) loadConfigs(DefaultConfig, cache, config) # load all config files # now process command line arguments again, because some options in the diff --git a/tools/nimsuggest/tests/tchk1.nim b/tools/nimsuggest/tests/tchk1.nim new file mode 100644 index 000000000..f9f0dc8fe --- /dev/null +++ b/tools/nimsuggest/tests/tchk1.nim @@ -0,0 +1,27 @@ +# test we get some suggestion at the end of the file + + + + + + + +type + + +template foo() = + +proc main = + +#[!]# +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;-1;;-1;;"tchk1 [Processing]";;0 +chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but found \'keyword template\'";;0 +chk;;skUnknown;;;;Error;;$file;;14;;0;;"complex statement requires indentation";;0 +chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0 +chk;;skUnknown;;;;Error;;$file;;17;;0;;"invalid indentation";;0 +chk;;skUnknown;;;;Hint;;$file;;12;;9;;"\'foo\' is declared but not used [XDeclaredButNotUsed]";;0 +chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'tchk1.main()\' is declared but not used [XDeclaredButNotUsed]";;0 +""" diff --git a/tools/nimsuggest/tests/tcursor_at_end.nim b/tools/nimsuggest/tests/tcursor_at_end.nim new file mode 100644 index 000000000..b3a0d1133 --- /dev/null +++ b/tools/nimsuggest/tests/tcursor_at_end.nim @@ -0,0 +1,12 @@ +# test we get some suggestion at the end of the file + +discard """ +$nimsuggest --tester $file +>sug $1 +sug;;skProc;;tcursor_at_end.main;;proc ();;$file;;10;;5;;"";;* +""" + + +proc main = discard + +#[!]# diff --git a/tools/nimsuggest/tests/twithin_macro_prefix.nim b/tools/nimsuggest/tests/twithin_macro_prefix.nim new file mode 100644 index 000000000..6ee9fb2dc --- /dev/null +++ b/tools/nimsuggest/tests/twithin_macro_prefix.nim @@ -0,0 +1,209 @@ + +import macros + +macro class*(head, body: untyped): untyped = + # The macro is immediate, since all its parameters are untyped. + # This means, it doesn't resolve identifiers passed to it. + + var typeName, baseName: NimNode + + # flag if object should be exported + var exported: bool + + if head.kind == nnkInfix and head[0].ident == !"of": + # `head` is expression `typeName of baseClass` + # echo head.treeRepr + # -------------------- + # Infix + # Ident !"of" + # Ident !"Animal" + # Ident !"RootObj" + typeName = head[1] + baseName = head[2] + + elif head.kind == nnkInfix and head[0].ident == !"*" and + head[2].kind == nnkPrefix and head[2][0].ident == !"of": + # `head` is expression `typeName* of baseClass` + # echo head.treeRepr + # -------------------- + # Infix + # Ident !"*" + # Ident !"Animal" + # Prefix + # Ident !"of" + # Ident !"RootObj" + typeName = head[1] + baseName = head[2][1] + exported = true + + else: + quit "Invalid node: " & head.lispRepr + + # The following prints out the AST structure: + # + # import macros + # dumptree: + # type X = ref object of Y + # z: int + # -------------------- + # StmtList + # TypeSection + # TypeDef + # Ident !"X" + # Empty + # RefTy + # ObjectTy + # Empty + # OfInherit + # Ident !"Y" + # RecList + # IdentDefs + # Ident !"z" + # Ident !"int" + # Empty + + # create a type section in the result + result = + if exported: + # mark `typeName` with an asterisk + quote do: + type `typeName`* = ref object of `baseName` + else: + quote do: + type `typeName` = ref object of `baseName` + + # echo treeRepr(body) + # -------------------- + # StmtList + # VarSection + # IdentDefs + # Ident !"name" + # Ident !"string" + # Empty + # IdentDefs + # Ident !"age" + # Ident !"int" + # Empty + # MethodDef + # Ident !"vocalize" + # Empty + # Empty + # FormalParams + # Ident !"string" + # Empty + # Empty + # StmtList + # StrLit ... + # MethodDef + # Ident !"age_human_yrs" + # Empty + # Empty + # FormalParams + # Ident !"int" + # Empty + # Empty + # StmtList + # DotExpr + # Ident !"this" + # Ident !"age" + + # var declarations will be turned into object fields + var recList = newNimNode(nnkRecList) + + # expected name of constructor + let ctorName = newIdentNode("new" & $typeName) + + # Iterate over the statements, adding `this: T` + # to the parameters of functions, unless the + # function is a constructor + for node in body.children: + case node.kind: + + of nnkMethodDef, nnkProcDef: + # check if it is the ctor proc + if node.name.kind != nnkAccQuoted and node.name.basename == ctorName: + # specify the return type of the ctor proc + node.params[0] = typeName + else: + # inject `self: T` into the arguments + node.params.insert(1, newIdentDefs(ident("self"), typeName)) + result.add(node) + + of nnkVarSection: + # variables get turned into fields of the type. + for n in node.children: + recList.add(n) + + else: + result.add(node) + + # Inspect the tree structure: + # + # echo result.treeRepr + # -------------------- + # StmtList + # TypeSection + # TypeDef + # Ident !"Animal" + # Empty + # RefTy + # ObjectTy + # Empty + # OfInherit + # Ident !"RootObj" + # Empty <= We want to replace this + # MethodDef + # ... + + result[0][0][2][0][2] = recList + + # Lets inspect the human-readable version of the output + #echo repr(result) + +# --- + +class Animal of RootObj: + var name: string + var age: int + method vocalize: string {.base.} = "..." # use `base` pragma to annonate base methods + method age_human_yrs: int {.base.} = self.age # `this` is injected + proc `$`: string = "animal:" & self.name & ":" & $self.age + +class Dog of Animal: + method vocalize: string = "woof" + method age_human_yrs: int = self.age * 7 + proc `$`: string = "dog:" & self.name & ":" & $self.age + +class Cat of Animal: + method vocalize: string = "meow" + proc `$`: string = "cat:" & self.name & ":" & $self.age + +class Rabbit of Animal: + proc newRabbit(name: string, age: int) = # the constructor doesn't need a return type + result = Rabbit(name: name, age: age) + method vocalize: string = "meep" + proc `$`: string = + self.ag#[!]# + result = "rabbit:" & self.name & ":" & $self.age + +# --- + +var animals: seq[Animal] = @[] +animals.add(Dog(name: "Sparky", age: 10)) +animals.add(Cat(name: "Mitten", age: 10)) + +for a in animals: + echo a.vocalize() + echo a.age_human_yrs() + +let r = newRabbit("Fluffy", 3) +echo r.vocalize() +echo r.age_human_yrs() +echo r + +discard """ +$nimsuggest --tester $file +>sug $1 +sug;;skField;;age;;int;;$file;;167;;6;;"";;100 +sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int;;$file;;169;;9;;"";;100 +""" |