Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New linters and bug fixes #214

Merged
merged 39 commits into from
Mar 13, 2017
Merged

New linters and bug fixes #214

merged 39 commits into from
Mar 13, 2017

Conversation

fangly
Copy link
Contributor

@fangly fangly commented Mar 8, 2017

No description provided.

fangly added 14 commits March 8, 2017 10:20
…nportable_path_linter() with a "lax" option for fewer false positibe lints.
…_terminator_linter()

* find some trailing semicolons not previously detected
* find compound semicolons, i.e. semicolon between two statements on the same line
* "type" option to lint "trailing" and/or "compound" semicolons
* features of the linter described at r-lib#194
* default_linters() changed accordingly (enforce lowerCamelCase)
* refactored make_object_linter() since it returned too many objects that were out of the scope, and failed to return some that were
* Do not lint comments within roxygen blocks (this follows the linter's description, strictly speaking). This allows to write comments that show the expected result in @example roxygen blocks, e.g.:
   #' @examples
   #' foo(1)  ## list(1)

* Do not consider "-" an R operator to avoid too many false positives, e.g. in "# Non-existent"
* do not display markers when check is NULL
* fixed malformed error messages
* more consistent errors messages
* check validity of Lint fields in 'checks'
* return invisible(NULL), just like expect_error()
* doc update
Copy link
Member

@jimhester jimhester left a comment

Choose a reason for hiding this comment

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

I think we need to deprecate the removed functions with .Deprecated() for at least one release, for the object linters like snake_case_linter I think you can just have it call object_name_linter("snake_case") internally.

More generally I think we should move away from using regular expressions if possible and towards using the token dataframe or more ideally XPath expressions on the XML versions of the parse tokens.

The regular expressions and custom logic make the function definitions pretty hard to follow and maintain, having each use a (perhaps complex) XPath expression could potentially homogenize the implementations.

"detach"="use roxygen2's @importFrom statement in packages, or `::` in scripts",
"library"= "use roxygen2's @importFrom statement in packages, or `::` in scripts",
"require"= "use roxygen2's @importFrom statement in packages, or `::` in scripts",
"loadNamespace"="use `::` or requireNamespace()",
Copy link
Member

Choose a reason for hiding this comment

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

I do not think loadNamespace should be linted, I actually prefer using it in most cases, most of the time you use it for suggested packages and do want to throw an error if a package is not installed. See http://r-pkgs.had.co.nz/namespace.html#search-path for a discussion of the differences.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you are generally aiming to follow Hadley's style and guidelines, loadNamespace should be left in by default.

"source"=NA,
"sapply"="use vapply() or lapply()",
"mapply"="use Map()",
"ifelse"="use an if () {} else {} block",
Copy link
Member

Choose a reason for hiding this comment

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

While it is true some (most?) of the time people use ifelse when it would be better to use if() there are cases where you do want to branch on a vectorized conditional, I think it is too aggressive to lint this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll remove it from the default. I will likely make it available a set of extended defaults for those that prefer to lint aggressively.

"sapply"="use vapply() or lapply()",
"mapply"="use Map()",
"ifelse"="use an if () {} else {} block",
"return"="let the last value of a function automatically be returned",
Copy link
Member

Choose a reason for hiding this comment

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

return() is still very useful for early returns, you would need to only lint this at the end of a function body.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, that just reflected my preference. I will remove if from the defaults. In the case of return, a separate linter would be preferable anyway,

"mapply"="use Map()",
"ifelse"="use an if () {} else {} block",
"return"="let the last value of a function automatically be returned",
"substring"="use substr()"
Copy link
Member

Choose a reason for hiding this comment

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

Hmm I think this is also too aggressive, substring recycles its first argument, which makes it useful for some things.

substring("abcdefghijklmnop", seq(1, 15, 5))
#> [1] "abcdefghijklmnop" "fghijklmnop"      "klmnop"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No worries. Again, this only reflected my own personal biases.

R/path_linters.R Outdated
)
}

