summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2017-03-14 15:56:08 +0100
committerAraq <rumpf_a@web.de>2017-03-14 15:56:08 +0100
commitb1c494a1504921a21f5578782db72f274efadd95 (patch)
treeb26c5e92edbc87e8f38cb8994e6e01f897d77494 /compiler
parent142b604c1353926208220aa7ce0b7724a72958c3 (diff)
parent0510c0cecefb50dedd691de82151bc629b35d816 (diff)
downloadNim-b1c494a1504921a21f5578782db72f274efadd95.tar.gz
Merge branch 'devel' of github.com:nim-lang/Nim into devel
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/astalgo.nim17
-rw-r--r--compiler/evaltempl.nim5
-rw-r--r--compiler/lexer.nim122
-rw-r--r--compiler/msgs.nim13
-rw-r--r--compiler/options.nim22
-rw-r--r--compiler/parser.nim5
-rw-r--r--compiler/sem.nim11
-rw-r--r--compiler/semcall.nim1
-rw-r--r--compiler/semexprs.nim21
-rw-r--r--compiler/semstmts.nim14
-rw-r--r--compiler/semtypes.nim5
-rw-r--r--compiler/sigmatch.nim5
-rw-r--r--compiler/suggest.nim194
-rw-r--r--compiler/transf.nim5
-rw-r--r--compiler/types.nim5
16 files changed, 291 insertions, 156 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index d6fc5920e..26305cf3b 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -460,6 +460,8 @@ type
                       # proc foo(T: typedesc, list: seq[T]): var T
                       # proc foo(L: static[int]): array[L, int]
                       # can be attached to ranges to indicate that the range
+                      # can be attached to generic procs with free standing
+                      # type parameters: e.g. proc foo[T]()
                       # depends on unresolved static params.
     tfRetType,        # marks return types in proc (used to detect type classes
                       # used as return types for return type inference)
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 226d5ee42..161e4d637 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -67,6 +67,23 @@ proc debug*(n: PSym) {.deprecated.}
 proc debug*(n: PType) {.deprecated.}
 proc debug*(n: PNode) {.deprecated.}
 
+template mdbg*: bool {.dirty.} =
+  when compiles(c.module):
+    c.module.fileIdx == gProjectMainIdx
+  elif compiles(m.c.module):
+    m.c.module.fileIdx == gProjectMainIdx
+  elif compiles(cl.c.module):
+    cl.c.module.fileIdx == gProjectMainIdx
+  elif compiles(p):
+    when compiles(p.lex):
+      p.lex.fileIdx == gProjectMainIdx
+    else:
+      p.module.module.fileIdx == gProjectMainIdx
+  elif compiles(L.fileIdx):
+    L.fileIdx == gProjectMainIdx
+  else:
+    false
+
 # --------------------------- ident tables ----------------------------------
 proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
 proc idTableGet*(t: TIdTable, key: int): RootRef
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 318254a80..5bd274a3e 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -80,9 +80,14 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
     expectedRegularParams = <s.typ.len
     givenRegularParams = totalParams - genericParams
   if givenRegularParams < 0: givenRegularParams = 0
+
   if totalParams > expectedRegularParams + genericParams:
     globalError(n.info, errWrongNumberOfArguments)
 
+  if totalParams < genericParams:
+    globalError(n.info, errMissingGenericParamsForTemplate,
+                n.renderTree)
+
   result = newNodeI(nkArgList, n.info)
   for i in 1 .. givenRegularParams:
     result.addSon n.sons[i]
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index c082a5cb3..2bb228f41 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -67,6 +67,10 @@ type
   TTokTypes* = set[TTokType]
 
 const
+  weakTokens = {tkComma, tkSemiColon, tkColon,
+                tkParRi, tkParDotRi, tkBracketRi, tkBracketDotRi,
+                tkCurlyRi} # \
+    # tokens that should not be considered for previousToken
   tokKeywordLow* = succ(tkSymbol)
   tokKeywordHigh* = pred(tkIntLit)
   TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]",
@@ -105,6 +109,9 @@ type
                               # so that it is the correct default value
     base2, base8, base16
 
+  CursorPosition* {.pure.} = enum ## XXX remove this again
+    None, InToken, BeforeToken, AfterToken
+
   TToken* = object            # a Nim token
     tokType*: TTokType        # the type of the token
     indent*: int              # the indentation; != -1 if the token has been
@@ -128,8 +135,11 @@ type
                               # needs so much look-ahead
     currLineIndent*: int
     strongSpaces*, allowTabs*: bool
+    cursor*: CursorPosition
     errorHandler*: TErrorHandler
     cache*: IdentCache
+    when defined(nimsuggest):
+      previousToken: TLineInfo
 
 var gLinesCompiled*: int  # all lines that have been compiled
 
@@ -203,6 +213,8 @@ proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
   lex.currLineIndent = 0
   inc(lex.lineNumber, inputstream.lineOffset)
   lex.cache = cache
+  when defined(nimsuggest):
+    lex.previousToken.fileIndex = fileIdx
 
 proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
                 cache: IdentCache) =
@@ -235,6 +247,41 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
 proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
   result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
 
