1
+ object_name_xpath <- paste0(
2
+ # assignments
3
+ " //SYMBOL[" ,
4
+ " not(preceding-sibling::OP-DOLLAR)" ,
5
+ " and ancestor::expr[" ,
6
+ " following-sibling::LEFT_ASSIGN" ,
7
+ " or preceding-sibling::RIGHT_ASSIGN" ,
8
+ " or following-sibling::EQ_ASSIGN" ,
9
+ " ]" ,
10
+ " and not(ancestor::expr[" ,
11
+ " preceding-sibling::OP-LEFT-BRACKET" ,
12
+ " or preceding-sibling::LBB" ,
13
+ " ])" ,
14
+ " ]" ,
15
+
16
+ " | " ,
17
+
18
+ " //STR_CONST[" ,
19
+ " not(preceding-sibling::OP-DOLLAR)" ,
20
+ " and ancestor::expr[" ,
21
+ " following-sibling::LEFT_ASSIGN" ,
22
+ " or preceding-sibling::RIGHT_ASSIGN" ,
23
+ " or following-sibling::EQ_ASSIGN" ,
24
+ " ]" ,
25
+ " and not(ancestor::expr[" ,
26
+ " preceding-sibling::OP-LEFT-BRACKET" ,
27
+ " or preceding-sibling::LBB" ,
28
+ " ])" ,
29
+ " ]" ,
30
+
31
+ # Or
32
+ " | " ,
33
+
34
+ # Formal argument names
35
+ " //SYMBOL_FORMALS"
36
+ )
37
+
1
38
# ' Object name linter
2
39
# '
3
40
# ' Check that object names conform to a naming style.
@@ -34,44 +71,7 @@ object_name_linter <- function(styles = c("snake_case", "symbols")) {
34
71
35
72
xml <- source_file $ full_xml_parsed_content
36
73
37
- xpath <- paste0(
38
- # assignments
39
- " //SYMBOL[" ,
40
- " not(preceding-sibling::OP-DOLLAR)" ,
41
- " and ancestor::expr[" ,
42
- " following-sibling::LEFT_ASSIGN" ,
43
- " or preceding-sibling::RIGHT_ASSIGN" ,
44
- " or following-sibling::EQ_ASSIGN" ,
45
- " ]" ,
46
- " and not(ancestor::expr[" ,
47
- " preceding-sibling::OP-LEFT-BRACKET" ,
48
- " or preceding-sibling::LBB" ,
49
- " ])" ,
50
- " ]" ,
51
-
52
- " | " ,
53
-
54
- " //STR_CONST[" ,
55
- " not(preceding-sibling::OP-DOLLAR)" ,
56
- " and ancestor::expr[" ,
57
- " following-sibling::LEFT_ASSIGN" ,
58
- " or preceding-sibling::RIGHT_ASSIGN" ,
59
- " or following-sibling::EQ_ASSIGN" ,
60
- " ]" ,
61
- " and not(ancestor::expr[" ,
62
- " preceding-sibling::OP-LEFT-BRACKET" ,
63
- " or preceding-sibling::LBB" ,
64
- " ])" ,
65
- " ]" ,
66
-
67
- # Or
68
- " | " ,
69
-
70
- # Formal argument names
71
- " //SYMBOL_FORMALS"
72
- )
73
-
74
- assignments <- xml2 :: xml_find_all(xml , xpath )
74
+ assignments <- xml2 :: xml_find_all(xml , object_name_xpath )
75
75
76
76
# Retrieve assigned name
77
77
nms <- strip_names(
@@ -93,7 +93,7 @@ object_name_linter <- function(styles = c("snake_case", "symbols")) {
93
93
94
94
lapply(
95
95
assignments [! matches_a_style ],
96
- object_lint2 ,
96
+ object_lint ,
97
97
source_file ,
98
98
lint_msg
99
99
)
@@ -131,7 +131,7 @@ strip_names <- function(x) {
131
131
x
132
132
}
133
133
134
- object_lint2 <- function (expr , source_file , message ) {
134
+ object_lint <- function (expr , source_file , message ) {
135
135
symbol <- xml2 :: as_list(expr )
136
136
Lint(
137
137
filename = source_file $ filename ,
@@ -144,135 +144,6 @@ object_lint2 <- function(expr, source_file, message) {
144
144
)
145
145
}
146
146
147
- object_linter_factory <- function (fun , name = linter_auto_name()) {
148
- force(name )
149
- Linter(function (source_file ) {
150
-
151
- token_nums <- ids_with_token(
152
- source_file , rex(start , " SYMBOL" %if_next_isnt % " _SUB" ), fun = re_matches
153
- )
154
- if (length(token_nums ) == 0 ) {
155
- return (list ())
156
- }
157
- tokens <- with_id(source_file , token_nums )
158
- names <- unquote(tokens [[" text" ]]) # remove surrounding backticks
159
-
160
- keep_indices <- which(
161
- ! is_operator(names ) &
162
- ! is_known_generic(names ) &
163
- ! is_base_function(names )
164
- )
165
-
166
- lapply(
167
- keep_indices ,
168
- function (i ) {
169
- token <- tokens [i , ]
170
- if (is_declared_here(token , source_file ) &&
171
- ! is_external_reference(source_file , token [[" id" ]])) {
172
- fun(source_file , token )
173
- }
174
- }
175
- )
176
- }, name = name )
177
- }
178
-
179
- known_generic_regex <- rex(
180
- start ,
181
- or(
182
- unique(
183
- # Clean up "as.data.frame" to "as", "names<-" to "names", etc
184
- re_substitutes(c(names(.knownS3Generics ), .S3PrimitiveGenerics ),
185
- rex(or(dot , " <-" ), anything , end ), " " )
186
- )
187
- ),
188
- dot
189
- )
190
-
191
- is_known_generic <- function (name ) {
192
- re_matches(name , known_generic_regex )
193
- }
194
-
195
- is_declared_here <- function (token , source_file ) {
196
- # The object was declared here if one of the following is true:
197
- # * its name precedes a left assign ("<-" or "<<-") or equal assign ("=")
198
- # * its name follows a right assign ("->" or "->>")
199
- # * its name is not "..." and its first sibling token is a function definition
200
- filt <- filter_out_token_type(source_file [[" parsed_content" ]], " expr" )
201
- assign_regex <- rex(start , or(" EQ_ASSIGN" , " LEFT_ASSIGN" ), end )
202
- l <- which(filt [, " id" ] == token [[" id" ]])
203
- if ((l + 1L < = dim(filt )[[1L ]] && re_matches(filt [l + 1L , " token" ], assign_regex )) ||
204
- (l > = 2L && filt [l - 1L , " token" ] == " RIGHT_ASSIGN" )) {
205
- # assigned variable or function parameter
206
- TRUE
207
- } else {
208
- sibling_ids <- siblings(source_file [[" parsed_content" ]], token [[" id" ]], 1L )
209
- if (token [[" text" ]] != " ..." &&
210
- length(sibling_ids ) &&
211
- with_id(source_file , sibling_ids [[1L ]])[[" text" ]] == " function" ) {
212
- # parameter in function definition
213
- TRUE
214
- } else {
215
- FALSE
216
- }
217
- }
218
- }
219
-
220
- is_operator <- function (name ) {
221
- name != make.names(name )
222
- }
223
-
224
- is_external_reference <- function (source_file , id ) {
225
- sibling_tokens <- with_id(source_file , siblings(source_file $ parsed_content , id , 1 ))$ token
226
- any(sibling_tokens %in% c(" NS_GET" , " NS_GET_INT" ))
227
- }
228
-
229
- # via unlist(tools:::.get_standard_package_names(), use.names = FALSE)
230
- base_pkgs <- c(
231
- " base" ,
232
- " tools" ,
233
- " utils" ,
234
- " grDevices" ,
235
- " graphics" ,
236
- " stats" ,
237
- " datasets" ,
238
- " methods" ,
239
- " grid" ,
240
- " splines" ,
241
- " stats4" ,
242
- " compiler" ,
243
- " parallel" ,
244
- " MASS" ,
245
- " lattice" ,
246
- " Matrix" ,
247
- " nlme" ,
248
- " survival" ,
249
- " boot" ,
250
- " cluster" ,
251
- " codetools" ,
252
- " foreign" ,
253
- " KernSmooth" ,
254
- " rpart" ,
255
- " class" ,
256
- " nnet" ,
257
- " spatial" ,
258
- " mgcv"
259
- )
260
-
261
- # some duplicates such as .onLoad appear in multiple packages; sort for efficiency
262
- base_funs <- sort(unique(unlist(lapply(
263
- base_pkgs ,
264
- function (x ) {
265
- name <- try_silently(getNamespace(x ))
266
- if (! inherits(name , " try-error" )) {
267
- ls(name , all.names = TRUE )
268
- }
269
- }
270
- ))))
271
-
272
- is_base_function <- function (x ) {
273
- x %in% base_funs
274
- }
275
-
276
147
# see ?".onLoad", ?Startup, and ?quit. Remove leading dot to match behavior of strip_names().
277
148
# All of .onLoad, .onAttach, and .onUnload are used in base packages,
278
149
# and should be caught in is_base_function; they're included here for completeness / stability
@@ -292,19 +163,6 @@ is_special_function <- function(x) {
292
163
x %in% special_funs
293
164
}
294
165
295
- object_lint <- function (source_file , token , message ) {
296
- Lint(
297
- filename = source_file $ filename ,
298
- line_number = token $ line1 ,
299
- column_number = token $ col1 ,
300
- type = " style" ,
301
- message = message ,
302
- line = source_file $ lines [as.character(token $ line1 )],
303
- ranges = list (c(token $ col1 , token $ col2 ))
304
- )
305
- }
306
-
307
-
308
166
loweralnum <- rex(one_of(lower , digit ))
309
167
upperalnum <- rex(one_of(upper , digit ))
310
168
@@ -330,13 +188,38 @@ regexes_rd <- toString(paste0("\\sQuote{", names(style_regexes), "}"))
330
188
# ' @seealso [linters] for a complete list of linters available in lintr.
331
189
# ' @export
332
190
object_length_linter <- function (length = 30L ) {
333
- object_linter_factory(function (source_file , token ) {
334
- if (nchar(token $ text ) > length ) {
335
- object_lint(
336
- source_file ,
337
- token ,
338
- paste0(" Variable and function names should not be longer than " , length , " characters." )
339
- )
340
- }
191
+ lint_msg <- paste0(" Variable and function names should not be longer than " , length , " characters." )
192
+
193
+ Linter(function (source_file ) {
194
+ if (is.null(source_file $ full_xml_parsed_content )) return (list ())
195
+
196
+ xml <- source_file $ full_xml_parsed_content
197
+
198
+ assignments <- xml2 :: xml_find_all(xml , object_name_xpath )
199
+
200
+ # Retrieve assigned name
201
+ nms <- strip_names(
202
+ xml2 :: xml_text(assignments )
203
+ )
204
+
205
+ generics <- strip_names(c(
206
+ declared_s3_generics(xml ),
207
+ imported_s3_generics(namespace_imports(find_package(source_file $ filename )))$ fun ,
208
+ .base_s3_generics
209
+ ))
210
+ generics <- unique(generics [nzchar(generics )])
211
+
212
+ # Remove generic function names from generic implementations
213
+ # This only lints S3 implementations if the class names are too long, still lints generics if they are too long.
214
+ nms_stripped <- re_substitutes(nms , rex(start , or(generics ), " ." ), " " )
215
+
216
+ too_long <- nchar(nms_stripped ) > length
217
+
218
+ lapply(
219
+ assignments [too_long ],
220
+ object_lint ,
221
+ source_file ,
222
+ lint_msg
223
+ )
341
224
})
342
225
}
0 commit comments