nimsuggest: fix and re-enable old tests (#16401)

A number of nimsuggest tests were disabled for various reasons, sometimes due
to brittleness. These tests have been fixed where needed and most have are now
enabled -- details below. The updates are meant to provide better regression
coverage for future nimsuggest improvements. To avoid brittleness some tests
were refactored.

Impact:
* test coverage has now increased
* faster execution of the test suite
* tests are less likely to break due to stdlib changes

Re-enabled Test & Test Description:
* `tchk1.nim`: check (chk) via nimsuggest works at end of file
* `tdot4.nim`: prioritize already used completion
* `tinclude.nim`: definition lookup (def) with includes
* `tstrutils.nim` -> `tdef2.nim`: test template definition lookup (def)
* `tsug_regression.nim`: regression test for [nimsuggest #52](https://github.com/nim-lang/nimsuggest/issues/52)
* `ttemplate_highlight.nim`: per the file name
* `twithin_macro_prefix.nim`: suggest within a macro with a prefix

Tests Not Re-Enabled:
* `twithin_macro.nim` still disabled as it doesn't provide a good test signal
* EPC highlight tests remain disabled -- requires out of scope tester changes

Additional Notes:
* todos added in comments for follow-up work
This commit is contained in:
Saem Ghani 2020-12-27 01:08:28 -08:00 • committed by GitHub
commit 4cf605dcf6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 292 additions and 376 deletions

View file

@ -0,0 +1,164 @@
import macros
macro class*(head, body: untyped): untyped =
# The macro is immediate, since all its parameters are untyped.
# This means, it doesn't resolve identifiers passed to it.
var typeName, baseName: NimNode
# flag if object should be exported
var exported: bool
if head.kind == nnkInfix and head[0].kind == nnkIdent and $head[0] == "of":
# `head` is expression `typeName of baseClass`
# echo head.treeRepr
# --------------------
# Infix
# Ident !"of"
# Ident !"Animal"
# Ident !"RootObj"
typeName = head[1]
baseName = head[2]
elif head.kind == nnkInfix and head[0].kind == nnkIdent and
$head[0] == "*" and head[2].kind == nnkPrefix and
head[2][0].kind == nnkIdent and $head[2][0] == "of":
# `head` is expression `typeName* of baseClass`
# echo head.treeRepr
# --------------------
# Infix
# Ident !"*"
# Ident !"Animal"
# Prefix
# Ident !"of"
# Ident !"RootObj"
typeName = head[1]
baseName = head[2][1]
exported = true
else:
quit "Invalid node: " & head.lispRepr
# The following prints out the AST structure:
#
# import macros
# dumptree:
# type X = ref object of Y
# z: int
# --------------------
# StmtList
# TypeSection
# TypeDef
# Ident !"X"
# Empty
# RefTy
# ObjectTy
# Empty
# OfInherit
# Ident !"Y"
# RecList
# IdentDefs
# Ident !"z"
# Ident !"int"
# Empty
# create a type section in the result
result = newNimNode(nnkStmtList)
result.add(
if exported:
# mark `typeName` with an asterisk
quote do:
type `typeName`* = ref object of `baseName`
else:
quote do:
type `typeName` = ref object of `baseName`
)
# echo treeRepr(body)
# --------------------
# StmtList
# VarSection
# IdentDefs
# Ident !"name"
# Ident !"string"
# Empty
# IdentDefs
# Ident !"age"
# Ident !"int"
# Empty
# MethodDef
# Ident !"vocalize"
# Empty
# Empty
# FormalParams
# Ident !"string"
# Empty
# Empty
# StmtList
# StrLit ...
# MethodDef
# Ident !"age_human_yrs"
# Empty
# Empty
# FormalParams
# Ident !"int"
# Empty
# Empty
# StmtList
# DotExpr
# Ident !"this"
# Ident !"age"
# var declarations will be turned into object fields
var recList = newNimNode(nnkRecList)
# expected name of constructor
let ctorName = newIdentNode("new" & $typeName)
# Iterate over the statements, adding `this: T`
# to the parameters of functions, unless the
# function is a constructor
for node in body.children:
case node.kind:
of nnkMethodDef, nnkProcDef:
# check if it is the ctor proc
if node.name.kind != nnkAccQuoted and node.name.basename == ctorName:
# specify the return type of the ctor proc
node.params[0] = typeName
else:
# inject `self: T` into the arguments
node.params.insert(1, newIdentDefs(ident("self"), typeName))
result.add(node)
of nnkVarSection:
# variables get turned into fields of the type.
for n in node.children:
recList.add(n)
else:
result.add(node)
# Inspect the tree structure:
#
# echo result.treeRepr
# --------------------
# StmtList
# TypeSection
# TypeDef
# Ident !"Animal"
# Empty
# RefTy
# ObjectTy
# Empty
# OfInherit
# Ident !"RootObj"
# Empty <= We want to replace this
# MethodDef
# ...
result[0][0][2][0][2] = recList
# Lets inspect the human-readable version of the output
#echo repr(result)

8
nimsuggest/tests/fixtures/mdep_v1.nim vendored Normal file
View file

@ -0,0 +1,8 @@
type
Foo* = object
x*, y*: int

9
nimsuggest/tests/fixtures/mdep_v2.nim vendored Normal file
View file

@ -0,0 +1,9 @@
type
Foo* = object
x*, y*: int
z*: string

View file

@ -0,0 +1,5 @@
# Template for testing defs
template fakeAssert*(cond: untyped, msg: string = "") =
## template to allow def lookup testing
if not cond: quit(1)

View file

@ -0,0 +1,15 @@
# Creates an awkward set of dependencies between this, import, and include.
# This pattern appears in the compiler, compiler/(sem|ast|semexprs).nim.
import mfakeassert
import minclude_types
proc say*(g: Greet): string =
fakeAssert(true, "always works")
g.greeting & ", " & g.subject & "!"
include minclude_include
proc say*(): string =
fakeAssert(1 + 1 == 2, "math works")
say(create())

View file

@ -0,0 +1,4 @@
# this file is included and relies on imports within the include
proc create*(greeting: string = "Hello", subject: string = "World"): Greet =
Greet(greeting: greeting, subject: subject)

View file

@ -0,0 +1,6 @@
# types used by minclude_* (import or include), to find with def in include
type
Greet* = object
greeting*: string
subject*: string

19
nimsuggest/tests/fixtures/mstrutils.nim vendored Normal file
View file

@ -0,0 +1,19 @@
import mfakeassert
func rereplace*(s, sub: string; by: string = ""): string {.used.} =
## competes for priority in suggestion, here first, but never used in test
fakeAssert(true, "always works")
result = by
func replace*(s, sub: string; by: string = ""): string =
## this is a test version of strutils.replace, it simply returns `by`
fakeAssert("".len == 0, "empty string is empty")
result = by
func rerereplace*(s, sub: string; by: string = ""): string {.used.} =
## isn't used and appears last, lowest priority
fakeAssert(false, "never works")
result = by