msgs.nim 20 KB

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