+template tokenBegin(pos) {.dirty.} =
+  when defined(nimsuggest):
+    var colA = getColNumber(L, pos)
+
+template tokenEnd(pos) {.dirty.} =
+  when defined(nimsuggest):
+    let colB = getColNumber(L, pos)+1
+    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
+        L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
+      L.cursor = CursorPosition.InToken
+      gTrackPos.col = colA.int16
+    colA = 0
+
+template tokenEndIgnore(pos) =
+  when defined(nimsuggest):
+    let colB = getColNumber(L, pos)
+    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
+        L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
+      gTrackPos.fileIndex = trackPosInvalidFileIdx
+      gTrackPos.line = -1
+    colA = 0
+
+template tokenEndPrevious(pos) =
+  when defined(nimsuggest):
+    # when we detect the cursor in whitespace, we attach the track position
+    # to the token that came before that, but only if we haven't detected
+    # the cursor in a string literal or comment:
+    let colB = getColNumber(L, pos)
+    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
+        L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
+      L.cursor = CursorPosition.BeforeToken
+      gTrackPos = L.previousToken
+      gTrackPosAttached = true
+    colA = 0
+
 {.push overflowChecks: off.}
 # We need to parse the largest uint literal without overflow checks
 proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
@@ -318,6 +365,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
   result.literal = ""
   result.base = base10
   startpos = L.bufpos
+  tokenBegin(startPos)
 
   # First stage: find out base, make verifications, build token literal string
   if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
@@ -526,6 +574,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
     lexMessageLitNum(L, errInvalidNumber, startpos)
   except OverflowError, RangeError:
     lexMessageLitNum(L, errNumberOutOfRange, startpos)
+  tokenEnd(postPos-1)
   L.bufpos = postPos
 
 proc handleHexChar(L: var TLexer, xi: var int) =
@@ -642,21 +691,11 @@ proc handleCRLF(L: var TLexer, pos: int): int =
     result = nimlexbase.handleLF(L, pos)
   else: result = pos
 
-template tokenRange(colA, pos) =
-  when defined(nimsuggest):
-    let colB = getColNumber(L, pos)
-    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
-        L.lineNumber == gTrackPos.line and gIdeCmd == ideSug:
-      gTrackPos.fileIndex = trackPosInvalidFileIdx
-      gTrackPos.line = -1
-    colA = 0
-
 proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
   var pos = L.bufpos + 1          # skip "
   var buf = L.buf                 # put `buf` in a register
   var line = L.lineNumber         # save linenumber for better error message
-  when defined(nimsuggest):
-    var colA = getColNumber(L, pos)
+  tokenBegin(pos)
   if buf[pos] == '\"' and buf[pos+1] == '\"':
     tok.tokType = tkTripleStrLit # long string literal:
     inc(pos, 2)               # skip ""
@@ -672,18 +711,18 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
       of '\"':
         if buf[pos+1] == '\"' and buf[pos+2] == '\"' and
             buf[pos+3] != '\"':
-          tokenRange(colA, pos+2)
+          tokenEndIgnore(pos+2)
           L.bufpos = pos + 3 # skip the three """
           break
         add(tok.literal, '\"')
         inc(pos)
       of CR, LF:
-        tokenRange(colA, pos)
+        tokenEndIgnore(pos)
         pos = handleCRLF(L, pos)
         buf = L.buf
         add(tok.literal, tnl)
       of nimlexbase.EndOfFile:
-        tokenRange(colA, pos)
+        tokenEndIgnore(pos)
         var line2 = L.lineNumber
         L.lineNumber = line
         lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
@@ -704,11 +743,11 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
           inc(pos, 2)
           add(tok.literal, '"')
         else:
-          tokenRange(colA, pos)
+          tokenEndIgnore(pos)
           inc(pos) # skip '"'
           break
       elif c in {CR, LF, nimlexbase.EndOfFile}:
-        tokenRange(colA, pos)
+        tokenEndIgnore(pos)
         lexMessage(L, errClosingQuoteExpected)
         break
       elif (c == '\\') and not rawMode:
@@ -721,6 +760,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
     L.bufpos = pos
 
 proc getCharacter(L: var TLexer, tok: var TToken) =
+  tokenBegin(L.bufpos)
   inc(L.bufpos)               # skip '
   var c = L.buf[L.bufpos]
   case c
@@ -730,12 +770,14 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
     tok.literal = $c
     inc(L.bufpos)
   if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
+  tokenEndIgnore(L.bufpos)
   inc(L.bufpos)               # skip '
 
 proc getSymbol(L: var TLexer, tok: var TToken) =
   var h: Hash = 0
   var pos = L.bufpos
   var buf = L.buf
+  tokenBegin(pos)
   while true:
     var c = buf[pos]
     case c
@@ -762,6 +804,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
       inc(pos)
 
     else: break
+  tokenEnd(pos-1)
   h = !$h
   tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
   L.bufpos = pos
