123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2013 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- import
- std/[strutils, os, tables, terminal, macros, times],
- std/private/miscdollars,
- options, lineinfos, pathutils
- import ropes except `%`
- when defined(nimPreviewSlimSystem):
- import std/[syncio, assertions]
- type InstantiationInfo* = typeof(instantiationInfo())
- template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
- template toStdOrrKind(stdOrr): untyped =
- if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
- proc toLowerAscii(a: var string) {.inline.} =
- for c in mitems(a):
- if isUpperAscii(c): c = char(uint8(c) xor 0b0010_0000'u8)
- proc flushDot*(conf: ConfigRef) =
- ## safe to call multiple times
- # xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`.
- let stdOrr = if optStdout in conf.globalOptions: stdout else: stderr
- let stdOrrKind = toStdOrrKind(stdOrr)
- if stdOrrKind in conf.lastMsgWasDot:
- conf.lastMsgWasDot.excl stdOrrKind
- write(stdOrr, "\n")
- proc toCChar*(c: char; result: var string) {.inline.} =
- case c
- of '\0'..'\x1F', '\x7F'..'\xFF':
- result.add '\\'
- result.add toOctal(c)
- of '\'', '\"', '\\', '?':
- result.add '\\'
- result.add c
- else:
- result.add c
- proc makeCString*(s: string): Rope =
- result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
- result.add("\"")
- for i in 0..<s.len:
- # line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
- # It also makes reading c sources or grepping harder, for zero benefit.
- # const MaxLineLength = 64
- # if (i + 1) mod MaxLineLength == 0:
- # res.add("\"\L\"")
- toCChar(s[i], result)
- result.add('\"')
- proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
- result.fullPath = fullPath
- #shallow(result.fullPath)
- result.projPath = projPath
- #shallow(result.projPath)
- result.shortName = fullPath.extractFilename
- result.quotedName = result.shortName.makeCString
- result.quotedFullName = fullPath.string.makeCString
- result.lines = @[]
- when defined(nimpretty):
- if not result.fullPath.isEmpty:
- try:
- result.fullContent = readFile(result.fullPath.string)
- except IOError:
- #rawMessage(errCannotOpenFile, result.fullPath)
- # XXX fixme
- result.fullContent = ""
- when defined(nimpretty):
- proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
- substr(conf.m.fileInfos[fid.int].fullContent, a, b)
- proc canonicalCase(path: var string) {.inline.} =
- ## the idea is to only use this for checking whether a path is already in
- ## the table but otherwise keep the original case
- when FileSystemCaseSensitive: discard
- else: toLowerAscii(path)
- proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool =
- var
- canon: AbsoluteFile
- try:
- canon = canonicalizePath(conf, filename)
- except OSError:
- canon = filename
- canon.string.canonicalCase
- result = conf.m.filenameToIndexTbl.hasKey(canon.string)
- proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool): FileIndex =
- var
- canon: AbsoluteFile
- pseudoPath = false
- try:
- canon = canonicalizePath(conf, filename)
- except OSError:
- canon = filename
- # The compiler uses "filenames" such as `command line` or `stdin`
- # This flag indicates that we are working with such a path here
- pseudoPath = true
- var canon2 = canon.string
- canon2.canonicalCase
- if conf.m.filenameToIndexTbl.hasKey(canon2):
- isKnownFile = true
- result = conf.m.filenameToIndexTbl[canon2]
- else:
- isKnownFile = false
- result = conf.m.fileInfos.len.FileIndex
- conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
- else: relativeTo(canon, conf.projectPath)))
- conf.m.filenameToIndexTbl[canon2] = result
- proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
- var dummy: bool
- result = fileInfoIdx(conf, filename, dummy)
- proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex =
- fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile)
- proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
- var dummy: bool
- fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
- proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
- result.fileIndex = fileInfoIdx
- if line < int high(uint16):
- result.line = uint16(line)
- else:
- result.line = high(uint16)
- if col < int high(int16):
- result.col = int16(col)
- else:
- result.col = -1
- proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} =
- result = newLineInfo(fileInfoIdx(conf, filename), line, col)
- const gCmdLineInfo* = newLineInfo(commandLineIdx, 1, 1)
- proc concat(strings: openArray[string]): string =
- var totalLen = 0
- for s in strings: totalLen += s.len
- result = newStringOfCap totalLen
- for s in strings: result.add s
- proc suggestWriteln*(conf: ConfigRef; s: string) =
- if eStdOut in conf.m.errorOutputs:
- if isNil(conf.writelnHook):
- writeLine(stdout, s)
- flushFile(stdout)
- else:
- conf.writelnHook(s)
- proc msgQuit*(x: int8) = quit x
- proc msgQuit*(x: string) = quit x
- proc suggestQuit*() =
- raise newException(ESuggestDone, "suggest done")
- # this format is understood by many text editors: it is the same that
- # Borland and Freepascal use
- const
- KindFormat = " [$1]"
- KindColor = fgCyan
- ErrorTitle = "Error: "
- ErrorColor = fgRed
- WarningTitle = "Warning: "
- WarningColor = fgYellow
- HintTitle = "Hint: "
- HintColor = fgGreen
- # NOTE: currently line info line numbers start with 1,
- # but column numbers start with 0, however most editors expect
- # first column to be 1, so we need to +1 here
- ColOffset* = 1
- commandLineDesc* = "command line"
- proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
- proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
- proc pushInfoContext*(conf: ConfigRef; info: TLineInfo; detail: string = "") =
- conf.m.msgContext.add((info, detail))
- proc popInfoContext*(conf: ConfigRef) =
- setLen(conf.m.msgContext, conf.m.msgContext.len - 1)
- proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
- let i = if index < 0: conf.m.msgContext.len + index else: index
- if i >=% conf.m.msgContext.len: result = unknownLineInfo
- else: result = conf.m.msgContext[i].info
- template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
- if fileIdx.int32 < 0 or conf == nil:
- (if fileIdx == commandLineIdx: commandLineDesc else: "???")
- else:
- conf.m.fileInfos[fileIdx.int32].shortName
- proc toProjPath*(conf: ConfigRef; fileIdx: FileIndex): string =
- if fileIdx.int32 < 0 or conf == nil:
- (if fileIdx == commandLineIdx: commandLineDesc else: "???")
- else: conf.m.fileInfos[fileIdx.int32].projPath.string
- proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
- if fileIdx.int32 < 0 or conf == nil:
- result = (if fileIdx == commandLineIdx: commandLineDesc else: "???")
- else:
- result = conf.m.fileInfos[fileIdx.int32].fullPath.string
- proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) =
- assert fileIdx.int32 >= 0
- conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
- setLen conf.m.fileInfos[fileIdx.int32].lines, 0
- proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
- assert fileIdx.int32 >= 0
- when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
- conf.m.fileInfos[fileIdx.int32].hash = hash
- else:
- shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
- proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
- assert fileIdx.int32 >= 0
- when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
- result = conf.m.fileInfos[fileIdx.int32].hash
- else:
- shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
- proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile =
- if fileIdx.int32 < 0:
- result = AbsoluteFile(if fileIdx == commandLineIdx: commandLineDesc else: "???")
- elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isEmpty:
- result = conf.m.fileInfos[fileIdx.int32].dirtyFile
- else:
- result = conf.m.fileInfos[fileIdx.int32].fullPath
- template toFilename*(conf: ConfigRef; info: TLineInfo): string =
- toFilename(conf, info.fileIndex)
- template toProjPath*(conf: ConfigRef; info: TLineInfo): string =
- toProjPath(conf, info.fileIndex)
- template toFullPath*(conf: ConfigRef; info: TLineInfo): string =
- toFullPath(conf, info.fileIndex)
- template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string =
- string toFullPathConsiderDirty(conf, info.fileIndex)
- proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string =
- case opt
- of foAbs: result = toFullPath(conf, fileIdx)
- of foRelProject: result = toProjPath(conf, fileIdx)
- of foCanonical:
- let absPath = toFullPath(conf, fileIdx)
- result = canonicalImportAux(conf, absPath.AbsoluteFile)
- of foName: result = toProjPath(conf, fileIdx).lastPathPart
- of foLegacyRelProj:
- let
- absPath = toFullPath(conf, fileIdx)
- relPath = toProjPath(conf, fileIdx)
- result = if (relPath.len > absPath.len) or (relPath.count("..") > 2):
- absPath
- else:
- relPath
- of foStacktrace:
- if optExcessiveStackTrace in conf.globalOptions:
- result = toFilenameOption(conf, fileIdx, foAbs)
- else:
- result = toFilenameOption(conf, fileIdx, foName)
- proc toMsgFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
- toFilenameOption(conf, fileIdx, conf.filenameOption)
- template toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
- toMsgFilename(conf, info.fileIndex)
- proc toLinenumber*(info: TLineInfo): int {.inline.} =
- result = int info.line
- proc toColumn*(info: TLineInfo): int {.inline.} =
- result = info.col
- proc toFileLineCol(info: InstantiationInfo): string {.inline.} =
- result = ""
- result.toLocation(info.filename, info.line, info.column + ColOffset)
- proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
- result = ""
- result.toLocation(toMsgFilename(conf, info), info.line.int, info.col.int + ColOffset)
- proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
- proc `$`*(info: TLineInfo): string {.error.} = discard
- proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool =
- # only for debugging purposes
- result = filename in toFilename(conf, info)
- type
- MsgFlag* = enum ## flags altering msgWriteln behavior
- msgStdout, ## force writing to stdout, even stderr is default
- msgSkipHook ## skip message hook even if it is present
- msgNoUnitSep ## the message is a complete "paragraph".
- MsgFlags* = set[MsgFlag]
- proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
- ## Writes given message string to stderr by default.
- ## If ``--stdout`` option is given, writes to stdout instead. If message hook
- ## is present, then it is used to output message rather than stderr/stdout.
- ## This behavior can be altered by given optional flags.
- ## This is used for 'nim dump' etc. where we don't have nimsuggest
- ## support.
- #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
- let sep = if msgNoUnitSep notin flags: conf.unitSep else: ""
- if not isNil(conf.writelnHook) and msgSkipHook notin flags:
- conf.writelnHook(s & sep)
- elif optStdout in conf.globalOptions or msgStdout in flags:
- if eStdOut in conf.m.errorOutputs:
- flushDot(conf)
- write stdout, s
- writeLine(stdout, sep)
- flushFile(stdout)
- else:
- if eStdErr in conf.m.errorOutputs:
- flushDot(conf)
- write stderr, s
- writeLine(stderr, sep)
- # On Windows stderr is fully-buffered when piped, regardless of C std.
- when defined(windows):
- flushFile(stderr)
- macro callIgnoringStyle(theProc: typed, first: typed,
- args: varargs[typed]): untyped =
- let typForegroundColor = bindSym"ForegroundColor".getType
- let typBackgroundColor = bindSym"BackgroundColor".getType
- let typStyle = bindSym"Style".getType
- let typTerminalCmd = bindSym"TerminalCmd".getType
- result = newCall(theProc)
- if first.kind != nnkNilLit: result.add(first)
- for arg in children(args[0][1]):
- if arg.kind == nnkNilLit: continue
- let typ = arg.getType
- if typ.kind != nnkEnumTy or
- typ != typForegroundColor and
- typ != typBackgroundColor and
- typ != typStyle and
- typ != typTerminalCmd:
- result.add(arg)
- macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
- result = newCall(bindSym"styledWriteLine")
- result.add(bindSym"stderr")
- for arg in children(args[0][1]):
- result.add(arg)
- when false:
- # not needed because styledWriteLine already ends with resetAttributes
- result = newStmtList(result, newCall(bindSym"resetAttributes", bindSym"stderr"))
- template callWritelnHook(args: varargs[string, `$`]) =
- conf.writelnHook concat(args)
- proc msgWrite(conf: ConfigRef; s: string) =
- if conf.m.errorOutputs != {}:
- let stdOrr =
- if optStdout in conf.globalOptions:
- stdout
- else:
- stderr
- write(stdOrr, s)
- flushFile(stdOrr)
- conf.lastMsgWasDot.incl stdOrr.toStdOrrKind() # subsequent writes need `flushDot`
- template styledMsgWriteln(args: varargs[typed]) =
- if not isNil(conf.writelnHook):
- callIgnoringStyle(callWritelnHook, nil, args)
- elif optStdout in conf.globalOptions:
- if eStdOut in conf.m.errorOutputs:
- flushDot(conf)
- callIgnoringStyle(writeLine, stdout, args)
- flushFile(stdout)
- elif eStdErr in conf.m.errorOutputs:
- flushDot(conf)
- if optUseColors in conf.globalOptions:
- callStyledWriteLineStderr(args)
- else:
- callIgnoringStyle(writeLine, stderr, args)
- # On Windows stderr is fully-buffered when piped, regardless of C std.
- when defined(windows):
- flushFile(stderr)
- proc msgKindToString*(kind: TMsgKind): string = MsgKindToStr[kind]
- # later versions may provide translated error messages
- proc getMessageStr(msg: TMsgKind, arg: string): string = msgKindToString(msg) % [arg]
- type TErrorHandling* = enum doNothing, doAbort, doRaise
- proc log*(s: string) =
- var f: File = default(File)
- if open(f, getHomeDir() / "nimsuggest.log", fmAppend):
- f.writeLine(s)
- close(f)
- proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} =
- if conf.isDefined("nimDebug"): quitOrRaise(conf, $msg)
- elif defined(debug) or msg == errInternal or conf.hasHint(hintStackTrace):
- {.gcsafe.}:
- if stackTraceAvailable() and isNil(conf.writelnHook):
- writeStackTrace()
- else:
- styledMsgWriteln(fgRed, """
- No stack traceback available
- To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 for details""" %
- [conf.command, "intern.html#debugging-the-compiler".createDocLink], conf.unitSep)
- quit 1
- proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
- if msg in fatalMsgs:
- if conf.cmd == cmdIdeTools: log(s)
- quit(conf, msg)
- if msg >= errMin and msg <= errMax or
- (msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
- inc(conf.errorCounter)
- conf.exitcode = 1'i8
- if conf.errorCounter >= conf.errorMax:
- # only really quit when we're not in the new 'nim check --def' mode:
- if conf.ideCmd == ideNone:
- quit(conf, msg)
- elif eh == doAbort and conf.cmd != cmdIdeTools:
- quit(conf, msg)
- elif eh == doRaise:
- raiseRecoverableError(s)
- 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(conf: ConfigRef; lastinfo: TLineInfo) =
- const instantiationFrom = "template/generic instantiation from here"
- const instantiationOfFrom = "template/generic instantiation of `$1` from here"
- var info = lastinfo
- for i in 0..<conf.m.msgContext.len:
- let context = conf.m.msgContext[i]
- if context.info != lastinfo and context.info != info:
- if conf.structuredErrorHook != nil:
- conf.structuredErrorHook(conf, context.info, instantiationFrom,
- Severity.Hint)
- else:
- let message =
- if context.detail == "":
- instantiationFrom
- else:
- instantiationOfFrom.format(context.detail)
- styledMsgWriteln(styleBright, conf.toFileLineCol(context.info), " ", resetStyle, message)
- info = context.info
- proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
- msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
- proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
- conf.m.fileInfos[fileIdx.int32].lines.add line
- proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
- ## xxx there's an off by 1 error that should be fixed; if a file ends with "foo" or "foo\n"
- ## it will return same number of lines (ie, a trailing empty line is discounted)
- result = conf.m.fileInfos[fileIdx.int32].lines.len
- if result == 0:
- try:
- for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
- addSourceLine conf, fileIdx, line
- except IOError:
- discard
- result = conf.m.fileInfos[fileIdx.int32].lines.len
- proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
- ## 1-based index (matches editor line numbers); 1st line is for i.line = 1
- ## last valid line is `numLines` inclusive
- if i.fileIndex.int32 < 0: return ""
- let num = numLines(conf, i.fileIndex)
- # can happen if the error points to EOF:
- if i.line.int > num: return ""
- result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1]
- proc getSurroundingSrc(conf: ConfigRef; info: TLineInfo): string =
- if conf.hasHint(hintSource) and info != unknownLineInfo:
- const indent = " "
- result = "\n" & indent & $sourceLine(conf, info)
- if info.col >= 0:
- result.add "\n" & indent & spaces(info.col) & '^'
- else:
- result = ""
- proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
- let title = case msg
- of warnMin..warnMax: WarningTitle
- of hintMin..hintMax: HintTitle
- else: ErrorTitle
- conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
- proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
- eh: TErrorHandling, info2: InstantiationInfo, isRaw = false,
- ignoreError = false) {.gcsafe, noinline.} =
- var
- title: string
- color: ForegroundColor
- ignoreMsg = false
- sev: Severity
- let errorOutputsOld = conf.m.errorOutputs
- if msg in fatalMsgs:
- # don't gag, refs bug #7080, bug #18278; this can happen with `{.fatal.}`
- # or inside a `tryConstExpr`.
- conf.m.errorOutputs = {eStdOut, eStdErr}
- let kind = if msg in warnMin..hintMax and msg != hintUserRaw: $msg else: "" # xxx not sure why hintUserRaw is special
- case msg
- of errMin..errMax:
- sev = Severity.Error
- writeContext(conf, info)
- title = ErrorTitle
- color = ErrorColor
- when false:
- # we try to filter error messages so that not two error message
- # in the same file and line are produced:
- # xxx `lastError` is only used in this disabled code; but could be useful to revive
- ignoreMsg = conf.m.lastError == info and info != unknownLineInfo and eh != doAbort
- if info != unknownLineInfo: conf.m.lastError = info
- of warnMin..warnMax:
- sev = Severity.Warning
- ignoreMsg = not conf.hasWarn(msg)
- if not ignoreMsg and msg in conf.warningAsErrors:
- title = ErrorTitle
- color = ErrorColor
- else:
- title = WarningTitle
- color = WarningColor
- if not ignoreMsg: writeContext(conf, info)
- inc(conf.warnCounter)
- of hintMin..hintMax:
- sev = Severity.Hint
- ignoreMsg = not conf.hasHint(msg)
- if not ignoreMsg and msg in conf.warningAsErrors:
- title = ErrorTitle
- else:
- title = HintTitle
- color = HintColor
- inc(conf.hintCounter)
- let s = if isRaw: arg else: getMessageStr(msg, arg)
- if not ignoreMsg:
- let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: ""
- # we could also show `conf.cmdInput` here for `projectIsCmd`
- var kindmsg = if kind.len > 0: KindFormat % kind else: ""
- if conf.structuredErrorHook != nil:
- conf.structuredErrorHook(conf, info, s & kindmsg, sev)
- if not ignoreMsgBecauseOfIdeTools(conf, msg):
- if msg == hintProcessing and conf.hintProcessingDots:
- msgWrite(conf, ".")
- else:
- styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg,
- resetStyle, conf.getSurroundingSrc(info), conf.unitSep)
- if hintMsgOrigin in conf.mainPackageNotes:
- # xxx needs a bit of refactoring to honor `conf.filenameOption`
- styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle,
- " compiler msg initiated here", KindColor,
- KindFormat % $hintMsgOrigin,
- resetStyle, conf.unitSep)
- if not ignoreError:
- handleError(conf, msg, eh, s, ignoreMsg)
- if msg in fatalMsgs:
- # most likely would have died here but just in case, we restore state
- conf.m.errorOutputs = errorOutputsOld
- template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
- let arg = msgKindToString(msg) % args
- liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc(), isRaw = true)
- template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
- liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc())
- template fatal*(conf: ConfigRef; info: TLineInfo, arg = "", msg = errFatal) =
- liMessage(conf, info, msg, arg, doAbort, instLoc())
- template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") =
- ## avoids boilerplate
- if not cond:
- var arg2 = "'$1' failed" % [astToStr(cond)]
- if arg.len > 0: arg2.add "; " & astToStr(arg) & ": " & arg
- liMessage(conf, info, errGenerated, arg2, doRaise, instLoc())
- template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
- ## `local` means compilation keeps going until errorMax is reached (via `doNothing`),
- ## `global` means it stops.
- liMessage(conf, info, msg, arg, doRaise, instLoc())
- template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
- liMessage(conf, info, errGenerated, arg, doRaise, instLoc())
- template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
- liMessage(conf, info, msg, arg, doNothing, instLoc())
- template localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
- liMessage(conf, info, errGenerated, arg, doNothing, instLoc())
- template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
- liMessage(conf, info, msg, arg, doNothing, instLoc())
- proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "") {.inline.} =
- message(conf, info, warnDeprecated, msg)
- proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
- if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
- writeContext(conf, info)
- liMessage(conf, info, errInternal, errMsg, doAbort, info2)
- template internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
- internalErrorImpl(conf, info, errMsg, instLoc())
- template internalError*(conf: ConfigRef; errMsg: string) =
- internalErrorImpl(conf, unknownLineInfo, errMsg, instLoc())
- template internalAssert*(conf: ConfigRef, e: bool) =
- # xxx merge with `globalAssert`
- if not e:
- const info2 = instLoc()
- let arg = info2.toFileLineCol
- internalErrorImpl(conf, unknownLineInfo, arg, info2)
- template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
- let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
- let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
- liMessage(conf, info, msg, m, doNothing, instLoc())
- proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
- if i.fileIndex.int32 < 0:
- result = makeCString "???"
- elif optExcessiveStackTrace in conf.globalOptions:
- result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
- else:
- result = conf.m.fileInfos[i.fileIndex.int32].quotedName
- template listMsg(title, r) =
- msgWriteln(conf, title, {msgNoUnitSep})
- for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", $a], {msgNoUnitSep})
- proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
- proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
- proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
- ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
- ## so that it is useful as a C identifier snippet.
- let path = AbsoluteFile toFullPath(conf, fid)
- let rel =
- if path.string.startsWith(conf.libpath.string):
- relativeTo(path, conf.libpath).string
- else:
- relativeTo(path, conf.projectPath).string
- let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
- result = newStringOfCap(trunc)
- for i in 0..<trunc:
- let c = rel[i]
- case c
- of 'a'..'z':
- result.add c
- of {os.DirSep, os.AltSep}:
- result.add 'Z' # because it looks a bit like '/'
- of '.':
- result.add 'O' # a circle
- else:
- # We mangle upper letters and digits too so that there cannot
- # be clashes with our special meanings of 'Z' and 'O'
- result.addInt ord(c)
- proc genSuccessX*(conf: ConfigRef) =
- let mem =
- when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
- else: formatSize(getTotalMem()) & " totmem"
- let loc = $conf.linesCompiled
- var build = ""
- var flags = ""
- const debugModeHints = "none (DEBUG BUILD, `-d:release` generates faster code)"
- if conf.cmd in cmdBackends:
- if conf.backend != backendJs:
- build.add "mm: $#; " % $conf.selectedGC
- if optThreads in conf.globalOptions: build.add "threads: on; "
- build.add "opt: "
- if optOptimizeSpeed in conf.options: build.add "speed"
- elif optOptimizeSize in conf.options: build.add "size"
- else: build.add debugModeHints
- # pending https://github.com/timotheecour/Nim/issues/752, point to optimization.html
- if isDefined(conf, "danger"): flags.add " -d:danger"
- elif isDefined(conf, "release"): flags.add " -d:release"
- else:
- build.add "opt: "
- if isDefined(conf, "danger"):
- build.add "speed"
- flags.add " -d:danger"
- elif isDefined(conf, "release"):
- build.add "speed"
- flags.add " -d:release"
- else: build.add debugModeHints
- if flags.len > 0: build.add "; options:" & flags
- let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
- let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName
- # xxx honor conf.filenameOption more accurately
- var output: string
- if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript:
- output = $conf.jsonBuildFile
- elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends:
- # for some cmd we expect a valid absOutFile
- output = "unknownOutput"
- else:
- output = $conf.absOutFile
- if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename
- # xxx honor filenameOption more accurately
- rawMessage(conf, hintSuccessX, [
- "build", build,
- "loc", loc,
- "sec", sec,
- "mem", mem,
- "project", project,
- "output", output,
- ])
|