File: test-keyword_quote_linter.R

package info (click to toggle)
r-cran-lintr 3.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,396 kB
  • sloc: sh: 13; xml: 10; makefile: 2
file content (195 lines) | stat: -rw-r--r-- 6,201 bytes parent folder | download
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
test_that("keyword_quote_linter skips allowed usages", {
  linter <- keyword_quote_linter()

  # main use case: c()
  expect_lint("x <- c(1, 2, 4, 5)", NULL, linter)
  expect_lint("x <- c(a = 1, 2)", NULL, linter)
  expect_lint("x <- c(a = 1, b = 2)", NULL, linter)
  expect_lint("y <- c(`a b` = 1, `c d` = 2)", NULL, linter)
  expect_lint('y <- c("a b" = 1, "c d" = 2)', NULL, linter)
  expect_lint("z <- c('a b' = 1, c = 2)", NULL, linter)

  # don't catch strings as arguments
  expect_lint('c(A = "a")', NULL, linter)
  # don't catch unnamed arguments
  expect_lint('c(1, 2, "a")', NULL, linter)
  # don't get thrown off by missing arguments
  expect_lint("alist(`a b` =)", NULL, linter)

  # other use cases: switch() and list()
  expect_lint("list(a = 1, b = list(c = 2))", NULL, linter)
  expect_lint("list(`a b` = 1, c = 2:6)", NULL, linter)

  expect_lint("switch(x, a = 1, b = 2)", NULL, linter)
  expect_lint(
    "switch(x, `a b` = 1, c = 2:6)",
    NULL,
    linter
  )
})

test_that("keyword_quote_linter blocks simple disallowed usages", {
  linter <- keyword_quote_linter()
  lint_msg <- "Only quote named arguments to functions"

  expect_lint('c("a" = 1, b = 2)', lint_msg, linter)

  expect_lint(
    "c('a' = 1, 'b' = 2)",
    list(lint_msg, lint_msg),
    linter
  )

  expect_lint(
    "c(`a` = 1, b = list(`c` = 2))",
    list(lint_msg, lint_msg),
    linter
  )

  # missing argument is caught
  expect_lint("alist(`a` = )", lint_msg, linter)

  expect_lint(
    "switch(x, `a` = c('b' = list(\"c\" = 1)))",
    list(lint_msg, lint_msg, lint_msg),
    linter
  )
})

test_that("keyword_quote_linter skips quoting on reserved words", {
  linter <- keyword_quote_linter()

  expect_lint("c(`next` = 1, `while` = 2)", NULL, linter)
  expect_lint("switch(x, `for` = 3, `TRUE` = 4)", NULL, linter)
  expect_lint("list('NA' = 5, 'Inf' = 6)", NULL, linter)
})

test_that("keyword_quote_linter works on more common functions", {
  linter <- keyword_quote_linter()
  lint_msg <- "Only quote named arguments to functions"

  expect_lint("data.frame('a' = 1)", lint_msg, linter)
  expect_lint("data.table('a' = 1)", lint_msg, linter)
  expect_lint("data.table::data.table('a' = 1)", lint_msg, linter)
  expect_lint("rbind('a' = 1)", lint_msg, linter)
  expect_lint("cbind('a' = 1)", lint_msg, linter)
})

test_that("keyword_quote_linter finds blocked usages in any function call", {
  expect_lint(
    "foo('a' = 1)",
    rex::rex("Only quote named arguments to functions"),
    keyword_quote_linter()
  )
})