@@ -782,6 +825,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int,
 proc getOperator(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
   var buf = L.buf
+  tokenBegin(pos)
   var h: Hash = 0
   while true:
     var c = buf[pos]
@@ -789,6 +833,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
     h = h !& ord(c)
     inc(pos)
   endOperator(L, tok, pos, h)
+  tokenEnd(pos-1)
   # advance pos but don't store it in L.bufpos so the next token (which might
   # be an operator too) gets the preceding spaces:
   tok.strongSpaceB = 0
@@ -803,8 +848,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
   var pos = start
   var buf = L.buf
   var toStrip = 0
-  when defined(nimsuggest):
-    var colA = getColNumber(L, pos)
+  tokenBegin(pos)
   # detect the amount of indentation:
   if isDoc:
     toStrip = getColNumber(L, pos)
@@ -831,20 +875,20 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
       if isDoc:
         if buf[pos+1] == '#' and buf[pos+2] == '#':
           if nesting == 0:
-            tokenRange(colA, pos+2)
+            tokenEndIgnore(pos+2)
             inc(pos, 3)
             break
           dec nesting
         tok.literal.add ']'
       elif buf[pos+1] == '#':
         if nesting == 0:
-          tokenRange(colA, pos+1)
+          tokenEndIgnore(pos+1)
           inc(pos, 2)
           break
         dec nesting
       inc pos
     of CR, LF:
-      tokenRange(colA, pos)
+      tokenEndIgnore(pos)
       pos = handleCRLF(L, pos)
       buf = L.buf
       # strip leading whitespace:
@@ -856,7 +900,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
           inc pos
           dec c
     of nimlexbase.EndOfFile:
-      tokenRange(colA, pos)
+      tokenEndIgnore(pos)
       lexMessagePos(L, errGenerated, pos, "end of multiline comment expected")
       break
     else:
@@ -867,8 +911,6 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
 proc scanComment(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
   var buf = L.buf
-  when defined(nimsuggest):
-    var colA = getColNumber(L, pos)
   tok.tokType = tkComment
   # iNumber contains the number of '\n' in the token
   tok.iNumber = 0
@@ -876,6 +918,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
   if buf[pos+2] == '[':
     skipMultiLineComment(L, tok, pos+3, true)
     return
+  tokenBegin(pos)
   inc(pos, 2)
 
   var toStrip = 0
@@ -889,7 +932,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
       if buf[pos] == '\\': lastBackslash = pos+1
       add(tok.literal, buf[pos])
       inc(pos)
-    tokenRange(colA, pos)
+    tokenEndIgnore(pos)
     pos = handleCRLF(L, pos)
     buf = L.buf
     var indent = 0
@@ -908,13 +951,14 @@ proc scanComment(L: var TLexer, tok: var TToken) =
     else:
       if buf[pos] > ' ':
         L.indentAhead = indent
-      tokenRange(colA, pos)
+      tokenEndIgnore(pos)
       break
   L.bufpos = pos
 
 proc skip(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
   var buf = L.buf
+  tokenBegin(pos)
   tok.strongSpaceA = 0
   while true:
     case buf[pos]
@@ -925,6 +969,7 @@ proc skip(L: var TLexer, tok: var TToken) =
       if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
       inc(pos)
     of CR, LF:
+      tokenEndPrevious(pos)
       pos = handleCRLF(L, pos)
       buf = L.buf
       var indent = 0
@@ -951,15 +996,24 @@ proc skip(L: var TLexer, tok: var TToken) =
         pos = L.bufpos
         buf = L.buf
       else:
-        when defined(nimsuggest):
-          var colA = getColNumber(L, pos)
+        tokenBegin(pos)
         while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
-        tokenRange(colA, pos)
+        tokenEndIgnore(pos+1)
     else:
       break                   # EndOfFile also leaves the loop
+  tokenEndPrevious(pos-1)
   L.bufpos = pos
 
 proc rawGetTok*(L: var TLexer, tok: var TToken) =
+  template atTokenEnd() {.dirty.} =
+    when defined(nimsuggest):
+      # we attach the cursor to the last *strong* token
+      if tok.tokType notin weakTokens:
+        L.previousToken.line = tok.line.int16
+        L.previousToken.col = tok.col.int16
+
+  when defined(nimsuggest):
+    L.cursor = CursorPosition.None
   fillToken(tok)
   if L.indentAhead >= 0:
     tok.indent = L.indentAhead
@@ -1007,6 +1061,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
         inc(L.bufpos)
       else:
         tok.tokType = tkParLe
+        when defined(nimsuggest):
+          if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
+                    tok.line == gTrackPos.line and gIdeCmd == ideCon:
+            gTrackPos.col = tok.col.int16
     of ')':
       tok.tokType = tkParRi
       inc(L.bufpos)
@@ -1022,10 +1080,13 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       inc(L.bufpos)
     of '.':
       when defined(nimsuggest):
-        if L.fileIdx == gTrackPos.fileIndex and tok.col == gTrackPos.col and
+        if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
             tok.line == gTrackPos.line and gIdeCmd == ideSug:
           tok.tokType = tkDot
+          L.cursor = CursorPosition.InToken
+          gTrackPos.col = tok.col.int16
           inc(L.bufpos)
+          atTokenEnd()
           return
       if L.buf[L.bufpos+1] == ']':
         tok.tokType = tkBracketDotRi
@@ -1092,3 +1153,4 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
         tok.tokType = tkInvalid
         lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
         inc(L.bufpos)
+  atTokenEnd()
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index e50ed0f2a..2db3646b5 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -64,6 +64,8 @@ type
     errVarForOutParamNeeded,
     errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
     errAmbiguousCallXYZ, errWrongNumberOfArguments,
+    errWrongNumberOfArgumentsInCall,
+    errMissingGenericParamsForTemplate,
     errXCannotBePassedToProcVar,
     errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
     errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
@@ -89,6 +91,7 @@ type
     errMainModuleMustBeSpecified,
     errXExpected,
     errTIsNotAConcreteType,
+    errCastToANonConcreteType,
     errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
     errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
     errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
@@ -107,6 +110,7 @@ type
     errCannotInferTypeOfTheLiteral,
     errCannotInferReturnType,
     errGenericLambdaNotAllowed,
+    errProcHasNoConcreteType,
     errCompilerDoesntSupportTarget,
     errUser,
     warnCannotOpenFile,
@@ -269,6 +273,8 @@ const
     errButExpectedX: "but expected \'$1\'",
     errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
     errWrongNumberOfArguments: "wrong number of arguments",
+    errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
+    errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
     errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
     errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
     errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
@@ -326,6 +332,7 @@ const
     errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
     errXExpected: "\'$1\' expected",
     errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
+    errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'",
     errInvalidSectionStart: "invalid section start",
     errGridTableNotImplemented: "grid table is not implemented",
     errGeneralParseError: "general parse error",
@@ -369,6 +376,7 @@ const
     errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
                                 "it is used as an operand to another routine and the types " &
                                 "of the generic paramers can be inferred from the expected signature.",
+    errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
     errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
     errUser: "$1",
     warnCannotOpenFile: "cannot open \'$1\'",
@@ -739,6 +747,8 @@ proc `??`* (info: TLineInfo, filename: string): bool =
 const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions
                                    # are produced within comments and string literals
 var gTrackPos*: TLineInfo
+var gTrackPosAttached*: bool ## whether the tracking position was attached to some
+                             ## close token.
 
 type
   MsgFlag* = enum  ## flags altering msgWriteln behavior
@@ -863,6 +873,9 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
 proc `==`*(a, b: TLineInfo): bool =
   result = a.line == b.line and a.fileIndex == b.fileIndex
 
+proc exactEquals*(a, b: TLineInfo): bool =
+  result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col
+
 proc writeContext(lastinfo: TLineInfo) =
   var info = lastinfo
   for i in countup(0, len(msgContext) - 1):
diff --git a/compiler/options.nim b/compiler/options.nim
index c6d016095..6372cddac 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -392,6 +392,23 @@ proc findModule*(modulename, currentModule: string): string =
     result = findFile(m)
   patchModule()
 
+proc findProjectNimFile*(pkg: string): string =
+  const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
+  var candidates: seq[string] = @[]
+  for k, f in os.walkDir(pkg, relative=true):
+    if k == pcFile and f != "config.nims":
+      let (_, name, ext) = splitFile(f)
+      if ext in extensions:
+        let x = changeFileExt(pkg / name, ".nim")
+        if fileExists(x):
+          candidates.add x
+  for c in candidates:
+    # nim-foo foo  or  foo  nfoo
+    if (pkg in c) or (c in pkg): return c
+  if candidates.len >= 1:
+    return candidates[0]
+  return ""
+
 proc canonDynlibName(s: string): string =
   let start = if s.startsWith("lib"): 3 else: 0
   let ende = strutils.find(s, {'(', ')', '.'})
@@ -420,11 +437,6 @@ proc binaryStrSearch*(x: openArray[string], y: string): int =
       return mid
   result = - 1
 
-template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx
-template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx
-template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx
-template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx
-
 proc parseIdeCmd*(s: string): IdeCmd =
   case s:
   of "sug": ideSug
diff --git a/compiler/parser.nim b/compiler/parser.nim
index d34a6d88a..362a5c286 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -1846,6 +1846,7 @@ proc parseTypeDef(p: var TParser): PNode =
   else:
     addSon(result, ast.emptyNode)
   if p.tok.tokType == tkEquals:
+    result.info = parLineInfo(p)
     getTok(p)
     optInd(p, result)
     addSon(result, parseTypeDefAux(p))
@@ -2002,12 +2003,12 @@ proc parseStmt(p: var TParser): PNode =
           break
         p.hasProgress = false
         var a = complexOrSimpleStmt(p)
-        if not p.hasProgress and p.tok.tokType == tkEof: break
-        if a.kind != nkEmpty and p.hasProgress:
+        if a.kind != nkEmpty:
           addSon(result, a)
         else:
           parMessage(p, errExprExpected, p.tok)
           getTok(p)
+        if not p.hasProgress and p.tok.tokType == tkEof: break
   else:
     # the case statement is only needed for better error messages:
     case p.tok.tokType
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 21a5c435a..6ad77e3fb 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -167,6 +167,8 @@ proc commonType*(x, y: PType): PType =
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
   result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
+  when defined(nimsuggest):
+    suggestDecl(c, n, result)
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
   proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii
@@ -191,6 +193,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
+  when defined(nimsuggest):
+    suggestDecl(c, n, result)
 
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym
@@ -381,6 +385,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
   if sym == c.p.owner:
     globalError(n.info, errRecursiveDependencyX, sym.name.s)
 
+  let genericParams = if sfImmediate in sym.flags: 0
+                      else: sym.ast[genericParamsPos].len
+  let suppliedParams = max(n.safeLen - 1, 0)
+
+  if suppliedParams < genericParams:
+    globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree)
+
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
   result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 98667b085..3a43c63b2 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -411,6 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
     let tm = typeRel(m, formal, arg, true)
     if tm in {isNone, isConvertible}: return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
+  newInst.typ.flags.excl tfUnresolved
   markUsed(n.info, s, c.graph.usageSym)
   styleCheckUse(n.info, s)
   result = newSymNode(newInst, n.info)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index a419cd000..8f1362691 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   #  result = errorNode(c, n)
   if result.typ != nil:
     # XXX tyGenericInst here?
+    if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
+      localError(n.info, errProcHasNoConcreteType, n.renderTree)
     if result.typ.kind == tyVar: result = newDeref(result)
   elif {efWantStmt, efAllowStmt} * flags != {}:
     result.typ = newTypeS(tyVoid, c)
@@ -218,13 +220,16 @@ proc semConv(c: PContext, n: PNode): PNode =
 proc semCast(c: PContext, n: PNode): PNode =
   ## Semantically analyze a casting ("cast[type](param)")
   checkSonsLen(n, 2)
+  let targetType = semTypeNode(c, n.sons[0], nil)
+  let castedExpr = semExprWithType(c, n.sons[1])
+  if tfHasMeta in targetType.flags:
+    localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
+  if not isCastable(targetType, castedExpr.typ):
+    localError(n.info, errExprCannotBeCastToX, $targetType)
   result = newNodeI(nkCast, n.info)
-  result.typ = semTypeNode(c, n.sons[0], nil)
+  result.typ = targetType
   addSon(result, copyTree(n.sons[0]))
-  addSon(result, semExprWithType(c, n.sons[1]))
-  if not isCastable(result.typ, result.sons[1].typ):
-    localError(result.info, errExprCannotBeCastToX,
-               typeToString(result.typ))
+  addSon(result, castedExpr)
 
 proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
   const
@@ -1054,7 +1059,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # here at all!
   #if isSymChoice(n.sons[1]): return
   when defined(nimsuggest):
-    if gCmd == cmdIdeTools: suggestExpr(c, n)
+    if gCmd == cmdIdeTools:
+      suggestExpr(c, n)
+      if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n)
 
   var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
   if s != nil:
@@ -2234,6 +2241,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
+    #when defined(nimsuggest):
+    #  if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n)
     let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
     var s = qualifiedLookUp(c, n.sons[0], mode)
     if s != nil:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 7c6e3af6d..9a1850932 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -205,7 +205,8 @@ proc semCase(c: PContext, n: PNode): PNode =
   var typ = commonTypeBegin
   var hasElse = false
   var notOrdinal = false
-  case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
+  let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
+  case caseTyp.kind
   of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
     chckCovered = true
   of tyFloat..tyFloat128, tyString, tyError:
@@ -215,6 +216,9 @@ proc semCase(c: PContext, n: PNode): PNode =
     return
   for i in countup(1, sonsLen(n) - 1):
     var x = n.sons[i]
+    when defined(nimsuggest):
+      if gIdeCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum:
+        suggestEnum(c, x, caseTyp)
     case x.kind
     of nkOfBranch:
       checkMinSonsLen(x, 2)
@@ -499,6 +503,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         if hasEmpty(typ):
           localError(def.info, errCannotInferTypeOfTheLiteral,
                      ($typ.kind).substr(2).toLowerAscii)
+        elif typ.kind == tyProc and tfUnresolved in typ.flags:
+          localError(def.info, errProcHasNoConcreteType, def.renderTree)
     else:
       if symkind == skLet: localError(a.info, errLetNeedsInit)
 
@@ -711,7 +717,11 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
   # we even look at the type definitions on the right
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
-    if gCmd == cmdIdeTools: suggestStmt(c, a)
+    when defined(nimsuggest):
+      if gCmd == cmdIdeTools:
+        inc c.inTypeContext
+        suggestStmt(c, a)
+        dec c.inTypeContext
     if a.kind == nkCommentStmt: continue
     if a.kind != nkTypeDef: illFormedAst(a)
     checkSonsLen(a, 3)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 83d0c83b2..7877a26a9 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       result.sons[0] = r
       result.n.typ = r
 
-  if genericParams != nil:
+  if genericParams != nil and genericParams.len > 0:
     for n in genericParams:
+      if {sfUsed, sfAnon} * n.sym.flags == {}:
+        result.flags.incl tfUnresolved
+
       if tfWildcard in n.sym.typ.flags:
         n.sym.kind = skType
         n.sym.typ.flags.excl tfWildcard
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index f2caab41f..bc9888df9 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1514,6 +1514,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
       if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
         copyCandidate(z, m)
         z.callee = arg.sons[i].typ
+        if tfUnresolved in z.callee.flags: continue
         z.calleeSym = arg.sons[i].sym
         #if arg.sons[i].sym.name.s == "cmp":
         #  ggDebug = true
@@ -1650,7 +1651,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
     if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
       incl(marker, formal.position)
       if container.isNil:
-        container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
+        container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
         setSon(m.call, formal.position + 1, container)
       else:
         incrIndexType(container.typ)
@@ -1738,7 +1739,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
 
         if formal.typ.isVarargsUntyped:
           if container.isNil:
-            container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
+            container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
             setSon(m.call, formal.position + 1, container)
           else:
             incrIndexType(container.typ)
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index ebabed465..f9210cc93 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -63,6 +63,7 @@ type
 var
   suggestionResultHook*: proc (result: Suggest) {.closure.}
   suggestVersion*: int
+  suggestMaxResults* = 10_000
 
 #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
 
@@ -71,7 +72,7 @@ template origModuleName(m: PSym): string = m.name.s
 proc findDocComment(n: PNode): PNode =
   if n == nil: return nil
   if not isNil(n.comment): return n
-  if n.kind in {nkStmtList, nkStmtListExpr} and n.len > 0:
+  if n.kind in {nkStmtList, nkStmtListExpr, nkObjectTy, nkRecList} and n.len > 0:
     result = findDocComment(n.sons[0])
     if result != nil: return
     if n.len > 1:
@@ -104,11 +105,11 @@ proc cmpSuggestions(a, b: Suggest): int =
   # independent of hashing order:
   result = cmp(a.name.s, b.name.s)
 
-proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
+proc symToSuggest(s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
                   quality: range[0..100]; prefix: PrefixMatch;
                   inTypeContext: bool; scope: int): Suggest =
   new(result)
-  result.section = parseIdeCmd(section)
+  result.section = section
   result.quality = quality
   result.isGlobal = sfGlobal in s.flags
   result.tokenLen = s.name.s.len
@@ -120,15 +121,10 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
     result.globalUsages = s.allUsages.len
     var c = 0
     for u in s.allUsages:
-      if u.fileIndex == li.fileIndex: inc c
+      if u.fileIndex == info.fileIndex: inc c
     result.localUsages = c
-  if optIdeTerse in gGlobalOptions:
-    result.symkind = s.kind
-    result.filePath = toFullPath(li)
-    result.line = toLinenumber(li)
-    result.column = toColumn(li)
-  else:
-    result.symkind = s.kind
+  result.symkind = s.kind
+  if optIdeTerse notin gGlobalOptions:
     result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
       let ow = s.owner
@@ -143,11 +139,12 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
       result.forth = typeToString(s.typ)
     else:
       result.forth = ""
-    result.filePath = toFullPath(li)
-    result.line = toLinenumber(li)
-    result.column = toColumn(li)
     when not defined(noDocgen):
       result.doc = s.extractDocComment
+  let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info
+  result.filePath = toFullPath(infox)
+  result.line = toLinenumber(infox)
+  result.column = toColumn(infox)
 
 proc `$`*(suggest: Suggest): string =
   result = $suggest.section
@@ -181,18 +178,13 @@ proc `$`*(suggest: Suggest): string =
     result.add(sep)
     when not defined(noDocgen):
       result.add(suggest.doc.escape)
-    if suggestVersion == 2:
+    if suggestVersion == 0:
       result.add(sep)
       result.add($suggest.quality)
       if suggest.section == ideSug:
         result.add(sep)
         result.add($suggest.prefix)
 
-proc symToSuggest(s: PSym, isLocal: bool, section: string;
-                  quality: range[0..100], prefix: PrefixMatch; inTypeContext: bool;
-                  scope: int): Suggest =
-  result = symToSuggest(s, isLocal, section, s.info, quality, prefix, inTypeContext, scope)
-
 proc suggestResult(s: Suggest) =
   if not isNil(suggestionResultHook):
     suggestionResultHook(s)
@@ -202,6 +194,10 @@ proc suggestResult(s: Suggest) =
 proc produceOutput(a: var Suggestions) =
   if gIdeCmd in {ideSug, ideCon}:
     a.sort cmpSuggestions
+  when defined(debug):
+    # debug code
+    writeStackTrace()
+  if a.len > suggestMaxResults: a.setLen(suggestMaxResults)
   if not isNil(suggestionResultHook):
     for s in a:
       suggestionResultHook(s)
@@ -237,10 +233,10 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
       result = true
       break
 
-proc suggestField(c: PContext, s: PSym; f: PNode; outputs: var Suggestions) =
+proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
   var pm: PrefixMatch
   if filterSym(s, f, pm) and fieldVisible(c, s):
-    outputs.add(symToSuggest(s, isLocal=true, $ideSug, 100, pm, c.inTypeContext > 0, 0))
+    outputs.add(symToSuggest(s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0))
 
 proc getQuality(s: PSym): range[0..100] =
   if s.typ != nil and s.typ.len > 1:
@@ -259,25 +255,25 @@ template wholeSymTab(cond, section: untyped) =
       let it {.inject.} = item
       var pm {.inject.}: PrefixMatch
       if cond:
-        outputs.add(symToSuggest(it, isLocal = isLocal, section, getQuality(it),
+        outputs.add(symToSuggest(it, isLocal = isLocal, section, info, getQuality(it),
                                  pm, c.inTypeContext > 0, scopeN))
 
-proc suggestSymList(c: PContext, list, f: PNode, outputs: var Suggestions) =
+proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) =
   for i in countup(0, sonsLen(list) - 1):
     if list.sons[i].kind == nkSym:
-      suggestField(c, list.sons[i].sym, f, outputs)
+      suggestField(c, list.sons[i].sym, f, info, outputs)
     #else: InternalError(list.info, "getSymFromList")
 
-proc suggestObject(c: PContext, n, f: PNode, outputs: var Suggestions) =
+proc suggestObject(c: PContext, n, f: PNode; info: TLineInfo, outputs: var Suggestions) =
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], f, outputs)
+    for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], f, info, outputs)
   of nkRecCase:
     var L = sonsLen(n)
     if L > 0:
