Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export(Lint)
export(Linter)
export(T_and_F_symbol_linter)
export(absolute_path_linter)
export(all_linters)
export(all_undesirable_functions)
export(all_undesirable_operators)
export(any_duplicated_linter)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@

* Added `format()` functions for `lint` and `lints` (#1784, @AshesITR)

* `all_linters()` function provides an easy way to access all available linters (#1843, @IndrajeetPatil)

### New linters

* `unnecessary_lambda_linter()`: detect unnecessary lambdas (anonymous functions), e.g.
Expand Down
2 changes: 1 addition & 1 deletion R/use_lintr.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use_lintr <- function(path = ".", type = c("tidyverse", "full")) {
encoding = '"UTF-8"'
),
full = list(
linters = 'linters_with_tags(tags = NULL, packages = "lintr") # see vignette("lintr")',
linters = 'all_linters(packages = "lintr") # see vignette("lintr")',
encoding = '"UTF-8"',
exclusions = 'list("renv", "packrat") # see ?lintr::exclude'
)
Expand Down
45 changes: 36 additions & 9 deletions R/with.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
#' @param defaults named list of elements to modify.
#' @return A modified list of elements, sorted by name. To achieve this sort in a platform-independent way, two
#' transformations are applied to the names: (1) replace `_` with `0` and (2) convert [tolower()].
#' @seealso [linters_with_tags], [linters_with_defaults] for creating linter lists.
#'
#' @seealso
#' - [linters_with_defaults] for basing off lintr's set of default linters.
#' - [all_linters] for basing off all available linters in lintr.
#' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages.
#' - [available_linters] to get a data frame of available linters.
#' - [linters] for a complete list of linters available in lintr.
#'
#' @examples
#' # custom list of undesirable functions:
#' # remove `sapply` (using `NULL`)
Expand Down Expand Up @@ -65,19 +72,17 @@ modify_defaults <- function(defaults, ...) {
#'
#' @return A modified list of linters.
#' @seealso
#' [linters_with_defaults] for basing off lintr's set of default linters.
#' [available_linters] to get a data frame of available linters.
#' [linters] for a complete list of linters available in lintr.
#' - [linters_with_defaults] for basing off lintr's set of default linters.
#' - [all_linters] for basing off all available linters in lintr.
#' - [available_linters] to get a data frame of available linters.
#' - [linters] for a complete list of linters available in lintr.
#'
#' @examples
#' # `linters_with_defaults()` and `linters_with_tags("default")` are the same:
#' all.equal(linters_with_defaults(), linters_with_tags("default"))
#'
#' # Get all linters useful for package development
#' linters <- linters_with_tags(tags = "package_development")
#' names(linters)
#'
#' # Get all linters provided by lintr
#' linters <- linters_with_tags(tags = NULL)
#' linters <- linters_with_tags(tags = c("package_development", "style"))
#' names(linters)
#'
#' # Get all linters tagged as "default" from lintr and mypkg
Expand Down Expand Up @@ -117,6 +122,23 @@ linters_with_tags <- function(tags, ..., packages = "lintr", exclude_tags = "dep
modify_defaults(..., defaults = tagged_linters)
}

#' Create a linter configuration based on all available linters
#'
#' @inheritParams linters_with_tags
#'
#' @examples
#' names(all_linters())
#'
#' @seealso
#' - [linters_with_defaults] for basing off lintr's set of default linters.
#' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages.
#' - [available_linters] to get a data frame of available linters.
#' - [linters] for a complete list of linters available in lintr.
#' @export
all_linters <- function(packages = "lintr", ...) {
linters_with_tags(tags = NULL, packages = packages, ...)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What happens in the unlikely case that tags is given as a named argument?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It will produce an error:

library(lintr)
all_linters(tags = "default")
#> Error in linters_with_tags(tags = NULL, packages = packages, ...): formal argument "tags" matched by multiple actual arguments

Created on 2022-12-18 with reprex v2.0.2

But I doubt anyone will do this because this assumes that the user has looked at the implementation details for this newly introduced function and figured out that all_linters() is a wrapper around linters_with_tags(), and is deliberately using this argument. But, in that case, they shouldn't be surprised by the error message they get.

Copy link
Collaborator

Choose a reason for hiding this comment

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

modify_defaults(..., defaults = linters_with_tags(tags = NULL, packages = packages) should not suffer from this bug.

}

#' Create a linter configuration based on defaults
#'
#' Make a new list based on \pkg{lintr}'s default linters.
Expand Down Expand Up @@ -148,6 +170,7 @@ linters_with_tags <- function(tags, ..., packages = "lintr", exclude_tags = "dep
#'
#' @seealso
#' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages.
#' - [all_linters] for basing off all available linters in lintr.
#' - [available_linters] to get a data frame of available linters.
#' - [linters] for a complete list of linters available in lintr.
#' @export
Expand Down Expand Up @@ -180,6 +203,8 @@ with_defaults <- function(..., default = default_linters) {
linters_with_defaults(..., defaults = default)
}

#' @keywords internal
#' @noRd
call_linter_factory <- function(linter_factory, linter_name, package) {
linter <- tryCatch(
linter_factory(),
Expand All @@ -192,6 +217,8 @@ call_linter_factory <- function(linter_factory, linter_name, package) {
linter
}

#' @keywords internal
#' @noRd
guess_names <- function(..., missing_index) {
args <- as.character(eval(substitute(alist(...)[missing_index])))
# foo_linter(x=1) => "foo"
Expand Down
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# lintr

[![R build status](https://github.com/r-lib/lintr/workflows/R-CMD-check/badge.svg)](https://github.com/r-lib/lintr/actions)
[![codecov.io](https://codecov.io/github/r-lib/lintr/coverage.svg?branch=main)](https://codecov.io/github/r-lib/lintr?branch=main)
[![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/lintr)](https://cran.r-project.org/package=lintr)

`lintr` provides [static code analysis for R](https://en.wikipedia.org/wiki/Static_program_analysis). It checks for adherence to a given style, identifying syntax errors and possible semantic issues, then reports them to you so you can take action. Watch lintr in action in the following animation:
`{lintr}` provides [static code analysis for R](https://en.wikipedia.org/wiki/Static_program_analysis). It checks for adherence to a given style, identifying syntax errors and possible semantic issues, then reports them to you so you can take action. Watch lintr in action in the following animation:

![](man/figures/demo.gif "lintr demo")

`lintr` is complementary to [the `styler` package](https://github.com/r-lib/styler) which automatically restyles code, eliminating some of the problems that `lintr` can detect.
`{lintr}` is complementary to [the `{styler}` package](https://github.com/r-lib/styler) which automatically restyles code, eliminating some of the problems that `{lintr}` can detect.

## Installation

Expand All @@ -26,15 +27,15 @@ remotes::install_github("r-lib/lintr")

## Usage

And then you can create a configuration file and run selected linters:

```R
# in a project:
lintr::use_lintr(type = "tidyverse")
usethis::use_github_action("lint-project")

# in a project:
lintr::lint_dir()

# in a package:
lintr::use_lintr(type = "tidyverse")
usethis::use_github_action("lint")
lintr::lint_package()
```

Expand All @@ -45,7 +46,19 @@ To see a list of linters included for each configuration:
names(lintr::linters_with_defaults())

# full
names(lintr::linters_with_tags(tags = NULL))
names(lintr::all_linters())
```

### Setting up GitHub Actions

`{usethis}` provides helper functions to generate lint workflows for GitHub Actions:

```R
# in a project:
usethis::use_github_action("lint-project")

# in a package:
usethis::use_github_action("lint")
```

You can also run lintr during continuous integration or within your IDE or text editor. See `vignette("continuous-integration")` and `vignette("editors")` for more details.
Expand Down
30 changes: 30 additions & 0 deletions man/all_linters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/linters_with_defaults.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 7 additions & 8 deletions man/linters_with_tags.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion man/modify_defaults.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions tests/testthat/test-with.R
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,17 @@ test_that("linters_with_defaults(default = .) is supported with a deprecation wa
expect_silent(linters <- linters_with_defaults(defaults = list(), default = default))
expect_named(linters, "default")
})

test_that("all_linters contains all available linters", {
all_linters <- all_linters(packages = "lintr")

expect_identical(linters_with_tags(NULL, packages = "lintr"), all_linters)
expect_length(all_linters, nrow(available_linters()))
})

test_that("all_linters respects ellipsis argument", {
expect_identical(
linters_with_tags(tags = NULL, implicit_integer_linter = NULL),
all_linters(packages = "lintr", implicit_integer_linter = NULL)
)
})
39 changes: 30 additions & 9 deletions vignettes/lintr.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ If an Encoding is found in a `.Rproj` file or a `DESCRIPTION` file, that encodin

If you only want to customize some linters, you can use the helper function `linters_with_defaults()`, which will keep all unnamed linters with the default settings.
Disable a linter by passing `NULL`.

For example, to set the line length limit to 120 characters and globally disable the `no_tab_linter`, you can put this into your `.lintr`:

``` r
Expand Down Expand Up @@ -194,29 +195,49 @@ defaults_table <- data.frame(
knitr::kable(defaults_table)
```

Another way to customize linters is by specifying tags in `linters_with_tags()`.
The available tags are listed below:

```{r}
lintr::available_tags(packages = "lintr")
```

You can select tags of interest to see which linters are included:

```{r}
linters <- lintr::linters_with_tags(tags = c("package_development", "readability"))
names(linters)
```

You can include tag-based linters in the configuration file, and customize them further:

```yaml
linters: linters_with_tags(
tags = c("package_development", "readability"),
yoda_test_linter = NULL
)
```

#### Using all available linters

The default lintr configuration includes only linters relevant to the tidyverse style guide, but there are many other linters available in `{lintr}`. You can see a list of all available linters using

```{r}
names(lintr::linters_with_tags(tags = NULL))
names(lintr::all_linters())
```

If you want to use all available linters, you can include this in your `.lintr` file:

```r
linters: linters_with_defaults(
defaults = linters_with_tags(tags = NULL)
)
```yaml
linters: all_linters()
```

If you want to use all available linters *except* a few, you can exclude them using `NULL`:

```r
linters: linters_with_defaults(
```yaml
linters: all_linters(
commented_code_linter = NULL,
implicit_integer_linter = NULL,
defaults = linters_with_tags(tags = NULL)
implicit_integer_linter = NULL
)
```

Expand Down