Skip to content

Conversation

@IndrajeetPatil
Copy link
Collaborator

Closes #823

@codecov-commenter
Copy link

codecov-commenter commented Oct 8, 2022

Codecov Report

Merging #1648 (af3ae53) into main (854b2c7) will not change coverage.
The diff coverage is n/a.

@@           Coverage Diff           @@
##             main    #1648   +/-   ##
=======================================
  Coverage   98.31%   98.31%           
=======================================
  Files         100      100           
  Lines        4397     4397           
=======================================
  Hits         4323     4323           
  Misses         74       74           
Impacted Files Coverage Δ
R/settings.R 100.00% <ø> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

R/zzz.R Outdated
#' names(default_settings$linters)
#'
#' # default values for a few of the other settings
#' default_settings$encoding
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just show all the settings at once

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the linters element completely overwhelms everything else:

library(lintr)

default_settings
#> $linters
#> $linters$assignment_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#> 
#>     bad_expr <- xml2::xml_find_all(xml, xpath)
#>     if (length(bad_expr) == 0L) {
#>       return(list())
#>     }
#> 
#>     operator <- xml2::xml_text(bad_expr)
#>     lint_message_fmt <- ifelse(
#>       operator %in% c("<<-", "->>"),
#>       "%s can have hard-to-predict behavior; prefer assigning to a specific environment instead (with assign() or <-).",
#>       "Use <-, not %s, for assignment."
#>     )
#> 
#>     if (!allow_trailing) {
#>       bad_trailing_expr <- xml2::xml_find_all(xml, trailing_assign_xpath)
#>       trailing_assignments <- xml2::xml_attrs(bad_expr) %in% xml2::xml_attrs(bad_trailing_expr)
#>       lint_message_fmt[trailing_assignments] <- "Assignment %s should not be trailing at end of line"
#>     }
#> 
#>     lint_message <- sprintf(lint_message_fmt, operator)
#>     xml_nodes_to_lints(bad_expr, source_expression, lint_message, type = "style")
#>   }
#> <bytecode: 0x15a9c30b0>
#> <environment: 0x15a9c1ad0>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "assignment_linter"
#> 
#> $linters$brace_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#>     lints <- list()
#> 
#>     lints <- c(lints, xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xp_open_curly),
#>       source_expression = source_expression,
#>       lint_message =
#>         "Opening curly braces should never go on their own line and should always be followed by a new line."
#>     ))
#> 
#>     lints <- c(lints, xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xp_paren_brace),
#>       source_expression = source_expression,
#>       lint_message = "There should be a space before an opening curly brace."
#>     ))
#> 
#>     lints <- c(lints, xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xp_closed_curly),
#>       source_expression = source_expression,
#>       lint_message = "Closing curly-braces should always be on their own line, unless they are followed by an else."
#>     ))
#> 
#>     lints <- c(lints, xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xp_else_same_line),
#>       source_expression = source_expression,
#>       lint_message = "`else` should come on the same line as the previous `}`."
#>     ))
#> 
#>     lints <- c(lints, xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xp_function_brace),
#>       source_expression = source_expression,
#>       lint_message = "Any function spanning multiple lines should use curly braces."
#>     ))
#> 
#>     lints <- c(lints, xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xp_if_else_match_brace),
#>       source_expression = source_expression,
#>       lint_message = "Either both or neither branch in `if`/`else` should use curly braces."
#>     ))
#> 
#>     lints
#>   }
#> <bytecode: 0x15a99cee8>
#> <environment: 0x15a99f2a0>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "brace_linter"
#> 
#> $linters$commas_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#>     xml <- source_expression$xml_parsed_content
#> 
#>     before_lints <- xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xpath_before),
#>       source_expression = source_expression,
#>       lint_message = "Commas should never have a space before.",
#>       range_start_xpath = "number(./preceding-sibling::*[1]/@col2 + 1)", # start after preceding expression
#>       range_end_xpath = "number(./@col1 - 1)" # end before comma
#>     )
#> 
#>     after_lints <- xml_nodes_to_lints(
#>       xml2::xml_find_all(xml, xpath_after),
#>       source_expression = source_expression,
#>       lint_message = "Commas should always have a space after.",
#>       range_start_xpath = "number(./@col2 + 1)", # start and end after comma
#>       range_end_xpath = "number(./@col2 + 1)"
#>     )
#> 
#>     c(before_lints, after_lints)
#>   }
#> <bytecode: 0x15a95b8f8>
#> <environment: 0x15a95dfc0>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "commas_linter"
#> 
#> $linters$commented_code_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#>     all_comment_nodes <- xml2::xml_find_all(source_expression$full_xml_parsed_content, "//COMMENT")
#>     all_comments <- xml2::xml_text(all_comment_nodes)
#>     code_candidates <- re_matches(all_comments, code_candidate_regex, global = FALSE, locations = TRUE)
#>     extracted_code <- code_candidates[, "code"]
#>     # ignore trailing ',' when testing for parsability
#>     extracted_code <- rex::re_substitutes(extracted_code, rex::rex(",", any_spaces, end), "")
#>     extracted_code <- rex::re_substitutes(extracted_code, rex::rex(start, any_spaces, ","), "")
#>     is_parsable <- which(vapply(extracted_code, parsable, logical(1L)))
#> 
#>     lint_list <- xml_nodes_to_lints(
#>       all_comment_nodes[is_parsable],
#>       source_expression = source_expression,
#>       lint_message = "Commented code should be removed."
#>     )
#> 
#>     # Location info needs updating
#>     for (i in seq_along(lint_list)) {
#>       rng <- lint_list[[i]]$ranges[[1L]]
#> 
#>       rng[2L] <- rng[1L] + code_candidates[is_parsable[i], "code.end"] - 1L
#>       rng[1L] <- rng[1L] + code_candidates[is_parsable[i], "code.start"] - 1L
#> 
#>       lint_list[[i]]$column_number <- rng[1L]
#>       lint_list[[i]]$ranges <- list(rng)
#>     }
#> 
#>     lint_list
#>   }
#> <bytecode: 0x13acf2e40>
#> <environment: 0x15a900008>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "commented_code_linter"
#> 
#> $linters$cyclocomp_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#>     complexity <- try_silently(
#>       cyclocomp::cyclocomp(parse(text = source_expression$content))
#>     )
#>     if (inherits(complexity, "try-error") || complexity <= complexity_limit) {
#>       return(list())
#>     }
#>     col1 <- source_expression[["column"]][1L]
#>     Lint(
#>       filename = source_expression[["filename"]],
#>       line_number = source_expression[["line"]][1L],
#>       column_number = source_expression[["column"]][1L],
#>       type = "style",
#>       message = sprintf(
#>         "Functions should have cyclomatic complexity of less than %d, this has %d.",
#>         complexity_limit, complexity
#>       ),
#>       ranges = list(rep(col1, 2L)),
#>       line = source_expression$lines[1L]
#>     )
#>   }
#> <bytecode: 0x13adda438>
#> <environment: 0x13ada9918>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "cyclocomp_linter"
#> 
#> $linters$equals_na_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#> 
#>     bad_expr <- xml2::xml_find_all(xml, xpath)
#> 
#>     xml_nodes_to_lints(
#>       bad_expr,
#>       source_expression,
#>       lint_message = "Use is.na for comparisons to NA (not == or !=)",
#>       type = "warning"
#>     )
#>   }
#> <bytecode: 0x13adeec18>
#> <environment: 0x13adebad0>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "equals_na_linter"
#> 
#> $linters$function_left_parentheses_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#>     bad_exprs <- xml2::xml_find_all(xml, xpath)
#> 
#>     xml_nodes_to_lints(
#>       bad_exprs,
#>       source_expression = source_expression,
#>       lint_message = "Remove spaces before the left parenthesis in a function call.",
#>       range_start_xpath = "number(./@col2 + 1)", # start after function / fun
#>       range_end_xpath = "number(./following-sibling::OP-LEFT-PAREN/@col1 - 1)" # end before (
#>     )
#>   }
#> <bytecode: 0x13adfceb8>
#> <environment: 0x13adf9c90>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "function_left_parentheses_linter"
#> 
#> $linters$infix_spaces_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#>     bad_expr <- xml2::xml_find_all(xml, xpath)
#> 
#>     xml_nodes_to_lints(bad_expr, source_expression = source_expression, lint_message = lint_message, type = "style")
#>   }
#> <bytecode: 0x13ae08da0>
#> <environment: 0x13ae03e88>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "infix_spaces_linter"
#> 
#> $linters$line_length_linter
#> function(source_expression) {
#>     # Only go over complete file
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     line_lengths <- nchar(source_expression$file_lines)
#>     long_lines <- which(line_lengths > length)
#> 
#>     lint_message <- sprintf("Lines should not be more than %d characters.", length)
#> 
#>     lapply(long_lines, function(long_line) {
#>       Lint(
#>         filename = source_expression$filename,
#>         line_number = long_line,
#>         column_number = length + 1L,
#>         type = "style",
#>         message = lint_message,
#>         line = source_expression$file_lines[long_line],
#>         ranges = list(c(1L, line_lengths[long_line]))
#>       )
#>     })
#>   }
#> <bytecode: 0x13ae14630>
#> <environment: 0x13ae118a0>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "line_length_linter"
#> 
#> $linters$no_tab_linter
#> function(source_expression) {
#>       if (!is_lint_level(source_expression, "expression")) {
#>         return(list())
#>       }
#> 
#>       all_matches <- re_matches(
#>         source_expression[["lines"]],
#>         regex,
#>         locations = TRUE,
#>         global = TRUE
#>       )
#> 
#>       line_numbers <- as.integer(names(source_expression[["lines"]]))
#> 
#>       lints <- Map(
#>         function(line_matches, line_number) {
#>           lapply(
#>             split(line_matches, seq_len(nrow(line_matches))),
#>             function(.match) {
#>               if (is.na(.match[["start"]]) ||
#>                 .in_ignorable_position(source_expression, line_number, .match)) {
#>                 return()
#>               }
#>               start <- .match[["start"]]
#>               end <- .match[["end"]]
#>               Lint(
#>                 filename = source_expression[["filename"]],
#>                 line_number = line_number,
#>                 column_number = start,
#>                 type = lint_type,
#>                 message = lint_msg,
#>                 line = source_expression[["lines"]][[as.character(line_number)]],
#>                 ranges = list(c(start, end))
#>               )
#>             }
#>           )
#>         },
#>         all_matches,
#>         line_numbers
#>       )
#> 
#>       Filter(function(x) any(lengths(x) > 0L), lints)
#>     }
#> <bytecode: 0x13ae2d8a0>
#> <environment: 0x13ae29fd8>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "no_tab_linter"
#> 
#> $linters$object_length_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$full_xml_parsed_content
#> 
#>     assignments <- xml2::xml_find_all(xml, object_name_xpath)
#> 
#>     # Retrieve assigned name
#>     nms <- strip_names(
#>       xml2::xml_text(assignments)
#>     )
#> 
#>     # run namespace_imports at run-time, not "compile" time to allow package structure to change
#>     ns_imports <- namespace_imports(find_package(source_expression$filename))
#>     generics <- strip_names(c(
#>       declared_s3_generics(xml),
#>       imported_s3_generics(ns_imports)$fun,
#>       .base_s3_generics
#>     ))
#>     generics <- unique(generics[nzchar(generics)])
#> 
#>     # Remove generic function names from generic implementations
#>     # This only lints S3 implementations if the class names are too long, still lints generics if they are too long.
#>     nms_stripped <- re_substitutes(nms, rex(start, or(generics), "."), "")
#> 
#>     too_long <- nchar(nms_stripped) > length
#> 
#>     xml_nodes_to_lints(
#>       assignments[too_long],
#>       source_expression = source_expression,
#>       lint_message = lint_message,
#>       type = "style"
#>     )
#>   }
#> <bytecode: 0x13ae5b8e0>
#> <environment: 0x13ae5c638>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "object_length_linter"
#> 
#> $linters$object_name_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$full_xml_parsed_content
#> 
#>     assignments <- xml2::xml_find_all(xml, object_name_xpath)
#> 
#>     # Retrieve assigned name
#>     nms <- strip_names(
#>       xml2::xml_text(assignments)
#>     )
#> 
#>     # run namespace_imports at run-time, not "compile" time to allow package structure to change
#>     generics <- c(
#>       declared_s3_generics(xml),
#>       imported_s3_generics(namespace_imports(find_package(source_expression$filename)))$fun,
#>       .base_s3_generics
#>     )
#>     generics <- unique(generics[nzchar(generics)])
#> 
#>     style_matches <- lapply(styles, function(style) {
#>       check_style(nms, style, generics)
#>     })
#> 
#>     matches_a_style <- Reduce(`|`, style_matches)
#> 
#>     xml_nodes_to_lints(
#>       assignments[!matches_a_style],
#>       source_expression,
#>       lint_message = lint_message,
#>       type = "style"
#>     )
#>   }
#> <bytecode: 0x13ae7acc8>
#> <environment: 0x13ae75ad8>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "object_name_linter"
#> 
#> $linters$object_usage_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     pkg_name <- pkg_name(find_package(dirname(source_expression$filename)))
#>     # run the following at run-time, not "compile" time to allow package structure to change
#>     env <- make_check_env(pkg_name)
#> 
#>     declared_globals <- try_silently(utils::globalVariables(package = pkg_name %||% globalenv()))
#> 
#>     xml <- source_expression$full_xml_parsed_content
#> 
#>     symbols <- c(
#>       get_assignment_symbols(xml),
#>       get_imported_symbols(xml)
#>     )
#> 
#>     # Just assign them an empty function
#>     for (symbol in symbols) {
#>       assign(symbol, function(...) invisible(), envir = env)
#>     }
#> 
#>     fun_assignments <- xml2::xml_find_all(xml, xpath_function_assignment)
#> 
#>     lapply(fun_assignments, function(fun_assignment) {
#>       code <- get_content(lines = source_expression$content, fun_assignment)
#>       fun <- try_silently(eval(
#>         envir = env,
#>         parse(
#>           text = code,
#>           keep.source = TRUE
#>         )
#>       ))
#> 
#>       if (inherits(fun, "try-error")) {
#>         return()
#>       }
#>       known_used_symbols <- get_used_symbols(fun_assignment, interpret_glue = interpret_glue)
#>       res <- parse_check_usage(
#>         fun,
#>         known_used_symbols = known_used_symbols,
#>         declared_globals = declared_globals,
#>         start_line = as.integer(xml2::xml_attr(fun_assignment, "line1")),
#>         skip_with = skip_with
#>       )
#> 
#>       # TODO handle assignment functions properly
#>       # e.g. `not_existing<-`(a, b)
#>       res$name <- rex::re_substitutes(res$name, rex::rex("<-"), "")
#> 
#>       lintable_symbols <- xml2::xml_find_all(
#>         fun_assignment,
#>         "
#>         descendant::SYMBOL
#>         | descendant::SYMBOL_FUNCTION_CALL
#>         | descendant::SPECIAL
#>         | descendant::LEFT_ASSIGN[text() = ':=']
#>         "
#>       )
#> 
#>       lintable_symbol_names <- gsub("^`|`$", "", xml2::xml_text(lintable_symbols))
#>       lintable_symbol_lines <- as.integer(xml2::xml_attr(lintable_symbols, "line1"))
#> 
#>       matched_symbol <- vapply(
#>         seq_len(nrow(res)),
#>         function(i) {
#>           match(
#>             TRUE,
#>             lintable_symbol_names == res$name[i] &
#>               lintable_symbol_lines >= res$line1[i] &
#>               lintable_symbol_lines <= res$line2[i]
#>           )
#>         },
#>         integer(1L)
#>       )
#> 
#>       nodes <- unclass(lintable_symbols)[matched_symbol]
#>       nodes[is.na(matched_symbol)] <- list(fun_assignment)
#> 
#>       xml_nodes_to_lints(nodes, source_expression = source_expression, lint_message = res$message, type = "warning")
#>     })
#>   }
#> <bytecode: 0x13ae95320>
#> <environment: 0x13ae92be8>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "object_usage_linter"
#> 
#> $linters$paren_body_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#>     matched_expressions <- xml2::xml_find_all(xml, xpath)
#> 
#>     xml_nodes_to_lints(
#>       matched_expressions,
#>       source_expression = source_expression,
#>       lint_message = "There should be a space between a right parenthesis and a body expression."
#>     )
#>   }
#> <bytecode: 0x13aee14a8>
#> <environment: 0x13aede280>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "paren_body_linter"
#> 
#> $linters$pipe_continuation_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#>     x <- source_expression$full_xml_parsed_content
#> 
#>     pipe_exprs <- xml_find_all(x, multiline_pipe_test)
#> 
#>     xml_nodes_to_lints(
#>       pipe_exprs,
#>       source_expression = source_expression,
#>       lint_message = paste(
#>         "`%>%` should always have a space before it and a new line after it,",
#>         "unless the full pipeline fits on one line."
#>       ),
#>       type = "style"
#>     )
#>   }
#> <bytecode: 0x13aeeb790>
#> <environment: 0x13aeec4e8>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "pipe_continuation_linter"
#> 
#> $linters$semicolon_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$full_xml_parsed_content
#>     bad_exprs <- xml2::xml_find_all(xml, xpath)
#>     if (need_detection) {
#>       is_trailing <- is.na(xml2::xml_find_first(bad_exprs, compound_xpath))
#>       msg <- ifelse(is_trailing, msg_trailing, msg_compound)
#>     }
#> 
#>     xml_nodes_to_lints(
#>       bad_exprs,
#>       source_expression = source_expression,
#>       lint_message = msg
#>     )
#>   }
#> <bytecode: 0x13aefaf68>
#> <environment: 0x13aef6130>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "semicolon_linter"
#> 
#> $linters$seq_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#> 
#>     badx <- xml2::xml_find_all(xml, xpath)
#> 
#>     # TODO: better message customization. For example, length(x):1
#>     #   would get rev(seq_along(x)) as the preferred replacement.
#>     dot_expr1 <- get_fun(badx, 1L)
#>     dot_expr2 <- get_fun(badx, 2L)
#>     seq_along_idx <- grepl("length(", dot_expr1, fixed = TRUE) | grepl("length(", dot_expr2, fixed = TRUE)
#>     replacement <- ifelse(seq_along_idx, "seq_along", "seq_len")
#> 
#>     dot_expr3 <- ifelse(seq_along_idx, "...", dot_expr2)
#>     lint_message <- ifelse(
#>       grepl("seq", dot_expr1, fixed = TRUE),
#>       sprintf(
#>         "%s(%s) is likely to be wrong in the empty edge case. Use %s(%s) instead.",
#>         dot_expr1, dot_expr2, replacement, dot_expr3
#>       ),
#>       sprintf(
#>         "%s:%s is likely to be wrong in the empty edge case. Use %s(%s) instead.",
#>         dot_expr1, dot_expr2, replacement, dot_expr3
#>       )
#>     )
#> 
#>     xml_nodes_to_lints(badx, source_expression, lint_message, type = "warning")
#>   }
#> <bytecode: 0x13af12210>
#> <environment: 0x13af0cb08>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "seq_linter"
#> 
#> $linters$single_quotes_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     content <- source_expression$full_parsed_content
#>     str_idx <- which(content$token == "STR_CONST")
#>     squote_matches <- which(re_matches(content[str_idx, "text"], squote_regex))
#> 
#>     lapply(
#>       squote_matches,
#>       function(id) {
#>         with(content[str_idx[id], ], {
#>           line <- source_expression$file_lines[[line1]]
#>           col2 <- if (line1 == line2) col2 else nchar(line)
#>           Lint(
#>             filename = source_expression$filename,
#>             line_number = line1,
#>             column_number = col1,
#>             type = "style",
#>             message = "Only use double-quotes.",
#>             line = line,
#>             ranges = list(c(col1, col2))
#>           )
#>         })
#>       }
#>     )
#>   }
#> <bytecode: 0x13af32800>
#> <environment: 0x13af33590>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "single_quotes_linter"
#> 
#> $linters$spaces_inside_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$full_xml_parsed_content
#> 
#>     left_expr <- xml2::xml_find_all(xml, left_xpath)
#>     left_msg <- ifelse(
#>       xml2::xml_text(left_expr) == "[",
#>       "Do not place spaces after square brackets.",
#>       "Do not place spaces after parentheses."
#>     )
#> 
#>     left_lints <- xml_nodes_to_lints(
#>       left_expr,
#>       source_expression = source_expression,
#>       lint_message = left_msg,
#>       range_start_xpath = "number(./@col2 + 1)", # start after ( or [
#>       range_end_xpath = "number(./following-sibling::*[1]/@col1 - 1)" # end before following expr
#>     )
#> 
#>     right_expr <- xml2::xml_find_all(xml, right_xpath)
#>     right_msg <- ifelse(
#>       xml2::xml_text(right_expr) == "]",
#>       "Do not place spaces before square brackets.",
#>       "Do not place spaces before parentheses."
#>     )
#> 
#>     right_lints <- xml_nodes_to_lints(
#>       right_expr,
#>       source_expression = source_expression,
#>       lint_message = right_msg,
#>       range_start_xpath = "number(./preceding-sibling::*[1]/@col2 + 1)", # start after preceding expression
#>       range_end_xpath = "number(./@col1 - 1)" # end before ) or ]
#>     )
#> 
#>     c(left_lints, right_lints)
#>   }
#> <bytecode: 0x13af50e58>
#> <environment: 0x13af4df08>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "spaces_inside_linter"
#> 
#> $linters$spaces_left_parentheses_linter
#> function(source_expression) {
#>     # else/if structure for trees that are missing XML content (both global & local)
#>     if (is_lint_level(source_expression, "file")) {
#>       # 'x = 1;(x + 2)' can't be detected from the expression-level tree
#>       xml <- source_expression$full_xml_parsed_content
#>       xpath <- file_level_xpath
#>     } else {
#>       xml <- source_expression$xml_parsed_content
#>       xpath <- expression_level_xpath
#>     }
#> 
#>     bad_paren <- xml2::xml_find_all(xml, xpath)
#> 
#>     xml_nodes_to_lints(
#>       bad_paren,
#>       source_expression,
#>       lint_message = "Place a space before left parenthesis, except in a function call.",
#>       type = "style"
#>     )
#>   }
#> <bytecode: 0x13af690f8>
#> <environment: 0x13af64480>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "spaces_left_parentheses_linter"
#> 
#> $linters$T_and_F_symbol_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     bad_exprs <- xml2::xml_find_all(source_expression$xml_parsed_content, xpath)
#>     bad_assigns <- xml2::xml_find_all(source_expression$xml_parsed_content, xpath_assignment)
#> 
#>     make_lints <- function(expr, fmt) {
#>       symbol <- xml2::xml_text(expr)
#>       lint_message <- sprintf(fmt, replacement_map[symbol], symbol)
#>       xml_nodes_to_lints(
#>         xml = expr,
#>         source_expression = source_expression,
#>         lint_message = lint_message,
#>         type = "style",
#>         column_number_xpath = "number(./@col2 + 1)", # mark at end
#>         range_end_xpath = "number(./@col2 + 1)" # end after T/F for easy fixing
#>       )
#>     }
#> 
#>     c(
#>       make_lints(bad_exprs, "Use %s instead of the symbol %s."),
#>       make_lints(bad_assigns, "Don't use %2$s as a variable name, as it can break code relying on %2$s being %1$s.")
#>     )
#>   }
#> <bytecode: 0x13af76c60>
#> <environment: 0x13af71bc0>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "T_and_F_symbol_linter"
#> 
#> $linters$trailing_blank_lines_linter
#> function(source_expression) {
#>     blanks <- re_matches(
#>       source_expression$file_lines,
#>       rex(start, any_spaces, end)
#>     )
#> 
#>     line_number <- length(source_expression$file_lines)
#>     lints <- list()
#>     while (line_number > 0L && (is.na(blanks[[line_number]]) || isTRUE(blanks[[line_number]]))) {
#>       if (!is.na(blanks[[line_number]])) {
#>         lints[[length(lints) + 1L]] <- Lint(
#>           filename = source_expression$filename,
#>           line_number = line_number,
#>           column_number = 1L,
#>           type = "style",
#>           message = "Trailing blank lines are superfluous.",
#>           line = source_expression$file_lines[[line_number]]
#>         )
#>       }
#>       line_number <- line_number - 1L
#>     }
#> 
#>     if (identical(source_expression$terminal_newline, FALSE)) { # could use isFALSE, but needs backports
#>       last_line <- tail(source_expression$file_lines, 1L)
#> 
#>       lints[[length(lints) + 1L]] <- Lint(
#>         filename = source_expression$filename,
#>         line_number = length(source_expression$file_lines),
#>         column_number = nchar(last_line) %||% 0L + 1L,
#>         type = "style",
#>         message = "Missing terminal newline.",
#>         line = last_line
#>       )
#>     }
#> 
#>     lints
#>   }
#> <bytecode: 0x13af908e0>
#> <environment: 0x13af91600>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "trailing_blank_lines_linter"
#> 
#> $linters$trailing_whitespace_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "file")) {
#>       return(list())
#>     }
#> 
#>     res <- re_matches(
#>       source_expression$file_lines,
#>       rex(blanks, end),
#>       locations = TRUE
#>     )
#> 
#>     if (isTRUE(allow_empty_lines)) {
#>       bad_lines <- which(res$start > 1L)
#>     } else {
#>       bad_lines <- which(!is.na(res$start))
#>     }
#> 
#>     if (isTRUE(allow_in_strings) && !is.null(source_expression$full_xml_parsed_content)) {
#>       all_str_consts <- xml2::xml_find_all(source_expression$full_xml_parsed_content, "//STR_CONST")
#>       start_lines <- as.integer(xml2::xml_attr(all_str_consts, "line1"))
#>       end_lines <- as.integer(xml2::xml_attr(all_str_consts, "line2"))
#> 
#>       is_in_str <- vapply(bad_lines, function(ln) {
#>         any(start_lines <= ln & ln < end_lines)
#>       }, logical(1L))
#>       bad_lines <- bad_lines[!is_in_str]
#>     }
#> 
#>     lapply(
#>       bad_lines,
#>       function(line) {
#>         Lint(
#>           filename = source_expression$filename,
#>           line_number = line,
#>           column_number = res$start[[line]],
#>           type = "style",
#>           message = "Trailing whitespace is superfluous.",
#>           line = source_expression$file_lines[[line]],
#>           ranges = list(c(res$start[[line]], res$end[[line]]))
#>         )
#>       }
#>     )
#>   }
#> <bytecode: 0x13afaab48>
#> <environment: 0x13afa6368>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "trailing_whitespace_linter"
#> 
#> $linters$vector_logic_linter
#> function(source_expression) {
#>     if (!is_lint_level(source_expression, "expression")) {
#>       return(list())
#>     }
#> 
#>     xml <- source_expression$xml_parsed_content
#>     bad_expr <- xml2::xml_find_all(xml, xpath)
#> 
#>     xml_nodes_to_lints(
#>       bad_expr,
#>       source_expression = source_expression,
#>       lint_message = "Conditional expressions require scalar logical operators (&& and ||)",
#>       type = "warning"
#>     )
#>   }
#> <bytecode: 0x13afd2c28>
#> <environment: 0x13afcfae0>
#> attr(,"class")
#> [1] "linter"   "function"
#> attr(,"name")
#> [1] "vector_logic_linter"
#> 
#> 
#> $encoding
#> [1] "UTF-8"
#> 
#> $exclude
#> #[[:space:]]*nolint
#> 
#> $exclude_start
#> #[[:space:]]*nolint start
#> 
#> $exclude_end
#> #[[:space:]]*nolint end
#> 
#> $exclude_linter
#> ^[[:space:]]*:[[:space:]]*(?<linters>(?:(?:[^,.])+[[:space:]]*,[[:space:]]*)*(?:[^,.])+)\.
#> 
#> $exclude_linter_sep
#> [[:space:]]*,[[:space:]]*
#> 
#> $exclusions
#> list()
#> 
#> $cache_directory
#> [1] "/Users/indrajeetpatil/Library/Caches/org.R-project.R/R/lintr"
#> 
#> $comment_token
#> [1] "0a12aa72507e6273daac3443dab7d42a1a71aa28"
#> 
#> $comment_bot
#> [1] TRUE
#> 
#> $error_on_lint
#> [1] FALSE

Created on 2022-10-08 with reprex v2.0.2

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, yea that's not very helpful. How about default_settings[setdiff(names(default_settings), "linters")] or just default_settings[c(...)] for the set we think are important?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

I am still retaining names(default_settings) and names(default_settings$linters) to show the names of everything included in these two objects.

@MichaelChirico
Copy link
Collaborator

I'm not sure this closes the issue -- where do we document what a .lintr file is / looks like?

@IndrajeetPatil
Copy link
Collaborator Author

I'm not sure this closes the issue -- where do we document what a .lintr file is / looks like?

Note that this targets only #823, and not #680, which can be addressed in a separate PR.

@MichaelChirico MichaelChirico merged commit 16e8576 into main Oct 9, 2022
@MichaelChirico MichaelChirico deleted the 823_document_settings branch October 9, 2022 05:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document settings in regular package help

4 participants