-      suggestObject(c, n.sons[0], f, outputs)
-      for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, outputs)
-  of nkSym: suggestField(c, n.sym, f, outputs)
+      suggestObject(c, n.sons[0], f, info, outputs)
+      for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, info, outputs)
+  of nkSym: suggestField(c, n.sym, f, info, outputs)
   else: discard
 
 proc nameFits(c: PContext, s: PSym, n: PNode): bool =
@@ -301,8 +297,9 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
     result = false
 
 proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var Suggestions) =
+  let info = n.info
   wholeSymTab(filterSym(it, nil, pm) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
-              $ideCon)
+              ideCon)
 
 proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
   if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
@@ -319,7 +316,8 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
 
 proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Suggestions) =
   assert typ != nil
-  wholeSymTab(filterSymNoOpr(it, f, pm) and typeFits(c, it, typ), $ideSug)
+  let info = n.info
+  wholeSymTab(filterSymNoOpr(it, f, pm) and typeFits(c, it, typ), ideSug)
 
 proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
   # do not produce too many symbols:
@@ -331,8 +329,9 @@ proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
     for it in items(scope.symbols):
       var pm: PrefixMatch
       if filterSym(it, f, pm):
-        outputs.add(symToSuggest(it, isLocal = isLocal, $ideSug, 0, pm, c.inTypeContext > 0, scopeN))
-    if scope == c.topLevelScope and f.isNil: break
+        outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, n.info, 0, pm,
+                                 c.inTypeContext > 0, scopeN))
+    #if scope == c.topLevelScope and f.isNil: break
 
 proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) =
   # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
