File: test-condition_message_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 (154 lines) | stat: -rw-r--r-- 4,917 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
test_that("condition_message_linter skips allowed usages", {
  linter <- condition_message_linter()

  expect_lint("stop('a string', 'another')", NULL, linter)
  expect_lint("warning('a string', 'another')", NULL, linter)
  expect_lint("message('a string', 'another')", NULL, linter)
  # extracted calls likely don't obey base::stop() semantics
  expect_lint("ctx$stop(paste('a', 'b'))", NULL, linter)
  expect_lint("ctx@stop(paste('a', 'b'))", NULL, linter)

  # sprintf is OK -- gettextf() enforcement is left to other linters
  expect_lint("stop(sprintf('A %s!', 'string'))", NULL, linter)

  # get multiple sep= in one expression
  expect_lint(
    trim_some("
      tryCatch(
        foo(x),
        error = function(e) stop(paste(a, b, sep = '-')),
        warning = function(w) warning(paste(a, b, sep = '--')),
      )
    "),
    NULL,
    linter
  )
})

skip_if_not_installed("tibble")
patrick::with_parameters_test_that(
  "paste/paste0 allowed by condition_message_linter when using other seps and/or collapse",
  expect_lint(
    sprintf("%s(%s(x, %s = '%s'))", condition, fun, parameter, arg),
    NULL,
    condition_message_linter()
  ),
  .cases = tibble::tribble(
    ~.test_name,                           ~condition, ~fun,     ~parameter, ~arg,
    "stop, paste and collapse = ''",       "stop",     "paste",  "collapse",  "",
    "warning, paste and collapse = '\n'",  "warning",  "paste",  "collapse",  "\n",
    "message, paste and collapse = '|'",   "message",  "paste",  "collapse",  "|",
    "stop, paste0 and collapse = ''",      "stop",     "paste0", "collapse",  "",
    "warning, paste0 and collapse = '\n'", "warning",  "paste0", "collapse",  "\n",
    "message, paste0 and collapse = '|'",  "message",  "paste0", "collapse",  "|",
    "stop, paste and sep = '-'",           "stop",     "paste",  "sep",       "-",
    "warning, paste and sep = '\n'",       "warning",  "paste",  "sep",       "\n",
    "message, paste and sep = '|'",        "message",  "paste",  "sep",       "|"
  )
)

test_that("condition_message_linter blocks simple disallowed usages", {
  expect_lint(
    "stop(paste('a string', 'another'))",
    rex::rex("Don't use paste to build stop strings."),
    condition_message_linter()
  )

  expect_lint(
    "warning(paste0('a string ', 'another'))",
    rex::rex("Don't use paste0 to build warning strings."),
    condition_message_linter()
  )

  # `sep` argument allowed, but only if it is different from default
  expect_lint(
    "stop(paste(x, sep = ' '))",
    rex::rex("Don't use paste to build stop strings."),
    condition_message_linter()
  )

  # not thrown off by named arguments
  expect_lint(
    "stop(paste('a', 'b'), call. = FALSE)",
    rex::rex("Don't use paste to build stop strings."),
    condition_message_linter()
  )

  expect_lint(
    "warning(paste0('a', 'b'), immediate. = TRUE)",
    rex::rex("Don't use paste0 to build warning strings."),
    condition_message_linter()
  )

  expect_lint(
    trim_some("
      tryCatch(
        foo(x),
        error = function(e) stop(paste(a, b)),
        warning = function(w) warning(paste(a, b, sep = '--')),
      )
    "),
    rex::rex("Don't use paste to build stop strings."),
    condition_message_linter()
  )

  # one with no sep, one with linted sep
  expect_lint(
    trim_some("
      tryCatch(
        foo(x),
        error = function(e) stop(paste(a, b)),
        warning = function(w) warning(paste(a, b, sep = '')),
      )
    "),
    list(
      list(message = rex::rex("Don't use paste to build stop strings."), line_number = 3L),
      list(message = rex::rex("Don't use paste to build warning strings"), line_number = 4L)
    ),
    condition_message_linter()
  )
})

test_that("packageStartupMessage usages are also matched", {
  expect_lint(
    "packageStartupMessage(paste('a string', 'another'))",
    rex::rex("Don't use paste to build packageStartupMessage strings."),
    condition_message_linter()
  )

  expect_lint(
    "packageStartupMessage(paste0('a string ', 'another'))",
    rex::rex("Don't use paste0 to build packageStartupMessage strings."),
    condition_message_linter()
  )
})

test_that("R>=4.0.0 raw strings are handled", {
  skip_if_not_r_version("4.0.0")
  expect_lint(
    "warning(paste(a, b, sep = R'( )'))",
    rex::rex("Don't use paste to build warning strings."),
    condition_message_linter()
  )
  expect_lint(
    "warning(paste(a, b, sep = R'---[ ]---'))",
    rex::rex("Don't use paste to build warning strings."),
    condition_message_linter()
  )
})

test_that("message vectorization works", {
  expect_lint(
    trim_some("
      foo <- function() {
        warning(paste('uh oh!', 'spaghettios'))
        stop(paste0('look out ', 'below!'))
      }
    "),
    list(
      rex::rex("Don't use paste to build warning strings"),
      rex::rex("Don't use paste0 to build stop strings")
    ),
    condition_message_linter()
  )
})