From 149ce8364e004cd2bb56f3ecb0a822b9d507e83c Mon Sep 17 00:00:00 2001 From: quantimnot Date: Thu, 25 Aug 2022 08:31:55 -0400 Subject: [PATCH] Add check of consistent foreign symbol usage [skip ci] Checking the style of foreign symbol usages was completely disabled by PR #19822. Now all style checks of a foreign symbol are checked against the first time the symbol is used within a module. --- compiler/linter.nim | 23 ++++++++++++++++++----- compiler/semdata.nim | 1 + tests/stylecheck/thint.nim | 5 +++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/compiler/linter.nim b/compiler/linter.nim index 122e2544bd519..4ac0dd7b33900 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -9,7 +9,7 @@ ## This module implements the style checker. -import std/strutils +import std/[strutils, tables] from std/sugar import dup import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages @@ -113,7 +113,7 @@ 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 = +proc extractIdent(conf: ConfigRef; info: TLineInfo): tuple[source: string, first, last: int] = let line = sourceLine(conf, info) var first = min(info.col.int, line.len) if first < 0: return @@ -121,8 +121,11 @@ proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = 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 + (line, first, last) + +proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = + let (line, first, last) = conf.extractIdent(info) result = differ(line, first, last, newName) proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = @@ -131,15 +134,25 @@ proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = if badName.len > 0: lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s))) +proc styleCheckUsagesMatchFirstUse(ctx: PContext; info: TLineInfo; sym: PSym) = + let (line, first, last) = ctx.config.extractIdent(info) + discard ctx.firstUsageOfForeignSym.hasKeyOrPut(sym.id, (line[first..last], info)) + let (firstUseIdent, firstUseLineInfo) = ctx.firstUsageOfForeignSym[sym.id] + let badName = differ(line, first, last, firstUseIdent) + if badName.len > 0: + lintReport(ctx.config, info, firstUseIdent, badName, " [first used at $1]" % [toFileLineCol(ctx.config, firstUseLineInfo)]) + 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) + if ctx.config.belongsToProjectPackage(sym): + styleCheckUseImpl(ctx.config, info, sym) + else: + styleCheckUsagesMatchFirstUse(ctx, info, sym) proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = let wanted = $w diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 363b7e5a4b2c1..e1b8fd0e38be0 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -162,6 +162,7 @@ type lastTLineInfo*: TLineInfo sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index inUncheckedAssignSection*: int + firstUsageOfForeignSym*: Table[int, tuple[ident: string, info: TLineInfo]] # (symbol.id, ...) template config*(c: PContext): ConfigRef = c.graph.config diff --git a/tests/stylecheck/thint.nim b/tests/stylecheck/thint.nim index c19aac1b89b35..669eb40b0e04c 100644 --- a/tests/stylecheck/thint.nim +++ b/tests/stylecheck/thint.nim @@ -41,3 +41,8 @@ proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Hint ^ 'snakeCase' should be: 'snake_case' [let declared in thint.nim(28, 7)] [Name] ]# generic_proc[int]() + +# Test that foreign symbols usages are consistent within a module: +discard host_os # `system.hostOS` +discard hostos #[tt.Hint + ^ 'hostos' should be: 'host_os' [first used at thint.nim(46, 9)] [Name] ]#