test_that("keyword_quote_linter blocks quoted assignment targets", {
  linter <- keyword_quote_linter()
  backtick_msg <- rex::rex("Use backticks to create non-syntactic names, not quotes.")
  assign_msg <- "Only quote targets of assignment if necessary"

  expect_lint('"foo bar" <- 1', backtick_msg, linter)
  expect_lint("'foo bar' = 1", backtick_msg, linter)
  # valid choice: use backticks
  expect_lint("`foo bar` = 1", NULL, linter)

  expect_lint('"foo" <- 1', assign_msg, linter)
  expect_lint("'foo' = 1", assign_msg, linter)
  expect_lint("`foo` = 1", assign_msg, linter)

  # don't include data.table assignments
  expect_lint('DT[, "a" := 1]', NULL, linter)
  expect_lint("DT[, 'a' := 1]", NULL, linter)
  expect_lint("DT[, `a` := 1]", NULL, linter)

  # include common use cases: [<-/$ methods and infixes
  expect_lint('"$.my_class" <- function(x, key) { }', backtick_msg, linter)
  expect_lint("'Setter[<-.my_class' = function(x, idx, value) { }", backtick_msg, linter)
  expect_lint('"%nin%" <- function(x, table) !x %in% table', backtick_msg, linter)

  # right assignment
  expect_lint('1 -> "foo"', assign_msg, linter)
  expect_lint("1 -> foo", NULL, linter)
  expect_lint('1 -> "a b"', backtick_msg, linter)
})

test_that("keyword_quote_linter blocks quoted $, @ extractions", {
  linter <- keyword_quote_linter()
  backtick_msg <- rex::rex("Use backticks to create non-syntactic names, not quotes.")
  dollar_msg <- rex::rex("Only quote targets of extraction with $ if necessary")
  at_msg <- rex::rex("Only quote targets of extraction with @ if necessary")

  expect_lint('x$"foo bar" <- 1', backtick_msg, linter)
  expect_lint("x$'foo bar' = 1", backtick_msg, linter)
  expect_lint('x@"foo bar" <- 1', backtick_msg, linter)
  expect_lint("x@'foo bar' = 1", backtick_msg, linter)
  # valid choice: non-syntactic name with backticks
  expect_lint("x@`foo bar` <- 1", NULL, linter)
  expect_lint("x@`foo bar` = 1", NULL, linter)

  expect_lint('x$"foo" <- 1', dollar_msg, linter)
  expect_lint("x$'foo' = 1", dollar_msg, linter)
  expect_lint('x@"foo" <- 1', at_msg, linter)
  expect_lint("x@'foo' = 1", at_msg, linter)
  expect_lint("x@`foo` <- 1", at_msg, linter)
  expect_lint("x@`foo` = 1", at_msg, linter)
})

test_that("multiple lints are generated correctly", {
  linter <- keyword_quote_linter()

  expect_lint(
    trim_some('{
      foo("a" = 1)
      "b" <- 2
      x$"c"
      y@"d"
    }'),
    list(
      "Only quote named arguments",
      "Only quote targets of assignment",
      "Only quote targets of extraction with \\$",
      "Only quote targets of extraction with @"
    ),
    linter
  )

  # multiple flavors of assignment lints
  expect_lint(
    trim_some('{
      "a" <- 1
      "a b" <- 1
      `a` <- 1
      `a b` <- 1
    }'),
    list(
      "Only quote targets of assignment if necessary",
      "Use backticks to create non-syntactic names, not quotes",
      "Only quote targets of assignment if necessary"
    ),
    linter
  )

  # multiple flavors of extraction lints
  expect_lint(
    trim_some('{
      x$"a"
      x$"a b" <- 1
      x$`a` <- 1
      x$`a b` <- 1
      y@"a"
      y@"a b" <- 1
      y@`a` <- 1
      y@`a b` <- 1
    }'),
    list(
      rex::rex("Only quote targets of extraction with $ if necessary"),
      rex::rex("Use backticks to create non-syntactic names, not quotes."),
      rex::rex("Only quote targets of extraction with $ if necessary"),
      rex::rex("Only quote targets of extraction with @ if necessary"),
      rex::rex("Use backticks to create non-syntactic names, not quotes."),
      rex::rex("Only quote targets of extraction with @ if necessary")
    ),
    linter
  )
})