@@ -340,7 +339,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
   var typ = n.typ
   var pm: PrefixMatch
   when defined(nimsuggest):
-    if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 2:
+    if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 0:
       # consider 'foo.|' where 'foo' is some not imported module.
       let fullPath = findModule(n.sym.name.s, n.info.toFullPath)
       if fullPath.len == 0:
@@ -352,8 +351,8 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
         else:
           for it in items(n.sym.tab):
             if filterSym(it, field, pm):
-              outputs.add(symToSuggest(it, isLocal=false, $ideSug, 100, pm, c.inTypeContext > 0, -100))
-          outputs.add(symToSuggest(m, isLocal=false, $ideMod, 100, PrefixMatch.None,
+              outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
+          outputs.add(symToSuggest(m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
             c.inTypeContext > 0, -99))
 
   if typ == nil:
@@ -363,11 +362,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
         # all symbols accessible, because we are in the current module:
         for it in items(c.topLevelScope.symbols):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(it, isLocal=false, $ideSug, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
       else:
         for it in items(n.sym.tab):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(it, isLocal=false, $ideSug, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
     else:
       # fallback:
       suggestEverything(c, n, field, outputs)
@@ -375,7 +374,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
     # look up if the identifier belongs to the enum:
     var t = typ
     while t != nil:
-      suggestSymList(c, t.n, field, outputs)
+      suggestSymList(c, t.n, field, n.info, outputs)
       t = t.sons[0]
     suggestOperations(c, n, field, typ, outputs)
   else:
@@ -384,11 +383,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
     if typ.kind == tyObject:
       var t = typ
       while true:
-        suggestObject(c, t.n, field, outputs)
+        suggestObject(c, t.n, field, n.info, outputs)
         if t.sons[0] == nil: break
         t = skipTypes(t.sons[0], skipPtrs)
     elif typ.kind == tyTuple and typ.n != nil:
-      suggestSymList(c, typ.n, field, outputs)
+      suggestSymList(c, typ.n, field, n.info, outputs)
     suggestOperations(c, n, field, orig, outputs)
     if typ != orig:
       suggestOperations(c, n, field, typ, outputs)
@@ -405,36 +404,12 @@ proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
     if current.line >= gTrackPos.line:
       return cpFuzzy
 
-proc findClosestDot(n: PNode): PNode =
-  if n.kind == nkDotExpr and inCheckpoint(n.info) == cpExact:
-    result = n
-  else:
-    for i in 0.. <safeLen(n):
-      result = findClosestDot(n.sons[i])
-      if result != nil: return
-
-proc findClosestCall(n: PNode): PNode =
-  if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
-    result = n
-  else:
-    for i in 0.. <safeLen(n):
-      result = findClosestCall(n.sons[i])
-      if result != nil: return
-
 proc isTracked*(current: TLineInfo, tokenLen: int): bool =
   if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line:
     let col = gTrackPos.col
     if col >= current.col and col <= current.col+tokenLen-1:
       return true
 
-proc findClosestSym(n: PNode): PNode =
-  if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
-    result = n
-  elif n.kind notin {nkNone..nkNilLit}:
-    for i in 0.. <sonsLen(n):
-      result = findClosestSym(n.sons[i])
-      if result != nil: return
-
 when defined(nimsuggest):
   # Since TLineInfo defined a == operator that doesn't include the column,
   # we map TLineInfo to a unique int here for this lookup table:
@@ -453,26 +428,26 @@ var
   lastLineInfo*: TLineInfo
 
 proc findUsages(info: TLineInfo; s: PSym; usageSym: var PSym) =
-  if suggestVersion < 2:
+  if suggestVersion == 1:
     if usageSym == nil and isTracked(info, s.name.s.len):
       usageSym = s
-      suggestResult(symToSuggest(s, isLocal=false, $ideUse, 100, PrefixMatch.None, false, 0))
+      suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
     elif s == usageSym:
       if lastLineInfo != info:
-        suggestResult(symToSuggest(s, isLocal=false, $ideUse, info, 100, PrefixMatch.None, false, 0))
+        suggestResult(symToSuggest(s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
       lastLineInfo = info
 
 when defined(nimsuggest):
   proc listUsages*(s: PSym) =
     #echo "usages ", len(s.allUsages)
     for info in s.allUsages:
-      let x = if info == s.info and info.col == s.info.col: "def" else: "use"
+      let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse
       suggestResult(symToSuggest(s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
 
 proc findDefinition(info: TLineInfo; s: PSym) =
   if s.isNil: return
   if isTracked(info, s.name.s.len):
-    suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100, PrefixMatch.None, false, 0))
+    suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
     suggestQuit()
 
 proc ensureIdx[T](x: var T, y: int) =
@@ -484,7 +459,7 @@ proc ensureSeq[T](x: var seq[T]) =
 proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
   when defined(nimsuggest):
-    if suggestVersion == 2:
+    if suggestVersion == 0:
       if s.allUsages.isNil:
         s.allUsages = @[info]
       else:
@@ -496,13 +471,13 @@ proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.in
       findDefinition(info, s)
     elif gIdeCmd == ideDus and s != nil:
       if isTracked(info, s.name.s.len):
-        suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100, PrefixMatch.None, false, 0))
+        suggestResult(symToSuggest(s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
       findUsages(info, s, usageSym)
     elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex:
-      suggestResult(symToSuggest(s, isLocal=false, $ideHighlight, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(symToSuggest(s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
     elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
         isDecl:
-      suggestResult(symToSuggest(s, isLocal=false, $ideOutline, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(symToSuggest(s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
 
 proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) =
   incl(s.flags, sfUsed)
@@ -525,40 +500,31 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
   except ERecoverableError:
     result = ast.emptyNode
 
-proc suggestExpr*(c: PContext, node: PNode) =
-  if gTrackPos.line < 0: return
-  var cp = inCheckpoint(node.info)
-  if cp == cpNone: return
+proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
+  if n.kind == nkDotExpr:
+    var obj = safeSemExpr(c, n.sons[0])
+    # it can happen that errnously we have collected the fieldname
+    # of the next line, so we check the 'field' is actually on the same
+    # line as the object to prevent this from happening:
+    let prefix = if n.len == 2 and n[1].info.line == n[0].info.line and
+       not gTrackPosAttached: n[1] else: nil
+    suggestFieldAccess(c, obj, prefix, outputs)
+
+    #if optIdeDebug in gGlobalOptions:
+    #  echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
+    #writeStackTrace()
+  else:
+    let prefix = if gTrackPosAttached: nil else: n
+    suggestEverything(c, n, prefix, outputs)
+
+proc suggestExprNoCheck*(c: PContext, n: PNode) =
   # This keeps semExpr() from coming here recursively:
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
-
   var outputs: Suggestions = @[]
   if gIdeCmd == ideSug:
-    var n = findClosestDot(node)
-    if n == nil: n = node
-    if n.kind == nkDotExpr:
-      var obj = safeSemExpr(c, n.sons[0])
-      # it can happen that errnously we have collected the fieldname
-      # of the next line, so we check the 'field' is actually on the same
-      # line as the object to prevent this from happening:
-      let prefix = if n.len == 2 and n[1].info.line == n[0].info.line: n[1] else: nil
-      suggestFieldAccess(c, obj, prefix, outputs)
-
-      #if optIdeDebug in gGlobalOptions:
-      #  echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
-      #writeStackTrace()
-    else:
-      #let m = findClosestSym(node)
-      #if m != nil:
-      #  suggestPrefix(c, m, outputs)
-      #else:
-      let prefix = if cp == cpExact: n else: nil
-      suggestEverything(c, n, prefix, outputs)
-
+    sugExpr(c, n, outputs)
   elif gIdeCmd == ideCon:
-    var n = findClosestCall(node)
-    if n == nil: n = node
     if n.kind in nkCallKinds:
       var a = copyNode(n)
       var x = safeSemExpr(c, n.sons[0])
@@ -576,16 +542,32 @@ proc suggestExpr*(c: PContext, node: PNode) =
     produceOutput(outputs)
     suggestQuit()
 
+proc suggestExpr*(c: PContext, n: PNode) =
+  if exactEquals(gTrackPos, n.info): suggestExprNoCheck(c, n)
+
+proc suggestDecl*(c: PContext, n: PNode; s: PSym) =
+  let attached = gTrackPosAttached
+  if attached: inc(c.inTypeContext)
+  defer:
+    if attached: dec(c.inTypeContext)
+  suggestExpr(c, n)
+
 proc suggestStmt*(c: PContext, n: PNode) =
   suggestExpr(c, n)
 
+proc suggestEnum*(c: PContext; n: PNode; t: PType) =
+  var outputs: Suggestions = @[]
+  suggestSymList(c, t.n, nil, n.info, outputs)
+  produceOutput(outputs)
+  if outputs.len > 0: suggestQuit()
+
 proc suggestSentinel*(c: PContext) =
   if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
+  var outputs: Suggestions = @[]
   # suggest everything:
   var isLocal = true
-  var outputs: Suggestions = @[]
   var scopeN = 0
   for scope in walkScopes(c.currentScope):
     if scope == c.topLevelScope: isLocal = false
@@ -593,7 +575,7 @@ proc suggestSentinel*(c: PContext) =
     for it in items(scope.symbols):
       var pm: PrefixMatch
       if filterSymNoOpr(it, nil, pm):
-        outputs.add(symToSuggest(it, isLocal = isLocal, $ideSug, 0, PrefixMatch.None, false, scopeN))
+        outputs.add(symToSuggest(it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN))
 
-  produceOutput(outputs)
   dec(c.compilesContextId)
+  produceOutput(outputs)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 0c53c0cbf..771dc58f4 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -291,10 +291,13 @@ proc transformBreak(c: PTransf, n: PNode): PTransNode =
       else:
         result = newTransNode(n.kind, n.info, 1)
         result[0] = lablCopy.PTransNode
-  else:
+  elif c.breakSyms.len > 0:
+    # this check can fail for 'nim check'
     let labl = c.breakSyms[c.breakSyms.high]
     result = transformSons(c, n)
     result[0] = newSymNode(labl).PTransNode
+  else:
+    result = n.PTransNode
 
 proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
   # XXX: BUG: what if `n` is an expression with side-effects?
diff --git a/compiler/types.nim b/compiler/types.nim
index df1d3e3ca..f4ef75094 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -20,6 +20,7 @@ type
     preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
 
 proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
+template `$`*(typ: PType): string = typeToString(typ)
 
 proc base*(t: PType): PType =
   result = t.sons[0]
@@ -547,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     if prefer != preferExported:
       result.add("(" & typeToString(t.sons[0]) & ")")
   of tyProc:
-    result = if tfIterator in t.flags: "iterator (" else: "proc ("
+    result = if tfIterator in t.flags: "iterator " else: "proc "
+    if tfUnresolved in t.flags: result.add "[*missing parameters*]"
+    result.add "("
     for i in countup(1, sonsLen(t) - 1):
       if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
         add(result, t.n[i].sym.name.s)