is_long_path <- function(str) {
Copy link
Member

Choose a reason for hiding this comment

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

I think path is a better name for this argument in all of these path functions.

Copy link
Contributor Author

@fangly fangly Mar 9, 2017

Choose a reason for hiding this comment

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

The three functions is_valid_path(), is_long_path() and is_valid_long_path() do indeed expect some sort of path as input. It makes sense to change the argument name as you suggested.

I am not so convinced about changing the argument name for the four functions is_absolute_path(), is_root_path(), is_relative_path(), is_path() because the input is a string and the functions determine if this is a path.

Copy link
Member

Choose a reason for hiding this comment

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

Fine, split_path() arguments need to be path and sep though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought we were splitting hairs, so I changed it to "path" anyway.
I will do the same for split_path as well.

#' @param style UpperCamelCase, lowerCamelCase, alllowercase, ALLUPPERCASE, snake_case or
#' dotted.case
#' @export
object_name_linter <- function(style = "lowerCamelCase") {
Copy link
Member

Choose a reason for hiding this comment

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

The default needs to be snake_case.

code_locs <<- register_code_locations(source_file, code_locs)
} else {
# non-code section: detect and report TODOs
if (length(file_lines) == 0L) {file_lines <- ""} # workaround rex bug goo.gl/2rbZDy
Copy link
Member

Choose a reason for hiding this comment

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

I would rather have a full link rather than short url, as the full link contains information that can be used to retrieve the issue even if the link breaks in the future, such as repo username and issue number.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is understandable.

#' @describeIn linters Check that the source contains no TODO comments (case-insensitive).
#' @param todo Vector of strings that identify TODO comments.
#' @export
todo_comment_linter <- function(todo=c("todo", "fixme")) {
Copy link
Member

Choose a reason for hiding this comment

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

I feel like this function should just use the getParseData() and check for a COMMENT token that has text ^#[[:spaces:]]TODO, why is all this complication needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The function is more complicated than anticipated because not all comments are reported within source_file$parsed_content.

# TODO 1
x <- 3 # TODO 2
foo <- function(x) { # TODO 3
  # TODO 4
  x * 2 # TODO 5
}
y <- foo(x) # TODO 6
# TODO 7

In the example above, only TODO 3-5 are returned as tokens. In semicolon_terminator_linter(), there is a similar issue that is addressed by the locate_other_sc function.

Deprecated function / replacement function / alias:
* camel_case_linter / object_name_linter / -
* snake_case_linter/ object_name_linter / -
* multiple_dots_linter / object_name_linter / -
* trailing_semicolons_linter / semicolon_terminator_linter / semicolon_terminator_linter(type = "trailing)
* absolute_paths_linter / absolute_path_linter / absolute_path_linter(lax = TRUE)
Use lintr:::expect_lint() instead of expect_lint() because it is a private function
@jimhester
Copy link
Member

Note: there is no alias for camel_case_linter(), snake_case_linter() and multiple_dots_linter() because they identify names to remove, while object_name_linter() identifies names to keep.

Yes this is true, we will probably need to keep the old definitions around until these functions are completely deprecated and gone then.

R/path_linters.R Outdated
)
}

is_long_path <- function(str) {
Copy link
Member

Choose a reason for hiding this comment

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

Fine, split_path() arguments need to be path and sep though.

R/path_linters.R Outdated
for (q in type) {
str <- re_substitutes(str, rex(start, q, capture(zero_or_more(any)), q, end), "\\1")
}
stringi::stri_unescape_unicode(str)
Copy link
Member

Choose a reason for hiding this comment

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

We cannot depend on stringi, you can look at base alternatives if you need this http://stackoverflow.com/a/25310392/2055486

@fangly
Copy link
Contributor Author

fangly commented Mar 10, 2017

I have implemented an unescape() function that relies on base R's gregexpr and regmatches() instead of stringi. I think there are no more open issues, Jim.

@codecov-io
Copy link

codecov-io commented Mar 10, 2017

Codecov Report

Merging #214 into master will increase coverage by 4.33%.
The diff coverage is 96.46%.

@@            Coverage Diff             @@
##           master     #214      +/-   ##
==========================================
+ Coverage   81.31%   85.65%   +4.33%     
==========================================
  Files          30       37       +7     
  Lines        1702     2133     +431     
==========================================
+ Hits         1384     1827     +443     
+ Misses        318      306      -12
Impacted Files Coverage Δ
R/cache.R 95.06% <ø> (-1.24%)
R/zzz.R 0% <0%> (ø)
R/lint.R 71.28% <100%> (+1.02%)
R/undesirable_function_linter.R 100% <100%> (ø)
R/T_and_F_symbol_linter.R 100% <100%> (ø)
R/extraction_operator_linter.R 100% <100%> (ø)
R/object_name_linters.R 100% <100%> (ø)
R/implicit_integer_linter.R 100% <100%> (ø)
R/deprecated.R 100% <100%> (ø)
R/undesirable_operator_linter.R 100% <100%> (ø)
... and 20 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5e16d6b...ae2870d. Read the comment docs.

@jimhester
Copy link
Member

Ok I think all that is left is updating the NEWS file and README explaining the changes / listing the new linters added.

@fangly
Copy link
Contributor Author

fangly commented Mar 13, 2017

This is done. It is a minor commit, so, I merging this PR now.

@fangly fangly closed this Mar 13, 2017
@jimhester
Copy link
Member

Please let me merge major things like this.

@fangly
Copy link
Contributor Author

fangly commented Mar 13, 2017

Ok, sorry for my impatience. Thank you, Jim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants