msgs.nim 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2013 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import
  10. options, strutils, os, tables, ropes, terminal, macros,
  11. lineinfos, pathutils
  12. import std/private/miscdollars
  13. import strutils2
  14. type InstantiationInfo* = typeof(instantiationInfo())
  15. template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
  16. template flushDot(conf, stdorr) =
  17. ## safe to call multiple times
  18. if conf.lastMsgWasDot:
  19. conf.lastMsgWasDot = false
  20. write(stdorr, "\n")
  21. proc toCChar*(c: char; result: var string) =
  22. case c
  23. of '\0'..'\x1F', '\x7F'..'\xFF':
  24. result.add '\\'
  25. result.add toOctal(c)
  26. of '\'', '\"', '\\', '?':
  27. result.add '\\'
  28. result.add c
  29. else:
  30. result.add c
  31. proc makeCString*(s: string): Rope =
  32. const MaxLineLength = 64
  33. result = nil
  34. var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
  35. res.add("\"")
  36. for i in 0..<s.len:
  37. if (i + 1) mod MaxLineLength == 0:
  38. res.add("\"\L\"")
  39. toCChar(s[i], res)
  40. res.add('\"')
  41. result.add(rope(res))
  42. proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
  43. result.fullPath = fullPath
  44. #shallow(result.fullPath)
  45. result.projPath = projPath
  46. #shallow(result.projPath)
  47. result.shortName = fullPath.extractFilename
  48. result.quotedName = result.shortName.makeCString
  49. result.quotedFullName = fullPath.string.makeCString
  50. result.lines = @[]
  51. when defined(nimpretty):
  52. if not result.fullPath.isEmpty:
  53. try:
  54. result.fullContent = readFile(result.fullPath.string)
  55. except IOError:
  56. #rawMessage(errCannotOpenFile, result.fullPath)
  57. # XXX fixme
  58. result.fullContent = ""
  59. when defined(nimpretty):
  60. proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
  61. substr(conf.m.fileInfos[fid.int].fullContent, a, b)
  62. proc canonicalCase(path: var string) =
  63. ## the idea is to only use this for checking whether a path is already in
  64. ## the table but otherwise keep the original case
  65. when FileSystemCaseSensitive: discard
  66. else: toLowerAscii(path)
  67. proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool =
  68. var
  69. canon: AbsoluteFile
  70. try:
  71. canon = canonicalizePath(conf, filename)
  72. except OSError:
  73. canon = filename
  74. canon.string.canonicalCase
  75. result = conf.m.filenameToIndexTbl.hasKey(canon.string)
  76. proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool): FileIndex =
  77. var
  78. canon: AbsoluteFile
  79. pseudoPath = false
  80. try:
  81. canon = canonicalizePath(conf, filename)
  82. shallow(canon.string)
  83. except OSError:
  84. canon = filename
  85. # The compiler uses "filenames" such as `command line` or `stdin`
  86. # This flag indicates that we are working with such a path here
  87. pseudoPath = true
  88. var canon2: string
  89. forceCopy(canon2, canon.string) # because `canon` may be shallow
  90. canon2.canonicalCase
  91. if conf.m.filenameToIndexTbl.hasKey(canon2):
  92. isKnownFile = true
  93. result = conf.m.filenameToIndexTbl[canon2]
  94. else:
  95. isKnownFile = false
  96. result = conf.m.fileInfos.len.FileIndex
  97. conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
  98. else: relativeTo(canon, conf.projectPath)))
  99. conf.m.filenameToIndexTbl[canon2] = result
  100. proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
  101. var dummy: bool
  102. result = fileInfoIdx(conf, filename, dummy)
  103. proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
  104. result.fileIndex = fileInfoIdx
  105. if line < int high(uint16):
  106. result.line = uint16(line)
  107. else:
  108. result.line = high(uint16)
  109. if col < int high(int16):
  110. result.col = int16(col)
  111. else:
  112. result.col = -1
  113. proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} =
  114. result = newLineInfo(fileInfoIdx(conf, filename), line, col)
  115. const gCmdLineInfo* = newLineInfo(commandLineIdx, 1, 1)
  116. proc concat(strings: openArray[string]): string =
  117. var totalLen = 0
  118. for s in strings: totalLen += s.len
  119. result = newStringOfCap totalLen
  120. for s in strings: result.add s
  121. proc suggestWriteln*(conf: ConfigRef; s: string) =
  122. if eStdOut in conf.m.errorOutputs:
  123. if isNil(conf.writelnHook):
  124. writeLine(stdout, s)
  125. flushFile(stdout)
  126. else:
  127. conf.writelnHook(s)
  128. proc msgQuit*(x: int8) = quit x
  129. proc msgQuit*(x: string) = quit x
  130. proc suggestQuit*() =
  131. raise newException(ESuggestDone, "suggest done")
  132. # this format is understood by many text editors: it is the same that
  133. # Borland and Freepascal use
  134. const
  135. KindFormat = " [$1]"
  136. KindColor = fgCyan
  137. ErrorTitle = "Error: "
  138. ErrorColor = fgRed
  139. WarningTitle = "Warning: "
  140. WarningColor = fgYellow
  141. HintTitle = "Hint: "
  142. HintColor = fgGreen
  143. # NOTE: currently line info line numbers start with 1,
  144. # but column numbers start with 0, however most editors expect
  145. # first column to be 1, so we need to +1 here
  146. ColOffset* = 1
  147. commandLineDesc* = "command line"
  148. proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
  149. proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
  150. proc pushInfoContext*(conf: ConfigRef; info: TLineInfo; detail: string = "") =
  151. conf.m.msgContext.add((info, detail))
  152. proc popInfoContext*(conf: ConfigRef) =
  153. setLen(conf.m.msgContext, conf.m.msgContext.len - 1)
  154. proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
  155. let i = if index < 0: conf.m.msgContext.len + index else: index
  156. if i >=% conf.m.msgContext.len: result = unknownLineInfo
  157. else: result = conf.m.msgContext[i].info
  158. template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
  159. if fileIdx.int32 < 0 or conf == nil:
  160. (if fileIdx == commandLineIdx: commandLineDesc else: "???")
  161. else:
  162. conf.m.fileInfos[fileIdx.int32].shortName
  163. proc toProjPath*(conf: ConfigRef; fileIdx: FileIndex): string =
  164. if fileIdx.int32 < 0 or conf == nil:
  165. (if fileIdx == commandLineIdx: commandLineDesc else: "???")
  166. else: conf.m.fileInfos[fileIdx.int32].projPath.string
  167. proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
  168. if fileIdx.int32 < 0 or conf == nil:
  169. result = (if fileIdx == commandLineIdx: commandLineDesc else: "???")
  170. else:
  171. result = conf.m.fileInfos[fileIdx.int32].fullPath.string
  172. proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) =
  173. assert fileIdx.int32 >= 0
  174. conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
  175. setLen conf.m.fileInfos[fileIdx.int32].lines, 0
  176. proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
  177. assert fileIdx.int32 >= 0
  178. shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
  179. proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
  180. assert fileIdx.int32 >= 0
  181. shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
  182. proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile =
  183. if fileIdx.int32 < 0:
  184. result = AbsoluteFile(if fileIdx == commandLineIdx: commandLineDesc else: "???")
  185. elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isEmpty:
  186. result = conf.m.fileInfos[fileIdx.int32].dirtyFile
  187. else:
  188. result = conf.m.fileInfos[fileIdx.int32].fullPath
  189. template toFilename*(conf: ConfigRef; info: TLineInfo): string =
  190. toFilename(conf, info.fileIndex)
  191. template toProjPath*(conf: ConfigRef; info: TLineInfo): string =
  192. toProjPath(conf, info.fileIndex)
  193. template toFullPath*(conf: ConfigRef; info: TLineInfo): string =
  194. toFullPath(conf, info.fileIndex)
  195. template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string =
  196. string toFullPathConsiderDirty(conf, info.fileIndex)
  197. type FilenameOption* = enum
  198. foAbs # absolute path, eg: /pathto/bar/foo.nim
  199. foRelProject # relative to project path, eg: ../foo.nim
  200. foMagicSauce # magic sauce, shortest of (foAbs, foRelProject)
  201. foName # lastPathPart, eg: foo.nim
  202. foShort # foName without extension, eg: foo
  203. foStacktrace # if optExcessiveStackTrace: foAbs else: foName
  204. proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string =
  205. case opt
  206. of foAbs: result = toFullPath(conf, fileIdx)
  207. of foRelProject: result = toProjPath(conf, fileIdx)
  208. of foMagicSauce:
  209. let
  210. absPath = toFullPath(conf, fileIdx)
  211. relPath = toProjPath(conf, fileIdx)
  212. result = if (optListFullPaths in conf.globalOptions) or
  213. (relPath.len > absPath.len) or
  214. (relPath.count("..") > 2):
  215. absPath
  216. else:
  217. relPath
  218. of foName: result = toProjPath(conf, fileIdx).lastPathPart
  219. of foShort: result = toFilename(conf, fileIdx)
  220. of foStacktrace:
  221. if optExcessiveStackTrace in conf.globalOptions:
  222. result = toFilenameOption(conf, fileIdx, foAbs)
  223. else:
  224. result = toFilenameOption(conf, fileIdx, foName)
  225. proc toMsgFilename*(conf: ConfigRef; info: FileIndex): string =
  226. toFilenameOption(conf, info, foMagicSauce)
  227. template toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
  228. toMsgFilename(conf, info.fileIndex)
  229. proc toLinenumber*(info: TLineInfo): int {.inline.} =
  230. result = int info.line
  231. proc toColumn*(info: TLineInfo): int {.inline.} =
  232. result = info.col
  233. proc toFileLineCol(info: InstantiationInfo): string {.inline.} =
  234. result.toLocation(info.filename, info.line, info.column + ColOffset)
  235. proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
  236. result.toLocation(toMsgFilename(conf, info), info.line.int, info.col.int + ColOffset)
  237. proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
  238. proc `$`*(info: TLineInfo): string {.error.} = discard
  239. proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool =
  240. # only for debugging purposes
  241. result = filename in toFilename(conf, info)
  242. type
  243. MsgFlag* = enum ## flags altering msgWriteln behavior
  244. msgStdout, ## force writing to stdout, even stderr is default
  245. msgSkipHook ## skip message hook even if it is present
  246. MsgFlags* = set[MsgFlag]
  247. proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
  248. ## Writes given message string to stderr by default.
  249. ## If ``--stdout`` option is given, writes to stdout instead. If message hook
  250. ## is present, then it is used to output message rather than stderr/stdout.
  251. ## This behavior can be altered by given optional flags.
  252. ## This is used for 'nim dump' etc. where we don't have nimsuggest
  253. ## support.
  254. #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
  255. if not isNil(conf.writelnHook) and msgSkipHook notin flags:
  256. conf.writelnHook(s)
  257. elif optStdout in conf.globalOptions or msgStdout in flags:
  258. if eStdOut in conf.m.errorOutputs:
  259. flushDot(conf, stdout)
  260. writeLine(stdout, s)
  261. flushFile(stdout)
  262. else:
  263. if eStdErr in conf.m.errorOutputs:
  264. flushDot(conf, stderr)
  265. writeLine(stderr, s)
  266. # On Windows stderr is fully-buffered when piped, regardless of C std.
  267. when defined(windows):
  268. flushFile(stderr)
  269. macro callIgnoringStyle(theProc: typed, first: typed,
  270. args: varargs[typed]): untyped =
  271. let typForegroundColor = bindSym"ForegroundColor".getType
  272. let typBackgroundColor = bindSym"BackgroundColor".getType
  273. let typStyle = bindSym"Style".getType
  274. let typTerminalCmd = bindSym"TerminalCmd".getType
  275. result = newCall(theProc)
  276. if first.kind != nnkNilLit: result.add(first)
  277. for arg in children(args[0][1]):
  278. if arg.kind == nnkNilLit: continue
  279. let typ = arg.getType
  280. if typ.kind != nnkEnumTy or
  281. typ != typForegroundColor and
  282. typ != typBackgroundColor and
  283. typ != typStyle and
  284. typ != typTerminalCmd:
  285. result.add(arg)
  286. macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
  287. result = newCall(bindSym"styledWriteLine")
  288. result.add(bindSym"stderr")
  289. for arg in children(args[0][1]):
  290. result.add(arg)
  291. when false:
  292. # not needed because styledWriteLine already ends with resetAttributes
  293. result = newStmtList(result, newCall(bindSym"resetAttributes", bindSym"stderr"))
  294. template callWritelnHook(args: varargs[string, `$`]) =
  295. conf.writelnHook concat(args)
  296. proc msgWrite(conf: ConfigRef; s: string) =
  297. if conf.m.errorOutputs != {}:
  298. let stdOrr =
  299. if optStdout in conf.globalOptions:
  300. stdout
  301. else:
  302. stderr
  303. write(stdOrr, s)
  304. flushFile(stdOrr)
  305. conf.lastMsgWasDot = true # subsequent writes need `flushDot`
  306. template styledMsgWriteln*(args: varargs[typed]) =
  307. if not isNil(conf.writelnHook):
  308. callIgnoringStyle(callWritelnHook, nil, args)
  309. elif optStdout in conf.globalOptions:
  310. if eStdOut in conf.m.errorOutputs:
  311. flushDot(conf, stdout)
  312. callIgnoringStyle(writeLine, stdout, args)
  313. flushFile(stdout)
  314. elif eStdErr in conf.m.errorOutputs:
  315. flushDot(conf, stderr)
  316. if optUseColors in conf.globalOptions:
  317. callStyledWriteLineStderr(args)
  318. else:
  319. callIgnoringStyle(writeLine, stderr, args)
  320. # On Windows stderr is fully-buffered when piped, regardless of C std.
  321. when defined(windows):
  322. flushFile(stderr)
  323. proc msgKindToString*(kind: TMsgKind): string =
  324. # later versions may provide translated error messages
  325. result = MsgKindToStr[kind]
  326. proc getMessageStr(msg: TMsgKind, arg: string): string =
  327. result = msgKindToString(msg) % [arg]
  328. type
  329. TErrorHandling* = enum doNothing, doAbort, doRaise
  330. proc log*(s: string) =
  331. var f: File
  332. if open(f, getHomeDir() / "nimsuggest.log", fmAppend):
  333. f.writeLine(s)
  334. close(f)
  335. proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} =
  336. if defined(debug) or msg == errInternal or conf.hasHint(hintStackTrace):
  337. {.gcsafe.}:
  338. if stackTraceAvailable() and isNil(conf.writelnHook):
  339. writeStackTrace()
  340. else:
  341. styledMsgWriteln(fgRed, """
  342. No stack traceback available
  343. To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 for details""" %
  344. [conf.command, "intern.html#debugging-the-compiler".createDocLink])
  345. quit 1
  346. proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) =
  347. if msg >= fatalMin and msg <= fatalMax:
  348. if conf.cmd == cmdIdeTools: log(s)
  349. quit(conf, msg)
  350. if msg >= errMin and msg <= errMax or
  351. (msg in warnMin..warnMax and msg in conf.warningAsErrors):
  352. inc(conf.errorCounter)
  353. conf.exitcode = 1'i8
  354. if conf.errorCounter >= conf.errorMax:
  355. quit(conf, msg)
  356. elif eh == doAbort and conf.cmd != cmdIdeTools:
  357. quit(conf, msg)
  358. elif eh == doRaise:
  359. raiseRecoverableError(s)
  360. proc `==`*(a, b: TLineInfo): bool =
  361. result = a.line == b.line and a.fileIndex == b.fileIndex
  362. proc exactEquals*(a, b: TLineInfo): bool =
  363. result = a.fileIndex == b.fileIndex and a.line == b.line and a.col == b.col
  364. proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
  365. const instantiationFrom = "template/generic instantiation from here"
  366. const instantiationOfFrom = "template/generic instantiation of `$1` from here"
  367. var info = lastinfo
  368. for i in 0..<conf.m.msgContext.len:
  369. let context = conf.m.msgContext[i]
  370. if context.info != lastinfo and context.info != info:
  371. if conf.structuredErrorHook != nil:
  372. conf.structuredErrorHook(conf, context.info, instantiationFrom,
  373. Severity.Hint)
  374. else:
  375. let message = if context.detail == "":
  376. instantiationFrom
  377. else:
  378. instantiationOfFrom.format(context.detail)
  379. styledMsgWriteln(styleBright, conf.toFileLineCol(context.info), " ", resetStyle, message)
  380. info = context.info
  381. proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
  382. msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
  383. proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
  384. conf.m.fileInfos[fileIdx.int32].lines.add line
  385. proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
  386. ## xxx there's an off by 1 error that should be fixed; if a file ends with "foo" or "foo\n"
  387. ## it will return same number of lines (ie, a trailing empty line is discounted)
  388. result = conf.m.fileInfos[fileIdx.int32].lines.len
  389. if result == 0:
  390. try:
  391. for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
  392. addSourceLine conf, fileIdx, line.string
  393. except IOError:
  394. discard
  395. result = conf.m.fileInfos[fileIdx.int32].lines.len
  396. proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
  397. ## 1-based index (matches editor line numbers); 1st line is for i.line = 1
  398. ## last valid line is `numLines` inclusive
  399. if i.fileIndex.int32 < 0: return ""
  400. let num = numLines(conf, i.fileIndex)
  401. # can happen if the error points to EOF:
  402. if i.line.int > num: return ""
  403. result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1]
  404. proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) =
  405. const indent = " "
  406. msgWriteln(conf, indent & $sourceLine(conf, info))
  407. if info.col >= 0:
  408. msgWriteln(conf, indent & spaces(info.col) & '^')
  409. proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
  410. let title = case msg
  411. of warnMin..warnMax: WarningTitle
  412. of hintMin..hintMax: HintTitle
  413. else: ErrorTitle
  414. conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
  415. proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
  416. eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.noinline.} =
  417. var
  418. title: string
  419. color: ForegroundColor
  420. ignoreMsg = false
  421. sev: Severity
  422. let kind = if msg != hintUserRaw: msg.msgToStr else: "" # xxx not sure why hintUserRaw is special
  423. case msg
  424. of errMin..errMax:
  425. sev = Severity.Error
  426. writeContext(conf, info)
  427. title = ErrorTitle
  428. color = ErrorColor
  429. when false:
  430. # we try to filter error messages so that not two error message
  431. # in the same file and line are produced:
  432. # xxx `lastError` is only used in this disabled code; but could be useful to revive
  433. ignoreMsg = conf.m.lastError == info and info != unknownLineInfo and eh != doAbort
  434. if info != unknownLineInfo: conf.m.lastError = info
  435. of warnMin..warnMax:
  436. sev = Severity.Warning
  437. ignoreMsg = not conf.hasWarn(msg)
  438. if msg in conf.warningAsErrors:
  439. ignoreMsg = false
  440. title = ErrorTitle
  441. else:
  442. title = WarningTitle
  443. if not ignoreMsg: writeContext(conf, info)
  444. color = WarningColor
  445. inc(conf.warnCounter)
  446. of hintMin..hintMax:
  447. sev = Severity.Hint
  448. ignoreMsg = not conf.hasHint(msg)
  449. title = HintTitle
  450. color = HintColor
  451. inc(conf.hintCounter)
  452. let s = if isRaw: arg else: getMessageStr(msg, arg)
  453. if not ignoreMsg:
  454. let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: ""
  455. var kindmsg = if kind.len > 0: KindFormat % kind else: ""
  456. if conf.structuredErrorHook != nil:
  457. conf.structuredErrorHook(conf, info, s & kindmsg, sev)
  458. if not ignoreMsgBecauseOfIdeTools(conf, msg):
  459. if msg == hintProcessing:
  460. msgWrite(conf, ".")
  461. else:
  462. styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg)
  463. if conf.hasHint(hintSource) and info != unknownLineInfo:
  464. conf.writeSurroundingSrc(info)
  465. if hintMsgOrigin in conf.mainPackageNotes:
  466. styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle,
  467. " compiler msg initiated here", KindColor,
  468. KindFormat % hintMsgOrigin.msgToStr,
  469. resetStyle)
  470. handleError(conf, msg, eh, s)
  471. template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
  472. let arg = msgKindToString(msg) % args
  473. liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc(), isRaw = true)
  474. template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
  475. liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc())
  476. template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
  477. # this fixes bug #7080 so that it is at least obvious 'fatal' was executed.
  478. conf.m.errorOutputs = {eStdOut, eStdErr}
  479. liMessage(conf, info, msg, arg, doAbort, instLoc())
  480. template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") =
  481. ## avoids boilerplate
  482. if not cond:
  483. var arg2 = "'$1' failed" % [astToStr(cond)]
  484. if arg.len > 0: arg2.add "; " & astToStr(arg) & ": " & arg
  485. liMessage(conf, info, errGenerated, arg2, doRaise, instLoc())
  486. template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
  487. ## `local` means compilation keeps going until errorMax is reached (via `doNothing`),
  488. ## `global` means it stops.
  489. liMessage(conf, info, msg, arg, doRaise, instLoc())
  490. template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
  491. liMessage(conf, info, errGenerated, arg, doRaise, instLoc())
  492. template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
  493. liMessage(conf, info, msg, arg, doNothing, instLoc())
  494. template localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
  495. liMessage(conf, info, errGenerated, arg, doNothing, instLoc())
  496. template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) =
  497. liMessage(conf, info, errGenerated, format % params, doNothing, instLoc())
  498. template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
  499. liMessage(conf, info, msg, arg, doNothing, instLoc())
  500. proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
  501. if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
  502. writeContext(conf, info)
  503. liMessage(conf, info, errInternal, errMsg, doAbort, info2)
  504. template internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
  505. internalErrorImpl(conf, info, errMsg, instLoc())
  506. template internalError*(conf: ConfigRef; errMsg: string) =
  507. internalErrorImpl(conf, unknownLineInfo, errMsg, instLoc())
  508. template internalAssert*(conf: ConfigRef, e: bool) =
  509. # xxx merge with `globalAssert`
  510. if not e:
  511. const info2 = instLoc()
  512. let arg = info2.toFileLineCol
  513. internalErrorImpl(conf, unknownLineInfo, arg, info2)
  514. template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) =
  515. let m = "'$2' should be: '$1'" % [beau, got]
  516. let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
  517. liMessage(conf, info, msg, m, doNothing, instLoc())
  518. proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
  519. if i.fileIndex.int32 < 0:
  520. result = makeCString "???"
  521. elif optExcessiveStackTrace in conf.globalOptions:
  522. result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
  523. else:
  524. result = conf.m.fileInfos[i.fileIndex.int32].quotedName
  525. template listMsg(title, r) =
  526. msgWriteln(conf, title)
  527. for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", a.msgToStr])
  528. proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
  529. proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)