1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the style checker.
import std/strutils
from std/sugar import dup
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
export packages
const
Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
proc identLen*(line: string, start: int): int =
result = 0
while start+result < line.len and line[start+result] in Letters:
inc result
proc `=~`(s: string, a: openArray[string]): bool =
result = false
for x in a:
if s.startsWith(x): return true
proc beautifyName(s: string, k: TSymKind): string =
# minimal set of rules here for transition:
# GC_ is allowed
let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'})
if allUpper and k in {skConst, skEnumField, skType}: return s
result = newStringOfCap(s.len)
var i = 0
case k
of skType, skGenericParam:
# Types should start with a capital unless builtins like 'int' etc.:
if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string",
"char", "byte", "bool", "openArray", "seq", "array", "void",
"pointer", "float", "csize", "csize_t", "cdouble", "cchar", "cschar",
"cshort", "cu", "nil", "typedesc", "auto", "any",
"range", "openarray", "varargs", "set", "cfloat", "ref", "ptr",
"untyped", "typed", "static", "sink", "lent", "type", "owned", "iterable"]:
result.add s[i]
else:
result.add toUpperAscii(s[i])
of skConst, skEnumField:
# for 'const' we keep how it's spelt; either upper case or lower case:
result.add s[0]
else:
# as a special rule, don't transform 'L' to 'l'
if s.len == 1 and s[0] == 'L': result.add 'L'
elif '_' in s: result.add(s[i])
else: result.add toLowerAscii(s[0])
inc i
while i < s.len:
if s[i] == '_':
if i+1 >= s.len:
discard "trailing underscores should be stripped off"
elif i > 0 and s[i-1] in {'A'..'Z'}:
# don't skip '_' as it's essential for e.g. 'GC_disable'
result.add('_')
inc i
result.add s[i]
else:
inc i
result.add toUpperAscii(s[i])
elif allUpper:
result.add toLowerAscii(s[i])
else:
result.add s[i]
inc i
proc differ*(line: string, a, b: int, x: string): string =
proc substrEq(s: string, pos, last: int, substr: string): bool =
result = true
for i in 0..<substr.len:
if pos+i > last or s[pos+i] != substr[i]: return false
result = ""
if not substrEq(line, a, b, x):
let y = line[a..b]
if cmpIgnoreStyle(y, x) == 0:
result = y
proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
let beau = beautifyName(s.name.s, k)
if s.name.s != beau:
lintReport(conf, info, beau, s.name.s)
template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
## Check symbol definitions adhere to NEP1 style rules.
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
sym.kind != skResult and # ignore `result`
sym.kind != skTemp and # ignore temporary variables created by the compiler
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
k notin {skType, skGenericParam} and # ignore types and generic params
(sym.typ == nil or sym.typ.kind != tyTypeDesc) and # ignore `typedesc`
{sfImportc, sfExportc} * sym.flags == {} and # ignore FFI
sfAnon notin sym.flags: # ignore if created by compiler
nep1CheckDefImpl(ctx.config, info, sym, k)
template styleCheckDef*(ctx: PContext; info: TLineInfo; s: PSym) =
## Check symbol definitions adhere to NEP1 style rules.
styleCheckDef(ctx, info, s, s.kind)
template styleCheckDef*(ctx: PContext; s: PSym) =
## Check symbol definitions adhere to NEP1 style rules.
styleCheckDef(ctx, s.info, s, s.kind)
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 first+1 < line.len and line[first] == '`': inc first
let last = first+identLen(line, first)-1
result = differ(line, first, last, newName)
proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
let newName = s.name.s
let badName = differs(conf, info, newName)
if badName.len > 0:
lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s)))
template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
## Check symbol uses match their definition's style.
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
sym.kind != skTemp and # ignore temporary variables created by the compiler
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
styleCheckUseImpl(ctx.config, info, sym)
proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
let wanted = $w
if pragmaName != wanted:
lintReport(conf, info, wanted, pragmaName)
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
## Check builtin pragma uses match their definition's style.
## Note: This only applies to builtin pragmas, not user pragmas.
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|