nim styleChecker: implemented all the missing features (bugfix)

(cherry picked from commit bd689849f2)
This commit is contained in:
Araq
2019-07-10 00:27:23 +02:00
committed by narimiran
parent b3b10ec849
commit b5ab4dabe2
7 changed files with 111 additions and 51 deletions

View File

@@ -878,6 +878,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
var h: Hash = 0
var pos = L.bufpos
tokenBegin(tok, pos)
var suspicious = false
while true:
var c = L.buf[pos]
case c
@@ -888,21 +889,26 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
inc(pos)
suspicious = true
of '_':
if L.buf[pos+1] notin SymChars:
lexMessage(L, errGenerated, "invalid token: trailing underscore")
break
inc(pos)
suspicious = true
else: break
tokenEnd(tok, pos-1)
h = !$h
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
tok.tokType = tkSymbol
else:
tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
L.bufpos = pos
proc endOperator(L: var TLexer, tok: var TToken, pos: int,
hash: Hash) {.inline.} =

View File

@@ -108,7 +108,7 @@ const
hintPath: "added path: '$1'",
hintConditionAlwaysTrue: "condition is always true: '$1'",
hintConditionAlwaysFalse: "condition is always false: '$1'",
hintName: "name should be: '$1'",
hintName: "$1",
hintPattern: "$1",
hintExecuting: "$1",
hintLinking: "",

View File

@@ -25,17 +25,12 @@ proc identLen*(line: string, start: int): int =
type
StyleCheck* {.pure.} = enum None, Warn, Auto
var
gOverWrite* = true
gStyleCheck*: StyleCheck
gCheckExtern*, gOnlyMainfile*: bool
proc overwriteFiles*(conf: ConfigRef) =
let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on"
for i in 0 .. high(conf.m.fileInfos):
if conf.m.fileInfos[i].dirty and
(not gOnlyMainfile or FileIndex(i) == conf.projectMainIdx):
let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath
(FileIndex(i) == conf.projectMainIdx):
let newFile = if false: conf.m.fileInfos[i].fullpath
else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim")
try:
var f = open(newFile.string, fmWrite)
@@ -69,7 +64,7 @@ proc beautifyName(s: string, k: TSymKind): string =
"pointer", "float", "csize", "cdouble", "cchar", "cschar",
"cshort", "cu", "nil", "typedesc", "auto", "any",
"range", "openarray", "varargs", "set", "cfloat", "ref", "ptr",
"untyped", "typed", "static", "sink", "lent", "type"]:
"untyped", "typed", "static", "sink", "lent", "type", "owned"]:
result.add s[i]
else:
result.add toUpperAscii(s[i])
@@ -98,46 +93,23 @@ proc beautifyName(s: string, k: TSymKind): string =
result.add s[i]
inc i
proc differ*(line: string, a, b: int, x: string): bool =
proc differ*(line: string, a, b: int, x: string): string =
let y = line[a..b]
result = cmpIgnoreStyle(y, x) == 0 and y != x
proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) =
let line = conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1]
var first = min(info.col.int, line.len)
if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first)
while first > 0 and line[first-1] in Letters: dec first
if first < 0: return
if line[first] == '`': inc first
let last = first+identLen(line, first)-1
if differ(line, first, last, newName):
# last-first+1 != newName.len or
var x = line.substr(0, first-1) & newName & line.substr(last+1)
system.shallowCopy(conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1], x)
conf.m.fileInfos[info.fileIndex.int].dirty = true
proc lintReport(conf: ConfigRef; info: TLineInfo, beau: string) =
if optStyleError in conf.globalOptions:
localError(conf, info, "name should be: '$1'" % beau)
if cmpIgnoreStyle(y, x) == 0 and y != x:
result = y
else:
message(conf, info, hintName, beau)
result = ""
proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
let beau = beautifyName(s, k)
if s != beau:
if gStyleCheck == StyleCheck.Auto:
sym.name = getIdent(cache, beau)
replaceInFile(conf, info, beau)
else:
lintReport(conf, info, beau)
lintReport(conf, info, beau, s)
proc styleCheckDefImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym; k: TSymKind) =
# operators stay as they are:
if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
if k in {skType, skGenericParam} and sfAnon in s.flags: return
if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
if {sfImportc, sfExportc} * s.flags == {}:
checkStyle(conf, cache, info, s.name.s, k, s)
proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
@@ -148,7 +120,7 @@ proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
if {sfImportc, sfExportc} * s.flags != {}: return
let beau = beautifyName(s.name.s, k)
if s.name.s != beau:
lintReport(conf, info, beau)
lintReport(conf, info, beau, s.name.s)
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
@@ -161,19 +133,57 @@ template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) =
template styleCheckDef*(conf: ConfigRef; s: PSym) =
styleCheckDef(conf, s.info, s, s.kind)
proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
let line = sourceLine(conf, info)
var first = min(info.col.int, line.len)
if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first)
while first > 0 and line[first-1] in Letters: dec first
if first < 0: return
if line[first] == '`': inc first
let last = first+identLen(line, first)-1
result = differ(line, first, last, newName)
proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) =
if info.fileIndex.int < 0: return
# we simply convert it to what it looks like in the definition
# for consistency
# operators stay as they are:
if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters:
if s.kind == skTemp or s.name.s[0] notin Letters or sfAnon in s.flags:
return
if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
let newName = s.name.s
replaceInFile(conf, info, newName)
#if newName == "File": writeStackTrace()
let newName = s.name.s
let oldName = differs(conf, info, newName)
if oldName.len > 0:
lintReport(conf, info, newName, oldName)
proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; pragmaName: string) =
const inMixedCase = [
"noSideEffect", "importCompilerProc", "incompleteStruct", "requiresInit",
"sideEffect", "compilerProc", "lineDir", "stackTrace", "lineTrace",
"rangeChecks", "boundChecks",
"overflowChecks", "nilChecks",
"floatChecks", "nanChecks", "infChecks", "moveChecks",
"nonReloadable", "executeOnReload",
"deadCodeElim",
"compileTime", "noInit", "fieldChecks",
"linearScanEnd",
"computedGoto", "injectStmt",
"asmNoStackframe", "implicitStatic", "codegenDecl", "liftLocals"
]
let name = pragmaName.normalize
for x in inMixedCase:
if x.normalize == name:
if pragmaName != x:
lintReport(conf, info, x, pragmaName)
return
let wanted = pragmaName.toLowerAscii.replace("_", "")
if pragmaName != wanted:
lintReport(conf, info, wanted, pragmaName)
template styleCheckUse*(info: TLineInfo; s: PSym) =
when defined(nimfix):

