msgs.nim 21 KB

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