View File

@@ -588,3 +588,10 @@ proc listHints*(conf: ConfigRef) =
if hint in conf.notes: "x" else: " ",
lineinfos.HintsToStr[ord(hint) - ord(hintMin)]
])
proc lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) =
let m = "'$2' should be: '$1'" % [beau, got]
if optStyleError in conf.globalOptions:
localError(conf, info, m)
else:
message(conf, info, hintName, m)

View File

@@ -12,7 +12,7 @@
import
os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
types, lookups, lineinfos, pathutils
types, lookups, lineinfos, pathutils, linter
const
FirstCallConv* = wNimcall
@@ -610,9 +610,9 @@ proc pragmaLine(c: PContext, n: PNode) =
proc processPragma(c: PContext, n: PNode, i: int) =
let it = n[i]
if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(c, n)
elif it[0].kind != nkIdent: invalidPragma(c, n)
elif it[1].kind != nkIdent: invalidPragma(c, n)
if it.kind notin nkPragmaCallKinds and it.safeLen == 2: invalidPragma(c, n)
elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent:
invalidPragma(c, n)
var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1])
@@ -762,6 +762,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
let ident = considerQuotedIdent(c, key)
var userPragma = strTableGet(c.userPragmas, ident)
if userPragma != nil:
if {optStyleHint, optStyleError} * c.config.globalOptions != {}:
styleCheckUse(c.config, key.info, userPragma)
# number of pragmas increase/decrease with user pragma expansion
inc c.instCounter
if c.instCounter > 100:
@@ -774,6 +777,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
else:
let k = whichKeyword(ident)
if k in validPragmas:
if {optStyleHint, optStyleError} * c.config.globalOptions != {}:
checkPragmaUse(c.config, key.info, ident.s)
case k
of wExportc:
makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)

View File

@@ -32,7 +32,7 @@
# included from sigmatch.nim
import algorithm, prefixmatches, lineinfos, pathutils, parseutils
import algorithm, prefixmatches, lineinfos, pathutils, parseutils, linter
from wordrecg import wDeprecated, wError, wAddr, wYield, specialWords
when defined(nimsuggest):
@@ -539,6 +539,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
if sfError in s.flags: userError(conf, info, s)
when defined(nimsuggest):
suggestSym(conf, info, s, usageSym, false)
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
styleCheckUse(conf, info, s)
proc safeSemExpr*(c: PContext, n: PNode): PNode =
# use only for idetools support!

30
tests/tools/tlinter.nim Normal file
View File

@@ -0,0 +1,30 @@
discard """
cmd: '''nim c --styleCheck:hint $file'''
nimout: '''
tlinter.nim(21, 14) Hint: 'nosideeffect' should be: 'noSideEffect' [Name]
tlinter.nim(21, 28) Hint: 'myown' should be: 'myOwn' [Name]
tlinter.nim(21, 35) Hint: 'inLine' should be: 'inline' [Name]
tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name]
tlinter.nim(23, 1) Hint: 'foO' should be: 'foo' [Name]
tlinter.nim(27, 14) Hint: 'Foo_bar' should be: 'FooBar' [Name]
tlinter.nim(29, 6) Hint: 'someVAR' should be: 'someVar' [Name]
'''
action: "compile"
"""
{.pragma: myOwn.}
proc foo() {.nosideeffect, myown, inLine.} = debugEcho "hi"
foO()
tyPE FooBar = string
var someVar: Foo_bar = "a"
echo someVAR