├── .Rbuildignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── issue_template.md ├── pull_request_template.md └── workflows │ ├── R-CMD-check.yaml │ ├── flir.yaml │ ├── rhub.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── create_CRU_df.R ├── create_CRU_stack.R ├── getCRUCLdata-package.R ├── get_CRU.R ├── get_CRU_df.R ├── get_CRU_stack.R ├── globals.R ├── internal_functions.R ├── manage_cached_files.R └── zzz.R ├── README.md ├── codecov.yml ├── codemeta.json ├── flint ├── cache_file_state.rds ├── config.yml └── rules │ └── builtin │ ├── T_and_F_symbol.yml │ ├── absolute_path.yml │ ├── any_duplicated.yml │ ├── any_is_na.yml │ ├── class_equals.yml │ ├── condition_message.yml │ ├── double_assignment.yml │ ├── duplicate_argument.yml │ ├── empty_assignment.yml │ ├── equal_assignment.yml │ ├── equals_na.yml │ ├── expect_comparison.yml │ ├── expect_identical.yml │ ├── expect_length.yml │ ├── expect_named.yml │ ├── expect_not.yml │ ├── expect_null.yml │ ├── expect_true_false.yml │ ├── expect_type.yml │ ├── for_loop_index.yml │ ├── function_return.yml │ ├── implicit_assignment.yml │ ├── is_numeric.yml │ ├── length_levels.yml │ ├── length_test.yml │ ├── lengths.yml │ ├── library_call.yml │ ├── literal_coercion.yml │ ├── matrix_apply.yml │ ├── missing_argument.yml │ ├── nested_ifelse.yml │ ├── numeric_leading_zero.yml │ ├── outer_negation.yml │ ├── package_hooks.yml │ ├── paste.yml │ ├── redundant_equals.yml │ ├── redundant_ifelse.yml │ ├── rep_len.yml │ ├── right_assignment.yml │ ├── sample_int.yml │ ├── semicolon.yml │ ├── seq.yml │ ├── sort.yml │ ├── todo_comment.yml │ ├── undesirable_function.yml │ ├── undesirable_operator.yml │ ├── unnecessary_nesting.yml │ ├── unreachable_code.yml │ └── which_grepl.yml ├── flir ├── cache_file_state.rds ├── config.yml └── rules │ └── builtin │ ├── T_and_F_symbol.yml │ ├── absolute_path.yml │ ├── any_duplicated.yml │ ├── any_is_na.yml │ ├── class_equals.yml │ ├── condition_message.yml │ ├── double_assignment.yml │ ├── duplicate_argument.yml │ ├── empty_assignment.yml │ ├── equal_assignment.yml │ ├── equals_na.yml │ ├── expect_comparison.yml │ ├── expect_identical.yml │ ├── expect_length.yml │ ├── expect_named.yml │ ├── expect_not.yml │ ├── expect_null.yml │ ├── expect_true_false.yml │ ├── expect_type.yml │ ├── for_loop_index.yml │ ├── function_return.yml │ ├── implicit_assignment.yml │ ├── is_numeric.yml │ ├── length_levels.yml │ ├── length_test.yml │ ├── lengths.yml │ ├── library_call.yml │ ├── list_comparison.yml │ ├── literal_coercion.yml │ ├── matrix_apply.yml │ ├── missing_argument.yml │ ├── nested_ifelse.yml │ ├── numeric_leading_zero.yml │ ├── outer_negation.yml │ ├── package_hooks.yml │ ├── paste.yml │ ├── redundant_equals.yml │ ├── redundant_ifelse.yml │ ├── rep_len.yml │ ├── right_assignment.yml │ ├── sample_int.yml │ ├── semicolon.yml │ ├── seq.yml │ ├── sort.yml │ ├── stopifnot_all.yml │ ├── todo_comment.yml │ ├── undesirable_function.yml │ ├── undesirable_operator.yml │ ├── unnecessary_nesting.yml │ ├── unreachable_code.yml │ └── which_grepl.yml ├── getCRUCLdata.Rproj ├── inst ├── CITATION ├── WORDLIST └── paper │ ├── paper.bib │ ├── paper.md │ └── paper.pdf ├── man ├── create_CRU_df.Rd ├── create_CRU_stack.Rd ├── dot-check_vars_FALSE.Rd ├── dot-create_df.Rd ├── dot-create_stack.Rd ├── dot-create_stacks.Rd ├── dot-get_CRU.Rd ├── dot-get_local.Rd ├── dot-read_cache.Rd ├── dot-set_cache.Rd ├── dot-tidy_df.Rd ├── dot-validate_dsn.Rd ├── figures │ ├── README-unnamed-chunk-10-1.png │ ├── README-unnamed-chunk-11-1.png │ ├── README-unnamed-chunk-6-1.png │ └── README-unnamed-chunk-7-1.png ├── getCRUCLdata-package.Rd ├── get_CRU_df.Rd ├── get_CRU_stack.Rd └── manage_cache.Rd ├── tests ├── spelling.R ├── testthat.R └── testthat │ ├── test-create_CRU_df.R │ ├── test-create_CRU_stack.R │ ├── test-create_df.R │ ├── test-create_stack.R │ ├── test-get_CRU.R │ ├── test-get_CRU_df.R │ ├── test-get_CRU_stack.R │ ├── test-validate_dsn.R │ ├── test_caching.R │ ├── test_get_local.R │ └── test_set_cache.R └── vignettes ├── Advanced_use.Rmd ├── getCRUCLdata.Rmd ├── getCRUCLdata.Rmd.orig ├── plot_t-1.png ├── precompile.R └── violin_plot-1.png /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^\.github$ 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^README\.Rmd$ 5 | ^README-.*\.png$ 6 | ^CONDUCT\.md$ 7 | ^\.travis\.yml$ 8 | ^appveyor\.yml$ 9 | ^revdep$ 10 | ^cran-comments\.md$ 11 | ^docs$ 12 | ^codecov\.yml$ 13 | ^codemeta\.json$ 14 | ^getCRUCLdata.code-workspace$ 15 | ^CRAN-RELEASE$ 16 | ^\.ccache$ 17 | ^tic\.R$ 18 | ^doc$ 19 | ^Meta$ 20 | 21 | 22 | # flint files 23 | ^flint$ 24 | ^man/dot-get_CRU\.Rd$ 25 | ^man/dot-check_vars_FALSE\.Rd$ 26 | ^man/dot-validate_dsn\.Rd$ 27 | ^man/dot-create_df\.Rd$ 28 | ^man/dot-tidy_df\.Rd$ 29 | ^man/dot-read_cache\.Rd$ 30 | ^man/dot-create_stacks\.Rd$ 31 | ^man/dot-create_stack\.Rd$ 32 | ^man/dot-set_cache\.Rd$ 33 | ^man/dot-get_local\.Rd$ 34 | 35 | 36 | # flir files 37 | ^flir$ 38 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Refer to [rOpenSci Code of Conduct](https://ropensci.org/code-of-conduct/). 2 | 3 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | ### Fixing typos 4 | 5 | Small typos or grammatical errors in documentation may be edited directly using 6 | the GitHub web interface, so long as the changes are made in the _source_ file. 7 | 8 | - YES: you edit a roxygen comment in a `.R` file below `R/`. 9 | - NO: you edit an `.Rd` file below `man/`. 10 | 11 | ### Prerequisites 12 | 13 | Before you make a substantial pull request, you should always file an issue and 14 | make sure someone from the team agrees that it’s a problem. If you’ve found a 15 | bug, create an associated issue and illustrate the bug with a minimal 16 | [reprex](https://www.tidyverse.org/help/#reprex). 17 | 18 | ### Pull request process 19 | 20 | - We recommend that you create a Git branch for each pull request (PR). 21 | - Look at the Travis and AppVeyor build status before and after making changes. 22 | The `README` should contain badges for any continuous integration services used 23 | by the package. 24 | - We recommend the tidyverse [style guide](http://style.tidyverse.org). 25 | You can use the [styler](https://CRAN.R-project.org/package=styler) package or 26 | the [Air formatter](https://posit-dev.github.io/air/formatter.html) to apply 27 | these styles, but please don't restyle code that has nothing to do with your PR. 28 | - We use [roxygen2](https://cran.r-project.org/package=roxygen2). 29 | - We use [testthat](https://cran.r-project.org/package=testthat). Contributions 30 | with test cases included are easier to accept. 31 | - For user-facing changes, add a bullet to the top of `NEWS.md` below the 32 | current development version header describing the changes made followed by your 33 | GitHub username, and links to relevant issue(s)/PR(s). 34 | 35 | ### Code of Conduct 36 | 37 | Please note that the nasapower project is released with a 38 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 39 | project you agree to abide by its terms. 40 | 41 | ### See rOpenSci [contributing guide](https://ropensci.github.io/dev_guide/contributingguide.html) 42 | 43 | for further details. 44 | 45 | ### Discussion forum 46 | 47 | Check out our [discussion forum](https://discuss.ropensci.org) if you think your issue requires a longer form discussion. 48 | 49 | ### Prefer to Email? 50 | 51 | Email the person listed as maintainer in the `DESCRIPTION` file of this repo. 52 | 53 | Though note that private discussions over email don't help others - of course email is totally warranted if it's a sensitive problem of any kind. 54 | 55 | ### Thanks for contributing 56 | 57 | This contributing guide is adapted from the tidyverse contributing guide available at 58 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
Session Info 6 | 7 | ```r 8 | 9 | ``` 10 |
11 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ## Description 8 | 9 | 10 | ## Related Issue 11 | 14 | 15 | ## Example 16 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | 8 | name: R-CMD-check.yaml 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | R-CMD-check: 14 | runs-on: ${{ matrix.config.os }} 15 | 16 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | config: 22 | - {os: macos-latest, r: 'release'} 23 | - {os: windows-latest, r: 'release'} 24 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 25 | - {os: ubuntu-latest, r: 'release'} 26 | - {os: ubuntu-latest, r: 'oldrel-1'} 27 | 28 | env: 29 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 30 | R_KEEP_PKG_SOURCE: yes 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | 35 | - uses: r-lib/actions/setup-pandoc@v2 36 | 37 | - uses: r-lib/actions/setup-r@v2 38 | with: 39 | r-version: ${{ matrix.config.r }} 40 | http-user-agent: ${{ matrix.config.http-user-agent }} 41 | use-public-rspm: true 42 | 43 | - uses: r-lib/actions/setup-r-dependencies@v2 44 | with: 45 | extra-packages: any::rcmdcheck 46 | needs: check 47 | 48 | - uses: r-lib/actions/check-r-package@v2 49 | with: 50 | upload-snapshots: true 51 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 52 | -------------------------------------------------------------------------------- /.github/workflows/flir.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: flir 13 | 14 | jobs: 15 | flir: 16 | runs-on: macOS-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: flir-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | permissions: 23 | contents: write 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | 29 | - name: Install flir 30 | run: install.packages("flir", repos = c("https://etiennebacher.r-universe.dev/", getOption("repos"))) 31 | shell: Rscript {0} 32 | 33 | - name: Run flir 34 | run: flir::lint() 35 | shell: Rscript {0} 36 | env: 37 | FLIR_ERROR_ON_LINT: true 38 | -------------------------------------------------------------------------------- /.github/workflows/rhub.yaml: -------------------------------------------------------------------------------- 1 | # R-hub's generic GitHub Actions workflow file. It's canonical location is at 2 | # https://github.com/r-hub/actions/blob/v1/workflows/rhub.yaml 3 | # You can update this file to a newer version using the rhub2 package: 4 | # 5 | # rhub::rhub_setup() 6 | # 7 | # It is unlikely that you need to modify this file manually. 8 | 9 | name: R-hub 10 | run-name: "${{ github.event.inputs.id }}: ${{ github.event.inputs.name || format('Manually run by {0}', github.triggering_actor) }}" 11 | 12 | on: 13 | workflow_dispatch: 14 | inputs: 15 | config: 16 | description: 'A comma separated list of R-hub platforms to use.' 17 | type: string 18 | default: 'linux,windows,macos' 19 | name: 20 | description: 'Run name. You can leave this empty now.' 21 | type: string 22 | id: 23 | description: 'Unique ID. You can leave this empty now.' 24 | type: string 25 | 26 | jobs: 27 | 28 | setup: 29 | runs-on: ubuntu-latest 30 | outputs: 31 | containers: ${{ steps.rhub-setup.outputs.containers }} 32 | platforms: ${{ steps.rhub-setup.outputs.platforms }} 33 | 34 | steps: 35 | # NO NEED TO CHECKOUT HERE 36 | - uses: r-hub/actions/setup@v1 37 | with: 38 | config: ${{ github.event.inputs.config }} 39 | id: rhub-setup 40 | 41 | linux-containers: 42 | needs: setup 43 | if: ${{ needs.setup.outputs.containers != '[]' }} 44 | runs-on: ubuntu-latest 45 | name: ${{ matrix.config.label }} 46 | strategy: 47 | fail-fast: false 48 | matrix: 49 | config: ${{ fromJson(needs.setup.outputs.containers) }} 50 | container: 51 | image: ${{ matrix.config.container }} 52 | 53 | steps: 54 | - uses: r-hub/actions/checkout@v1 55 | - uses: r-hub/actions/platform-info@v1 56 | with: 57 | token: ${{ secrets.RHUB_TOKEN }} 58 | job-config: ${{ matrix.config.job-config }} 59 | - uses: r-hub/actions/setup-deps@v1 60 | with: 61 | token: ${{ secrets.RHUB_TOKEN }} 62 | job-config: ${{ matrix.config.job-config }} 63 | - uses: r-hub/actions/run-check@v1 64 | with: 65 | token: ${{ secrets.RHUB_TOKEN }} 66 | job-config: ${{ matrix.config.job-config }} 67 | 68 | other-platforms: 69 | needs: setup 70 | if: ${{ needs.setup.outputs.platforms != '[]' }} 71 | runs-on: ${{ matrix.config.os }} 72 | name: ${{ matrix.config.label }} 73 | strategy: 74 | fail-fast: false 75 | matrix: 76 | config: ${{ fromJson(needs.setup.outputs.platforms) }} 77 | 78 | steps: 79 | - uses: r-hub/actions/checkout@v1 80 | - uses: r-hub/actions/setup-r@v1 81 | with: 82 | job-config: ${{ matrix.config.job-config }} 83 | token: ${{ secrets.RHUB_TOKEN }} 84 | - uses: r-hub/actions/platform-info@v1 85 | with: 86 | token: ${{ secrets.RHUB_TOKEN }} 87 | job-config: ${{ matrix.config.job-config }} 88 | - uses: r-hub/actions/setup-deps@v1 89 | with: 90 | job-config: ${{ matrix.config.job-config }} 91 | token: ${{ secrets.RHUB_TOKEN }} 92 | - uses: r-hub/actions/run-check@v1 93 | with: 94 | job-config: ${{ matrix.config.job-config }} 95 | token: ${{ secrets.RHUB_TOKEN }} 96 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | cov <- covr::package_coverage( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 37 | ) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v4 42 | with: 43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 44 | file: ./cobertura.xml 45 | plugin: noop 46 | disable_search: true 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | 49 | - name: Show testthat output 50 | if: always() 51 | run: | 52 | ## -------------------------------------------------------------------- 53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 54 | shell: bash 55 | 56 | - name: Upload test results 57 | if: failure() 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: coverage-test-failures 61 | path: ${{ runner.temp }}/package 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .Rproj 6 | inst/doc 7 | .DS_Store 8 | *.code-workspace 9 | docs/ 10 | doc 11 | Meta 12 | /doc/ 13 | /Meta/ 14 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: getCRUCLdata 3 | Title: 'CRU' 'CL' v. 2.0 Climatology Client 4 | Version: 1.0.3 5 | Authors@R: c( 6 | person("Adam H.", "Sparks", , "adamhsparks@gmail.com", role = c("aut", "cre"), 7 | comment = c(ORCID = "0000-0002-0061-8359")), 8 | person("Curtin University of Technology", role = "cph", 9 | comment = "Provided support through Adam Sparks's time."), 10 | person("Grains Research and Development Corporation", role = "cph", 11 | comment = "GRDC Project CUR2210-005OPX (AAGI-CU)") 12 | ) 13 | Description: Provides functions that automate downloading and importing 14 | University of East Anglia Climate Research Unit ('CRU') 'CL' v. 2.0 15 | climatology data, facilitates the calculation of minimum temperature 16 | and maximum temperature and formats the data into a data.table object 17 | or a list of 'terra' 'rast' objects for use. 'CRU' 'CL' v. 2.0 data 18 | are a gridded climatology of 1961-1990 monthly means released in 2002 19 | and cover all land areas (excluding Antarctica) at 10 arc minutes 20 | (0.1666667 degree) resolution. For more information see the 21 | description of the data provided by the University of East Anglia 22 | Climate Research Unit, 23 | . 24 | License: MIT + file LICENSE 25 | URL: https://github.com/ropensci/getCRUCLdata, 26 | https://docs.ropensci.org/getCRUCLdata/ 27 | BugReports: https://github.com/ropensci/getCRUCLdata/issues 28 | Depends: 29 | R (>= 4.0.0) 30 | Imports: 31 | cli, 32 | curl, 33 | data.table, 34 | hoardr, 35 | rlang, 36 | terra, 37 | utils 38 | Suggests: 39 | ggplot2, 40 | knitr, 41 | rappdirs, 42 | rmarkdown, 43 | roxygen2, 44 | roxyglobals, 45 | spelling, 46 | testthat, 47 | viridis, 48 | withr 49 | VignetteBuilder: 50 | knitr 51 | ByteCompile: TRUE 52 | Config/Needs/build: moodymudskipper/devtag 53 | Config/roxyglobals/filename: globals.R 54 | Config/roxyglobals/unique: FALSE 55 | Config/roxylint: list(linters = roxylint::tidy) 56 | Config/testthat/edition: 3 57 | Config/testthat/parallel: true 58 | Encoding: UTF-8 59 | Language: en-US 60 | Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", 61 | "roxyglobals::global_roclet", "roxylint::roxylint", 62 | "devtag::dev_roclet")) 63 | RoxygenNote: 7.3.2 64 | X-schema.org-applicationCategory: Tools 65 | X-schema.org-isPartOf: https://ropensci.org 66 | X-schema.org-keywords: anglia-cru, climate-data, cru-cl2, temperature, 67 | rainfall, elevation, data-access, wind, relative-humidity, 68 | solar-radiation, diurnal-temperature, frost 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2025 2 | COPYRIGHT HOLDER: Adam H. Sparks 3 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(create_CRU_df) 4 | export(create_CRU_stack) 5 | export(create_cru_df) 6 | export(create_cru_stack) 7 | export(get_CRU_df) 8 | export(get_CRU_stack) 9 | export(get_cru_df) 10 | export(get_cru_stack) 11 | export(manage_cache) 12 | importFrom(data.table,":=") 13 | importFrom(data.table,.BY) 14 | importFrom(data.table,.EACHI) 15 | importFrom(data.table,.GRP) 16 | importFrom(data.table,.I) 17 | importFrom(data.table,.N) 18 | importFrom(data.table,.NGRP) 19 | importFrom(data.table,.SD) 20 | importFrom(data.table,data.table) 21 | -------------------------------------------------------------------------------- /R/create_CRU_df.R: -------------------------------------------------------------------------------- 1 | #' Create a data.table of CRU CL v. 2.0 climatology elements from local disk files 2 | #' 3 | #' Automates importing \acronym{CRU} \acronym{CL} v.2.0 climatology 4 | #' data and creates a \CRANpkg{data.table} of the data. If requested, minimum 5 | #' and maximum temperature may also be automatically calculated as described in 6 | #' the data [readme.txt](https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt) 7 | #' file. Data may be cached for later use by this function, saving time 8 | #' downloading files in future using this function. This function can be useful 9 | #' if you have network connection issues that mean automated downloading of the 10 | #' files using \R does not work properly. 11 | #' 12 | #' @inheritSection get_CRU_df Nomenclature and Units 13 | #' 14 | #' @param pre Loads precipitation (millimetres/month) from server and 15 | #' returns in the data frame, `TRUE`. Defaults to `FALSE`. 16 | #' @param pre_cv Loads cv of precipitation (percent) from server and 17 | #' returns in the data frame, `TRUE`. Defaults to `FALSE`. NOTE. Setting this 18 | #' to `TRUE` will always results in **pre** being set to `TRUE` and 19 | #' returned as well. 20 | #' @param rd0 Loads wet-days (number days with >0.1 millimetres rain per 21 | #' month) and returns in the data frame, `TRUE`. Defaults to `FALSE`. 22 | #' @param dtr Loads mean diurnal temperature range (degrees Celsius) 23 | #' and returns it in the data frame, `TRUE`. Defaults to `FALSE`. 24 | #' @param tmp Loads temperature (degrees Celsius) and returns it in the 25 | #' data frame, `TRUE`. Defaults to `FALSE`. 26 | #' @param tmn Calculate minimum temperature values (degrees Celsius) 27 | #' and returns it in the data frame, `TRUE`. Defaults to `FALSE`. 28 | #' @param tmx Calculate maximum temperature (degrees Celsius) and 29 | #' return it in the data frame, `TRUE`. Defaults to `FALSE`. 30 | #' @param reh Loads relative humidity and returns it in the data frame, `TRUE`. 31 | #' Defaults to `FALSE`. 32 | #' @param sunp Loads sunshine, percent of maximum possible (percent of 33 | #' day length) and returns it in the data frame, `TRUE`. Defaults to `FALSE`. 34 | #' @param frs Loads ground-frost records (number of days with ground- 35 | #' frost per month) and returns it in the data frame, `TRUE`. Defaults to 36 | #' `FALSE`. 37 | #' @param wnd Load 10 m wind speed (metres/second) and returns it in the 38 | #' data frame, `TRUE`. Defaults to `FALSE`. 39 | #' @param elv Loads elevation (converted to metres) and returns it in 40 | #' the data frame, `TRUE`. Defaults to `FALSE`. 41 | #' @param dsn Local file path where \acronym{CRU} \acronym{CL} v.2.0 .dat.gz 42 | #' files are located. 43 | #' 44 | #' @examplesIf interactive() 45 | #' # Create a data frame of temperature from locally available files in the 46 | #' # tempdir() directory. 47 | #' 48 | #' download.file( 49 | #' url = "https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_tmp.dat.gz", 50 | #' destfile = file.path(tempdir(), "grid_10min_tmp.dat.gz") 51 | #' ) 52 | #' 53 | #' CRU_tmp <- create_CRU_df(tmp = TRUE, dsn = tempdir()) 54 | #' 55 | #' CRU_tmp 56 | #' 57 | #' @seealso 58 | #' [get_CRU_df]. 59 | #' 60 | #' @returns A [data.table::data.table] object of \acronym{CRU} \acronym{CL} v. 61 | #' 2.0 climatology elements. 62 | #' 63 | #' @author Adam H. Sparks, \email{adamhsparks@@gmail.com} 64 | #' 65 | #' @source 66 | #' \describe{ 67 | #' \item{pre}{} 68 | #' \item{rd0}{} 69 | #' \item{tmp}{} 70 | #' \item{dtr}{} 71 | #' \item{reh}{} 72 | #' \item{sunp}{} 73 | #' \item{frs}{} 74 | #' \item{wnd}{, areas originally including Antarctica are removed.} 75 | #' \item{elv}{, values are converted from kilometres to metres.} 76 | #' } 77 | #' This package crops all spatial outputs to an extent of ymin = -60, ymax = 85, 78 | #' xmin = -180, xmax = 180. 79 | #' 80 | #' @references New, Mark, et al. "A high-resolution data set of surface climate 81 | #' over global land areas." Climate research 21.1 (2002): 1-25. 82 | #' 83 | #' 84 | #' @export 85 | 86 | create_CRU_df <- function( 87 | pre = FALSE, 88 | pre_cv = FALSE, 89 | rd0 = FALSE, 90 | tmp = FALSE, 91 | dtr = FALSE, 92 | reh = FALSE, 93 | tmn = FALSE, 94 | tmx = FALSE, 95 | sunp = FALSE, 96 | frs = FALSE, 97 | wnd = FALSE, 98 | elv = FALSE, 99 | dsn 100 | ) { 101 | .check_vars_FALSE( 102 | pre, 103 | pre_cv, 104 | rd0, 105 | tmp, 106 | dtr, 107 | reh, 108 | tmn, 109 | tmx, 110 | sunp, 111 | frs, 112 | wnd, 113 | elv 114 | ) 115 | 116 | .validate_dsn(dsn) 117 | 118 | files <- .get_local( 119 | pre, 120 | pre_cv, 121 | rd0, 122 | tmp, 123 | dtr, 124 | reh, 125 | tmn, 126 | tmx, 127 | sunp, 128 | frs, 129 | wnd, 130 | elv, 131 | cache_dir = dsn 132 | ) 133 | 134 | if (length(files) == 0) { 135 | cli::cli_abort( 136 | "No CRU CL 2.0 data files were found in {.var dsn}. 137 | Please check that you have the proper file location." 138 | ) 139 | } 140 | 141 | return(.create_df(tmn, tmx, tmp, dtr, pre, pre_cv, elv, files)) 142 | } 143 | 144 | #' @export 145 | #' @rdname create_CRU_df 146 | create_cru_df <- create_CRU_df 147 | -------------------------------------------------------------------------------- /R/create_CRU_stack.R: -------------------------------------------------------------------------------- 1 | #' Create a list of terra rast objects of CRU CL v. 2.0 climatology elements from local disk files 2 | #' 3 | #' Automates importing \acronym{CRU} \acronym{CL} v.2.0 climatology 4 | #' data and creates a \CRANpkg{terra} [terra::rast] object of the 5 | #' data. If requested, minimum and maximum temperature may also be 6 | #' automatically calculated as described in the data 7 | #' [readme.txt](https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt) file. 8 | #' Data may be cached for later use by this function, saving time downloading 9 | #' files in future using this function. This function can be useful if you 10 | #' have network connection issues that mean automated downloading of the files 11 | #' using \R does not work properly or you have cached the files locally for 12 | #' your own future use. 13 | #' 14 | #' @inheritSection get_CRU_df Nomenclature and Units 15 | #' @inheritParams create_CRU_df 16 | #' @inherit create_CRU_df author 17 | #' @inherit create_CRU_df source 18 | #' @inherit create_CRU_df references 19 | #' @inherit create_CRU_stack return 20 | #' 21 | #' @examplesIf interactive() 22 | #' 23 | #' download.file( 24 | #' url = "https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_tmp.dat.gz", 25 | #' destfile = file.path(tempdir(), "grid_10min_tmp.dat.gz") 26 | #' ) 27 | #' 28 | #' CRU_tmp <- create_CRU_stack(tmp = TRUE, dsn = tempdir()) 29 | #' 30 | #' CRU_tmp 31 | #' 32 | #' @seealso 33 | #' [get_CRU_stack]. 34 | #' 35 | #' @returns A [base::list] of [terra::rast] objects of \acronym{CRU} 36 | #' \acronym{CL} v. 2.0 climatology elements. 37 | #' 38 | #' @export 39 | 40 | create_CRU_stack <- function( 41 | pre = FALSE, 42 | pre_cv = FALSE, 43 | rd0 = FALSE, 44 | tmp = FALSE, 45 | dtr = FALSE, 46 | reh = FALSE, 47 | tmn = FALSE, 48 | tmx = FALSE, 49 | sunp = FALSE, 50 | frs = FALSE, 51 | wnd = FALSE, 52 | elv = FALSE, 53 | dsn 54 | ) { 55 | .check_vars_FALSE( 56 | pre, 57 | pre_cv, 58 | rd0, 59 | tmp, 60 | dtr, 61 | reh, 62 | tmn, 63 | tmx, 64 | sunp, 65 | frs, 66 | wnd, 67 | elv 68 | ) 69 | 70 | .validate_dsn(dsn) 71 | 72 | files <- .get_local( 73 | pre, 74 | pre_cv, 75 | rd0, 76 | tmp, 77 | dtr, 78 | reh, 79 | tmn, 80 | tmx, 81 | sunp, 82 | frs, 83 | wnd, 84 | elv, 85 | cache_dir = dsn 86 | ) 87 | 88 | if (length(files) == 0) { 89 | cli::cli_abort( 90 | "No CRU CL 2.0 data files were found in {.var dsn}. 91 | Please check that you have the proper file location." 92 | ) 93 | } 94 | 95 | return(.create_stacks(tmn, tmx, tmp, dtr, pre, pre_cv, files)) 96 | } 97 | 98 | #' @export 99 | #' @rdname create_CRU_stack 100 | create_cru_stack <- create_CRU_stack 101 | -------------------------------------------------------------------------------- /R/getCRUCLdata-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | # The following block is used by usethis to automatically manage 5 | # roxygen namespace tags. Modify with care! 6 | ## usethis namespace: start 7 | #' @importFrom data.table := 8 | #' @importFrom data.table .BY 9 | #' @importFrom data.table .EACHI 10 | #' @importFrom data.table .GRP 11 | #' @importFrom data.table .I 12 | #' @importFrom data.table .N 13 | #' @importFrom data.table .NGRP 14 | #' @importFrom data.table .SD 15 | #' @importFrom data.table data.table 16 | ## usethis namespace: end 17 | NULL 18 | -------------------------------------------------------------------------------- /R/get_CRU.R: -------------------------------------------------------------------------------- 1 | #' Downloads and formats CRU CL 2.0 data 2 | #' 3 | #' @param pre Logical. If TRUE, downloads precipitation data. 4 | #' @param pre_cv Logical. If TRUE, downloads precipitation data. 5 | #' @param rd0 Logical. If TRUE, downloads runoff data. 6 | #' @param tmp Logical. If TRUE, downloads temperature data. 7 | #' @param dtr Logical. If TRUE, downloads diurnal temperature range data. 8 | #' @param reh Logical. If TRUE, downloads relative humidity data. 9 | #' @param tmn Logical. If TRUE, downloads minimum temperature data. 10 | #' @param tmx Logical. If TRUE, downloads maximum temperature data. 11 | #' @param sunp Logical. If TRUE, downloads sunshine data. 12 | #' @param frs Logical. If TRUE, downloads frost day frequency data. 13 | #' @param wnd Logical. If TRUE, downloads wind speed data. 14 | #' @param elv Logical. If TRUE, downloads elevation data. 15 | #' @param cache_dir Character. Path to the cache directory. 16 | #' 17 | #' Handles the downloading of CRU CL 2.0 data. This function is called by 18 | #' [get_cru_df] and [get_cru_stack]. It is not intended to be called directly. 19 | #' 20 | #' @returns A data.table with the requested data. 21 | #' 22 | #' @dev 23 | .get_CRU <- 24 | function( 25 | pre, 26 | pre_cv, 27 | rd0, 28 | tmp, 29 | dtr, 30 | reh, 31 | tmn, 32 | tmx, 33 | sunp, 34 | frs, 35 | wnd, 36 | elv, 37 | cache_dir 38 | ) { 39 | dtr_file <- "grid_10min_dtr.dat.gz" 40 | tmp_file <- "grid_10min_tmp.dat.gz" 41 | reh_file <- "grid_10min_reh.dat.gz" 42 | elv_file <- "grid_10min_elv.dat.gz" 43 | pre_file <- "grid_10min_pre.dat.gz" 44 | sun_file <- "grid_10min_sunp.dat.gz" 45 | wnd_file <- "grid_10min_wnd.dat.gz" 46 | frs_file <- "grid_10min_frs.dat.gz" 47 | rd0_file <- "grid_10min_rd0.dat.gz" 48 | 49 | # check if pre_cv or tmx/tmn (derived) are true, make sure proper --------- 50 | # parameters set TRUE 51 | if (pre_cv) { 52 | pre <- TRUE 53 | } 54 | 55 | if (any(tmn, tmx)) { 56 | dtr <- tmp <- TRUE 57 | } 58 | # create object list to filter downloads ---------------------------------- 59 | object_list <- c(dtr, tmp, reh, elv, pre, sunp, wnd, frs, rd0) 60 | 61 | files <- 62 | c( 63 | dtr_file, 64 | tmp_file, 65 | reh_file, 66 | elv_file, 67 | pre_file, 68 | sun_file, 69 | wnd_file, 70 | frs_file, 71 | rd0_file 72 | ) 73 | names(files) <- 74 | names(object_list) <- 75 | c( 76 | "dtr_file", 77 | "tmp_file", 78 | "reh_file", 79 | "elv_file", 80 | "pre_file", 81 | "sun_file", 82 | "wnd_file", 83 | "frs_file", 84 | "rd0_file" 85 | ) 86 | 87 | # filter downloaded ------------------------------------------------------- 88 | # which files are being requested? 89 | files <- files[which(object_list)] 90 | 91 | # which files are locally available? 92 | cache_dir_contents <- 93 | list.files(cache_dir, pattern = ".dat.gz$") 94 | 95 | # which files requested need to be downloaded 96 | dl_files <- files[!(files %in% cache_dir_contents)] 97 | 98 | # download files ---------------------------------------------------------- 99 | if (length(dl_files) > 0) { 100 | CRU_url <- "https://crudata.uea.ac.uk/cru/data/hrg/tmc/" 101 | dl_files <- as.list(paste0(CRU_url, dl_files)) 102 | 103 | tryCatch( 104 | for (f in seq_along(dl_files)) { 105 | curl::curl_download( 106 | url = dl_files[[f]], 107 | destfile = (file.path(cache_dir, basename(dl_files[[f]]))), 108 | mode = "wb" 109 | ) 110 | }, 111 | error = function(x) { 112 | manage_cache$delete_all() 113 | cli::cli_abort( 114 | "The file downloads have failed. 115 | Please start the download again." 116 | ) 117 | } 118 | ) 119 | } 120 | 121 | # filter files from cache directory in case there are local files for which 122 | # we do not want data 123 | cache_dir_contents <- as.list(list.files(cache_dir, pattern = ".dat.gz$")) 124 | 125 | files <- cache_dir_contents[cache_dir_contents %in% files] 126 | 127 | # add full file path to the files 128 | files <- file.path(cache_dir, files) 129 | 130 | # fill the space with a "\" for R, if one exists 131 | files <- gsub(" ", "\\ ", files, fixed = TRUE) 132 | 133 | return(files) 134 | } 135 | -------------------------------------------------------------------------------- /R/get_CRU_df.R: -------------------------------------------------------------------------------- 1 | #' Download and create a data.table of CRU CL v. 2.0 climatology elements 2 | #' 3 | #' This function automates downloading and importing \acronym{CRU} 4 | #' \acronym{CL} v. 2.0 climatology data and creates a \CRANpkg{data.table} of 5 | #' the data. If requested, minimum and maximum temperature may also be 6 | #' automatically calculated as described in the data 7 | #' [readme.txt](https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt) file. 8 | #' Data may be cached for later use by this function, saving time downloading 9 | #' files in future use of this function. 10 | #' 11 | #' @section Nomenclature and Units: 12 | #' \describe{ 13 | #' \item{pre}{precipitation (millimetres/month)} 14 | #' \describe{ 15 | #' \item{cv}{cv of precipitation (percent)} 16 | #' } 17 | #' \item{rd0}{wet-days (number days with >0.1 millimetres rain per month)} 18 | #' \item{tmp}{mean temperature (degrees Celsius)} 19 | #' \item{dtr}{mean diurnal temperature range (degrees Celsius)} 20 | #' \item{reh}{relative humidity (percent)} 21 | #' \item{sunp}{sunshine (percent of maximum possible (percent of day length))} 22 | #' \item{frs}{ground-frost (number of days with ground-frost per month)} 23 | #' \item{wnd}{10 metre windspeed (metres/second)} 24 | #' \item{elv}{elevation (automatically converted to metres)} 25 | #' } 26 | #' For more information see the description of the data provided by 27 | #' \acronym{CRU}, 28 | #' 29 | #' @param pre Fetches precipitation (millimetres/month) from server and 30 | #' returns it in the data frame, `TRUE`. Defaults to `FALSE`. 31 | #' @param pre_cv Fetch cv of precipitation (percent) from server and 32 | #' returns it in the data frame, `TRUE`. Defaults to `FALSE`. NOTE Setting 33 | #' this to `TRUE` will always results in **pre** being set to `TRUE` and 34 | #' returned as well. 35 | #' @param rd0 Fetches wet-days (number days with >0.1 millimetres rain 36 | #' per month) and returns it in the data frame? Defaults to `FALSE`. 37 | #' @param dtr Fetches mean diurnal temperature range (degrees Celsius) 38 | #' and returns it in the data frame? Defaults to `FALSE`. 39 | #' @param tmp Fetches temperature (degrees Celsius) and returns it in the 40 | #' data frame, `TRUE`. Defaults to `FALSE`. 41 | #' @param tmn Calculates minimum temperature values (degrees Celsius) 42 | #' and returns it in the data frame, `TRUE`. Defaults to `FALSE`. 43 | #' @param tmx Calculates maximum temperature (degrees Celsius) and 44 | #' returns it in the data frame, `TRUE`. Defaults to `FALSE`. 45 | #' @param reh Fetches relative humidity and returns it in the data frame, 46 | #' `TRUE`. Defaults to FALSE. 47 | #' @param sunp Fetch sunshine, percent of maximum possible (percent of 48 | #' day length) and return it in the data frame? Defaults to `FALSE`. 49 | #' @param frs Fetches ground-frost records (number of days with ground- 50 | #' frost per month) and return it in the data frame? Defaults to `FALSE`. 51 | #' @param wnd Fetches 10m wind speed (metres/second) and returns it in the 52 | #' data frame, `TRUE`. Defaults to `FALSE`. 53 | #' @param elv Fetches elevation (converted to metres) and returns it in 54 | #' the data frame, `TRUE`. Defaults to `FALSE`. 55 | #' @param cache Stores CRU CL v. 2.0 data files locally for later use. 56 | #' If `FALSE`, the downloaded files are removed when the \R session is closed. 57 | #' To take advantage of cached files in future sessions, use `cache = TRUE` 58 | #' even after the initial download and caching. Defaults to `FALSE`. 59 | #' 60 | #' @examplesIf interactive() 61 | #' # Download data and create a data frame of precipitation and temperature 62 | #' # without caching the data files 63 | #' CRU_pre_tmp <- get_CRU_df(pre = TRUE, tmp = TRUE) 64 | #' 65 | #' head(CRU_pre_tmp) 66 | #' CRU_pre_tmp 67 | #' 68 | #' @seealso 69 | #' [create_CRU_stack], [manage_cache]. 70 | #' 71 | #' @inherit create_CRU_df author 72 | #' @inherit create_CRU_df source 73 | #' @inherit create_CRU_df references 74 | #' @inherit create_CRU_df return 75 | #' 76 | #' @export 77 | 78 | get_CRU_df <- function( 79 | pre = FALSE, 80 | pre_cv = FALSE, 81 | rd0 = FALSE, 82 | tmp = FALSE, 83 | dtr = FALSE, 84 | reh = FALSE, 85 | tmn = FALSE, 86 | tmx = FALSE, 87 | sunp = FALSE, 88 | frs = FALSE, 89 | wnd = FALSE, 90 | elv = FALSE, 91 | cache = FALSE 92 | ) { 93 | .check_vars_FALSE( 94 | pre, 95 | pre_cv, 96 | rd0, 97 | tmp, 98 | dtr, 99 | reh, 100 | tmn, 101 | tmx, 102 | sunp, 103 | frs, 104 | wnd, 105 | elv 106 | ) 107 | 108 | cache_dir <- .set_cache(cache) 109 | 110 | files <- .get_CRU( 111 | pre, 112 | pre_cv, 113 | rd0, 114 | tmp, 115 | dtr, 116 | reh, 117 | tmn, 118 | tmx, 119 | sunp, 120 | frs, 121 | wnd, 122 | elv, 123 | cache_dir 124 | ) 125 | 126 | return(.create_df(tmn, tmx, tmp, dtr, pre, pre_cv, elv, files)) 127 | } 128 | 129 | #' @export 130 | #' @rdname get_CRU_df 131 | get_cru_df <- get_CRU_df 132 | -------------------------------------------------------------------------------- /R/get_CRU_stack.R: -------------------------------------------------------------------------------- 1 | #' Download and create a list of terra rast objects of CRU CL v. 2.0 climatology elements 2 | #' 3 | #' This function automates downloading and importing CRU CL v. 2.0 4 | #' climatology data into \R and creates a list of \CRANpkg{terra} 5 | #' [terra::rast] objects of the data. If requested, minimum and maximum 6 | #' temperature may also be automatically calculated as described in the data 7 | #' [readme.txt](https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt) file. 8 | #' Data may be cached for later use by this function, saving time downloading 9 | #' files in future using this function. 10 | #' 11 | #' @inheritSection get_CRU_df Nomenclature and Units 12 | #' @inheritParams get_CRU_df 13 | #' @inherit create_CRU_df author 14 | #' @inherit create_CRU_df source 15 | #' @inherit create_CRU_df references 16 | #' @inherit create_CRU_stack return 17 | #' 18 | #' @examplesIf interactive() 19 | #' 20 | #' # Download data and create a list of {terra} `rast` objects of precipitation 21 | #' # and temperature without caching the data files 22 | #' CRU_pre_tmp <- get_CRU_stack(pre = TRUE, tmp = TRUE) 23 | #' 24 | #' CRU_pre_tmp 25 | #' 26 | #' @seealso 27 | #' [create_CRU_stack], [manage_cache]. 28 | #' 29 | #' @export 30 | 31 | get_CRU_stack <- 32 | function( 33 | pre = FALSE, 34 | pre_cv = FALSE, 35 | rd0 = FALSE, 36 | tmp = FALSE, 37 | dtr = FALSE, 38 | reh = FALSE, 39 | tmn = FALSE, 40 | tmx = FALSE, 41 | sunp = FALSE, 42 | frs = FALSE, 43 | wnd = FALSE, 44 | elv = FALSE, 45 | cache = FALSE 46 | ) { 47 | .check_vars_FALSE( 48 | pre, 49 | pre_cv, 50 | rd0, 51 | tmp, 52 | dtr, 53 | reh, 54 | tmn, 55 | tmx, 56 | sunp, 57 | frs, 58 | wnd, 59 | elv 60 | ) 61 | 62 | cache_dir <- .set_cache(cache) 63 | 64 | files <- .get_CRU( 65 | pre, 66 | pre_cv, 67 | rd0, 68 | tmp, 69 | dtr, 70 | reh, 71 | tmn, 72 | tmx, 73 | sunp, 74 | frs, 75 | wnd, 76 | elv, 77 | cache_dir 78 | ) 79 | 80 | if (pre_cv) { 81 | pre <- TRUE 82 | } 83 | 84 | return(.create_stacks(tmn, tmx, tmp, dtr, pre, pre_cv, files)) 85 | } 86 | 87 | #' @export 88 | #' @rdname get_CRU_stack 89 | get_cru_stack <- get_CRU_stack 90 | -------------------------------------------------------------------------------- /R/globals.R: -------------------------------------------------------------------------------- 1 | # Generated by roxyglobals: do not edit by hand 2 | 3 | utils::globalVariables(c( 4 | "month", # <.create_df> 5 | "pre_cv", # <.read_cache> 6 | "i.pre_cv", # <.read_cache> 7 | "elv", # <.read_cache> 8 | NULL 9 | )) 10 | -------------------------------------------------------------------------------- /R/manage_cached_files.R: -------------------------------------------------------------------------------- 1 | #' Manage locally cached CRU CL v. 2.0 files 2 | #' 3 | #' Manage cached \pkg{getCRUCLdata} files with \CRANpkg{hoardr}. 4 | #' 5 | #' @export 6 | #' @name manage_cache 7 | #' 8 | #' @details The default cache directory is 9 | #' `tools::R_user_dir(package = "getCRUCLdata")`, but you can set your own path 10 | #' using `manage_cache$cache_path_set()`. 11 | #' 12 | #' `manage_cache$cache_delete` only accepts one file name, while 13 | #' `manage_cache$cache_delete_all` 14 | #' does not accept any names, but deletes all files. For deleting many specific 15 | #' files, use `manage_cache$cache_delete` in an [base::lapply] type call. 16 | #' 17 | #' @section Useful user functions: 18 | #' \describe{ 19 | #' \item{`manage_cache$cache_path_get()`}{get cache path} 20 | #' \item{`manage_cache$cache_path_set()`}{set cache path} 21 | #' \item{`manage_cache$list()`}{returns a character vector of full 22 | #' path file names} 23 | #' \item{`manage_cache$files()`}{returns file objects with metadata} 24 | #' \item{`manage_cache$details()`}{returns files with details} 25 | #' \item{`manage_cache$delete()`}{delete specific files} 26 | #' \item{`manage_cache$delete_all()`}{delete all files, returns 27 | #' nothing} 28 | #' } 29 | #' 30 | #' @author Adam H. Sparks, \email{adamhsparks@@gmail.com} 31 | #' 32 | #' @examples \dontrun{ 33 | #' 34 | #' # list files in cache 35 | #' manage_cache$list() 36 | #' 37 | #' # delete certain database files 38 | #' manage_cache$delete("file path") 39 | #' manage_cache$list() 40 | #' 41 | #' # delete all files in cache 42 | #' manage_cache$delete_all() 43 | #' manage_cache$list() 44 | #' 45 | #' # set a different cache path from the default 46 | #' manage_cache$cache_path_set("~/tmp") 47 | #' } 48 | NULL 49 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | manage_cache <- NULL # nocov start 2 | 3 | .onLoad <- 4 | function(libname = find.package("getCRUCLdata"), pkgname = "getCRUCLdata") { 5 | # CRAN Note avoidance 6 | if (getRversion() >= "2.15.1") { 7 | utils::globalVariables(c(".")) 8 | 9 | x <- hoardr::hoard() 10 | x$cache_path_set( 11 | path = "getCRUCLdata", 12 | prefix = "org.R-project.R/R", 13 | type = "user_cache_dir" 14 | ) 15 | manage_cache <<- x 16 | } 17 | } 18 | 19 | # nocov end 20 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | patch: 10 | default: 11 | target: auto 12 | threshold: 1% 13 | -------------------------------------------------------------------------------- /flint/cache_file_state.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/flint/cache_file_state.rds -------------------------------------------------------------------------------- /flint/config.yml: -------------------------------------------------------------------------------- 1 | keep: 2 | - any_duplicated 3 | - any_is_na 4 | - class_equals 5 | - condition_message 6 | - double_assignment 7 | - duplicate_argument 8 | - empty_assignment 9 | - equal_assignment 10 | - equals_na 11 | - expect_comparison 12 | - expect_identical 13 | - expect_length 14 | - expect_named 15 | - expect_not 16 | - expect_null 17 | - expect_true_false 18 | - expect_type 19 | - for_loop_index 20 | - function_return 21 | - implicit_assignment 22 | - is_numeric 23 | - length_levels 24 | - length_test 25 | - lengths 26 | - library_call 27 | - literal_coercion 28 | - matrix_apply 29 | - missing_argument 30 | - nested_ifelse 31 | - numeric_leading_zero 32 | - outer_negation 33 | - package_hooks 34 | - paste 35 | - redundant_equals 36 | - redundant_ifelse 37 | - rep_len 38 | - right_assignment 39 | - sample_int 40 | - semicolon 41 | - seq 42 | - sort 43 | - T_and_F_symbol 44 | - todo_comment 45 | - undesirable_function 46 | - undesirable_operator 47 | - unnecessary_nesting 48 | - unreachable_code 49 | - which_grepl 50 | -------------------------------------------------------------------------------- /flint/rules/builtin/T_and_F_symbol.yml: -------------------------------------------------------------------------------- 1 | id: true_false_symbol 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: T 6 | kind: identifier 7 | not: 8 | any: 9 | - precedes: 10 | any: 11 | - pattern: <- 12 | - pattern: = 13 | - regex: ^~$ 14 | - follows: 15 | any: 16 | - pattern: $ 17 | - regex: ^~$ 18 | - inside: 19 | any: 20 | - kind: parameter 21 | - kind: call 22 | - kind: binary_operator 23 | follows: 24 | regex: ^~$ 25 | stopBy: end 26 | stopBy: 27 | kind: 28 | argument 29 | fix: TRUE 30 | message: Use TRUE instead of the symbol T. 31 | 32 | --- 33 | 34 | id: true_false_symbol-2 35 | language: r 36 | severity: warning 37 | rule: 38 | pattern: F 39 | kind: identifier 40 | not: 41 | any: 42 | - precedes: 43 | any: 44 | - pattern: <- 45 | - pattern: = 46 | - regex: ^~$ 47 | - follows: 48 | any: 49 | - pattern: $ 50 | - regex: ^~$ 51 | - inside: 52 | any: 53 | - kind: parameter 54 | - kind: call 55 | - kind: binary_operator 56 | follows: 57 | regex: ^~$ 58 | stopBy: end 59 | stopBy: 60 | kind: 61 | argument 62 | fix: FALSE 63 | message: Use FALSE instead of the symbol F. 64 | 65 | --- 66 | 67 | id: true_false_symbol-3 68 | language: r 69 | severity: warning 70 | rule: 71 | pattern: T 72 | kind: identifier 73 | precedes: 74 | any: 75 | - pattern: <- 76 | - pattern: = 77 | not: 78 | inside: 79 | kind: argument 80 | message: Don't use T as a variable name, as it can break code relying on T being TRUE. 81 | 82 | --- 83 | 84 | id: true_false_symbol-4 85 | language: r 86 | severity: warning 87 | rule: 88 | pattern: F 89 | kind: identifier 90 | precedes: 91 | any: 92 | - pattern: <- 93 | - pattern: = 94 | not: 95 | inside: 96 | kind: argument 97 | message: Don't use F as a variable name, as it can break code relying on F being FALSE. 98 | -------------------------------------------------------------------------------- /flint/rules/builtin/absolute_path.yml: -------------------------------------------------------------------------------- 1 | # id: absolute_path-1 2 | # language: r 3 | # severity: warning 4 | # rule: 5 | # kind: string_content 6 | # any: 7 | # - regex: '^~[[:alpha:]]' 8 | # - regex: '^~/[[:alpha:]]' 9 | # - regex: '^[[:alpha:]]:' 10 | # - regex: '^(/|~)$' 11 | # - regex: '^/[[:alpha:]]' 12 | # - regex: '^\\' 13 | # message: Do not use absolute paths. 14 | -------------------------------------------------------------------------------- /flint/rules/builtin/any_duplicated.yml: -------------------------------------------------------------------------------- 1 | id: any_duplicated-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: any($$$ duplicated($MYVAR) $$$) 6 | fix: anyDuplicated(~~MYVAR~~) > 0 7 | message: anyDuplicated(x, ...) > 0 is better than any(duplicated(x), ...). 8 | 9 | --- 10 | 11 | id: any_duplicated-2 12 | language: r 13 | severity: warning 14 | rule: 15 | any: 16 | - pattern: length(unique($MYVAR)) == length($MYVAR) 17 | - pattern: length($MYVAR) == length(unique($MYVAR)) 18 | fix: anyDuplicated(~~MYVAR~~) == 0L 19 | message: anyDuplicated(x) == 0L is better than length(unique(x)) == length(x). 20 | 21 | --- 22 | 23 | id: any_duplicated-3 24 | language: r 25 | severity: warning 26 | rule: 27 | pattern: length(unique($MYVAR)) != length($MYVAR) 28 | fix: anyDuplicated(~~MYVAR~~) != 0L 29 | message: | 30 | Use anyDuplicated(x) != 0L (or > or <) instead of length(unique(x)) != length(x) 31 | (or > or <). 32 | 33 | --- 34 | 35 | id: any_duplicated-4 36 | language: r 37 | severity: warning 38 | rule: 39 | any: 40 | - pattern: nrow($DATA) != length(unique($DATA$µCOL)) 41 | - pattern: length(unique($DATA$µCOL)) != nrow($DATA) 42 | fix: anyDuplicated(~~DATA~~$~~COL~~) != 0L 43 | message: | 44 | anyDuplicated(DF$col) != 0L is better than length(unique(DF$col)) != nrow(DF) 45 | 46 | --- 47 | 48 | # id: any_duplicated-5 49 | # language: r 50 | # severity: warning 51 | # rule: 52 | # any: 53 | # - pattern: 54 | # context: nrow($DATA) != length(unique($DATA[["µCOL"]])) 55 | # strictness: ast 56 | # - pattern: 57 | # context: length(unique($DATA[["µCOL"]])) != nrow($DATA) 58 | # strictness: ast 59 | # fix: anyDuplicated(~~DATA~~[["~~COL~~"]]) != 0L 60 | # message: | 61 | # anyDuplicated(DF[["col"]]) != 0L is better than length(unique(DF[["col"]])) != nrow(DF) 62 | # 63 | # --- 64 | 65 | id: any_duplicated-6 66 | language: r 67 | severity: warning 68 | rule: 69 | any: 70 | - pattern: nrow($DATA) == length(unique($DATA$µCOL)) 71 | - pattern: length(unique($DATA$µCOL)) == nrow($DATA) 72 | fix: anyDuplicated(~~DATA~~$~~COL~~) == 0L 73 | message: | 74 | anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF) 75 | 76 | # --- 77 | # 78 | # id: any_duplicated-7 79 | # language: r 80 | # severity: warning 81 | # rule: 82 | # any: 83 | # - pattern: 84 | # context: nrow($DATA) == length(unique($DATA[["µCOL"]])) 85 | # strictness: ast 86 | # - pattern: 87 | # context: length(unique($DATA[["µCOL"]])) == nrow($DATA) 88 | # strictness: ast 89 | # fix: anyDuplicated(~~DATA~~[["~~COL~~"]]) == 0L 90 | # message: | 91 | # anyDuplicated(DF[["col"]]) == 0L is better than length(unique(DF[["col"]])) == nrow(DF) 92 | -------------------------------------------------------------------------------- /flint/rules/builtin/any_is_na.yml: -------------------------------------------------------------------------------- 1 | id: any_na-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: any($$$ is.na($MYVAR) $$$) 6 | fix: anyNA(~~MYVAR~~) 7 | message: anyNA(x) is better than any(is.na(x)). 8 | -------------------------------------------------------------------------------- /flint/rules/builtin/class_equals.yml: -------------------------------------------------------------------------------- 1 | id: class_equals-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: class($VAR) == $CLASSNAME 7 | - pattern: $CLASSNAME == class($VAR) 8 | not: 9 | inside: 10 | kind: argument 11 | fix: inherits(~~VAR~~, ~~CLASSNAME~~) 12 | message: Instead of comparing class(x) with ==, use inherits(x, 'class-name') or is. or is(x, 'class') 13 | 14 | --- 15 | 16 | id: class_equals-2 17 | language: r 18 | severity: warning 19 | rule: 20 | any: 21 | - pattern: class($VAR) != $CLASSNAME 22 | - pattern: $CLASSNAME != class($VAR) 23 | not: 24 | inside: 25 | kind: argument 26 | fix: "!inherits(~~VAR~~, ~~CLASSNAME~~)" 27 | message: "Instead of comparing class(x) with !=, use !inherits(x, 'class-name') or is. or is(x, 'class')" 28 | 29 | --- 30 | 31 | id: class_equals-3 32 | language: r 33 | severity: warning 34 | rule: 35 | any: 36 | - pattern: $CLASSNAME %in% class($VAR) 37 | - pattern: class($VAR) %in% $CLASSNAME 38 | constraints: 39 | CLASSNAME: 40 | kind: string 41 | fix: inherits(~~VAR~~, ~~CLASSNAME~~) 42 | message: Instead of comparing class(x) with %in%, use inherits(x, 'class-name') or is. or is(x, 'class') 43 | -------------------------------------------------------------------------------- /flint/rules/builtin/condition_message.yml: -------------------------------------------------------------------------------- 1 | id: condition_message-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($$$ paste0($$$MSG) $$$) 6 | kind: call 7 | not: 8 | any: 9 | - has: 10 | kind: extract_operator 11 | - has: 12 | stopBy: end 13 | kind: argument 14 | has: 15 | field: name 16 | regex: "^collapse|recycle0$" 17 | stopBy: end 18 | constraints: 19 | FUN: 20 | regex: "^(packageStartupMessage|stop|warning)$" 21 | fix: ~~FUN~~(~~MSG~~) 22 | message: | 23 | ~~FUN~~(paste0(...)) can be rewritten as ~~FUN~~(...). 24 | -------------------------------------------------------------------------------- /flint/rules/builtin/double_assignment.yml: -------------------------------------------------------------------------------- 1 | id: right_double_assignment 2 | language: r 3 | severity: hint 4 | rule: 5 | pattern: $RHS ->> $LHS 6 | has: 7 | field: rhs 8 | kind: identifier 9 | message: ->> can have hard-to-predict behavior; prefer assigning to a 10 | specific environment instead (with assign() or <-). 11 | 12 | --- 13 | 14 | id: left_double_assignment 15 | language: r 16 | severity: hint 17 | rule: 18 | pattern: $LHS <<- $RHS 19 | has: 20 | field: lhs 21 | kind: identifier 22 | message: <<- can have hard-to-predict behavior; prefer assigning to a 23 | specific environment instead (with assign() or <-). 24 | -------------------------------------------------------------------------------- /flint/rules/builtin/duplicate_argument.yml: -------------------------------------------------------------------------------- 1 | id: duplicate_argument-1 2 | language: r 3 | severity: warning 4 | rule: 5 | # Look for a function argument... 6 | kind: argument 7 | any: 8 | - has: 9 | kind: identifier 10 | field: name 11 | pattern: $OBJ 12 | - has: 13 | kind: string_content 14 | pattern: $OBJ 15 | stopBy: end 16 | 17 | # ... that follows other argument(s) with the same name... 18 | follows: 19 | kind: argument 20 | stopBy: end 21 | has: 22 | stopBy: end 23 | kind: identifier 24 | field: name 25 | pattern: $OBJ 26 | 27 | # ... inside a function call (or a subset environment for data.table)... 28 | inside: 29 | kind: arguments 30 | follows: 31 | any: 32 | - kind: identifier 33 | pattern: $FUN 34 | - kind: string 35 | inside: 36 | any: 37 | - kind: call 38 | - kind: subset 39 | 40 | # ... that is not a function listed below. 41 | constraints: 42 | FUN: 43 | not: 44 | regex: ^(mutate|transmute)$ 45 | 46 | message: Avoid duplicate arguments in function calls. 47 | -------------------------------------------------------------------------------- /flint/rules/builtin/empty_assignment.yml: -------------------------------------------------------------------------------- 1 | id: empty_assignment-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $OBJ <- {} 7 | - pattern: $OBJ <- {$CONTENT} 8 | - pattern: $OBJ = {} 9 | - pattern: $OBJ = {$CONTENT} 10 | constraints: 11 | CONTENT: 12 | regex: ^\s+$ 13 | message: | 14 | Assign NULL explicitly or, whenever possible, allocate the empty object with 15 | the right type and size. 16 | -------------------------------------------------------------------------------- /flint/rules/builtin/equal_assignment.yml: -------------------------------------------------------------------------------- 1 | id: equal_assignment 2 | language: r 3 | severity: hint 4 | rule: 5 | pattern: $LHS = $RHS 6 | has: 7 | field: lhs 8 | kind: identifier 9 | fix: ~~LHS~~ <- ~~RHS~~ 10 | message: Use <-, not =, for assignment. 11 | -------------------------------------------------------------------------------- /flint/rules/builtin/equals_na.yml: -------------------------------------------------------------------------------- 1 | id: equals_na 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $MYVAR == NA 7 | - pattern: $MYVAR == NA_integer_ 8 | - pattern: $MYVAR == NA_real_ 9 | - pattern: $MYVAR == NA_complex_ 10 | - pattern: $MYVAR == NA_character_ 11 | - pattern: NA == $MYVAR 12 | - pattern: NA_integer_ == $MYVAR 13 | - pattern: NA_real_ == $MYVAR 14 | - pattern: NA_complex_ == $MYVAR 15 | - pattern: NA_character_ == $MYVAR 16 | fix: is.na(~~MYVAR~~) 17 | message: Use is.na for comparisons to NA (not == or !=). 18 | 19 | --- 20 | 21 | id: equals_na-2 22 | language: r 23 | severity: warning 24 | rule: 25 | any: 26 | - pattern: $MYVAR != NA 27 | - pattern: $MYVAR != NA_integer_ 28 | - pattern: $MYVAR != NA_real_ 29 | - pattern: $MYVAR != NA_complex_ 30 | - pattern: $MYVAR != NA_character_ 31 | - pattern: NA != $MYVAR 32 | - pattern: NA_integer_ != $MYVAR 33 | - pattern: NA_real_ != $MYVAR 34 | - pattern: NA_complex_ != $MYVAR 35 | - pattern: NA_character_ != $MYVAR 36 | fix: is.na(~~MYVAR~~) 37 | message: Use is.na for comparisons to NA (not == or !=). 38 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_comparison.yml: -------------------------------------------------------------------------------- 1 | id: expect_comparison-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: expect_true($X > $Y) 6 | fix: expect_gt(~~X~~, ~~Y~~) 7 | message: expect_gt(x, y) is better than expect_true(x > y). 8 | 9 | --- 10 | 11 | id: expect_comparison-2 12 | language: r 13 | severity: warning 14 | rule: 15 | pattern: expect_true($X >= $Y) 16 | fix: expect_gte(~~X~~, ~~Y~~) 17 | message: expect_gte(x, y) is better than expect_true(x >= y). 18 | 19 | --- 20 | 21 | id: expect_comparison-3 22 | language: r 23 | severity: warning 24 | rule: 25 | pattern: expect_true($X < $Y) 26 | fix: expect_lt(~~X~~, ~~Y~~) 27 | message: expect_lt(x, y) is better than expect_true(x < y). 28 | 29 | --- 30 | 31 | id: expect_comparison-4 32 | language: r 33 | severity: warning 34 | rule: 35 | pattern: expect_true($X <= $Y) 36 | fix: expect_lte(~~X~~, ~~Y~~) 37 | message: expect_lte(x, y) is better than expect_true(x <= y). 38 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_identical.yml: -------------------------------------------------------------------------------- 1 | id: expect_identical-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: expect_true(identical($VAL1, $VAL2)) 6 | fix: expect_identical(~~VAL1~~, ~~VAL2~~) 7 | message: Use expect_identical(x, y) instead of expect_true(identical(x, y)). 8 | 9 | --- 10 | 11 | id: expect_identical-2 12 | language: r 13 | severity: warning 14 | rule: 15 | pattern: expect_equal($VAL1, $VAL2) 16 | fix: expect_identical(~~VAL1~~, ~~VAL2~~) 17 | constraints: 18 | VAL1: 19 | all: 20 | - not: 21 | has: 22 | stopBy: end 23 | kind: float 24 | regex: \. 25 | - not: 26 | regex: ^typeof 27 | - not: 28 | pattern: NULL 29 | VAL2: 30 | all: 31 | - not: 32 | has: 33 | stopBy: end 34 | kind: float 35 | regex: \. 36 | - not: 37 | regex: ^typeof 38 | - not: 39 | pattern: NULL 40 | message: | 41 | Use expect_identical(x, y) by default; resort to expect_equal() only when 42 | needed, e.g. when setting ignore_attr= or tolerance=. 43 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_length.yml: -------------------------------------------------------------------------------- 1 | id: expect_length-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $FUN(length($OBJ), $VALUE) 7 | - pattern: $FUN($VALUE, length($OBJ)) 8 | constraints: 9 | FUN: 10 | regex: ^(expect_identical|expect_equal)$ 11 | fix: expect_length(~~OBJ~~, ~~VALUE~~) 12 | message: expect_length(x, n) is better than ~~FUN~~(length(x), n). 13 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_named.yml: -------------------------------------------------------------------------------- 1 | id: expect_named-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: 7 | context: expect_identical(names($OBJ), $VALUES) 8 | strictness: ast 9 | - pattern: 10 | context: expect_identical($VALUES, names($OBJ)) 11 | strictness: ast 12 | constraints: 13 | VALUES: 14 | not: 15 | regex: colnames|rownames|dimnames|NULL 16 | has: 17 | kind: null 18 | fix: expect_named(~~OBJ~~, ~~VALUES~~) 19 | message: expect_named(x, n) is better than expect_identical(names(x), n). 20 | 21 | --- 22 | 23 | id: expect_named-2 24 | language: r 25 | severity: warning 26 | rule: 27 | any: 28 | - pattern: 29 | context: expect_equal(names($OBJ), $VALUES) 30 | strictness: ast 31 | - pattern: 32 | context: expect_equal($VALUES, names($OBJ)) 33 | strictness: ast 34 | constraints: 35 | VALUES: 36 | not: 37 | regex: colnames|rownames|dimnames|NULL 38 | fix: expect_named(~~OBJ~~, ~~VALUES~~) 39 | message: expect_named(x, n) is better than expect_equal(names(x), n). 40 | 41 | --- 42 | 43 | id: expect_named-3 44 | language: r 45 | severity: warning 46 | rule: 47 | any: 48 | - pattern: 49 | context: testthat::expect_identical(names($OBJ), $VALUES) 50 | strictness: ast 51 | - pattern: 52 | context: testthat::expect_identical($VALUES, names($OBJ)) 53 | strictness: ast 54 | constraints: 55 | VALUES: 56 | not: 57 | regex: colnames|rownames|dimnames|NULL 58 | fix: testthat::expect_named(~~OBJ~~, ~~VALUES~~) 59 | message: expect_named(x, n) is better than expect_identical(names(x), n). 60 | 61 | --- 62 | 63 | id: expect_named-4 64 | language: r 65 | severity: warning 66 | rule: 67 | any: 68 | - pattern: 69 | context: testthat::expect_equal(names($OBJ), $VALUES) 70 | strictness: ast 71 | - pattern: 72 | context: testthat::expect_equal($VALUES, names($OBJ)) 73 | strictness: ast 74 | constraints: 75 | VALUES: 76 | not: 77 | regex: colnames|rownames|dimnames|NULL 78 | fix: testthat::expect_named(~~OBJ~~, ~~VALUES~~) 79 | message: expect_named(x, n) is better than expect_equal(names(x), n). 80 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_not.yml: -------------------------------------------------------------------------------- 1 | id: expect_not-1 2 | language: r 3 | severity: warning 4 | rule: 5 | all: 6 | - pattern: expect_true(!$COND) 7 | - not: 8 | regex: '^expect_true\(!!' 9 | fix: expect_false(~~COND~~) 10 | message: expect_false(x) is better than expect_true(!x), and vice versa. 11 | 12 | --- 13 | 14 | id: expect_not-2 15 | language: r 16 | severity: warning 17 | rule: 18 | all: 19 | - pattern: expect_false(!$COND) 20 | - not: 21 | regex: '^expect_false\(!!' 22 | fix: expect_true(~~COND~~) 23 | message: expect_false(x) is better than expect_true(!x), and vice versa. 24 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_null.yml: -------------------------------------------------------------------------------- 1 | id: expect_null-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $FUN(NULL, $VALUES) 7 | - pattern: $FUN($VALUES, NULL) 8 | constraints: 9 | FUN: 10 | regex: ^(expect_identical|expect_equal)$ 11 | fix: expect_null(~~VALUES~~) 12 | message: expect_null(x) is better than ~~FUN~~(x, NULL). 13 | 14 | --- 15 | 16 | id: expect_null-2 17 | language: r 18 | severity: warning 19 | rule: 20 | pattern: expect_true(is.null($VALUES)) 21 | fix: expect_null(~~VALUES~~) 22 | message: expect_null(x) is better than expect_true(is.null(x)). 23 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_true_false.yml: -------------------------------------------------------------------------------- 1 | id: expect_true_false-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $FUN(TRUE, $VALUES) 7 | - pattern: $FUN($VALUES, TRUE) 8 | constraints: 9 | FUN: 10 | regex: ^(expect_identical|expect_equal)$ 11 | fix: expect_true(~~VALUES~~) 12 | message: expect_true(x) is better than ~~FUN~~(x, TRUE). 13 | 14 | --- 15 | 16 | id: expect_true_false-2 17 | language: r 18 | severity: warning 19 | rule: 20 | any: 21 | - pattern: $FUN(FALSE, $VALUES) 22 | - pattern: $FUN($VALUES, FALSE) 23 | constraints: 24 | FUN: 25 | regex: ^(expect_identical|expect_equal)$ 26 | fix: expect_false(~~VALUES~~) 27 | message: expect_false(x) is better than ~~FUN~~(x, FALSE). 28 | 29 | -------------------------------------------------------------------------------- /flint/rules/builtin/expect_type.yml: -------------------------------------------------------------------------------- 1 | id: expect_type-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: 7 | context: expect_identical(typeof($OBJ), $VALUES) 8 | strictness: ast 9 | - pattern: 10 | context: expect_identical($VALUES, typeof($OBJ)) 11 | strictness: ast 12 | constraints: 13 | VALUES: 14 | not: 15 | regex: typeof 16 | fix: expect_type(~~OBJ~~, ~~VALUES~~) 17 | message: expect_type(x, t) is better than expect_identical(typeof(x), t). 18 | 19 | --- 20 | 21 | id: expect_type-2 22 | language: r 23 | severity: warning 24 | rule: 25 | any: 26 | - pattern: 27 | context: expect_equal(typeof($OBJ), $VALUES) 28 | strictness: ast 29 | - pattern: 30 | context: expect_equal($VALUES, typeof($OBJ)) 31 | strictness: ast 32 | constraints: 33 | VALUES: 34 | not: 35 | regex: typeof 36 | fix: expect_type(~~OBJ~~, ~~VALUES~~) 37 | message: expect_type(x, t) is better than expect_equal(typeof(x), t). 38 | 39 | --- 40 | 41 | id: expect_type-3 42 | language: r 43 | severity: warning 44 | rule: 45 | pattern: expect_true($FUN($OBJ)) 46 | constraints: 47 | FUN: 48 | regex: ^is\. 49 | not: 50 | regex: data\.frame$ 51 | message: expect_type(x, t) is better than expect_true(is.(x)). 52 | -------------------------------------------------------------------------------- /flint/rules/builtin/for_loop_index.yml: -------------------------------------------------------------------------------- 1 | id: for_loop_index-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: for ($IDX in $IDX) 6 | message: Don't re-use any sequence symbols as the index symbol in a for loop. 7 | 8 | --- 9 | 10 | id: for_loop_index-2 11 | language: r 12 | severity: warning 13 | rule: 14 | pattern: for ($IDX in $SEQ) 15 | constraints: 16 | SEQ: 17 | kind: call 18 | has: 19 | kind: arguments 20 | has: 21 | kind: argument 22 | stopBy: end 23 | has: 24 | kind: identifier 25 | field: value 26 | pattern: $IDX 27 | message: Don't re-use any sequence symbols as the index symbol in a for loop. 28 | -------------------------------------------------------------------------------- /flint/rules/builtin/function_return.yml: -------------------------------------------------------------------------------- 1 | id: function_return-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: return($OBJ <- $VAL) 7 | - pattern: return($OBJ <<- $VAL) 8 | - pattern: return($VAL -> $OBJ) 9 | - pattern: return($VAL ->> $OBJ) 10 | message: | 11 | Move the assignment outside of the return() clause, or skip assignment altogether. 12 | -------------------------------------------------------------------------------- /flint/rules/builtin/implicit_assignment.yml: -------------------------------------------------------------------------------- 1 | id: implicit_assignment-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $RECEIVER <- $VALUE 7 | - pattern: $RECEIVER <<- $VALUE 8 | - pattern: $VALUE -> $RECEIVER 9 | - pattern: $VALUE ->> $RECEIVER 10 | inside: 11 | any: 12 | - kind: if_statement 13 | - kind: while_statement 14 | field: condition 15 | stopBy: end 16 | strictness: cst 17 | message: | 18 | Avoid implicit assignments in function calls. For example, instead of 19 | `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. 20 | 21 | --- 22 | 23 | id: implicit_assignment-2 24 | language: r 25 | severity: warning 26 | rule: 27 | any: 28 | - pattern: $RECEIVER <- $VALUE 29 | - pattern: $RECEIVER <<- $VALUE 30 | - pattern: $VALUE -> $RECEIVER 31 | - pattern: $VALUE ->> $RECEIVER 32 | inside: 33 | kind: for_statement 34 | field: sequence 35 | stopBy: end 36 | strictness: cst 37 | message: | 38 | Avoid implicit assignments in function calls. For example, instead of 39 | `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. 40 | 41 | # --- 42 | # 43 | # id: implicit_assignment-3 44 | # language: r 45 | # severity: warning 46 | # rule: 47 | # any: 48 | # - pattern: $RECEIVER <- $VALUE 49 | # - pattern: $RECEIVER <<- $VALUE 50 | # - pattern: $VALUE -> $RECEIVER 51 | # - pattern: $VALUE ->> $RECEIVER 52 | # inside: 53 | # kind: argument 54 | # field: value 55 | # strictness: cst 56 | # stopBy: end 57 | # not: 58 | # inside: 59 | # kind: call 60 | # field: function 61 | # has: 62 | # kind: identifier 63 | # regex: ^(lapply)$ 64 | # stopBy: end 65 | # strictness: cst 66 | # message: | 67 | # Avoid implicit assignments in function calls. For example, instead of 68 | # `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. 69 | 70 | -------------------------------------------------------------------------------- /flint/rules/builtin/is_numeric.yml: -------------------------------------------------------------------------------- 1 | id: is_numeric-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: is.numeric($VAR) || is.integer($VAR) 7 | - pattern: is.integer($VAR) || is.numeric($VAR) 8 | message: is.numeric(x) || is.integer(x) can be simplified to is.numeric(x). Use 9 | is.double(x) to test for objects stored as 64-bit floating point. 10 | 11 | --- 12 | 13 | id: is_numeric-2 14 | language: r 15 | severity: warning 16 | rule: 17 | any: 18 | - pattern: 19 | context: class($VAR) %in% c("numeric", "integer") 20 | strictness: ast 21 | - pattern: 22 | context: class($VAR) %in% c("integer", "numeric") 23 | strictness: ast 24 | message: class(x) %in% c("numeric", "integer") can be simplified to is.numeric(x). Use 25 | is.double(x) to test for objects stored as 64-bit floating point. 26 | -------------------------------------------------------------------------------- /flint/rules/builtin/length_levels.yml: -------------------------------------------------------------------------------- 1 | id: length_levels-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: length(levels($VAR)) 6 | fix: nlevels(~~VAR~~) 7 | message: nlevels(x) is better than length(levels(x)). df 8 | -------------------------------------------------------------------------------- /flint/rules/builtin/length_test.yml: -------------------------------------------------------------------------------- 1 | # Strangely, having something like pattern: length($VAR $OP $VAR2) doesn't work 2 | 3 | id: length_test-1 4 | language: r 5 | severity: warning 6 | rule: 7 | pattern: length($VAR == $VAR2) 8 | fix: length(~~VAR~~) == ~~VAR2~~ 9 | message: Checking the length of a logical vector is likely a mistake. 10 | 11 | --- 12 | 13 | id: length_test-2 14 | language: r 15 | severity: warning 16 | rule: 17 | pattern: length($VAR != $VAR2) 18 | fix: length(~~VAR~~) != ~~VAR2~~ 19 | message: Checking the length of a logical vector is likely a mistake. 20 | 21 | --- 22 | 23 | id: length_test-3 24 | language: r 25 | severity: warning 26 | rule: 27 | pattern: length($VAR > $VAR2) 28 | fix: length(~~VAR~~) > ~~VAR2~~ 29 | message: Checking the length of a logical vector is likely a mistake. 30 | 31 | --- 32 | 33 | id: length_test-4 34 | language: r 35 | severity: warning 36 | rule: 37 | pattern: length($VAR >= $VAR2) 38 | fix: length(~~VAR~~) >= ~~VAR2~~ 39 | message: Checking the length of a logical vector is likely a mistake. 40 | 41 | --- 42 | 43 | id: length_test-5 44 | language: r 45 | severity: warning 46 | rule: 47 | pattern: length($VAR < $VAR2) 48 | fix: length(~~VAR~~) < ~~VAR2~~ 49 | message: Checking the length of a logical vector is likely a mistake. 50 | 51 | --- 52 | 53 | id: length_test-6 54 | language: r 55 | severity: warning 56 | rule: 57 | pattern: length($VAR <= $VAR2) 58 | fix: length(~~VAR~~) <= ~~VAR2~~ 59 | message: Checking the length of a logical vector is likely a mistake. 60 | -------------------------------------------------------------------------------- /flint/rules/builtin/lengths.yml: -------------------------------------------------------------------------------- 1 | id: sapply_lengths-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: sapply($MYVAR, length) 7 | - pattern: sapply(FUN = length, $MYVAR) 8 | - pattern: sapply($MYVAR, FUN = length) 9 | - pattern: vapply($MYVAR, length $$$) 10 | 11 | - pattern: map_dbl($MYVAR, length) 12 | - pattern: map_dbl($MYVAR, .f = length) 13 | - pattern: map_dbl(.f = length, $MYVAR) 14 | - pattern: map_int($MYVAR, length) 15 | - pattern: map_int($MYVAR, .f = length) 16 | - pattern: map_int(.f = length, $MYVAR) 17 | 18 | - pattern: purrr::map_dbl($MYVAR, length) 19 | - pattern: purrr::map_dbl($MYVAR, .f = length) 20 | - pattern: purrr::map_dbl(.f = length, $MYVAR) 21 | - pattern: purrr::map_int($MYVAR, length) 22 | - pattern: purrr::map_int($MYVAR, .f = length) 23 | - pattern: purrr::map_int(.f = length, $MYVAR) 24 | fix: lengths(~~MYVAR~~) 25 | message: Use lengths() to find the length of each element in a list. 26 | 27 | --- 28 | 29 | id: sapply_lengths-2 30 | language: r 31 | severity: warning 32 | rule: 33 | any: 34 | - pattern: $MYVAR |> sapply(length) 35 | - pattern: $MYVAR |> sapply(FUN = length) 36 | - pattern: $MYVAR |> vapply(length $$$) 37 | - pattern: $MYVAR |> map_int(length) 38 | - pattern: $MYVAR |> map_int(length $$$) 39 | - pattern: $MYVAR |> purrr::map_int(length) 40 | - pattern: $MYVAR |> purrr::map_int(length $$$) 41 | fix: ~~MYVAR~~ |> lengths() 42 | message: Use lengths() to find the length of each element in a list. 43 | 44 | --- 45 | 46 | id: sapply_lengths-3 47 | language: r 48 | severity: warning 49 | rule: 50 | any: 51 | - pattern: $MYVAR %>% sapply(length) 52 | - pattern: $MYVAR %>% sapply(FUN = length) 53 | - pattern: $MYVAR %>% vapply(length $$$) 54 | - pattern: $MYVAR %>% map_int(length) 55 | - pattern: $MYVAR %>% map_int(length $$$) 56 | - pattern: $MYVAR %>% purrr::map_int(length) 57 | - pattern: $MYVAR %>% purrr::map_int(length $$$) 58 | fix: ~~MYVAR~~ %>% lengths() 59 | message: Use lengths() to find the length of each element in a list. 60 | -------------------------------------------------------------------------------- /flint/rules/builtin/library_call.yml: -------------------------------------------------------------------------------- 1 | id: library_call 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: call 6 | has: 7 | regex: ^library|require$ 8 | kind: identifier 9 | follows: 10 | not: 11 | any: 12 | - kind: call 13 | has: 14 | regex: ^library|require$ 15 | kind: identifier 16 | - kind: comment 17 | not: 18 | inside: 19 | any: 20 | - kind: function_definition 21 | - kind: call 22 | has: 23 | pattern: suppressPackageStartupMessages 24 | kind: identifier 25 | message: Move all library/require calls to the top of the script. 26 | 27 | -------------------------------------------------------------------------------- /flint/rules/builtin/literal_coercion.yml: -------------------------------------------------------------------------------- 1 | id: literal_coercion-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($VALUE) 6 | constraints: 7 | VALUE: 8 | kind: argument 9 | has: 10 | kind: float 11 | not: 12 | regex: 'e' 13 | FUN: 14 | regex: ^(int|as\.integer)$ 15 | fix: ~~VALUE~~L 16 | message: | 17 | Use ~~VALUE~~L instead of ~~FUN~~(~~VALUE~~), i.e., use literals directly 18 | where possible, instead of coercion. 19 | 20 | --- 21 | 22 | id: literal_coercion-2 23 | language: r 24 | severity: warning 25 | rule: 26 | pattern: as.character(NA) 27 | fix: NA_character_ 28 | message: | 29 | Use NA_character_ instead of as.character(NA), i.e., use literals directly 30 | where possible, instead of coercion. 31 | 32 | --- 33 | 34 | id: literal_coercion-3 35 | language: r 36 | severity: warning 37 | rule: 38 | pattern: as.logical($VAR) 39 | constraints: 40 | VAR: 41 | kind: argument 42 | has: 43 | any: 44 | - regex: ^1L$ 45 | - regex: ^1$ 46 | - regex: 'true' 47 | fix: TRUE 48 | message: Use TRUE instead of as.logical(~~VAR~~). 49 | 50 | --- 51 | 52 | id: literal_coercion-4 53 | language: r 54 | severity: warning 55 | rule: 56 | pattern: $FUN($VAR) 57 | constraints: 58 | VAR: 59 | kind: argument 60 | has: 61 | kind: float 62 | FUN: 63 | regex: ^(as\.numeric|as\.double)$ 64 | fix: ~~VAR~~ 65 | message: Use ~~VAR~~ instead of ~~FUN~~(~~VAR~~). 66 | 67 | --- 68 | 69 | id: literal_coercion-5 70 | language: r 71 | severity: warning 72 | rule: 73 | pattern: as.integer(NA) 74 | fix: NA_integer_ 75 | message: Use NA_integer_ instead of as.integer(NA). 76 | 77 | --- 78 | 79 | id: literal_coercion-6 80 | language: r 81 | severity: warning 82 | rule: 83 | pattern: $FUN(NA) 84 | constraints: 85 | FUN: 86 | regex: ^(as\.numeric|as\.double)$ 87 | fix: NA_real_ 88 | message: Use NA_real_ instead of ~~FUN~~(NA). 89 | 90 | -------------------------------------------------------------------------------- /flint/rules/builtin/matrix_apply.yml: -------------------------------------------------------------------------------- 1 | id: matrix_apply-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: apply($INPUT, 2, sum) 7 | - pattern: apply($INPUT, MARGIN = 2, sum) 8 | - pattern: apply($INPUT, 2, FUN = sum) 9 | - pattern: apply($INPUT, MARGIN = 2, FUN = sum) 10 | fix: colSums(~~INPUT~~) 11 | message: Use colSums(x) rather than apply(x, 1, sum) 12 | 13 | --- 14 | 15 | id: matrix_apply-2 16 | language: r 17 | severity: warning 18 | rule: 19 | any: 20 | - pattern: apply($INPUT, 2, sum, na.rm = $NARM) 21 | - pattern: apply($INPUT, MARGIN = 2, sum, na.rm = $NARM) 22 | - pattern: apply($INPUT, 2, FUN = sum, na.rm = $NARM) 23 | - pattern: apply($INPUT, MARGIN = 2, FUN = sum, na.rm = $NARM) 24 | fix: colSums(~~INPUT~~, na.rm = ~~NARM~~) 25 | message: Use colSums(x, na.rm = ~~NARM~~) rather than apply(x, 2, sum, na.rm = ~~NARM~~). 26 | 27 | --- 28 | 29 | id: matrix_apply-3 30 | language: r 31 | severity: warning 32 | rule: 33 | any: 34 | - pattern: apply($INPUT, 1, sum) 35 | - pattern: apply($INPUT, MARGIN = 1, sum) 36 | - pattern: apply($INPUT, 1, FUN = sum) 37 | - pattern: apply($INPUT, MARGIN = 1, FUN = sum) 38 | fix: rowSums(~~INPUT~~) 39 | message: Use rowSums(x) rather than apply(x, 1, sum) 40 | 41 | --- 42 | 43 | id: matrix_apply-4 44 | language: r 45 | severity: warning 46 | rule: 47 | any: 48 | - pattern: apply($INPUT, 1, sum, na.rm = $NARM) 49 | - pattern: apply($INPUT, MARGIN = 1, sum, na.rm = $NARM) 50 | - pattern: apply($INPUT, 1, FUN = sum, na.rm = $NARM) 51 | - pattern: apply($INPUT, MARGIN = 1, FUN = sum, na.rm = $NARM) 52 | fix: rowSums(~~INPUT~~, na.rm = ~~NARM~~) 53 | message: Use rowSums(x, na.rm = ~~NARM~~) rather than apply(x, 1, sum, na.rm = ~~NARM~~). 54 | 55 | --- 56 | 57 | id: matrix_apply-5 58 | language: r 59 | severity: warning 60 | rule: 61 | any: 62 | - pattern: apply($INPUT, 1, mean) 63 | - pattern: apply($INPUT, MARGIN = 1, mean) 64 | - pattern: apply($INPUT, 1, FUN = mean) 65 | - pattern: apply($INPUT, MARGIN = 1, FUN = mean) 66 | fix: rowMeans(~~INPUT~~) 67 | message: Use rowMeans(x) rather than apply(x, 1, mean). 68 | 69 | --- 70 | 71 | id: matrix_apply-6 72 | language: r 73 | severity: warning 74 | rule: 75 | any: 76 | - pattern: apply($INPUT, 1, mean, na.rm = $NARM) 77 | - pattern: apply($INPUT, MARGIN = 1, mean, na.rm = $NARM) 78 | - pattern: apply($INPUT, 1, FUN = mean, na.rm = $NARM) 79 | - pattern: apply($INPUT, MARGIN = 1, FUN = mean, na.rm = $NARM) 80 | fix: rowMeans(~~INPUT~~, na.rm = ~~NARM~~) 81 | message: Use rowMeans(x, na.rm = ~~NARM~~) rather than apply(x, 1, mean, na.rm = ~~NARM~~). 82 | 83 | --- 84 | 85 | id: matrix_apply-7 86 | language: r 87 | severity: warning 88 | rule: 89 | any: 90 | - pattern: apply($INPUT, 2, mean) 91 | - pattern: apply($INPUT, MARGIN = 2, mean) 92 | - pattern: apply($INPUT, 2, FUN = mean) 93 | - pattern: apply($INPUT, MARGIN = 2, FUN = mean) 94 | fix: colMeans(~~INPUT~~) 95 | message: Use colMeans(x) rather than apply(x, 2, mean). 96 | 97 | --- 98 | 99 | id: matrix_apply-8 100 | language: r 101 | severity: warning 102 | rule: 103 | any: 104 | - pattern: apply($INPUT, 2, mean, na.rm = $NARM) 105 | - pattern: apply($INPUT, MARGIN = 2, mean, na.rm = $NARM) 106 | - pattern: apply($INPUT, 2, FUN = mean, na.rm = $NARM) 107 | - pattern: apply($INPUT, MARGIN = 2, FUN = mean, na.rm = $NARM) 108 | fix: colMeans(~~INPUT~~, na.rm = ~~NARM~~) 109 | message: Use colMeans(x, na.rm = ~~NARM~~) rather than apply(x, 2, mean, na.rm = ~~NARM~~). 110 | 111 | -------------------------------------------------------------------------------- /flint/rules/builtin/missing_argument.yml: -------------------------------------------------------------------------------- 1 | id: missing_argument-1 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: arguments 6 | has: 7 | kind: comma 8 | any: 9 | - precedes: 10 | stopBy: neighbor 11 | any: 12 | - regex: '^\)$' 13 | - kind: comma 14 | - follows: 15 | any: 16 | - regex: '^\($' 17 | - kind: argument 18 | regex: '=$' 19 | follows: 20 | kind: identifier 21 | not: 22 | regex: '^(quote|switch|alist)$' 23 | inside: 24 | kind: call 25 | message: Missing argument in function call. 26 | 27 | --- 28 | 29 | id: missing_argument-2 30 | language: r 31 | severity: warning 32 | rule: 33 | kind: arguments 34 | regex: '=(\s+|)\)$' 35 | follows: 36 | any: 37 | - kind: identifier 38 | - kind: extract_operator 39 | - kind: namespace_operator 40 | not: 41 | regex: '^(quote|switch|alist)$' 42 | inside: 43 | kind: call 44 | message: Missing argument in function call. 45 | -------------------------------------------------------------------------------- /flint/rules/builtin/nested_ifelse.yml: -------------------------------------------------------------------------------- 1 | id: nested_ifelse-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($COND, $TRUE, $FALSE) 6 | constraints: 7 | FALSE: 8 | regex: ^(ifelse|if_else|fifelse) 9 | FUN: 10 | regex: ^(ifelse|if_else|fifelse) 11 | message: | 12 | Don't use nested ~~FUN~~() calls; instead, try (1) data.table::fcase; 13 | (2) dplyr::case_when; or (3) using a lookup table. 14 | 15 | --- 16 | 17 | id: nested_ifelse-2 18 | language: r 19 | severity: warning 20 | rule: 21 | pattern: $FUN($COND, $TRUE, $FALSE) 22 | constraints: 23 | TRUE: 24 | regex: ^(ifelse|if_else|fifelse) 25 | FUN: 26 | regex: ^(ifelse|if_else|fifelse) 27 | message: | 28 | Don't use nested ~~FUN~~() calls; instead, try (1) data.table::fcase; 29 | (2) dplyr::case_when; or (3) using a lookup table. 30 | -------------------------------------------------------------------------------- /flint/rules/builtin/numeric_leading_zero.yml: -------------------------------------------------------------------------------- 1 | id: numeric_leading_zero-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $VALUE 6 | any: 7 | - kind: float 8 | - kind: identifier 9 | regex: ^\.[0-9] 10 | fix: 0~~VALUE~~ 11 | message: Include the leading zero for fractional numeric constants. 12 | -------------------------------------------------------------------------------- /flint/rules/builtin/outer_negation.yml: -------------------------------------------------------------------------------- 1 | id: outer_negation-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: all(!$VAR) 6 | constraints: 7 | VAR: 8 | not: 9 | regex: '^!' 10 | fix: '!any(~~VAR~~)' 11 | message: | 12 | !any(x) is better than all(!x). The former applies negation only once after 13 | aggregation instead of many times for each element of x. 14 | 15 | --- 16 | 17 | id: outer_negation-2 18 | language: r 19 | severity: warning 20 | rule: 21 | pattern: any(! $VAR) 22 | constraints: 23 | VAR: 24 | not: 25 | regex: '^!' 26 | fix: '!all(~~VAR~~)' 27 | message: | 28 | !all(x) is better than any(!x). The former applies negation only once after 29 | aggregation instead of many times for each element of x. 30 | -------------------------------------------------------------------------------- /flint/rules/builtin/package_hooks.yml: -------------------------------------------------------------------------------- 1 | id: package_hooks-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: packageStartupMessage($$$) 6 | inside: 7 | stopBy: end 8 | kind: binary_operator 9 | has: 10 | stopBy: end 11 | field: lhs 12 | pattern: .onLoad 13 | message: Put packageStartupMessage() calls in .onAttach(), not .onLoad(). 14 | 15 | --- 16 | 17 | id: package_hooks-2 18 | language: r 19 | severity: warning 20 | rule: 21 | pattern: library.dynam($$$) 22 | inside: 23 | stopBy: end 24 | kind: binary_operator 25 | has: 26 | stopBy: end 27 | field: lhs 28 | pattern: .onAttach 29 | message: Put library.dynam() calls in .onLoad(), not .onAttach(). 30 | 31 | --- 32 | 33 | id: package_hooks-3 34 | language: r 35 | severity: warning 36 | rule: 37 | pattern: $FN($$$) 38 | inside: 39 | stopBy: end 40 | kind: binary_operator 41 | has: 42 | stopBy: end 43 | field: lhs 44 | pattern: .onLoad 45 | constraints: 46 | FN: 47 | regex: '^(cat|installed.packages|message|packageStartupMessage|print|writeLines)$' 48 | message: Don't use ~~FN~~() in .onLoad(). 49 | 50 | --- 51 | 52 | id: package_hooks-4 53 | language: r 54 | severity: warning 55 | rule: 56 | pattern: $FN($$$) 57 | inside: 58 | stopBy: end 59 | kind: binary_operator 60 | has: 61 | stopBy: end 62 | field: lhs 63 | pattern: .onAttach 64 | constraints: 65 | FN: 66 | # library.dynam already has its own linter 67 | regex: '^(cat|installed.packages|message|print|writeLines)$' 68 | message: Don't use ~~FN~~() in .onAttach(). 69 | 70 | --- 71 | 72 | id: package_hooks-5 73 | language: r 74 | severity: warning 75 | rule: 76 | pattern: $FN($$$) 77 | inside: 78 | stopBy: end 79 | kind: binary_operator 80 | has: 81 | stopBy: end 82 | field: lhs 83 | pattern: $LOAD 84 | constraints: 85 | LOAD: 86 | regex: '^(\.onAttach|\.onLoad)$' 87 | FN: 88 | regex: '^(require|library)$' 89 | message: Don't alter the search() path in ~~LOAD~~() by calling ~~FN~~(). 90 | 91 | --- 92 | 93 | id: package_hooks-6 94 | language: r 95 | severity: warning 96 | rule: 97 | pattern: installed.packages($$$) 98 | inside: 99 | stopBy: end 100 | kind: binary_operator 101 | has: 102 | stopBy: end 103 | field: lhs 104 | pattern: $LOAD 105 | constraints: 106 | LOAD: 107 | regex: '^(\.onAttach|\.onLoad)$' 108 | message: Don't slow down package load by running installed.packages() in ~~LOAD~~(). 109 | 110 | --- 111 | 112 | id: package_hooks-7 113 | language: r 114 | severity: warning 115 | rule: 116 | pattern: library.dynam.unload($$$) 117 | inside: 118 | stopBy: end 119 | kind: binary_operator 120 | has: 121 | stopBy: end 122 | field: lhs 123 | pattern: $LOAD 124 | constraints: 125 | LOAD: 126 | regex: '^(\.onDetach|\.Last\.lib)$' 127 | message: Use library.dynam.unload() calls in .onUnload(), not ~~LOAD~~(). 128 | -------------------------------------------------------------------------------- /flint/rules/builtin/paste.yml: -------------------------------------------------------------------------------- 1 | id: paste-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: 6 | context: paste($$$CONTENT sep = "" $$$CONTENT2) 7 | strictness: ast 8 | # fix: paste0($$$CONTENT) 9 | message: paste0(...) is better than paste(..., sep = ""). 10 | 11 | --- 12 | 13 | id: paste-2 14 | language: r 15 | severity: warning 16 | rule: 17 | any: 18 | - pattern: 19 | context: paste($CONTENT, collapse = ", ") 20 | strictness: ast 21 | - pattern: 22 | context: paste(collapse = ", ", $CONTENT) 23 | strictness: ast 24 | # fix: paste0($$$CONTENT) 25 | message: toString(.) is more expressive than paste(., collapse = ", "). 26 | 27 | --- 28 | 29 | id: paste-3 30 | language: r 31 | severity: warning 32 | rule: 33 | pattern: 34 | context: paste0($$$CONTENT sep = $USELESS $$$CONTENT2) 35 | strictness: ast 36 | # fix: paste0($$$CONTENT) 37 | message: | 38 | sep= is not a formal argument to paste0(); did you mean to use paste(), or 39 | collapse=? 40 | 41 | --- 42 | 43 | id: paste-4 44 | language: r 45 | severity: warning 46 | rule: 47 | any: 48 | - pattern: 49 | context: paste0($CONTENT, collapse = $FOO) 50 | strictness: ast 51 | - pattern: 52 | context: paste0(collapse = $FOO, $CONTENT) 53 | strictness: ast 54 | not: 55 | has: 56 | regex: sep 57 | kind: argument 58 | # fix: paste0($$$CONTENT) 59 | message: | 60 | Use paste(), not paste0(), to collapse a character vector when sep= is not used. 61 | 62 | # --- 63 | # 64 | # id: paste-5 65 | # language: r 66 | # severity: warning 67 | # rule: 68 | # pattern: 69 | # context: paste0(rep($VAR, $TIMES), collapse = "") 70 | # strictness: ast 71 | # constraints: 72 | # VAR: 73 | # kind: string 74 | # fix: strrep(~~VAR~~, ~~TIMES~~) 75 | # message: strrep(x, times) is better than paste0(rep(x, times), collapse = ""). 76 | -------------------------------------------------------------------------------- /flint/rules/builtin/redundant_equals.yml: -------------------------------------------------------------------------------- 1 | id: redundant_equals-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $VAR == TRUE 7 | - pattern: TRUE == $VAR 8 | - pattern: $VAR == FALSE 9 | - pattern: FALSE == $VAR 10 | message: | 11 | Using == on a logical vector is redundant. Well-named logical vectors can be 12 | used directly in filtering. For data.table's `i` argument, wrap the column 13 | name in (), like `DT[(is_treatment)]`. 14 | 15 | --- 16 | 17 | id: redundant_equals-2 18 | language: r 19 | severity: warning 20 | rule: 21 | any: 22 | - pattern: $VAR != TRUE 23 | - pattern: TRUE != $VAR 24 | - pattern: $VAR != FALSE 25 | - pattern: FALSE != $VAR 26 | message: | 27 | Using != on a logical vector is redundant. Well-named logical vectors can be 28 | used directly in filtering. For data.table's `i` argument, wrap the column 29 | name in (), like `DT[(is_treatment)]`. 30 | -------------------------------------------------------------------------------- /flint/rules/builtin/redundant_ifelse.yml: -------------------------------------------------------------------------------- 1 | id: redundant_ifelse-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($COND, $VAL1, $VAL2) 6 | constraints: 7 | VAL1: 8 | regex: ^TRUE$ 9 | VAL2: 10 | regex: ^FALSE$ 11 | FUN: 12 | regex: ^(ifelse|fifelse|if_else)$ 13 | fix: ~~COND~~ 14 | message: | 15 | Use ~~COND~~ directly instead of calling ~~FUN~~(~~COND~~, TRUE, FALSE). 16 | 17 | --- 18 | 19 | id: redundant_ifelse-2 20 | language: r 21 | severity: warning 22 | rule: 23 | pattern: $FUN($COND, $VAL1, $VAL2) 24 | constraints: 25 | VAL1: 26 | regex: ^FALSE$ 27 | VAL2: 28 | regex: ^TRUE$ 29 | FUN: 30 | regex: ^(ifelse|fifelse|if_else)$ 31 | fix: '!(~~COND~~)' 32 | message: | 33 | Use !(~~COND~~) directly instead of calling ~~FUN~~(~~COND~~, FALSE, TRUE). 34 | 35 | --- 36 | 37 | id: redundant_ifelse-3 38 | language: r 39 | severity: warning 40 | rule: 41 | pattern: $FUN($COND, $VAL1, $VAL2) 42 | constraints: 43 | VAL1: 44 | regex: ^(1|1L)$ 45 | VAL2: 46 | regex: ^(0|0L)$ 47 | FUN: 48 | regex: ^(ifelse|fifelse|if_else)$ 49 | fix: as.integer(~~COND~~) 50 | message: Prefer as.integer(~~COND~~) to ~~FUN~~(~~COND~~, ~~VAL1~~, ~~VAL2~~). 51 | 52 | --- 53 | 54 | id: redundant_ifelse-4 55 | language: r 56 | severity: warning 57 | rule: 58 | pattern: $FUN($COND, $VAL1, $VAL2) 59 | constraints: 60 | VAL1: 61 | regex: ^(0|0L)$ 62 | VAL2: 63 | regex: ^(1|1L)$ 64 | FUN: 65 | regex: ^(ifelse|fifelse|if_else)$ 66 | fix: as.integer(!(~~COND~~)) 67 | message: Prefer as.integer(!(~~COND~~)) to ~~FUN~~(~~COND~~, ~~VAL1~~, ~~VAL2~~). 68 | -------------------------------------------------------------------------------- /flint/rules/builtin/rep_len.yml: -------------------------------------------------------------------------------- 1 | id: rep_len-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: rep($OBJ, length.out = $LEN) 6 | fix: rep_len(~~OBJ~~, ~~LEN~~) 7 | message: Use rep_len(x, n) instead of rep(x, length.out = n). 8 | -------------------------------------------------------------------------------- /flint/rules/builtin/right_assignment.yml: -------------------------------------------------------------------------------- 1 | id: right_assignment 2 | language: r 3 | severity: hint 4 | rule: 5 | pattern: $RHS -> $LHS 6 | has: 7 | field: rhs 8 | kind: identifier 9 | fix: ~~LHS~~<- ~~RHS~~ 10 | message: Use <-, not ->, for assignment. 11 | -------------------------------------------------------------------------------- /flint/rules/builtin/sample_int.yml: -------------------------------------------------------------------------------- 1 | id: sample_int-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: sample(1:$N, $$$OTHER) 7 | - pattern: sample(1L:$N, $$$OTHER) 8 | fix: sample.int(~~N~~, ~~OTHER~~) 9 | message: sample.int(n, m, ...) is preferable to sample(1:n, m, ...). 10 | 11 | --- 12 | 13 | id: sample_int-2 14 | language: r 15 | severity: warning 16 | rule: 17 | pattern: sample(seq($N), $$$OTHER) 18 | fix: sample.int(~~N~~, ~~OTHER~~) 19 | message: sample.int(n, m, ...) is preferable to sample(seq(n), m, ...). 20 | 21 | --- 22 | 23 | id: sample_int-3 24 | language: r 25 | severity: warning 26 | rule: 27 | pattern: sample(seq_len($N), $$$OTHER) 28 | fix: sample.int(~~N~~, ~~OTHER~~) 29 | message: sample.int(n, m, ...) is preferable to sample(seq_len(n), m, ...). 30 | 31 | --- 32 | 33 | # Strangely this panicks if I rename FIRST to N 34 | id: sample_int-4 35 | language: r 36 | severity: warning 37 | rule: 38 | pattern: sample($FIRST, $$$OTHER) 39 | constraints: 40 | FIRST: 41 | regex: ^\d+(L|)$ 42 | fix: sample.int(~~N~~, ~~OTHER~~) 43 | message: sample.int(n, m, ...) is preferable to sample(n, m, ...). 44 | -------------------------------------------------------------------------------- /flint/rules/builtin/semicolon.yml: -------------------------------------------------------------------------------- 1 | id: semicolon-1 2 | language: r 3 | severity: warning 4 | rule: 5 | regex: ;\s+$ 6 | not: 7 | inside: 8 | kind: string 9 | stopBy: end 10 | message: Trailing semicolons are not needed. 11 | -------------------------------------------------------------------------------- /flint/rules/builtin/seq.yml: -------------------------------------------------------------------------------- 1 | id: seq-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: seq(length($VAR)) 6 | fix: seq_along(~~VAR~~) 7 | message: | 8 | seq(length(...)) is likely to be wrong in the empty edge case. Use seq_along(...) instead. 9 | 10 | --- 11 | 12 | id: seq-2 13 | language: r 14 | severity: warning 15 | rule: 16 | any: 17 | - pattern: 1:nrow($VAR) 18 | - pattern: 1L:nrow($VAR) 19 | regex: ^1 20 | fix: seq_len(nrow(~~VAR~~)) 21 | message: | 22 | 1:nrow(...) is likely to be wrong in the empty edge case. Use seq_len(nrow(...)) instead. 23 | 24 | --- 25 | 26 | id: seq-3 27 | language: r 28 | severity: warning 29 | rule: 30 | any: 31 | - pattern: 1:n() 32 | - pattern: 1L:n() 33 | regex: ^1 34 | fix: seq_len(n()) 35 | message: | 36 | 1:n() is likely to be wrong in the empty edge case. Use seq_len(n()) instead. 37 | 38 | --- 39 | 40 | id: seq-4 41 | language: r 42 | severity: warning 43 | rule: 44 | pattern: seq(nrow($VAR)) 45 | fix: seq_len(nrow(~~VAR~~)) 46 | message: | 47 | seq(nrow(...)) is likely to be wrong in the empty edge case. Use seq_len(nrow(...)) instead. 48 | 49 | --- 50 | 51 | id: seq-5 52 | language: r 53 | severity: warning 54 | rule: 55 | any: 56 | - pattern: 1:length($VAR) 57 | - pattern: 1L:length($VAR) 58 | regex: ^1 59 | fix: seq_along(~~VAR~~) 60 | message: | 61 | 1:length(...) is likely to be wrong in the empty edge case. Use seq_along(...) instead. 62 | 63 | --- 64 | 65 | id: seq-6 66 | language: r 67 | severity: warning 68 | rule: 69 | any: 70 | - pattern: 1:ncol($VAR) 71 | - pattern: 1L:ncol($VAR) 72 | regex: ^1 73 | fix: seq_len(ncol(~~VAR~~)) 74 | message: | 75 | 1:ncol(...) is likely to be wrong in the empty edge case. Use seq_len(ncol(...)) instead. 76 | 77 | --- 78 | 79 | id: seq-7 80 | language: r 81 | severity: warning 82 | rule: 83 | any: 84 | - pattern: 1:NCOL($VAR) 85 | - pattern: 1L:NCOL($VAR) 86 | regex: ^1 87 | fix: seq_len(NCOL(~~VAR~~)) 88 | message: | 89 | 1:NCOL(...) is likely to be wrong in the empty edge case. Use seq_len(NCOL(...)) instead. 90 | 91 | --- 92 | 93 | id: seq-8 94 | language: r 95 | severity: warning 96 | rule: 97 | any: 98 | - pattern: 1:NROW($VAR) 99 | - pattern: 1L:NROW($VAR) 100 | regex: ^1 101 | fix: seq_len(NROW(~~VAR~~)) 102 | message: | 103 | 1:NROW(...) is likely to be wrong in the empty edge case. Use seq_len(NROW(...)) instead. 104 | 105 | 106 | --- 107 | 108 | id: seq-9 109 | language: r 110 | severity: warning 111 | rule: 112 | pattern: seq(1, $VAL) 113 | not: 114 | pattern: seq(1, 0) 115 | constraints: 116 | VAL: 117 | regex: ^\d+(|L)$ 118 | fix: seq_len(~~VAL~~) 119 | message: seq_len(~~VAL~~) is more efficient than seq(1, ~~VAL~~). 120 | 121 | 122 | -------------------------------------------------------------------------------- /flint/rules/builtin/sort.yml: -------------------------------------------------------------------------------- 1 | id: sort-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $OBJ[order($OBJ)] 6 | fix: sort(~~OBJ~~, na.last = TRUE) 7 | message: sort(~~OBJ~~, na.last = TRUE) is better than ~~OBJ~~[order(~~OBJ~~)]. 8 | 9 | --- 10 | 11 | id: sort-2 12 | language: r 13 | severity: warning 14 | rule: 15 | any: 16 | - pattern: $OBJ[order($OBJ, decreasing = $DECREASING)] 17 | - pattern: $OBJ[order(decreasing = $DECREASING, $OBJ)] 18 | constraints: 19 | DECREASING: 20 | regex: ^(TRUE|FALSE)$ 21 | fix: sort(~~OBJ~~, decreasing = ~~DECREASING~~, na.last = TRUE) 22 | message: | 23 | sort(~~OBJ~~, decreasing = ~~DECREASING~~, na.last = TRUE) is better than 24 | ~~OBJ~~[order(~~OBJ~~, decreasing = ~~DECREASING~~)]. 25 | 26 | --- 27 | 28 | id: sort-3 29 | language: r 30 | severity: warning 31 | rule: 32 | any: 33 | - pattern: $OBJ[order($OBJ, na.last = $NALAST)] 34 | - pattern: $OBJ[order(na.last = $NALAST, $OBJ)] 35 | constraints: 36 | NALAST: 37 | regex: ^(TRUE|FALSE)$ 38 | fix: sort(~~OBJ~~, na.last = ~~NALAST~~, na.last = TRUE) 39 | message: | 40 | sort(~~OBJ~~, na.last = ~~NALAST~~, na.last = TRUE) is better than 41 | ~~OBJ~~[order(~~OBJ~~, na.last = ~~NALAST~~)]. 42 | 43 | --- 44 | 45 | id: sort-4 46 | language: r 47 | severity: warning 48 | rule: 49 | any: 50 | - pattern: $OBJ[order($OBJ, decreasing = TRUE, na.last = FALSE)] 51 | - pattern: $OBJ[order($OBJ, na.last = FALSE, decreasing = TRUE)] 52 | - pattern: $OBJ[order(decreasing = TRUE, $OBJ, na.last = FALSE)] 53 | - pattern: $OBJ[order(decreasing = TRUE, na.last = FALSE, $OBJ)] 54 | - pattern: $OBJ[order(na.last = FALSE, decreasing = TRUE, $OBJ)] 55 | - pattern: $OBJ[order(na.last = FALSE, $OBJ, decreasing = TRUE)] 56 | fix: sort(~~OBJ~~, decreasing = TRUE, na.last = FALSE) 57 | message: | 58 | sort(~~OBJ~~, decreasing = TRUE, na.last = FALSE) is better than 59 | ~~OBJ~~[order(~~OBJ~~, na.last = FALSE, decreasing = TRUE)]. 60 | 61 | --- 62 | 63 | id: sort-5 64 | language: r 65 | severity: warning 66 | rule: 67 | any: 68 | - pattern: sort($OBJ) == $OBJ 69 | - pattern: $OBJ == sort($OBJ) 70 | fix: !is.unsorted(~~OBJ~~) 71 | message: | 72 | Use !is.unsorted(~~OBJ~~) to test the sortedness of a vector. 73 | 74 | --- 75 | 76 | id: sort-6 77 | language: r 78 | severity: warning 79 | rule: 80 | any: 81 | - pattern: sort($OBJ) != $OBJ 82 | - pattern: $OBJ != sort($OBJ) 83 | fix: is.unsorted(~~OBJ~~) 84 | message: | 85 | Use is.unsorted(~~OBJ~~) to test the unsortedness of a vector. 86 | -------------------------------------------------------------------------------- /flint/rules/builtin/todo_comment.yml: -------------------------------------------------------------------------------- 1 | id: todo_comment-1 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: comment 6 | regex: '(?i)#(|\s+)\b(todo|fixme)\b' 7 | message: Remove TODO comments. 8 | -------------------------------------------------------------------------------- /flint/rules/builtin/undesirable_function.yml: -------------------------------------------------------------------------------- 1 | id: undesirable_function-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN 6 | kind: identifier 7 | not: 8 | inside: 9 | kind: argument 10 | constraints: 11 | FUN: 12 | regex: ^(\.libPaths|attach|browser|debug|debugcall|debugonce|detach|par|setwd|structure|Sys\.setenv|Sys\.setlocale|trace|undebug|untrace)$ 13 | message: Function "~~FUN~~()" is undesirable. 14 | -------------------------------------------------------------------------------- /flint/rules/builtin/undesirable_operator.yml: -------------------------------------------------------------------------------- 1 | id: undesirable_operator-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $X <<- $Y 7 | - pattern: $X ->> $Y 8 | message: | 9 | Avoid undesirable operators `<<-` and `->>`. They assign outside the current 10 | environment in a way that can be hard to reason about. Prefer fully-encapsulated 11 | functions wherever possible, or, if necessary, assign to a specific environment 12 | with assign(). Recall that you can create an environment at the desired scope 13 | with new.env(). 14 | 15 | --- 16 | 17 | id: undesirable_operator-2 18 | language: r 19 | severity: warning 20 | rule: 21 | kind: namespace_operator 22 | has: 23 | pattern: ':::' 24 | message: | 25 | Operator `:::` is undesirable. It accesses non-exported functions inside 26 | packages. Code relying on these is likely to break in future versions of the 27 | package because the functions are not part of the public interface and may be 28 | changed or removed by the maintainers without notice. Use public functions 29 | via :: instead. 30 | -------------------------------------------------------------------------------- /flint/rules/builtin/unnecessary_nesting.yml: -------------------------------------------------------------------------------- 1 | id: unnecessary_nesting-1 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: if_statement 6 | any: 7 | - has: 8 | kind: 'braced_expression' 9 | field: consequence 10 | has: 11 | kind: if_statement 12 | stopBy: neighbor 13 | not: 14 | has: 15 | kind: 'braced_expression' 16 | field: alternative 17 | stopBy: end 18 | not: 19 | any: 20 | - has: 21 | nthChild: 2 22 | - precedes: 23 | regex: "^else$" 24 | - has: 25 | kind: if_statement 26 | field: consequence 27 | stopBy: neighbor 28 | # Can be in if(), but not else if() 29 | not: 30 | inside: 31 | field: alternative 32 | kind: if_statement 33 | message: | 34 | Don't use nested `if` statements, where a single `if` with the combined 35 | conditional expression will do. For example, instead of `if (x) { if (y) { ... }}`, 36 | use `if (x && y) { ... }`. 37 | -------------------------------------------------------------------------------- /flint/rules/builtin/unreachable_code.yml: -------------------------------------------------------------------------------- 1 | id: unreachable_code-1 2 | language: r 3 | severity: warning 4 | rule: 5 | regex: '[^}]+' 6 | not: 7 | regex: 'else' 8 | follows: 9 | any: 10 | - pattern: return($$$A) 11 | - pattern: stop($$$A) 12 | not: 13 | precedes: 14 | regex: 'else' 15 | stopBy: end 16 | message: Code and comments coming after a return() or stop() should be removed. 17 | 18 | --- 19 | 20 | id: unreachable_code-2 21 | language: r 22 | severity: warning 23 | rule: 24 | regex: '[^}]+' 25 | not: 26 | regex: 'else' 27 | follows: 28 | any: 29 | - pattern: next 30 | - pattern: break 31 | stopBy: end 32 | message: Remove code and comments coming after `next` or `break` 33 | 34 | --- 35 | 36 | id: unreachable_code-3 37 | language: r 38 | severity: warning 39 | rule: 40 | inside: 41 | any: 42 | - kind: if_statement 43 | pattern: if (FALSE) 44 | - kind: while_statement 45 | pattern: while (FALSE) 46 | stopBy: end 47 | message: Remove code inside a conditional loop with a deterministically false condition. 48 | 49 | --- 50 | 51 | id: unreachable_code-4 52 | language: r 53 | severity: warning 54 | rule: 55 | inside: 56 | any: 57 | - kind: if_statement 58 | pattern: if (TRUE) 59 | - kind: while_statement 60 | pattern: while (TRUE) 61 | stopBy: end 62 | message: | 63 | One branch has a a deterministically true condition. The other branches can 64 | be removed. 65 | -------------------------------------------------------------------------------- /flint/rules/builtin/which_grepl.yml: -------------------------------------------------------------------------------- 1 | id: which_grepl-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: which(grepl($$$ARGS)) 6 | fix: grep(~~ARGS~~) 7 | message: grep(pattern, x) is better than which(grepl(pattern, x)). 8 | -------------------------------------------------------------------------------- /flir/cache_file_state.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/flir/cache_file_state.rds -------------------------------------------------------------------------------- /flir/config.yml: -------------------------------------------------------------------------------- 1 | keep: 2 | - any_duplicated 3 | - any_is_na 4 | - class_equals 5 | - condition_message 6 | - double_assignment 7 | - duplicate_argument 8 | - empty_assignment 9 | - equal_assignment 10 | - equals_na 11 | - expect_comparison 12 | - expect_identical 13 | - expect_length 14 | - expect_named 15 | - expect_not 16 | - expect_null 17 | - expect_true_false 18 | - expect_type 19 | - for_loop_index 20 | - function_return 21 | - implicit_assignment 22 | - is_numeric 23 | - length_levels 24 | - length_test 25 | - lengths 26 | - library_call 27 | - list_comparison 28 | - literal_coercion 29 | - matrix_apply 30 | - missing_argument 31 | - nested_ifelse 32 | - numeric_leading_zero 33 | - outer_negation 34 | - package_hooks 35 | - paste 36 | - redundant_equals 37 | - redundant_ifelse 38 | - rep_len 39 | - right_assignment 40 | - sample_int 41 | - semicolon 42 | - seq 43 | - sort 44 | - stopifnot_all 45 | - T_and_F_symbol 46 | - todo_comment 47 | - undesirable_function 48 | - undesirable_operator 49 | - unnecessary_nesting 50 | - unreachable_code 51 | - which_grepl 52 | -------------------------------------------------------------------------------- /flir/rules/builtin/T_and_F_symbol.yml: -------------------------------------------------------------------------------- 1 | id: true_false_symbol 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: T 6 | kind: identifier 7 | not: 8 | any: 9 | - precedes: 10 | any: 11 | - pattern: <- 12 | - pattern: = 13 | - regex: ^~$ 14 | - follows: 15 | any: 16 | - pattern: $ 17 | - regex: ^~$ 18 | - inside: 19 | any: 20 | - kind: parameter 21 | - kind: call 22 | - kind: binary_operator 23 | follows: 24 | regex: ^~$ 25 | stopBy: end 26 | stopBy: 27 | kind: 28 | argument 29 | fix: TRUE 30 | message: Use TRUE instead of the symbol T. 31 | 32 | --- 33 | 34 | id: true_false_symbol-2 35 | language: r 36 | severity: warning 37 | rule: 38 | pattern: F 39 | kind: identifier 40 | not: 41 | any: 42 | - precedes: 43 | any: 44 | - pattern: <- 45 | - pattern: = 46 | - regex: ^~$ 47 | - follows: 48 | any: 49 | - pattern: $ 50 | - regex: ^~$ 51 | - inside: 52 | any: 53 | - kind: parameter 54 | - kind: call 55 | - kind: binary_operator 56 | follows: 57 | regex: ^~$ 58 | stopBy: end 59 | stopBy: 60 | kind: 61 | argument 62 | fix: FALSE 63 | message: Use FALSE instead of the symbol F. 64 | 65 | --- 66 | 67 | id: true_false_symbol-3 68 | language: r 69 | severity: warning 70 | rule: 71 | pattern: T 72 | kind: identifier 73 | precedes: 74 | any: 75 | - pattern: <- 76 | - pattern: = 77 | not: 78 | inside: 79 | kind: argument 80 | message: Don't use T as a variable name, as it can break code relying on T being TRUE. 81 | 82 | --- 83 | 84 | id: true_false_symbol-4 85 | language: r 86 | severity: warning 87 | rule: 88 | pattern: F 89 | kind: identifier 90 | precedes: 91 | any: 92 | - pattern: <- 93 | - pattern: = 94 | not: 95 | inside: 96 | kind: argument 97 | message: Don't use F as a variable name, as it can break code relying on F being FALSE. 98 | -------------------------------------------------------------------------------- /flir/rules/builtin/absolute_path.yml: -------------------------------------------------------------------------------- 1 | # id: absolute_path-1 2 | # language: r 3 | # severity: warning 4 | # rule: 5 | # kind: string_content 6 | # any: 7 | # - regex: '^~[[:alpha:]]' 8 | # - regex: '^~/[[:alpha:]]' 9 | # - regex: '^[[:alpha:]]:' 10 | # - regex: '^(/|~)$' 11 | # - regex: '^/[[:alpha:]]' 12 | # - regex: '^\\' 13 | # message: Do not use absolute paths. 14 | -------------------------------------------------------------------------------- /flir/rules/builtin/any_duplicated.yml: -------------------------------------------------------------------------------- 1 | id: any_duplicated-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: any($$$ duplicated($MYVAR) $$$) 6 | fix: anyDuplicated(~~MYVAR~~) > 0 7 | message: anyDuplicated(x, ...) > 0 is better than any(duplicated(x), ...). 8 | 9 | --- 10 | 11 | id: any_duplicated-2 12 | language: r 13 | severity: warning 14 | rule: 15 | any: 16 | - pattern: length(unique($MYVAR)) == length($MYVAR) 17 | - pattern: length($MYVAR) == length(unique($MYVAR)) 18 | fix: anyDuplicated(~~MYVAR~~) == 0L 19 | message: anyDuplicated(x) == 0L is better than length(unique(x)) == length(x). 20 | 21 | --- 22 | 23 | id: any_duplicated-3 24 | language: r 25 | severity: warning 26 | rule: 27 | pattern: length(unique($MYVAR)) != length($MYVAR) 28 | fix: anyDuplicated(~~MYVAR~~) != 0L 29 | message: | 30 | Use anyDuplicated(x) != 0L (or > or <) instead of length(unique(x)) != length(x) 31 | (or > or <). 32 | 33 | --- 34 | 35 | id: any_duplicated-4 36 | language: r 37 | severity: warning 38 | rule: 39 | any: 40 | - pattern: nrow($DATA) != length(unique($DATA$µCOL)) 41 | - pattern: length(unique($DATA$µCOL)) != nrow($DATA) 42 | fix: anyDuplicated(~~DATA~~$~~COL~~) != 0L 43 | message: | 44 | anyDuplicated(DF$col) != 0L is better than length(unique(DF$col)) != nrow(DF) 45 | 46 | --- 47 | 48 | # id: any_duplicated-5 49 | # language: r 50 | # severity: warning 51 | # rule: 52 | # any: 53 | # - pattern: 54 | # context: nrow($DATA) != length(unique($DATA[["µCOL"]])) 55 | # strictness: ast 56 | # - pattern: 57 | # context: length(unique($DATA[["µCOL"]])) != nrow($DATA) 58 | # strictness: ast 59 | # fix: anyDuplicated(~~DATA~~[["~~COL~~"]]) != 0L 60 | # message: | 61 | # anyDuplicated(DF[["col"]]) != 0L is better than length(unique(DF[["col"]])) != nrow(DF) 62 | # 63 | # --- 64 | 65 | id: any_duplicated-6 66 | language: r 67 | severity: warning 68 | rule: 69 | any: 70 | - pattern: nrow($DATA) == length(unique($DATA$µCOL)) 71 | - pattern: length(unique($DATA$µCOL)) == nrow($DATA) 72 | fix: anyDuplicated(~~DATA~~$~~COL~~) == 0L 73 | message: | 74 | anyDuplicated(DF$col) == 0L is better than length(unique(DF$col)) == nrow(DF) 75 | 76 | # --- 77 | # 78 | # id: any_duplicated-7 79 | # language: r 80 | # severity: warning 81 | # rule: 82 | # any: 83 | # - pattern: 84 | # context: nrow($DATA) == length(unique($DATA[["µCOL"]])) 85 | # strictness: ast 86 | # - pattern: 87 | # context: length(unique($DATA[["µCOL"]])) == nrow($DATA) 88 | # strictness: ast 89 | # fix: anyDuplicated(~~DATA~~[["~~COL~~"]]) == 0L 90 | # message: | 91 | # anyDuplicated(DF[["col"]]) == 0L is better than length(unique(DF[["col"]])) == nrow(DF) 92 | -------------------------------------------------------------------------------- /flir/rules/builtin/any_is_na.yml: -------------------------------------------------------------------------------- 1 | id: any_na-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: any(is.na($MYVAR)) 7 | - pattern: any(na.rm = $NARM, is.na($MYVAR)) 8 | - pattern: any(is.na($MYVAR), na.rm = $NARM) 9 | fix: anyNA(~~MYVAR~~) 10 | message: anyNA(x) is better than any(is.na(x)). 11 | 12 | --- 13 | 14 | id: any_na-2 15 | language: r 16 | severity: warning 17 | rule: 18 | any: 19 | - pattern: NA %in% $ELEM 20 | - pattern: NA_real_ %in% $ELEM 21 | - pattern: NA_logical_ %in% $ELEM 22 | - pattern: NA_character_ %in% $ELEM 23 | - pattern: NA_complex_ %in% $ELEM 24 | fix: anyNA(~~ELEM~~) 25 | message: anyNA(x) is better than NA %in% x. 26 | -------------------------------------------------------------------------------- /flir/rules/builtin/class_equals.yml: -------------------------------------------------------------------------------- 1 | id: class_equals-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: class($VAR) == $CLASSNAME 7 | - pattern: $CLASSNAME == class($VAR) 8 | not: 9 | inside: 10 | kind: argument 11 | fix: inherits(~~VAR~~, ~~CLASSNAME~~) 12 | message: Instead of comparing class(x) with ==, use inherits(x, 'class-name') or is. or is(x, 'class') 13 | 14 | --- 15 | 16 | id: class_equals-2 17 | language: r 18 | severity: warning 19 | rule: 20 | any: 21 | - pattern: class($VAR) != $CLASSNAME 22 | - pattern: $CLASSNAME != class($VAR) 23 | not: 24 | inside: 25 | kind: argument 26 | fix: "!inherits(~~VAR~~, ~~CLASSNAME~~)" 27 | message: "Instead of comparing class(x) with !=, use !inherits(x, 'class-name') or is. or is(x, 'class')" 28 | 29 | --- 30 | 31 | id: class_equals-3 32 | language: r 33 | severity: warning 34 | rule: 35 | any: 36 | - pattern: $CLASSNAME %in% class($VAR) 37 | - pattern: class($VAR) %in% $CLASSNAME 38 | constraints: 39 | CLASSNAME: 40 | kind: string 41 | fix: inherits(~~VAR~~, ~~CLASSNAME~~) 42 | message: Instead of comparing class(x) with %in%, use inherits(x, 'class-name') or is. or is(x, 'class') 43 | -------------------------------------------------------------------------------- /flir/rules/builtin/condition_message.yml: -------------------------------------------------------------------------------- 1 | id: condition_message-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($$$ paste0($$$MSG) $$$) 6 | kind: call 7 | not: 8 | any: 9 | - has: 10 | kind: extract_operator 11 | - has: 12 | stopBy: end 13 | kind: argument 14 | has: 15 | field: name 16 | regex: "^collapse|recycle0$" 17 | stopBy: end 18 | constraints: 19 | FUN: 20 | regex: "^(packageStartupMessage|stop|warning)$" 21 | fix: ~~FUN~~(~~MSG~~) 22 | message: | 23 | ~~FUN~~(paste0(...)) can be rewritten as ~~FUN~~(...). 24 | -------------------------------------------------------------------------------- /flir/rules/builtin/double_assignment.yml: -------------------------------------------------------------------------------- 1 | id: right_double_assignment 2 | language: r 3 | severity: hint 4 | rule: 5 | pattern: $RHS ->> $LHS 6 | has: 7 | field: rhs 8 | kind: identifier 9 | message: ->> can have hard-to-predict behavior; prefer assigning to a 10 | specific environment instead (with assign() or <-). 11 | 12 | --- 13 | 14 | id: left_double_assignment 15 | language: r 16 | severity: hint 17 | rule: 18 | pattern: $LHS <<- $RHS 19 | has: 20 | field: lhs 21 | kind: identifier 22 | message: <<- can have hard-to-predict behavior; prefer assigning to a 23 | specific environment instead (with assign() or <-). 24 | -------------------------------------------------------------------------------- /flir/rules/builtin/duplicate_argument.yml: -------------------------------------------------------------------------------- 1 | id: duplicate_argument-1 2 | language: r 3 | severity: warning 4 | rule: 5 | # Look for a function argument... 6 | kind: argument 7 | any: 8 | - has: 9 | kind: identifier 10 | field: name 11 | pattern: $OBJ 12 | - has: 13 | kind: string_content 14 | pattern: $OBJ 15 | stopBy: end 16 | 17 | # ... that follows other argument(s) with the same name... 18 | follows: 19 | kind: argument 20 | stopBy: end 21 | has: 22 | stopBy: end 23 | kind: identifier 24 | field: name 25 | pattern: $OBJ 26 | 27 | # ... inside a function call (or a subset environment for data.table)... 28 | inside: 29 | kind: arguments 30 | follows: 31 | any: 32 | - kind: identifier 33 | pattern: $FUN 34 | - kind: string 35 | inside: 36 | any: 37 | - kind: call 38 | - kind: subset 39 | 40 | # ... that is not a function listed below. 41 | constraints: 42 | FUN: 43 | not: 44 | regex: ^(mutate|transmute)$ 45 | 46 | message: Avoid duplicate arguments in function calls. 47 | -------------------------------------------------------------------------------- /flir/rules/builtin/empty_assignment.yml: -------------------------------------------------------------------------------- 1 | id: empty_assignment-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $OBJ <- {} 7 | - pattern: $OBJ <- {$CONTENT} 8 | - pattern: $OBJ = {} 9 | - pattern: $OBJ = {$CONTENT} 10 | constraints: 11 | CONTENT: 12 | regex: ^\s+$ 13 | message: | 14 | Assign NULL explicitly or, whenever possible, allocate the empty object with 15 | the right type and size. 16 | -------------------------------------------------------------------------------- /flir/rules/builtin/equal_assignment.yml: -------------------------------------------------------------------------------- 1 | id: equal_assignment 2 | language: r 3 | severity: hint 4 | rule: 5 | pattern: $LHS = $RHS 6 | has: 7 | field: lhs 8 | kind: identifier 9 | fix: ~~LHS~~ <- ~~RHS~~ 10 | message: Use <-, not =, for assignment. 11 | -------------------------------------------------------------------------------- /flir/rules/builtin/equals_na.yml: -------------------------------------------------------------------------------- 1 | id: equals_na 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $MYVAR == NA 7 | - pattern: $MYVAR == NA_integer_ 8 | - pattern: $MYVAR == NA_real_ 9 | - pattern: $MYVAR == NA_complex_ 10 | - pattern: $MYVAR == NA_character_ 11 | - pattern: NA == $MYVAR 12 | - pattern: NA_integer_ == $MYVAR 13 | - pattern: NA_real_ == $MYVAR 14 | - pattern: NA_complex_ == $MYVAR 15 | - pattern: NA_character_ == $MYVAR 16 | fix: is.na(~~MYVAR~~) 17 | message: Use is.na for comparisons to NA (not == or !=). 18 | 19 | --- 20 | 21 | id: equals_na-2 22 | language: r 23 | severity: warning 24 | rule: 25 | any: 26 | - pattern: $MYVAR != NA 27 | - pattern: $MYVAR != NA_integer_ 28 | - pattern: $MYVAR != NA_real_ 29 | - pattern: $MYVAR != NA_complex_ 30 | - pattern: $MYVAR != NA_character_ 31 | - pattern: NA != $MYVAR 32 | - pattern: NA_integer_ != $MYVAR 33 | - pattern: NA_real_ != $MYVAR 34 | - pattern: NA_complex_ != $MYVAR 35 | - pattern: NA_character_ != $MYVAR 36 | fix: is.na(~~MYVAR~~) 37 | message: Use is.na for comparisons to NA (not == or !=). 38 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_comparison.yml: -------------------------------------------------------------------------------- 1 | id: expect_comparison-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: expect_true($X > $Y) 6 | fix: expect_gt(~~X~~, ~~Y~~) 7 | message: expect_gt(x, y) is better than expect_true(x > y). 8 | 9 | --- 10 | 11 | id: expect_comparison-2 12 | language: r 13 | severity: warning 14 | rule: 15 | pattern: expect_true($X >= $Y) 16 | fix: expect_gte(~~X~~, ~~Y~~) 17 | message: expect_gte(x, y) is better than expect_true(x >= y). 18 | 19 | --- 20 | 21 | id: expect_comparison-3 22 | language: r 23 | severity: warning 24 | rule: 25 | pattern: expect_true($X < $Y) 26 | fix: expect_lt(~~X~~, ~~Y~~) 27 | message: expect_lt(x, y) is better than expect_true(x < y). 28 | 29 | --- 30 | 31 | id: expect_comparison-4 32 | language: r 33 | severity: warning 34 | rule: 35 | pattern: expect_true($X <= $Y) 36 | fix: expect_lte(~~X~~, ~~Y~~) 37 | message: expect_lte(x, y) is better than expect_true(x <= y). 38 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_identical.yml: -------------------------------------------------------------------------------- 1 | id: expect_identical-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: expect_true(identical($VAL1, $VAL2)) 6 | fix: expect_identical(~~VAL1~~, ~~VAL2~~) 7 | message: Use expect_identical(x, y) instead of expect_true(identical(x, y)). 8 | 9 | --- 10 | 11 | id: expect_identical-2 12 | language: r 13 | severity: warning 14 | rule: 15 | pattern: expect_equal($VAL1, $VAL2) 16 | fix: expect_identical(~~VAL1~~, ~~VAL2~~) 17 | constraints: 18 | VAL1: 19 | all: 20 | - not: 21 | has: 22 | stopBy: end 23 | kind: float 24 | regex: \. 25 | - not: 26 | regex: ^typeof 27 | - not: 28 | pattern: NULL 29 | VAL2: 30 | all: 31 | - not: 32 | has: 33 | stopBy: end 34 | kind: float 35 | regex: \. 36 | - not: 37 | regex: ^typeof 38 | - not: 39 | pattern: NULL 40 | message: | 41 | Use expect_identical(x, y) by default; resort to expect_equal() only when 42 | needed, e.g. when setting ignore_attr= or tolerance=. 43 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_length.yml: -------------------------------------------------------------------------------- 1 | id: expect_length-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $FUN(length($OBJ), $VALUE) 7 | - pattern: $FUN($VALUE, length($OBJ)) 8 | constraints: 9 | FUN: 10 | regex: ^(expect_identical|expect_equal)$ 11 | VALUE: 12 | not: 13 | regex: length\( 14 | fix: expect_length(~~OBJ~~, ~~VALUE~~) 15 | message: expect_length(x, n) is better than ~~FUN~~(length(x), n). 16 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_named.yml: -------------------------------------------------------------------------------- 1 | id: expect_named-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: 7 | context: expect_identical(names($OBJ), $VALUES) 8 | strictness: ast 9 | - pattern: 10 | context: expect_identical($VALUES, names($OBJ)) 11 | strictness: ast 12 | constraints: 13 | VALUES: 14 | not: 15 | regex: ^(colnames\(|rownames\(|dimnames\(|NULL|names\() 16 | has: 17 | kind: null 18 | fix: expect_named(~~OBJ~~, ~~VALUES~~) 19 | message: expect_named(x, n) is better than expect_identical(names(x), n). 20 | 21 | --- 22 | 23 | id: expect_named-2 24 | language: r 25 | severity: warning 26 | rule: 27 | any: 28 | - pattern: 29 | context: expect_equal(names($OBJ), $VALUES) 30 | strictness: ast 31 | - pattern: 32 | context: expect_equal($VALUES, names($OBJ)) 33 | strictness: ast 34 | constraints: 35 | VALUES: 36 | not: 37 | regex: ^(colnames\(|rownames\(|dimnames\(|NULL|names\() 38 | fix: expect_named(~~OBJ~~, ~~VALUES~~) 39 | message: expect_named(x, n) is better than expect_equal(names(x), n). 40 | 41 | --- 42 | 43 | id: expect_named-3 44 | language: r 45 | severity: warning 46 | rule: 47 | any: 48 | - pattern: 49 | context: testthat::expect_identical(names($OBJ), $VALUES) 50 | strictness: ast 51 | - pattern: 52 | context: testthat::expect_identical($VALUES, names($OBJ)) 53 | strictness: ast 54 | constraints: 55 | VALUES: 56 | not: 57 | regex: ^(colnames\(|rownames\(|dimnames\(|NULL|names\() 58 | fix: testthat::expect_named(~~OBJ~~, ~~VALUES~~) 59 | message: expect_named(x, n) is better than expect_identical(names(x), n). 60 | 61 | --- 62 | 63 | id: expect_named-4 64 | language: r 65 | severity: warning 66 | rule: 67 | any: 68 | - pattern: 69 | context: testthat::expect_equal(names($OBJ), $VALUES) 70 | strictness: ast 71 | - pattern: 72 | context: testthat::expect_equal($VALUES, names($OBJ)) 73 | strictness: ast 74 | constraints: 75 | VALUES: 76 | not: 77 | regex: ^(colnames\(|rownames\(|dimnames\(|NULL|names\() 78 | fix: testthat::expect_named(~~OBJ~~, ~~VALUES~~) 79 | message: expect_named(x, n) is better than expect_equal(names(x), n). 80 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_not.yml: -------------------------------------------------------------------------------- 1 | id: expect_not-1 2 | language: r 3 | severity: warning 4 | rule: 5 | all: 6 | - pattern: expect_true(!$COND) 7 | - not: 8 | regex: '^expect_true\(!!' 9 | fix: expect_false(~~COND~~) 10 | message: expect_false(x) is better than expect_true(!x), and vice versa. 11 | 12 | --- 13 | 14 | id: expect_not-2 15 | language: r 16 | severity: warning 17 | rule: 18 | all: 19 | - pattern: expect_false(!$COND) 20 | - not: 21 | regex: '^expect_false\(!!' 22 | fix: expect_true(~~COND~~) 23 | message: expect_false(x) is better than expect_true(!x), and vice versa. 24 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_null.yml: -------------------------------------------------------------------------------- 1 | id: expect_null-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $FUN(NULL, $VALUES) 7 | - pattern: $FUN($VALUES, NULL) 8 | constraints: 9 | FUN: 10 | regex: ^(expect_identical|expect_equal)$ 11 | fix: expect_null(~~VALUES~~) 12 | message: expect_null(x) is better than ~~FUN~~(x, NULL). 13 | 14 | --- 15 | 16 | id: expect_null-2 17 | language: r 18 | severity: warning 19 | rule: 20 | pattern: expect_true(is.null($VALUES)) 21 | fix: expect_null(~~VALUES~~) 22 | message: expect_null(x) is better than expect_true(is.null(x)). 23 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_true_false.yml: -------------------------------------------------------------------------------- 1 | id: expect_true_false-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $FUN(TRUE, $VALUES) 7 | - pattern: $FUN($VALUES, TRUE) 8 | constraints: 9 | FUN: 10 | regex: ^(expect_identical|expect_equal)$ 11 | fix: expect_true(~~VALUES~~) 12 | message: expect_true(x) is better than ~~FUN~~(x, TRUE). 13 | 14 | --- 15 | 16 | id: expect_true_false-2 17 | language: r 18 | severity: warning 19 | rule: 20 | any: 21 | - pattern: $FUN(FALSE, $VALUES) 22 | - pattern: $FUN($VALUES, FALSE) 23 | constraints: 24 | FUN: 25 | regex: ^(expect_identical|expect_equal)$ 26 | fix: expect_false(~~VALUES~~) 27 | message: expect_false(x) is better than ~~FUN~~(x, FALSE). 28 | 29 | -------------------------------------------------------------------------------- /flir/rules/builtin/expect_type.yml: -------------------------------------------------------------------------------- 1 | id: expect_type-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: 7 | context: expect_identical(typeof($OBJ), $VALUES) 8 | strictness: ast 9 | - pattern: 10 | context: expect_identical($VALUES, typeof($OBJ)) 11 | strictness: ast 12 | constraints: 13 | VALUES: 14 | not: 15 | regex: typeof 16 | fix: expect_type(~~OBJ~~, ~~VALUES~~) 17 | message: expect_type(x, t) is better than expect_identical(typeof(x), t). 18 | 19 | --- 20 | 21 | id: expect_type-2 22 | language: r 23 | severity: warning 24 | rule: 25 | any: 26 | - pattern: 27 | context: expect_equal(typeof($OBJ), $VALUES) 28 | strictness: ast 29 | - pattern: 30 | context: expect_equal($VALUES, typeof($OBJ)) 31 | strictness: ast 32 | constraints: 33 | VALUES: 34 | not: 35 | regex: typeof 36 | fix: expect_type(~~OBJ~~, ~~VALUES~~) 37 | message: expect_type(x, t) is better than expect_equal(typeof(x), t). 38 | 39 | --- 40 | 41 | id: expect_type-3 42 | language: r 43 | severity: warning 44 | rule: 45 | pattern: expect_true($FUN($OBJ)) 46 | constraints: 47 | FUN: 48 | regex: ^is\. 49 | not: 50 | regex: data\.frame$ 51 | message: expect_type(x, t) is better than expect_true(is.(x)). 52 | -------------------------------------------------------------------------------- /flir/rules/builtin/for_loop_index.yml: -------------------------------------------------------------------------------- 1 | id: for_loop_index-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: for ($IDX in $IDX) 6 | message: Don't re-use any sequence symbols as the index symbol in a for loop. 7 | 8 | --- 9 | 10 | id: for_loop_index-2 11 | language: r 12 | severity: warning 13 | rule: 14 | pattern: for ($IDX in $SEQ) 15 | constraints: 16 | SEQ: 17 | kind: call 18 | has: 19 | kind: arguments 20 | has: 21 | kind: argument 22 | stopBy: end 23 | has: 24 | kind: identifier 25 | field: value 26 | pattern: $IDX 27 | message: Don't re-use any sequence symbols as the index symbol in a for loop. 28 | -------------------------------------------------------------------------------- /flir/rules/builtin/function_return.yml: -------------------------------------------------------------------------------- 1 | id: function_return-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: return($OBJ <- $VAL) 7 | - pattern: return($OBJ <<- $VAL) 8 | - pattern: return($VAL -> $OBJ) 9 | - pattern: return($VAL ->> $OBJ) 10 | message: | 11 | Move the assignment outside of the return() clause, or skip assignment altogether. 12 | -------------------------------------------------------------------------------- /flir/rules/builtin/implicit_assignment.yml: -------------------------------------------------------------------------------- 1 | id: implicit_assignment-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $RECEIVER <- $VALUE 7 | - pattern: $RECEIVER <<- $VALUE 8 | - pattern: $VALUE -> $RECEIVER 9 | - pattern: $VALUE ->> $RECEIVER 10 | inside: 11 | any: 12 | - kind: if_statement 13 | - kind: while_statement 14 | field: condition 15 | stopBy: end 16 | strictness: cst 17 | message: | 18 | Avoid implicit assignments in function calls. For example, instead of 19 | `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. 20 | 21 | --- 22 | 23 | id: implicit_assignment-2 24 | language: r 25 | severity: warning 26 | rule: 27 | any: 28 | - pattern: $RECEIVER <- $VALUE 29 | - pattern: $RECEIVER <<- $VALUE 30 | - pattern: $VALUE -> $RECEIVER 31 | - pattern: $VALUE ->> $RECEIVER 32 | inside: 33 | kind: for_statement 34 | field: sequence 35 | stopBy: end 36 | strictness: cst 37 | message: | 38 | Avoid implicit assignments in function calls. For example, instead of 39 | `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. 40 | 41 | # --- 42 | # 43 | # id: implicit_assignment-3 44 | # language: r 45 | # severity: warning 46 | # rule: 47 | # any: 48 | # - pattern: $RECEIVER <- $VALUE 49 | # - pattern: $RECEIVER <<- $VALUE 50 | # - pattern: $VALUE -> $RECEIVER 51 | # - pattern: $VALUE ->> $RECEIVER 52 | # inside: 53 | # kind: argument 54 | # field: value 55 | # strictness: cst 56 | # stopBy: end 57 | # not: 58 | # inside: 59 | # kind: call 60 | # field: function 61 | # has: 62 | # kind: identifier 63 | # regex: ^(lapply)$ 64 | # stopBy: end 65 | # strictness: cst 66 | # message: | 67 | # Avoid implicit assignments in function calls. For example, instead of 68 | # `if (x <- 1L) { ... }`, write `x <- 1L; if (x) { ... }`. 69 | 70 | -------------------------------------------------------------------------------- /flir/rules/builtin/is_numeric.yml: -------------------------------------------------------------------------------- 1 | id: is_numeric-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: is.numeric($VAR) || is.integer($VAR) 7 | - pattern: is.integer($VAR) || is.numeric($VAR) 8 | message: is.numeric(x) || is.integer(x) can be simplified to is.numeric(x). Use 9 | is.double(x) to test for objects stored as 64-bit floating point. 10 | 11 | --- 12 | 13 | id: is_numeric-2 14 | language: r 15 | severity: warning 16 | rule: 17 | any: 18 | - pattern: 19 | context: class($VAR) %in% c("numeric", "integer") 20 | strictness: ast 21 | - pattern: 22 | context: class($VAR) %in% c("integer", "numeric") 23 | strictness: ast 24 | message: class(x) %in% c("numeric", "integer") can be simplified to is.numeric(x). Use 25 | is.double(x) to test for objects stored as 64-bit floating point. 26 | -------------------------------------------------------------------------------- /flir/rules/builtin/length_levels.yml: -------------------------------------------------------------------------------- 1 | id: length_levels-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: length(levels($VAR)) 6 | fix: nlevels(~~VAR~~) 7 | message: nlevels(x) is better than length(levels(x)). df 8 | -------------------------------------------------------------------------------- /flir/rules/builtin/length_test.yml: -------------------------------------------------------------------------------- 1 | # Strangely, having something like pattern: length($VAR $OP $VAR2) doesn't work 2 | 3 | id: length_test-1 4 | language: r 5 | severity: warning 6 | rule: 7 | pattern: length($VAR == $VAR2) 8 | fix: length(~~VAR~~) == ~~VAR2~~ 9 | message: Checking the length of a logical vector is likely a mistake. 10 | 11 | --- 12 | 13 | id: length_test-2 14 | language: r 15 | severity: warning 16 | rule: 17 | pattern: length($VAR != $VAR2) 18 | fix: length(~~VAR~~) != ~~VAR2~~ 19 | message: Checking the length of a logical vector is likely a mistake. 20 | 21 | --- 22 | 23 | id: length_test-3 24 | language: r 25 | severity: warning 26 | rule: 27 | pattern: length($VAR > $VAR2) 28 | fix: length(~~VAR~~) > ~~VAR2~~ 29 | message: Checking the length of a logical vector is likely a mistake. 30 | 31 | --- 32 | 33 | id: length_test-4 34 | language: r 35 | severity: warning 36 | rule: 37 | pattern: length($VAR >= $VAR2) 38 | fix: length(~~VAR~~) >= ~~VAR2~~ 39 | message: Checking the length of a logical vector is likely a mistake. 40 | 41 | --- 42 | 43 | id: length_test-5 44 | language: r 45 | severity: warning 46 | rule: 47 | pattern: length($VAR < $VAR2) 48 | fix: length(~~VAR~~) < ~~VAR2~~ 49 | message: Checking the length of a logical vector is likely a mistake. 50 | 51 | --- 52 | 53 | id: length_test-6 54 | language: r 55 | severity: warning 56 | rule: 57 | pattern: length($VAR <= $VAR2) 58 | fix: length(~~VAR~~) <= ~~VAR2~~ 59 | message: Checking the length of a logical vector is likely a mistake. 60 | -------------------------------------------------------------------------------- /flir/rules/builtin/lengths.yml: -------------------------------------------------------------------------------- 1 | id: sapply_lengths-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: sapply($MYVAR, length) 7 | - pattern: sapply(FUN = length, $MYVAR) 8 | - pattern: sapply($MYVAR, FUN = length) 9 | - pattern: vapply($MYVAR, length $$$) 10 | 11 | - pattern: map_dbl($MYVAR, length) 12 | - pattern: map_dbl($MYVAR, .f = length) 13 | - pattern: map_dbl(.f = length, $MYVAR) 14 | - pattern: map_int($MYVAR, length) 15 | - pattern: map_int($MYVAR, .f = length) 16 | - pattern: map_int(.f = length, $MYVAR) 17 | 18 | - pattern: purrr::map_dbl($MYVAR, length) 19 | - pattern: purrr::map_dbl($MYVAR, .f = length) 20 | - pattern: purrr::map_dbl(.f = length, $MYVAR) 21 | - pattern: purrr::map_int($MYVAR, length) 22 | - pattern: purrr::map_int($MYVAR, .f = length) 23 | - pattern: purrr::map_int(.f = length, $MYVAR) 24 | fix: lengths(~~MYVAR~~) 25 | message: Use lengths() to find the length of each element in a list. 26 | 27 | --- 28 | 29 | id: sapply_lengths-2 30 | language: r 31 | severity: warning 32 | rule: 33 | any: 34 | - pattern: $MYVAR |> sapply(length) 35 | - pattern: $MYVAR |> sapply(FUN = length) 36 | - pattern: $MYVAR |> vapply(length $$$) 37 | - pattern: $MYVAR |> map_int(length) 38 | - pattern: $MYVAR |> map_int(length $$$) 39 | - pattern: $MYVAR |> purrr::map_int(length) 40 | - pattern: $MYVAR |> purrr::map_int(length $$$) 41 | fix: ~~MYVAR~~ |> lengths() 42 | message: Use lengths() to find the length of each element in a list. 43 | 44 | --- 45 | 46 | id: sapply_lengths-3 47 | language: r 48 | severity: warning 49 | rule: 50 | any: 51 | - pattern: $MYVAR %>% sapply(length) 52 | - pattern: $MYVAR %>% sapply(FUN = length) 53 | - pattern: $MYVAR %>% vapply(length $$$) 54 | - pattern: $MYVAR %>% map_int(length) 55 | - pattern: $MYVAR %>% map_int(length $$$) 56 | - pattern: $MYVAR %>% purrr::map_int(length) 57 | - pattern: $MYVAR %>% purrr::map_int(length $$$) 58 | fix: ~~MYVAR~~ %>% lengths() 59 | message: Use lengths() to find the length of each element in a list. 60 | -------------------------------------------------------------------------------- /flir/rules/builtin/library_call.yml: -------------------------------------------------------------------------------- 1 | id: library_call 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: call 6 | has: 7 | regex: ^library|require$ 8 | kind: identifier 9 | follows: 10 | not: 11 | any: 12 | - kind: call 13 | has: 14 | regex: ^library|require$ 15 | kind: identifier 16 | - kind: comment 17 | not: 18 | inside: 19 | stopBy: end 20 | any: 21 | - kind: function_definition 22 | - kind: call 23 | has: 24 | pattern: suppressPackageStartupMessages 25 | kind: identifier 26 | message: Move all library/require calls to the top of the script. 27 | -------------------------------------------------------------------------------- /flir/rules/builtin/list_comparison.yml: -------------------------------------------------------------------------------- 1 | id: list_comparison-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $FUN($$$) > $$$ 7 | - pattern: $FUN($$$) >= $$$ 8 | - pattern: $FUN($$$) < $$$ 9 | - pattern: $FUN($$$) <= $$$ 10 | - pattern: $FUN($$$) == $$$ 11 | - pattern: $FUN($$$) != $$$ 12 | constraints: 13 | FUN: 14 | regex: ^(lapply|map|Map|\.mapply)$ 15 | message: | 16 | The output of ~~FUN~~(), a list, is being coerced for comparison. 17 | Instead, use a mapper that generates a vector with the correct type directly, 18 | for example vapply(x, FUN, character(1L)) if the output is a string. 19 | -------------------------------------------------------------------------------- /flir/rules/builtin/literal_coercion.yml: -------------------------------------------------------------------------------- 1 | id: literal_coercion-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($VALUE) 6 | constraints: 7 | VALUE: 8 | kind: argument 9 | has: 10 | kind: float 11 | not: 12 | regex: 'e' 13 | FUN: 14 | regex: ^(int|as\.integer)$ 15 | fix: ~~VALUE~~L 16 | message: | 17 | Use ~~VALUE~~L instead of ~~FUN~~(~~VALUE~~), i.e., use literals directly 18 | where possible, instead of coercion. 19 | 20 | --- 21 | 22 | id: literal_coercion-2 23 | language: r 24 | severity: warning 25 | rule: 26 | pattern: as.character(NA) 27 | fix: NA_character_ 28 | message: | 29 | Use NA_character_ instead of as.character(NA), i.e., use literals directly 30 | where possible, instead of coercion. 31 | 32 | --- 33 | 34 | id: literal_coercion-3 35 | language: r 36 | severity: warning 37 | rule: 38 | pattern: as.logical($VAR) 39 | constraints: 40 | VAR: 41 | kind: argument 42 | has: 43 | any: 44 | - regex: ^1L$ 45 | - regex: ^1$ 46 | - regex: 'true' 47 | fix: TRUE 48 | message: Use TRUE instead of as.logical(~~VAR~~). 49 | 50 | --- 51 | 52 | id: literal_coercion-4 53 | language: r 54 | severity: warning 55 | rule: 56 | pattern: $FUN($VAR) 57 | constraints: 58 | VAR: 59 | kind: argument 60 | has: 61 | kind: float 62 | FUN: 63 | regex: ^(as\.numeric|as\.double)$ 64 | fix: ~~VAR~~ 65 | message: Use ~~VAR~~ instead of ~~FUN~~(~~VAR~~). 66 | 67 | --- 68 | 69 | id: literal_coercion-5 70 | language: r 71 | severity: warning 72 | rule: 73 | pattern: as.integer(NA) 74 | fix: NA_integer_ 75 | message: Use NA_integer_ instead of as.integer(NA). 76 | 77 | --- 78 | 79 | id: literal_coercion-6 80 | language: r 81 | severity: warning 82 | rule: 83 | pattern: $FUN(NA) 84 | constraints: 85 | FUN: 86 | regex: ^(as\.numeric|as\.double)$ 87 | fix: NA_real_ 88 | message: Use NA_real_ instead of ~~FUN~~(NA). 89 | 90 | -------------------------------------------------------------------------------- /flir/rules/builtin/matrix_apply.yml: -------------------------------------------------------------------------------- 1 | id: matrix_apply-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: apply($INPUT, $MARG, sum) 7 | - pattern: apply($INPUT, MARGIN = $MARG, sum) 8 | - pattern: apply($INPUT, $MARG, FUN = sum) 9 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = sum) 10 | constraints: 11 | MARG: 12 | has: 13 | regex: ^(2|2L)$ 14 | fix: colSums(~~INPUT~~) 15 | message: Use colSums(x) rather than apply(x, 2, sum) 16 | 17 | --- 18 | 19 | id: matrix_apply-2 20 | language: r 21 | severity: warning 22 | rule: 23 | any: 24 | - pattern: apply($INPUT, $MARG, sum, na.rm = $NARM) 25 | - pattern: apply($INPUT, MARGIN = $MARG, sum, na.rm = $NARM) 26 | - pattern: apply($INPUT, $MARG, FUN = sum, na.rm = $NARM) 27 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = sum, na.rm = $NARM) 28 | constraints: 29 | MARG: 30 | has: 31 | regex: ^(2|2L)$ 32 | fix: colSums(~~INPUT~~, na.rm = ~~NARM~~) 33 | message: Use colSums(x, na.rm = ~~NARM~~) rather than apply(x, 2, sum, na.rm = ~~NARM~~). 34 | 35 | --- 36 | 37 | id: matrix_apply-3 38 | language: r 39 | severity: warning 40 | rule: 41 | any: 42 | - pattern: apply($INPUT, $MARG, sum) 43 | - pattern: apply($INPUT, MARGIN = $MARG, sum) 44 | - pattern: apply($INPUT, $MARG, FUN = sum) 45 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = sum) 46 | constraints: 47 | MARG: 48 | has: 49 | regex: ^(1|1L)$ 50 | fix: rowSums(~~INPUT~~) 51 | message: Use rowSums(x) rather than apply(x, 1, sum) 52 | 53 | --- 54 | 55 | id: matrix_apply-4 56 | language: r 57 | severity: warning 58 | rule: 59 | any: 60 | - pattern: apply($INPUT, $MARG, sum, na.rm = $NARM) 61 | - pattern: apply($INPUT, MARGIN = $MARG, sum, na.rm = $NARM) 62 | - pattern: apply($INPUT, $MARG, FUN = sum, na.rm = $NARM) 63 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = sum, na.rm = $NARM) 64 | constraints: 65 | MARG: 66 | has: 67 | regex: ^(1|1L)$ 68 | fix: rowSums(~~INPUT~~, na.rm = ~~NARM~~) 69 | message: Use rowSums(x, na.rm = ~~NARM~~) rather than apply(x, 1, sum, na.rm = ~~NARM~~). 70 | 71 | --- 72 | 73 | id: matrix_apply-5 74 | language: r 75 | severity: warning 76 | rule: 77 | any: 78 | - pattern: apply($INPUT, $MARG, mean) 79 | - pattern: apply($INPUT, MARGIN = $MARG, mean) 80 | - pattern: apply($INPUT, $MARG, FUN = mean) 81 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = mean) 82 | constraints: 83 | MARG: 84 | has: 85 | regex: ^(1|1L)$ 86 | fix: rowMeans(~~INPUT~~) 87 | message: Use rowMeans(x) rather than apply(x, 1, mean). 88 | 89 | --- 90 | 91 | id: matrix_apply-6 92 | language: r 93 | severity: warning 94 | rule: 95 | any: 96 | - pattern: apply($INPUT, $MARG, mean, na.rm = $NARM) 97 | - pattern: apply($INPUT, MARGIN = $MARG, mean, na.rm = $NARM) 98 | - pattern: apply($INPUT, $MARG, FUN = mean, na.rm = $NARM) 99 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = mean, na.rm = $NARM) 100 | constraints: 101 | MARG: 102 | has: 103 | regex: ^(1|1L)$ 104 | fix: rowMeans(~~INPUT~~, na.rm = ~~NARM~~) 105 | message: Use rowMeans(x, na.rm = ~~NARM~~) rather than apply(x, 1, mean, na.rm = ~~NARM~~). 106 | 107 | --- 108 | 109 | id: matrix_apply-7 110 | language: r 111 | severity: warning 112 | rule: 113 | any: 114 | - pattern: apply($INPUT, $MARG, mean) 115 | - pattern: apply($INPUT, MARGIN = $MARG, mean) 116 | - pattern: apply($INPUT, $MARG, FUN = mean) 117 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = mean) 118 | constraints: 119 | MARG: 120 | has: 121 | regex: ^(2|2L)$ 122 | fix: colMeans(~~INPUT~~) 123 | message: Use colMeans(x) rather than apply(x, 2, mean). 124 | 125 | --- 126 | 127 | id: matrix_apply-8 128 | language: r 129 | severity: warning 130 | rule: 131 | any: 132 | - pattern: apply($INPUT, $MARG, mean, na.rm = $NARM) 133 | - pattern: apply($INPUT, MARGIN = $MARG, mean, na.rm = $NARM) 134 | - pattern: apply($INPUT, $MARG, FUN = mean, na.rm = $NARM) 135 | - pattern: apply($INPUT, MARGIN = $MARG, FUN = mean, na.rm = $NARM) 136 | constraints: 137 | MARG: 138 | has: 139 | regex: ^(2|2L)$ 140 | fix: colMeans(~~INPUT~~, na.rm = ~~NARM~~) 141 | message: Use colMeans(x, na.rm = ~~NARM~~) rather than apply(x, 2, mean, na.rm = ~~NARM~~). 142 | 143 | -------------------------------------------------------------------------------- /flir/rules/builtin/missing_argument.yml: -------------------------------------------------------------------------------- 1 | id: missing_argument-1 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: arguments 6 | has: 7 | kind: comma 8 | any: 9 | - precedes: 10 | stopBy: neighbor 11 | any: 12 | - regex: '^\)$' 13 | - kind: comma 14 | - follows: 15 | any: 16 | - regex: '^\($' 17 | - kind: argument 18 | regex: '=$' 19 | follows: 20 | kind: identifier 21 | not: 22 | regex: '^(quote|switch|alist)$' 23 | inside: 24 | kind: call 25 | message: Missing argument in function call. 26 | 27 | --- 28 | 29 | id: missing_argument-2 30 | language: r 31 | severity: warning 32 | rule: 33 | kind: arguments 34 | regex: '=(\s+|)\)$' 35 | follows: 36 | any: 37 | - kind: identifier 38 | - kind: extract_operator 39 | - kind: namespace_operator 40 | not: 41 | regex: '^(quote|switch|alist)$' 42 | inside: 43 | kind: call 44 | message: Missing argument in function call. 45 | -------------------------------------------------------------------------------- /flir/rules/builtin/nested_ifelse.yml: -------------------------------------------------------------------------------- 1 | id: nested_ifelse-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($COND, $TRUE, $FALSE) 6 | constraints: 7 | FALSE: 8 | regex: ^(ifelse|if_else|fifelse) 9 | FUN: 10 | regex: ^(ifelse|if_else|fifelse) 11 | message: | 12 | Don't use nested ~~FUN~~() calls; instead, try (1) data.table::fcase; 13 | (2) dplyr::case_when; or (3) using a lookup table. 14 | 15 | --- 16 | 17 | id: nested_ifelse-2 18 | language: r 19 | severity: warning 20 | rule: 21 | pattern: $FUN($COND, $TRUE, $FALSE) 22 | constraints: 23 | TRUE: 24 | regex: ^(ifelse|if_else|fifelse) 25 | FUN: 26 | regex: ^(ifelse|if_else|fifelse) 27 | message: | 28 | Don't use nested ~~FUN~~() calls; instead, try (1) data.table::fcase; 29 | (2) dplyr::case_when; or (3) using a lookup table. 30 | -------------------------------------------------------------------------------- /flir/rules/builtin/numeric_leading_zero.yml: -------------------------------------------------------------------------------- 1 | id: numeric_leading_zero-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $VALUE 6 | any: 7 | - kind: float 8 | - kind: identifier 9 | regex: ^\.[0-9] 10 | fix: 0~~VALUE~~ 11 | message: Include the leading zero for fractional numeric constants. 12 | -------------------------------------------------------------------------------- /flir/rules/builtin/outer_negation.yml: -------------------------------------------------------------------------------- 1 | id: outer_negation-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: all(!$VAR) 6 | constraints: 7 | VAR: 8 | not: 9 | regex: '^!' 10 | fix: '!any(~~VAR~~)' 11 | message: | 12 | !any(x) is better than all(!x). The former applies negation only once after 13 | aggregation instead of many times for each element of x. 14 | 15 | --- 16 | 17 | id: outer_negation-2 18 | language: r 19 | severity: warning 20 | rule: 21 | pattern: any(! $VAR) 22 | constraints: 23 | VAR: 24 | not: 25 | regex: '^!' 26 | fix: '!all(~~VAR~~)' 27 | message: | 28 | !all(x) is better than any(!x). The former applies negation only once after 29 | aggregation instead of many times for each element of x. 30 | -------------------------------------------------------------------------------- /flir/rules/builtin/package_hooks.yml: -------------------------------------------------------------------------------- 1 | id: package_hooks-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: packageStartupMessage($$$) 6 | inside: 7 | stopBy: end 8 | kind: binary_operator 9 | has: 10 | stopBy: end 11 | field: lhs 12 | pattern: .onLoad 13 | message: Put packageStartupMessage() calls in .onAttach(), not .onLoad(). 14 | 15 | --- 16 | 17 | id: package_hooks-2 18 | language: r 19 | severity: warning 20 | rule: 21 | pattern: library.dynam($$$) 22 | inside: 23 | stopBy: end 24 | kind: binary_operator 25 | has: 26 | stopBy: end 27 | field: lhs 28 | pattern: .onAttach 29 | message: Put library.dynam() calls in .onLoad(), not .onAttach(). 30 | 31 | --- 32 | 33 | id: package_hooks-3 34 | language: r 35 | severity: warning 36 | rule: 37 | pattern: $FN($$$) 38 | inside: 39 | stopBy: end 40 | kind: binary_operator 41 | has: 42 | stopBy: end 43 | field: lhs 44 | pattern: .onLoad 45 | constraints: 46 | FN: 47 | regex: '^(cat|installed.packages|message|packageStartupMessage|print|writeLines)$' 48 | message: Don't use ~~FN~~() in .onLoad(). 49 | 50 | --- 51 | 52 | id: package_hooks-4 53 | language: r 54 | severity: warning 55 | rule: 56 | pattern: $FN($$$) 57 | inside: 58 | stopBy: end 59 | kind: binary_operator 60 | has: 61 | stopBy: end 62 | field: lhs 63 | pattern: .onAttach 64 | constraints: 65 | FN: 66 | # library.dynam already has its own linter 67 | regex: '^(cat|installed.packages|message|print|writeLines)$' 68 | message: Don't use ~~FN~~() in .onAttach(). 69 | 70 | --- 71 | 72 | id: package_hooks-5 73 | language: r 74 | severity: warning 75 | rule: 76 | pattern: $FN($$$) 77 | inside: 78 | stopBy: end 79 | kind: binary_operator 80 | has: 81 | stopBy: end 82 | field: lhs 83 | pattern: $LOAD 84 | constraints: 85 | LOAD: 86 | regex: '^(\.onAttach|\.onLoad)$' 87 | FN: 88 | regex: '^(require|library)$' 89 | message: Don't alter the search() path in ~~LOAD~~() by calling ~~FN~~(). 90 | 91 | --- 92 | 93 | id: package_hooks-6 94 | language: r 95 | severity: warning 96 | rule: 97 | pattern: installed.packages($$$) 98 | inside: 99 | stopBy: end 100 | kind: binary_operator 101 | has: 102 | stopBy: end 103 | field: lhs 104 | pattern: $LOAD 105 | constraints: 106 | LOAD: 107 | regex: '^(\.onAttach|\.onLoad)$' 108 | message: Don't slow down package load by running installed.packages() in ~~LOAD~~(). 109 | 110 | --- 111 | 112 | id: package_hooks-7 113 | language: r 114 | severity: warning 115 | rule: 116 | pattern: library.dynam.unload($$$) 117 | inside: 118 | stopBy: end 119 | kind: binary_operator 120 | has: 121 | stopBy: end 122 | field: lhs 123 | pattern: $LOAD 124 | constraints: 125 | LOAD: 126 | regex: '^(\.onDetach|\.Last\.lib)$' 127 | message: Use library.dynam.unload() calls in .onUnload(), not ~~LOAD~~(). 128 | -------------------------------------------------------------------------------- /flir/rules/builtin/paste.yml: -------------------------------------------------------------------------------- 1 | id: paste-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: 6 | context: paste($$$CONTENT sep = "" $$$CONTENT2) 7 | strictness: ast 8 | # fix: paste0($$$CONTENT) 9 | message: paste0(...) is better than paste(..., sep = ""). 10 | 11 | --- 12 | 13 | id: paste-2 14 | language: r 15 | severity: warning 16 | rule: 17 | any: 18 | - pattern: 19 | context: paste($CONTENT, collapse = ", ") 20 | strictness: ast 21 | - pattern: 22 | context: paste(collapse = ", ", $CONTENT) 23 | strictness: ast 24 | # fix: paste0($$$CONTENT) 25 | message: toString(.) is more expressive than paste(., collapse = ", "). 26 | 27 | --- 28 | 29 | id: paste-3 30 | language: r 31 | severity: warning 32 | rule: 33 | pattern: 34 | context: paste0($$$CONTENT sep = $USELESS $$$CONTENT2) 35 | strictness: ast 36 | # fix: paste0($$$CONTENT) 37 | message: | 38 | sep= is not a formal argument to paste0(); did you mean to use paste(), or 39 | collapse=? 40 | 41 | --- 42 | 43 | id: paste-4 44 | language: r 45 | severity: warning 46 | rule: 47 | any: 48 | - pattern: 49 | context: paste0($CONTENT, collapse = $FOO) 50 | strictness: ast 51 | - pattern: 52 | context: paste0(collapse = $FOO, $CONTENT) 53 | strictness: ast 54 | not: 55 | has: 56 | regex: sep 57 | kind: argument 58 | # fix: paste0($$$CONTENT) 59 | message: | 60 | Use paste(), not paste0(), to collapse a character vector when sep= is not used. 61 | 62 | # --- 63 | # 64 | # id: paste-5 65 | # language: r 66 | # severity: warning 67 | # rule: 68 | # pattern: 69 | # context: paste0(rep($VAR, $TIMES), collapse = "") 70 | # strictness: ast 71 | # constraints: 72 | # VAR: 73 | # kind: string 74 | # fix: strrep(~~VAR~~, ~~TIMES~~) 75 | # message: strrep(x, times) is better than paste0(rep(x, times), collapse = ""). 76 | -------------------------------------------------------------------------------- /flir/rules/builtin/redundant_equals.yml: -------------------------------------------------------------------------------- 1 | id: redundant_equals-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $VAR == TRUE 7 | - pattern: TRUE == $VAR 8 | - pattern: $VAR == FALSE 9 | - pattern: FALSE == $VAR 10 | message: | 11 | Using == on a logical vector is redundant. Well-named logical vectors can be 12 | used directly in filtering. For data.table's `i` argument, wrap the column 13 | name in (), like `DT[(is_treatment)]`. 14 | 15 | --- 16 | 17 | id: redundant_equals-2 18 | language: r 19 | severity: warning 20 | rule: 21 | any: 22 | - pattern: $VAR != TRUE 23 | - pattern: TRUE != $VAR 24 | - pattern: $VAR != FALSE 25 | - pattern: FALSE != $VAR 26 | message: | 27 | Using != on a logical vector is redundant. Well-named logical vectors can be 28 | used directly in filtering. For data.table's `i` argument, wrap the column 29 | name in (), like `DT[(is_treatment)]`. 30 | -------------------------------------------------------------------------------- /flir/rules/builtin/redundant_ifelse.yml: -------------------------------------------------------------------------------- 1 | id: redundant_ifelse-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN($COND, $VAL1, $VAL2) 6 | constraints: 7 | VAL1: 8 | regex: ^TRUE$ 9 | VAL2: 10 | regex: ^FALSE$ 11 | FUN: 12 | regex: ^(ifelse|fifelse|if_else)$ 13 | fix: ~~COND~~ 14 | message: | 15 | Use ~~COND~~ directly instead of calling ~~FUN~~(~~COND~~, TRUE, FALSE). 16 | 17 | --- 18 | 19 | id: redundant_ifelse-2 20 | language: r 21 | severity: warning 22 | rule: 23 | pattern: $FUN($COND, $VAL1, $VAL2) 24 | constraints: 25 | VAL1: 26 | regex: ^FALSE$ 27 | VAL2: 28 | regex: ^TRUE$ 29 | FUN: 30 | regex: ^(ifelse|fifelse|if_else)$ 31 | fix: '!(~~COND~~)' 32 | message: | 33 | Use !(~~COND~~) directly instead of calling ~~FUN~~(~~COND~~, FALSE, TRUE). 34 | 35 | --- 36 | 37 | id: redundant_ifelse-3 38 | language: r 39 | severity: warning 40 | rule: 41 | pattern: $FUN($COND, $VAL1, $VAL2) 42 | constraints: 43 | VAL1: 44 | regex: ^(1|1L)$ 45 | VAL2: 46 | regex: ^(0|0L)$ 47 | FUN: 48 | regex: ^(ifelse|fifelse|if_else)$ 49 | fix: as.integer(~~COND~~) 50 | message: Prefer as.integer(~~COND~~) to ~~FUN~~(~~COND~~, ~~VAL1~~, ~~VAL2~~). 51 | 52 | --- 53 | 54 | id: redundant_ifelse-4 55 | language: r 56 | severity: warning 57 | rule: 58 | pattern: $FUN($COND, $VAL1, $VAL2) 59 | constraints: 60 | VAL1: 61 | regex: ^(0|0L)$ 62 | VAL2: 63 | regex: ^(1|1L)$ 64 | FUN: 65 | regex: ^(ifelse|fifelse|if_else)$ 66 | fix: as.integer(!(~~COND~~)) 67 | message: Prefer as.integer(!(~~COND~~)) to ~~FUN~~(~~COND~~, ~~VAL1~~, ~~VAL2~~). 68 | -------------------------------------------------------------------------------- /flir/rules/builtin/rep_len.yml: -------------------------------------------------------------------------------- 1 | id: rep_len-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: rep($OBJ, length.out = $LEN) 6 | fix: rep_len(~~OBJ~~, ~~LEN~~) 7 | message: Use rep_len(x, n) instead of rep(x, length.out = n). 8 | -------------------------------------------------------------------------------- /flir/rules/builtin/right_assignment.yml: -------------------------------------------------------------------------------- 1 | id: right_assignment 2 | language: r 3 | severity: hint 4 | rule: 5 | pattern: $RHS -> $LHS 6 | has: 7 | field: rhs 8 | kind: identifier 9 | fix: ~~LHS~~<- ~~RHS~~ 10 | message: Use <-, not ->, for assignment. 11 | -------------------------------------------------------------------------------- /flir/rules/builtin/sample_int.yml: -------------------------------------------------------------------------------- 1 | id: sample_int-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: sample(1:$N, $$$OTHER) 7 | - pattern: sample(1L:$N, $$$OTHER) 8 | fix: sample.int(~~N~~, ~~OTHER~~) 9 | message: sample.int(n, m, ...) is preferable to sample(1:n, m, ...). 10 | 11 | --- 12 | 13 | id: sample_int-2 14 | language: r 15 | severity: warning 16 | rule: 17 | pattern: sample(seq($N), $$$OTHER) 18 | fix: sample.int(~~N~~, ~~OTHER~~) 19 | message: sample.int(n, m, ...) is preferable to sample(seq(n), m, ...). 20 | 21 | --- 22 | 23 | id: sample_int-3 24 | language: r 25 | severity: warning 26 | rule: 27 | pattern: sample(seq_len($N), $$$OTHER) 28 | fix: sample.int(~~N~~, ~~OTHER~~) 29 | message: sample.int(n, m, ...) is preferable to sample(seq_len(n), m, ...). 30 | 31 | --- 32 | 33 | # Strangely this panicks if I rename FIRST to N 34 | id: sample_int-4 35 | language: r 36 | severity: warning 37 | rule: 38 | pattern: sample($FIRST, $$$OTHER) 39 | constraints: 40 | FIRST: 41 | regex: ^\d+(L|)$ 42 | fix: sample.int(~~N~~, ~~OTHER~~) 43 | message: sample.int(n, m, ...) is preferable to sample(n, m, ...). 44 | -------------------------------------------------------------------------------- /flir/rules/builtin/semicolon.yml: -------------------------------------------------------------------------------- 1 | id: semicolon-1 2 | language: r 3 | severity: warning 4 | rule: 5 | regex: ;\s+$ 6 | not: 7 | inside: 8 | kind: string 9 | stopBy: end 10 | message: Trailing semicolons are not needed. 11 | -------------------------------------------------------------------------------- /flir/rules/builtin/seq.yml: -------------------------------------------------------------------------------- 1 | id: seq-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: seq(length($VAR)) 6 | fix: seq_along(~~VAR~~) 7 | message: | 8 | seq(length(...)) is likely to be wrong in the empty edge case. Use seq_along(...) instead. 9 | 10 | --- 11 | 12 | id: seq-2 13 | language: r 14 | severity: warning 15 | rule: 16 | any: 17 | - pattern: 1:nrow($VAR) 18 | - pattern: 1L:nrow($VAR) 19 | regex: ^1 20 | fix: seq_len(nrow(~~VAR~~)) 21 | message: | 22 | 1:nrow(...) is likely to be wrong in the empty edge case. Use seq_len(nrow(...)) instead. 23 | 24 | --- 25 | 26 | id: seq-3 27 | language: r 28 | severity: warning 29 | rule: 30 | any: 31 | - pattern: 1:n() 32 | - pattern: 1L:n() 33 | regex: ^1 34 | fix: seq_len(n()) 35 | message: | 36 | 1:n() is likely to be wrong in the empty edge case. Use seq_len(n()) instead. 37 | 38 | --- 39 | 40 | id: seq-4 41 | language: r 42 | severity: warning 43 | rule: 44 | pattern: seq(nrow($VAR)) 45 | fix: seq_len(nrow(~~VAR~~)) 46 | message: | 47 | seq(nrow(...)) is likely to be wrong in the empty edge case. Use seq_len(nrow(...)) instead. 48 | 49 | --- 50 | 51 | id: seq-5 52 | language: r 53 | severity: warning 54 | rule: 55 | any: 56 | - pattern: 1:length($VAR) 57 | - pattern: 1L:length($VAR) 58 | regex: ^1 59 | fix: seq_along(~~VAR~~) 60 | message: | 61 | 1:length(...) is likely to be wrong in the empty edge case. Use seq_along(...) instead. 62 | 63 | --- 64 | 65 | id: seq-6 66 | language: r 67 | severity: warning 68 | rule: 69 | any: 70 | - pattern: 1:ncol($VAR) 71 | - pattern: 1L:ncol($VAR) 72 | regex: ^1 73 | fix: seq_len(ncol(~~VAR~~)) 74 | message: | 75 | 1:ncol(...) is likely to be wrong in the empty edge case. Use seq_len(ncol(...)) instead. 76 | 77 | --- 78 | 79 | id: seq-7 80 | language: r 81 | severity: warning 82 | rule: 83 | any: 84 | - pattern: 1:NCOL($VAR) 85 | - pattern: 1L:NCOL($VAR) 86 | regex: ^1 87 | fix: seq_len(NCOL(~~VAR~~)) 88 | message: | 89 | 1:NCOL(...) is likely to be wrong in the empty edge case. Use seq_len(NCOL(...)) instead. 90 | 91 | --- 92 | 93 | id: seq-8 94 | language: r 95 | severity: warning 96 | rule: 97 | any: 98 | - pattern: 1:NROW($VAR) 99 | - pattern: 1L:NROW($VAR) 100 | regex: ^1 101 | fix: seq_len(NROW(~~VAR~~)) 102 | message: | 103 | 1:NROW(...) is likely to be wrong in the empty edge case. Use seq_len(NROW(...)) instead. 104 | 105 | 106 | --- 107 | 108 | id: seq-9 109 | language: r 110 | severity: warning 111 | rule: 112 | pattern: seq(1, $VAL) 113 | not: 114 | pattern: seq(1, 0) 115 | constraints: 116 | VAL: 117 | regex: ^\d+(|L)$ 118 | fix: seq_len(~~VAL~~) 119 | message: seq_len(~~VAL~~) is more efficient than seq(1, ~~VAL~~). 120 | 121 | 122 | -------------------------------------------------------------------------------- /flir/rules/builtin/sort.yml: -------------------------------------------------------------------------------- 1 | id: sort-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $OBJ[order($OBJ)] 6 | fix: sort(~~OBJ~~, na.last = TRUE) 7 | message: sort(~~OBJ~~, na.last = TRUE) is better than ~~OBJ~~[order(~~OBJ~~)]. 8 | 9 | --- 10 | 11 | id: sort-2 12 | language: r 13 | severity: warning 14 | rule: 15 | any: 16 | - pattern: $OBJ[order($OBJ, decreasing = $DECREASING)] 17 | - pattern: $OBJ[order(decreasing = $DECREASING, $OBJ)] 18 | constraints: 19 | DECREASING: 20 | regex: ^(TRUE|FALSE)$ 21 | fix: sort(~~OBJ~~, decreasing = ~~DECREASING~~, na.last = TRUE) 22 | message: | 23 | sort(~~OBJ~~, decreasing = ~~DECREASING~~, na.last = TRUE) is better than 24 | ~~OBJ~~[order(~~OBJ~~, decreasing = ~~DECREASING~~)]. 25 | 26 | --- 27 | 28 | id: sort-3 29 | language: r 30 | severity: warning 31 | rule: 32 | any: 33 | - pattern: $OBJ[order($OBJ, na.last = $NALAST)] 34 | - pattern: $OBJ[order(na.last = $NALAST, $OBJ)] 35 | constraints: 36 | NALAST: 37 | regex: ^(TRUE|FALSE)$ 38 | fix: sort(~~OBJ~~, na.last = ~~NALAST~~, na.last = TRUE) 39 | message: | 40 | sort(~~OBJ~~, na.last = ~~NALAST~~, na.last = TRUE) is better than 41 | ~~OBJ~~[order(~~OBJ~~, na.last = ~~NALAST~~)]. 42 | 43 | --- 44 | 45 | id: sort-4 46 | language: r 47 | severity: warning 48 | rule: 49 | any: 50 | - pattern: $OBJ[order($OBJ, decreasing = TRUE, na.last = FALSE)] 51 | - pattern: $OBJ[order($OBJ, na.last = FALSE, decreasing = TRUE)] 52 | - pattern: $OBJ[order(decreasing = TRUE, $OBJ, na.last = FALSE)] 53 | - pattern: $OBJ[order(decreasing = TRUE, na.last = FALSE, $OBJ)] 54 | - pattern: $OBJ[order(na.last = FALSE, decreasing = TRUE, $OBJ)] 55 | - pattern: $OBJ[order(na.last = FALSE, $OBJ, decreasing = TRUE)] 56 | fix: sort(~~OBJ~~, decreasing = TRUE, na.last = FALSE) 57 | message: | 58 | sort(~~OBJ~~, decreasing = TRUE, na.last = FALSE) is better than 59 | ~~OBJ~~[order(~~OBJ~~, na.last = FALSE, decreasing = TRUE)]. 60 | 61 | --- 62 | 63 | id: sort-5 64 | language: r 65 | severity: warning 66 | rule: 67 | any: 68 | - pattern: sort($OBJ) == $OBJ 69 | - pattern: $OBJ == sort($OBJ) 70 | fix: !is.unsorted(~~OBJ~~) 71 | message: | 72 | Use !is.unsorted(~~OBJ~~) to test the sortedness of a vector. 73 | 74 | --- 75 | 76 | id: sort-6 77 | language: r 78 | severity: warning 79 | rule: 80 | any: 81 | - pattern: sort($OBJ) != $OBJ 82 | - pattern: $OBJ != sort($OBJ) 83 | fix: is.unsorted(~~OBJ~~) 84 | message: | 85 | Use is.unsorted(~~OBJ~~) to test the unsortedness of a vector. 86 | -------------------------------------------------------------------------------- /flir/rules/builtin/stopifnot_all.yml: -------------------------------------------------------------------------------- 1 | id: stopifnot_all-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: stopifnot(all($$$CODE)) 6 | fix: stopifnot(~~CODE~~) 7 | message: | 8 | Use stopifnot(x) instead of stopifnot(all(x)). stopifnot(x) runs all() 'under 9 | the hood' and provides a better error message in case of failure. 10 | 11 | --- 12 | 13 | id: stopifnot_all-2 14 | language: r 15 | severity: warning 16 | rule: 17 | pattern: stopifnot(exprs = { all($$$CODE) }) 18 | fix: | 19 | stopifnot(exprs = { 20 | ~~CODE~~ 21 | }) 22 | message: | 23 | Use stopifnot(x) instead of stopifnot(all(x)). stopifnot(x) runs all() 'under 24 | the hood' and provides a better error message in case of failure. 25 | -------------------------------------------------------------------------------- /flir/rules/builtin/todo_comment.yml: -------------------------------------------------------------------------------- 1 | id: todo_comment-1 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: comment 6 | regex: '(?i)#(|\s+)\b(todo|fixme)\b' 7 | message: Remove TODO comments. 8 | -------------------------------------------------------------------------------- /flir/rules/builtin/undesirable_function.yml: -------------------------------------------------------------------------------- 1 | id: undesirable_function-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: $FUN 6 | kind: identifier 7 | not: 8 | inside: 9 | kind: argument 10 | constraints: 11 | FUN: 12 | regex: ^(\.libPaths|attach|browser|debug|debugcall|debugonce|detach|par|setwd|structure|Sys\.setenv|Sys\.setlocale|trace|undebug|untrace)$ 13 | message: Function "~~FUN~~()" is undesirable. 14 | -------------------------------------------------------------------------------- /flir/rules/builtin/undesirable_operator.yml: -------------------------------------------------------------------------------- 1 | id: undesirable_operator-1 2 | language: r 3 | severity: warning 4 | rule: 5 | any: 6 | - pattern: $X <<- $Y 7 | - pattern: $X ->> $Y 8 | message: | 9 | Avoid undesirable operators `<<-` and `->>`. They assign outside the current 10 | environment in a way that can be hard to reason about. Prefer fully-encapsulated 11 | functions wherever possible, or, if necessary, assign to a specific environment 12 | with assign(). Recall that you can create an environment at the desired scope 13 | with new.env(). 14 | 15 | --- 16 | 17 | id: undesirable_operator-2 18 | language: r 19 | severity: warning 20 | rule: 21 | kind: namespace_operator 22 | has: 23 | pattern: ':::' 24 | message: | 25 | Operator `:::` is undesirable. It accesses non-exported functions inside 26 | packages. Code relying on these is likely to break in future versions of the 27 | package because the functions are not part of the public interface and may be 28 | changed or removed by the maintainers without notice. Use public functions 29 | via :: instead. 30 | -------------------------------------------------------------------------------- /flir/rules/builtin/unnecessary_nesting.yml: -------------------------------------------------------------------------------- 1 | id: unnecessary_nesting-1 2 | language: r 3 | severity: warning 4 | rule: 5 | kind: if_statement 6 | any: 7 | - has: 8 | kind: 'braced_expression' 9 | field: consequence 10 | has: 11 | kind: if_statement 12 | stopBy: neighbor 13 | not: 14 | has: 15 | kind: 'braced_expression' 16 | field: alternative 17 | stopBy: end 18 | not: 19 | any: 20 | - has: 21 | nthChild: 2 22 | - precedes: 23 | regex: "^else$" 24 | - has: 25 | kind: if_statement 26 | field: consequence 27 | stopBy: neighbor 28 | # Can be in if(), but not else if() 29 | not: 30 | inside: 31 | field: alternative 32 | kind: if_statement 33 | message: | 34 | Don't use nested `if` statements, where a single `if` with the combined 35 | conditional expression will do. For example, instead of `if (x) { if (y) { ... }}`, 36 | use `if (x && y) { ... }`. 37 | -------------------------------------------------------------------------------- /flir/rules/builtin/unreachable_code.yml: -------------------------------------------------------------------------------- 1 | id: unreachable_code-1 2 | language: r 3 | severity: warning 4 | rule: 5 | regex: '[^}]+' 6 | not: 7 | regex: 'else' 8 | follows: 9 | any: 10 | - pattern: return($$$A) 11 | - pattern: stop($$$A) 12 | not: 13 | precedes: 14 | regex: 'else' 15 | stopBy: end 16 | message: Code and comments coming after a return() or stop() should be removed. 17 | 18 | --- 19 | 20 | id: unreachable_code-2 21 | language: r 22 | severity: warning 23 | rule: 24 | regex: '[^}]+' 25 | not: 26 | regex: 'else' 27 | follows: 28 | any: 29 | - pattern: next 30 | - pattern: break 31 | stopBy: end 32 | message: Remove code and comments coming after `next` or `break` 33 | 34 | --- 35 | 36 | id: unreachable_code-3 37 | language: r 38 | severity: warning 39 | rule: 40 | inside: 41 | any: 42 | - kind: if_statement 43 | pattern: if (FALSE) 44 | - kind: while_statement 45 | pattern: while (FALSE) 46 | stopBy: end 47 | message: Remove code inside a conditional loop with a deterministically false condition. 48 | 49 | --- 50 | 51 | id: unreachable_code-4 52 | language: r 53 | severity: warning 54 | rule: 55 | inside: 56 | any: 57 | - kind: if_statement 58 | pattern: if (TRUE) 59 | - kind: while_statement 60 | pattern: while (TRUE) 61 | stopBy: end 62 | message: | 63 | One branch has a a deterministically true condition. The other branches can 64 | be removed. 65 | -------------------------------------------------------------------------------- /flir/rules/builtin/which_grepl.yml: -------------------------------------------------------------------------------- 1 | id: which_grepl-1 2 | language: r 3 | severity: warning 4 | rule: 5 | pattern: which(grepl($$$ARGS)) 6 | fix: grep(~~ARGS~~) 7 | message: grep(pattern, x) is better than which(grepl(pattern, x)). 8 | -------------------------------------------------------------------------------- /getCRUCLdata.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: b876aac1-663c-4e95-b4b7-a129e578fd9b 3 | 4 | RestoreWorkspace: Default 5 | SaveWorkspace: Default 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | bibentry(bibtype = "Article", 2 | key = "Sparks2017", 3 | author = person(given = c("Adam", "H"), 4 | family = "Sparks"), 5 | title = "{getCRUCLdata}: Use and Explore {CRU CL} v. 2.0 Climatology Elements in {R}", 6 | journal = "Journal of Open Source Software", 7 | year = "2017", 8 | publisher = "The Open Journal", 9 | volume = "2", 10 | number = "12", 11 | pages = "230", 12 | doi = "10.21105/joss.00230", 13 | url = "https://doi.org/10.21105/joss.00230") 14 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | AAGI 2 | CMD 3 | CRU 4 | Centre 5 | Curtin 6 | DOI 7 | GRDC 8 | Hulme 9 | JOSS 10 | Makin 11 | NOTEs 12 | OPX 13 | ORCID 14 | PURRR 15 | Precompile 16 | README 17 | ROxygen 18 | Reknit 19 | Reorganises 20 | Sri 21 | TJ 22 | adamhsparks 23 | al 24 | arcminutes 25 | behaviour 26 | biogeochemical 27 | centred 28 | cli 29 | codecov 30 | colour 31 | cv 32 | dat 33 | dec 34 | dplyr 35 | dsn 36 | dtr 37 | elv 38 | et 39 | frs 40 | geotiff 41 | getCRUCL 42 | getCRUdata 43 | ggplot 44 | globals 45 | gridded 46 | gz 47 | hoardr 48 | ivanhanigan 49 | jan 50 | jeroen 51 | karthik 52 | kilometres 53 | magrittr 54 | maintainer's 55 | md 56 | metre 57 | metres 58 | mhesselbarth 59 | millimetres 60 | modelled 61 | modelling 62 | pkgdown 63 | plyr 64 | pre 65 | purrr 66 | rOpenSci 67 | rast 68 | readme 69 | readr 70 | reh 71 | roxyglobals 72 | sckott 73 | sp 74 | sunjj 75 | sunp 76 | terra 77 | tibble 78 | tidyr 79 | tmax 80 | tmin 81 | tmn 82 | tmp 83 | tmx 84 | viridis 85 | visualise 86 | windspeed 87 | wnd 88 | xmax 89 | xmin 90 | ymax 91 | ymin 92 | -------------------------------------------------------------------------------- /inst/paper/paper.bib: -------------------------------------------------------------------------------- 1 | @article{New2002, 2 | author = {New, M and Lister, D and Hulme, M and Makin, I A}, 3 | doi = {10.3354/cr021001}, 4 | issn = {0936-577X}, 5 | journal = {Climate Research}, 6 | keywords = {applied climatology,cru,high resolution data set,iwmi climate atlas,surface climate}, 7 | mendeley-groups = {KSU.002}, 8 | pages = {1--25}, 9 | title = {{A high-resolution data set of surface climate over global land areas}}, 10 | url = {http://www.int-res.com/abstracts/cr/v21/n1/p1-25/}, 11 | volume = {21}, 12 | year = {2002} 13 | } 14 | 15 | @Manual{R-base, 16 | title = {R: A Language and Environment for Statistical Computing}, 17 | author = {{R Core Team}}, 18 | organization = {R Foundation for Statistical Computing}, 19 | address = {Vienna, Austria}, 20 | year = {2016}, 21 | url = {https://www.R-project.org/} 22 | } 23 | 24 | 25 | @Manual{Raster, 26 | title = {raster: Geographic Data Analysis and Modeling}, 27 | author = {Robert J. Hijmans}, 28 | year = {2016}, 29 | note = {R package version 2.5-8}, 30 | url = {https://CRAN.R-project.org/package=raster}, 31 | } 32 | 33 | @article{Wickham2014, 34 | author = {Hadley Wickham}, 35 | title = {Tidy Data}, 36 | journal = {Journal of Statistical Software}, 37 | volume = {59}, 38 | number = {1}, 39 | year = {2014}, 40 | keywords = {}, 41 | abstract = {A huge amount of effort is spent cleaning data to get it ready for analysis, but there has been little research on how to make data cleaning as easy and effective as possible. This paper tackles a small, but important, component of data cleaning: data tidying. Tidy datasets are easy to manipulate, model and visualize, and have a specific structure: each variable is a column, each observation is a row, and each type of observational unit is a table. This framework makes it easy to tidy messy datasets because only a small set of tools are needed to deal with a wide range of un-tidy datasets. This structure also makes it easier to develop tidy tools for data analysis, tools that both input and output tidy datasets. The advantages of a consistent data structure and matching tools are demonstrated with a case study free from mundane data manipulation chores.}, 42 | issn = {1548-7660}, 43 | pages = {1--23}, 44 | doi = {10.18637/jss.v059.i10}, 45 | url = {https://www.jstatsoft.org/index.php/jss/article/view/v059i10} 46 | } 47 | 48 | @Manual{Wickham2017, 49 | title = {tibble: Simple Data Frames}, 50 | author = {Hadley Wickham and Romain Francois and Kirill Müller}, 51 | year = {2017}, 52 | note = {R package version 1.3.0}, 53 | url = {https://CRAN.R-project.org/package=tibble}, 54 | } 55 | -------------------------------------------------------------------------------- /inst/paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'getCRUCLdata: Use and Explore CRU CL v. 2.0 Climatology Elements in R' 3 | authors: 4 | - affiliation: 1 5 | name: Adam H Sparks 6 | orcid: 0000-0002-0061-8359 7 | output: pdf_document 8 | tags: 9 | - climate 10 | - R 11 | - applied climatology 12 | - high resolution surface 13 | - data 14 | affiliations: 15 | index: 1 16 | name: University of Southern Queensland, Centre for Crop Health, Toowoomba Queensland 4350, Australia 17 | bibliography: paper.bib 18 | date: "04 April 2017" 19 | 20 | --- 21 | 22 | # Summary 23 | 24 | The CRU CL v. 2.0 data are a gridded climatology of 1961-1990 monthly means released in 2002 and cover all land areas (excluding Antarctica) at 10 arcminutes (0.1666667 degree) resolution [@New2002] providing precipitation, cv of precipitation, wet-days, mean temperature, mean diurnal temperature range, relative humidity, sunshine, ground-frost, windspeed and elevation. While these data have a high resolution and are freely available, the data format can be cumbersome for working with. Four functions are provided by _getCRUCLdata_ that automate importing these data into R [@R-base]. All of the functions facilitate the calculation of minimum temperature and maximum temperature, and format the data into a tidy data frame [@Wickham2014] in a _tibble_ [@Wickham2017] object or a list of _raster_ stack objects [@Raster] for use in R or easily exported to a raster format file for use in a geographic information system (GIS). Two functions, `get_CRU_df()` and `get_CRU_stack()` provide the ability to easily download CRU CL v. 2.0 data from the CRU website and import the data into R and allow for caching downloaded data. The other two functions, `create_CRU_df()` and `create_CRU_stack()` allow the user to easily import the data files from a local disk location and transform them into a tidy data frame _tibble_ or _raster_ stack. The data have applications in applied climatology, biogeochemical modelling, hydrology and agricultural meteorology [@New2002]. 25 | 26 | # References 27 | -------------------------------------------------------------------------------- /inst/paper/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/inst/paper/paper.pdf -------------------------------------------------------------------------------- /man/create_CRU_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/create_CRU_df.R 3 | \name{create_CRU_df} 4 | \alias{create_CRU_df} 5 | \alias{create_cru_df} 6 | \title{Create a data.table of CRU CL v. 2.0 climatology elements from local disk files} 7 | \source{ 8 | \describe{ 9 | \item{pre}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_pre.dat.gz}} 10 | \item{rd0}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_rd0.dat.gz}} 11 | \item{tmp}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_tmp.dat.gz}} 12 | \item{dtr}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_dtr.dat.gz}} 13 | \item{reh}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_reh.dat.gz}} 14 | \item{sunp}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_sunp.dat.gz}} 15 | \item{frs}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_frs.dat.gz}} 16 | \item{wnd}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_wnd.dat.gz}, areas originally including Antarctica are removed.} 17 | \item{elv}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_elv.dat.gz}, values are converted from kilometres to metres.} 18 | } 19 | This package crops all spatial outputs to an extent of ymin = -60, ymax = 85, 20 | xmin = -180, xmax = 180. 21 | } 22 | \usage{ 23 | create_CRU_df( 24 | pre = FALSE, 25 | pre_cv = FALSE, 26 | rd0 = FALSE, 27 | tmp = FALSE, 28 | dtr = FALSE, 29 | reh = FALSE, 30 | tmn = FALSE, 31 | tmx = FALSE, 32 | sunp = FALSE, 33 | frs = FALSE, 34 | wnd = FALSE, 35 | elv = FALSE, 36 | dsn 37 | ) 38 | 39 | create_cru_df( 40 | pre = FALSE, 41 | pre_cv = FALSE, 42 | rd0 = FALSE, 43 | tmp = FALSE, 44 | dtr = FALSE, 45 | reh = FALSE, 46 | tmn = FALSE, 47 | tmx = FALSE, 48 | sunp = FALSE, 49 | frs = FALSE, 50 | wnd = FALSE, 51 | elv = FALSE, 52 | dsn 53 | ) 54 | } 55 | \arguments{ 56 | \item{pre}{Loads precipitation (millimetres/month) from server and 57 | returns in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 58 | 59 | \item{pre_cv}{Loads cv of precipitation (percent) from server and 60 | returns in the data frame, \code{TRUE}. Defaults to \code{FALSE}. NOTE. Setting this 61 | to \code{TRUE} will always results in \strong{pre} being set to \code{TRUE} and 62 | returned as well.} 63 | 64 | \item{rd0}{Loads wet-days (number days with >0.1 millimetres rain per 65 | month) and returns in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 66 | 67 | \item{tmp}{Loads temperature (degrees Celsius) and returns it in the 68 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 69 | 70 | \item{dtr}{Loads mean diurnal temperature range (degrees Celsius) 71 | and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 72 | 73 | \item{reh}{Loads relative humidity and returns it in the data frame, \code{TRUE}. 74 | Defaults to \code{FALSE}.} 75 | 76 | \item{tmn}{Calculate minimum temperature values (degrees Celsius) 77 | and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 78 | 79 | \item{tmx}{Calculate maximum temperature (degrees Celsius) and 80 | return it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 81 | 82 | \item{sunp}{Loads sunshine, percent of maximum possible (percent of 83 | day length) and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 84 | 85 | \item{frs}{Loads ground-frost records (number of days with ground- 86 | frost per month) and returns it in the data frame, \code{TRUE}. Defaults to 87 | \code{FALSE}.} 88 | 89 | \item{wnd}{Load 10 m wind speed (metres/second) and returns it in the 90 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 91 | 92 | \item{elv}{Loads elevation (converted to metres) and returns it in 93 | the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 94 | 95 | \item{dsn}{Local file path where \acronym{CRU} \acronym{CL} v.2.0 .dat.gz 96 | files are located.} 97 | } 98 | \value{ 99 | A \link[data.table:data.table]{data.table::data.table} object of \acronym{CRU} \acronym{CL} v. 100 | 2.0 climatology elements. 101 | } 102 | \description{ 103 | Automates importing \acronym{CRU} \acronym{CL} v.2.0 climatology 104 | data and creates a \CRANpkg{data.table} of the data. If requested, minimum 105 | and maximum temperature may also be automatically calculated as described in 106 | the data \href{https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt}{readme.txt} 107 | file. Data may be cached for later use by this function, saving time 108 | downloading files in future using this function. This function can be useful 109 | if you have network connection issues that mean automated downloading of the 110 | files using \R does not work properly. 111 | } 112 | \section{Nomenclature and Units}{ 113 | 114 | \describe{ 115 | \item{pre}{precipitation (millimetres/month)} 116 | \describe{ 117 | \item{cv}{cv of precipitation (percent)} 118 | } 119 | \item{rd0}{wet-days (number days with >0.1 millimetres rain per month)} 120 | \item{tmp}{mean temperature (degrees Celsius)} 121 | \item{dtr}{mean diurnal temperature range (degrees Celsius)} 122 | \item{reh}{relative humidity (percent)} 123 | \item{sunp}{sunshine (percent of maximum possible (percent of day length))} 124 | \item{frs}{ground-frost (number of days with ground-frost per month)} 125 | \item{wnd}{10 metre windspeed (metres/second)} 126 | \item{elv}{elevation (automatically converted to metres)} 127 | } 128 | For more information see the description of the data provided by 129 | \acronym{CRU}, \url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt} 130 | } 131 | 132 | \examples{ 133 | \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 134 | # Create a data frame of temperature from locally available files in the 135 | # tempdir() directory. 136 | 137 | download.file( 138 | url = "https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_tmp.dat.gz", 139 | destfile = file.path(tempdir(), "grid_10min_tmp.dat.gz") 140 | ) 141 | 142 | CRU_tmp <- create_CRU_df(tmp = TRUE, dsn = tempdir()) 143 | 144 | CRU_tmp 145 | \dontshow{\}) # examplesIf} 146 | } 147 | \references{ 148 | New, Mark, et al. "A high-resolution data set of surface climate 149 | over global land areas." Climate research 21.1 (2002): 1-25. 150 | \url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/new_et_al_10minute_climate_CR.pdf} 151 | } 152 | \seealso{ 153 | \link{get_CRU_df}. 154 | } 155 | \author{ 156 | Adam H. Sparks, \email{adamhsparks@gmail.com} 157 | } 158 | -------------------------------------------------------------------------------- /man/dot-check_vars_FALSE.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.check_vars_FALSE} 4 | \alias{.check_vars_FALSE} 5 | \title{Check that at least one element is requested} 6 | \usage{ 7 | .check_vars_FALSE( 8 | pre, 9 | pre_cv, 10 | rd0, 11 | tmp, 12 | dtr, 13 | reh, 14 | tmn, 15 | tmx, 16 | sunp, 17 | frs, 18 | wnd, 19 | elv 20 | ) 21 | } 22 | \arguments{ 23 | \item{pre}{Fetches precipitation (millimetres/month) from server and 24 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 25 | 26 | \item{pre_cv}{Fetches cv of precipitation (percent) from server and 27 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}. Note, setting 28 | this to \code{TRUE} will always results in \strong{pre} being set to \code{TRUE} and 29 | returned as well.} 30 | 31 | \item{rd0}{Fetches wet-days (number days with >0.1 millimetres rain per 32 | month) and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 33 | 34 | \item{tmp}{Fetches temperature (degrees Celsius) and returns it in the 35 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 36 | 37 | \item{dtr}{Fetches mean diurnal temperature range (degrees Celsius) 38 | and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 39 | 40 | \item{reh}{Fetches relative humidity and returns it in the data frame, 41 | \code{TRUE}. Defaults to \code{FALSE}.} 42 | 43 | \item{tmn}{Calculates minimum temperature values (degrees Celsius) 44 | and returns it in the data frame. Defaults to \code{FALSE}.} 45 | 46 | \item{tmx}{Boolean. Calculates maximum temperature (degrees Celsius) and 47 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 48 | 49 | \item{sunp}{Fetches sunshine, percent of maximum possible (percent of 50 | day length), and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 51 | 52 | \item{frs}{Boolean. Fetches ground-frost records (number of days with 53 | ground-frost per month) and returns it in data frame, \code{TRUE}. Defaults to 54 | \code{FALSE}.} 55 | 56 | \item{wnd}{Fetches 10m wind speed (metres/second) and returns it in the data 57 | frame, \code{TRUE}. Defaults to \code{FALSE}.} 58 | 59 | \item{elv}{Fetches elevation (converted to metres) and returns it in the 60 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 61 | } 62 | \description{ 63 | Check that at least one element is requested 64 | } 65 | \examples{ 66 | .check_vars_FALSE( 67 | pre, 68 | pre_cv, 69 | rd0, 70 | tmp, 71 | dtr, 72 | reh, 73 | tmn, 74 | tmx, 75 | sunp, 76 | frs, 77 | wnd, 78 | elv 79 | ) 80 | } 81 | \keyword{internal} 82 | -------------------------------------------------------------------------------- /man/dot-create_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.create_df} 4 | \alias{.create_df} 5 | \title{Creates a data.table from the CRU data} 6 | \usage{ 7 | .create_df(tmn, tmx, tmp, dtr, pre, pre_cv, elv, files) 8 | } 9 | \arguments{ 10 | \item{tmn}{Is tmx to be calculated? Boolean.} 11 | 12 | \item{dtr}{Is dtr to be returned? Boolean.} 13 | 14 | \item{pre}{Is pre to be returned? Boolean.} 15 | 16 | \item{pre_cv}{Is pre_cv to be returned? Boolean.} 17 | 18 | \item{elv}{Is elv to be returned? Boolean.} 19 | 20 | \item{files}{File list to be used for creating data frame.} 21 | } 22 | \value{ 23 | A \CRANpkg{data.table} of all requested values. 24 | } 25 | \description{ 26 | Creates a data.table from the CRU data 27 | } 28 | \keyword{internal} 29 | -------------------------------------------------------------------------------- /man/dot-create_stack.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.create_stack} 4 | \alias{.create_stack} 5 | \title{Helper Function Used in .create_stacks()} 6 | \usage{ 7 | .create_stack(files, wrld, month_names, pre, pre_cv) 8 | } 9 | \arguments{ 10 | \item{files}{A list of files to use in creating \code{rast} objects.} 11 | 12 | \item{wrld}{An empty \link[terra:rast]{terra::rast} object for filling with values.} 13 | 14 | \item{month_names}{A vector of month names from jan -- dec.} 15 | 16 | \item{pre}{\code{Boolean} include precipitation.} 17 | 18 | \item{pre_cv}{\code{Boolean} include precipitation cv.} 19 | } 20 | \description{ 21 | Helper Function Used in .create_stacks() 22 | } 23 | \keyword{internal} 24 | -------------------------------------------------------------------------------- /man/dot-create_stacks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.create_stacks} 4 | \alias{.create_stacks} 5 | \title{Create terra rast objects} 6 | \usage{ 7 | .create_stacks(tmn, tmx, tmp, dtr, pre, pre_cv, files) 8 | } 9 | \arguments{ 10 | \item{tmn}{Return minimum temperature values (degrees Celsius) 11 | in the \code{rast}, Boolean.} 12 | 13 | \item{tmx}{Return maximum temperature (degrees Celsius) in the 14 | \code{rast}, Boolean.} 15 | 16 | \item{tmp}{Return temperature (degrees Celsius) in the \code{rast}, Boolean.} 17 | 18 | \item{dtr}{Return mean diurnal temperature range (degrees Celsius) 19 | in the \code{rast}, Boolean.} 20 | 21 | \item{pre}{Return precipitation in the \code{rast}, Boolean.} 22 | 23 | \item{pre_cv}{Return cv of precipitation (percent) in the \code{rast}, Boolean.} 24 | 25 | \item{files}{List. Files that are to be used in creating the \code{rast} object.} 26 | } 27 | \description{ 28 | Create terra rast objects 29 | } 30 | \keyword{internal} 31 | -------------------------------------------------------------------------------- /man/dot-get_CRU.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_CRU.R 3 | \name{.get_CRU} 4 | \alias{.get_CRU} 5 | \title{Downloads and formats CRU CL 2.0 data} 6 | \usage{ 7 | .get_CRU( 8 | pre, 9 | pre_cv, 10 | rd0, 11 | tmp, 12 | dtr, 13 | reh, 14 | tmn, 15 | tmx, 16 | sunp, 17 | frs, 18 | wnd, 19 | elv, 20 | cache_dir 21 | ) 22 | } 23 | \arguments{ 24 | \item{pre}{Logical. If TRUE, downloads precipitation data.} 25 | 26 | \item{pre_cv}{Logical. If TRUE, downloads precipitation data.} 27 | 28 | \item{rd0}{Logical. If TRUE, downloads runoff data.} 29 | 30 | \item{tmp}{Logical. If TRUE, downloads temperature data.} 31 | 32 | \item{dtr}{Logical. If TRUE, downloads diurnal temperature range data.} 33 | 34 | \item{reh}{Logical. If TRUE, downloads relative humidity data.} 35 | 36 | \item{tmn}{Logical. If TRUE, downloads minimum temperature data.} 37 | 38 | \item{tmx}{Logical. If TRUE, downloads maximum temperature data.} 39 | 40 | \item{sunp}{Logical. If TRUE, downloads sunshine data.} 41 | 42 | \item{frs}{Logical. If TRUE, downloads frost day frequency data.} 43 | 44 | \item{wnd}{Logical. If TRUE, downloads wind speed data.} 45 | 46 | \item{elv}{Logical. If TRUE, downloads elevation data.} 47 | 48 | \item{cache_dir}{Character. Path to the cache directory. 49 | 50 | Handles the downloading of CRU CL 2.0 data. This function is called by 51 | \link{get_cru_df} and \link{get_cru_stack}. It is not intended to be called directly.} 52 | } 53 | \value{ 54 | A data.table with the requested data. 55 | } 56 | \description{ 57 | Downloads and formats CRU CL 2.0 data 58 | } 59 | \keyword{internal} 60 | -------------------------------------------------------------------------------- /man/dot-get_local.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.get_local} 4 | \alias{.get_local} 5 | \title{Create a List of Locally Cached Files for Import} 6 | \usage{ 7 | .get_local( 8 | pre, 9 | pre_cv, 10 | rd0, 11 | tmp, 12 | dtr, 13 | reh, 14 | tmn, 15 | tmx, 16 | sunp, 17 | frs, 18 | wnd, 19 | elv, 20 | cache_dir 21 | ) 22 | } 23 | \arguments{ 24 | \item{pre}{Boolean, loads precipitation (millimetres/month) from server and 25 | returns in the data frame.} 26 | 27 | \item{pre_cv}{Boolean, loads cv of precipitation (percent) from server and 28 | returns in the data frame.} 29 | 30 | \item{rd0}{Boolean, loads wet-days (number days with >0.1 millimetres rain 31 | per month) and returns in the data frame.} 32 | 33 | \item{tmp}{Boolean, loads temperature (degrees Celsius) and returns it in 34 | the data frame.} 35 | 36 | \item{dtr}{Boolean, loads mean diurnal temperature range (degrees Celsius) 37 | and returns it in the data frame.} 38 | 39 | \item{reh}{Boolean, loads relative humidity and return it in the data frame.} 40 | 41 | \item{tmn}{Boolean, calculates minimum temperature values (degrees Celsius) 42 | and returns it in the data frame.} 43 | 44 | \item{tmx}{Boolean, calculate maximum temperature (degrees Celsius) and 45 | returns it in the data frame.} 46 | 47 | \item{sunp}{Boolean, loads sunshine, percent of maximum possible (percent of 48 | day length) and returns it in data frame.} 49 | 50 | \item{frs}{Boolean, loads ground-frost records (number of days with ground- 51 | frost per month) and return it in data frame.} 52 | 53 | \item{wnd}{Boolean, loads 10m wind speed (metres/second) and returns it in 54 | the data frame.} 55 | 56 | \item{elv}{Boolean, loads elevation (converted to metres) and returns it in 57 | the data frame.} 58 | } 59 | \description{ 60 | Create a List of Locally Cached Files for Import 61 | } 62 | \keyword{internal} 63 | -------------------------------------------------------------------------------- /man/dot-read_cache.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.read_cache} 4 | \alias{.read_cache} 5 | \title{Read Files From Local cache} 6 | \usage{ 7 | .read_cache(.files, .pre_cv) 8 | } 9 | \arguments{ 10 | \item{.files}{a list of CRU CL2.0 files in local storage.} 11 | 12 | \item{.pre_cv}{\code{Boolean} return pre_cv in the data.} 13 | } 14 | \description{ 15 | Read Files From Local cache 16 | } 17 | \keyword{internal} 18 | -------------------------------------------------------------------------------- /man/dot-set_cache.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.set_cache} 4 | \alias{.set_cache} 5 | \title{Set Up User Cache} 6 | \usage{ 7 | .set_cache(cache) 8 | } 9 | \arguments{ 10 | \item{cache}{\code{Boolean} (create) and use local file cache.} 11 | } 12 | \description{ 13 | Creates local directory for caching and/or uses it for local caching or 14 | uses the \R session \code{tempdir()}. 15 | } 16 | \keyword{internal} 17 | -------------------------------------------------------------------------------- /man/dot-tidy_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.tidy_df} 4 | \alias{.tidy_df} 5 | \title{Read Files from Disk Directory and Tidy Them} 6 | \usage{ 7 | .tidy_df(pre_cv, elv, tmn, tmx, .files) 8 | } 9 | \description{ 10 | Read Files from Disk Directory and Tidy Them 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /man/dot-validate_dsn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internal_functions.R 3 | \name{.validate_dsn} 4 | \alias{.validate_dsn} 5 | \title{Validates user entered dsn value} 6 | \usage{ 7 | .validate_dsn(dsn) 8 | } 9 | \arguments{ 10 | \item{dsn}{User provided value for checking.} 11 | } 12 | \description{ 13 | Validates user entered dsn value 14 | } 15 | \keyword{internal} 16 | -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-10-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/man/figures/README-unnamed-chunk-10-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-11-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/man/figures/README-unnamed-chunk-11-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-6-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/man/figures/README-unnamed-chunk-6-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-7-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/man/figures/README-unnamed-chunk-7-1.png -------------------------------------------------------------------------------- /man/getCRUCLdata-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getCRUCLdata-package.R 3 | \docType{package} 4 | \name{getCRUCLdata-package} 5 | \alias{getCRUCLdata} 6 | \alias{getCRUCLdata-package} 7 | \title{getCRUCLdata: 'CRU' 'CL' v. 2.0 Climatology Client} 8 | \description{ 9 | Provides functions that automate downloading and importing University of East Anglia Climate Research Unit ('CRU') 'CL' v. 2.0 climatology data, facilitates the calculation of minimum temperature and maximum temperature and formats the data into a data.table object or a list of 'terra' 'rast' objects for use. 'CRU' 'CL' v. 2.0 data are a gridded climatology of 1961-1990 monthly means released in 2002 and cover all land areas (excluding Antarctica) at 10 arc minutes (0.1666667 degree) resolution. For more information see the description of the data provided by the University of East Anglia Climate Research Unit, \url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt}. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/ropensci/getCRUCLdata} 15 | \item \url{https://docs.ropensci.org/getCRUCLdata/} 16 | \item Report bugs at \url{https://github.com/ropensci/getCRUCLdata/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Adam H. Sparks \email{adamhsparks@gmail.com} (\href{https://orcid.org/0000-0002-0061-8359}{ORCID}) 22 | 23 | Other contributors: 24 | \itemize{ 25 | \item Curtin University of Technology (Provided support through Adam Sparks's time.) [copyright holder] 26 | \item Grains Research and Development Corporation (GRDC Project CUR2210-005OPX (AAGI-CU)) [copyright holder] 27 | } 28 | 29 | } 30 | \keyword{internal} 31 | -------------------------------------------------------------------------------- /man/get_CRU_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_CRU_df.R 3 | \name{get_CRU_df} 4 | \alias{get_CRU_df} 5 | \alias{get_cru_df} 6 | \title{Download and create a data.table of CRU CL v. 2.0 climatology elements} 7 | \source{ 8 | \describe{ 9 | \item{pre}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_pre.dat.gz}} 10 | \item{rd0}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_rd0.dat.gz}} 11 | \item{tmp}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_tmp.dat.gz}} 12 | \item{dtr}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_dtr.dat.gz}} 13 | \item{reh}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_reh.dat.gz}} 14 | \item{sunp}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_sunp.dat.gz}} 15 | \item{frs}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_frs.dat.gz}} 16 | \item{wnd}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_wnd.dat.gz}, areas originally including Antarctica are removed.} 17 | \item{elv}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_elv.dat.gz}, values are converted from kilometres to metres.} 18 | } 19 | This package crops all spatial outputs to an extent of ymin = -60, ymax = 85, 20 | xmin = -180, xmax = 180. 21 | } 22 | \usage{ 23 | get_CRU_df( 24 | pre = FALSE, 25 | pre_cv = FALSE, 26 | rd0 = FALSE, 27 | tmp = FALSE, 28 | dtr = FALSE, 29 | reh = FALSE, 30 | tmn = FALSE, 31 | tmx = FALSE, 32 | sunp = FALSE, 33 | frs = FALSE, 34 | wnd = FALSE, 35 | elv = FALSE, 36 | cache = FALSE 37 | ) 38 | 39 | get_cru_df( 40 | pre = FALSE, 41 | pre_cv = FALSE, 42 | rd0 = FALSE, 43 | tmp = FALSE, 44 | dtr = FALSE, 45 | reh = FALSE, 46 | tmn = FALSE, 47 | tmx = FALSE, 48 | sunp = FALSE, 49 | frs = FALSE, 50 | wnd = FALSE, 51 | elv = FALSE, 52 | cache = FALSE 53 | ) 54 | } 55 | \arguments{ 56 | \item{pre}{Fetches precipitation (millimetres/month) from server and 57 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 58 | 59 | \item{pre_cv}{Fetch cv of precipitation (percent) from server and 60 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}. NOTE Setting 61 | this to \code{TRUE} will always results in \strong{pre} being set to \code{TRUE} and 62 | returned as well.} 63 | 64 | \item{rd0}{Fetches wet-days (number days with >0.1 millimetres rain 65 | per month) and returns it in the data frame? Defaults to \code{FALSE}.} 66 | 67 | \item{tmp}{Fetches temperature (degrees Celsius) and returns it in the 68 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 69 | 70 | \item{dtr}{Fetches mean diurnal temperature range (degrees Celsius) 71 | and returns it in the data frame? Defaults to \code{FALSE}.} 72 | 73 | \item{reh}{Fetches relative humidity and returns it in the data frame, 74 | \code{TRUE}. Defaults to FALSE.} 75 | 76 | \item{tmn}{Calculates minimum temperature values (degrees Celsius) 77 | and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 78 | 79 | \item{tmx}{Calculates maximum temperature (degrees Celsius) and 80 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 81 | 82 | \item{sunp}{Fetch sunshine, percent of maximum possible (percent of 83 | day length) and return it in the data frame? Defaults to \code{FALSE}.} 84 | 85 | \item{frs}{Fetches ground-frost records (number of days with ground- 86 | frost per month) and return it in the data frame? Defaults to \code{FALSE}.} 87 | 88 | \item{wnd}{Fetches 10m wind speed (metres/second) and returns it in the 89 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 90 | 91 | \item{elv}{Fetches elevation (converted to metres) and returns it in 92 | the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 93 | 94 | \item{cache}{Stores CRU CL v. 2.0 data files locally for later use. 95 | If \code{FALSE}, the downloaded files are removed when the \R session is closed. 96 | To take advantage of cached files in future sessions, use \code{cache = TRUE} 97 | even after the initial download and caching. Defaults to \code{FALSE}.} 98 | } 99 | \value{ 100 | A \link[data.table:data.table]{data.table::data.table} object of \acronym{CRU} \acronym{CL} v. 101 | 2.0 climatology elements. 102 | } 103 | \description{ 104 | This function automates downloading and importing \acronym{CRU} 105 | \acronym{CL} v. 2.0 climatology data and creates a \CRANpkg{data.table} of 106 | the data. If requested, minimum and maximum temperature may also be 107 | automatically calculated as described in the data 108 | \href{https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt}{readme.txt} file. 109 | Data may be cached for later use by this function, saving time downloading 110 | files in future use of this function. 111 | } 112 | \section{Nomenclature and Units}{ 113 | 114 | \describe{ 115 | \item{pre}{precipitation (millimetres/month)} 116 | \describe{ 117 | \item{cv}{cv of precipitation (percent)} 118 | } 119 | \item{rd0}{wet-days (number days with >0.1 millimetres rain per month)} 120 | \item{tmp}{mean temperature (degrees Celsius)} 121 | \item{dtr}{mean diurnal temperature range (degrees Celsius)} 122 | \item{reh}{relative humidity (percent)} 123 | \item{sunp}{sunshine (percent of maximum possible (percent of day length))} 124 | \item{frs}{ground-frost (number of days with ground-frost per month)} 125 | \item{wnd}{10 metre windspeed (metres/second)} 126 | \item{elv}{elevation (automatically converted to metres)} 127 | } 128 | For more information see the description of the data provided by 129 | \acronym{CRU}, \url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt} 130 | } 131 | 132 | \examples{ 133 | \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 134 | # Download data and create a data frame of precipitation and temperature 135 | # without caching the data files 136 | CRU_pre_tmp <- get_CRU_df(pre = TRUE, tmp = TRUE) 137 | 138 | head(CRU_pre_tmp) 139 | CRU_pre_tmp 140 | \dontshow{\}) # examplesIf} 141 | } 142 | \references{ 143 | New, Mark, et al. "A high-resolution data set of surface climate 144 | over global land areas." Climate research 21.1 (2002): 1-25. 145 | \url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/new_et_al_10minute_climate_CR.pdf} 146 | } 147 | \seealso{ 148 | \link{create_CRU_stack}, \link{manage_cache}. 149 | } 150 | \author{ 151 | Adam H. Sparks, \email{adamhsparks@gmail.com} 152 | } 153 | -------------------------------------------------------------------------------- /man/get_CRU_stack.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_CRU_stack.R 3 | \name{get_CRU_stack} 4 | \alias{get_CRU_stack} 5 | \alias{get_cru_stack} 6 | \title{Download and create a list of terra rast objects of CRU CL v. 2.0 climatology elements} 7 | \source{ 8 | \describe{ 9 | \item{pre}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_pre.dat.gz}} 10 | \item{rd0}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_rd0.dat.gz}} 11 | \item{tmp}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_tmp.dat.gz}} 12 | \item{dtr}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_dtr.dat.gz}} 13 | \item{reh}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_reh.dat.gz}} 14 | \item{sunp}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_sunp.dat.gz}} 15 | \item{frs}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_frs.dat.gz}} 16 | \item{wnd}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_wnd.dat.gz}, areas originally including Antarctica are removed.} 17 | \item{elv}{\url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/grid_10min_elv.dat.gz}, values are converted from kilometres to metres.} 18 | } 19 | This package crops all spatial outputs to an extent of ymin = -60, ymax = 85, 20 | xmin = -180, xmax = 180. 21 | } 22 | \usage{ 23 | get_CRU_stack( 24 | pre = FALSE, 25 | pre_cv = FALSE, 26 | rd0 = FALSE, 27 | tmp = FALSE, 28 | dtr = FALSE, 29 | reh = FALSE, 30 | tmn = FALSE, 31 | tmx = FALSE, 32 | sunp = FALSE, 33 | frs = FALSE, 34 | wnd = FALSE, 35 | elv = FALSE, 36 | cache = FALSE 37 | ) 38 | 39 | get_cru_stack( 40 | pre = FALSE, 41 | pre_cv = FALSE, 42 | rd0 = FALSE, 43 | tmp = FALSE, 44 | dtr = FALSE, 45 | reh = FALSE, 46 | tmn = FALSE, 47 | tmx = FALSE, 48 | sunp = FALSE, 49 | frs = FALSE, 50 | wnd = FALSE, 51 | elv = FALSE, 52 | cache = FALSE 53 | ) 54 | } 55 | \arguments{ 56 | \item{pre}{Fetches precipitation (millimetres/month) from server and 57 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 58 | 59 | \item{pre_cv}{Fetch cv of precipitation (percent) from server and 60 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}. NOTE Setting 61 | this to \code{TRUE} will always results in \strong{pre} being set to \code{TRUE} and 62 | returned as well.} 63 | 64 | \item{rd0}{Fetches wet-days (number days with >0.1 millimetres rain 65 | per month) and returns it in the data frame? Defaults to \code{FALSE}.} 66 | 67 | \item{tmp}{Fetches temperature (degrees Celsius) and returns it in the 68 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 69 | 70 | \item{dtr}{Fetches mean diurnal temperature range (degrees Celsius) 71 | and returns it in the data frame? Defaults to \code{FALSE}.} 72 | 73 | \item{reh}{Fetches relative humidity and returns it in the data frame, 74 | \code{TRUE}. Defaults to FALSE.} 75 | 76 | \item{tmn}{Calculates minimum temperature values (degrees Celsius) 77 | and returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 78 | 79 | \item{tmx}{Calculates maximum temperature (degrees Celsius) and 80 | returns it in the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 81 | 82 | \item{sunp}{Fetch sunshine, percent of maximum possible (percent of 83 | day length) and return it in the data frame? Defaults to \code{FALSE}.} 84 | 85 | \item{frs}{Fetches ground-frost records (number of days with ground- 86 | frost per month) and return it in the data frame? Defaults to \code{FALSE}.} 87 | 88 | \item{wnd}{Fetches 10m wind speed (metres/second) and returns it in the 89 | data frame, \code{TRUE}. Defaults to \code{FALSE}.} 90 | 91 | \item{elv}{Fetches elevation (converted to metres) and returns it in 92 | the data frame, \code{TRUE}. Defaults to \code{FALSE}.} 93 | 94 | \item{cache}{Stores CRU CL v. 2.0 data files locally for later use. 95 | If \code{FALSE}, the downloaded files are removed when the \R session is closed. 96 | To take advantage of cached files in future sessions, use \code{cache = TRUE} 97 | even after the initial download and caching. Defaults to \code{FALSE}.} 98 | } 99 | \value{ 100 | A \link[base:list]{base::list} of \link[terra:rast]{terra::rast} objects of \acronym{CRU} 101 | \acronym{CL} v. 2.0 climatology elements. 102 | } 103 | \description{ 104 | This function automates downloading and importing CRU CL v. 2.0 105 | climatology data into \R and creates a list of \CRANpkg{terra} 106 | \link[terra:rast]{terra::rast} objects of the data. If requested, minimum and maximum 107 | temperature may also be automatically calculated as described in the data 108 | \href{https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt}{readme.txt} file. 109 | Data may be cached for later use by this function, saving time downloading 110 | files in future using this function. 111 | } 112 | \section{Nomenclature and Units}{ 113 | 114 | \describe{ 115 | \item{pre}{precipitation (millimetres/month)} 116 | \describe{ 117 | \item{cv}{cv of precipitation (percent)} 118 | } 119 | \item{rd0}{wet-days (number days with >0.1 millimetres rain per month)} 120 | \item{tmp}{mean temperature (degrees Celsius)} 121 | \item{dtr}{mean diurnal temperature range (degrees Celsius)} 122 | \item{reh}{relative humidity (percent)} 123 | \item{sunp}{sunshine (percent of maximum possible (percent of day length))} 124 | \item{frs}{ground-frost (number of days with ground-frost per month)} 125 | \item{wnd}{10 metre windspeed (metres/second)} 126 | \item{elv}{elevation (automatically converted to metres)} 127 | } 128 | For more information see the description of the data provided by 129 | \acronym{CRU}, \url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/readme.txt} 130 | } 131 | 132 | \examples{ 133 | \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 134 | 135 | # Download data and create a list of {terra} `rast` objects of precipitation 136 | # and temperature without caching the data files 137 | CRU_pre_tmp <- get_CRU_stack(pre = TRUE, tmp = TRUE) 138 | 139 | CRU_pre_tmp 140 | \dontshow{\}) # examplesIf} 141 | } 142 | \references{ 143 | New, Mark, et al. "A high-resolution data set of surface climate 144 | over global land areas." Climate research 21.1 (2002): 1-25. 145 | \url{https://crudata.uea.ac.uk/cru/data/hrg/tmc/new_et_al_10minute_climate_CR.pdf} 146 | } 147 | \seealso{ 148 | \link{create_CRU_stack}, \link{manage_cache}. 149 | } 150 | \author{ 151 | Adam H. Sparks, \email{adamhsparks@gmail.com} 152 | } 153 | -------------------------------------------------------------------------------- /man/manage_cache.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/manage_cached_files.R 3 | \name{manage_cache} 4 | \alias{manage_cache} 5 | \title{Manage locally cached CRU CL v. 2.0 files} 6 | \description{ 7 | Manage cached \pkg{getCRUCLdata} files with \CRANpkg{hoardr}. 8 | } 9 | \details{ 10 | The default cache directory is 11 | \code{tools::R_user_dir(package = "getCRUCLdata")}, but you can set your own path 12 | using \code{manage_cache$cache_path_set()}. 13 | 14 | \code{manage_cache$cache_delete} only accepts one file name, while 15 | \code{manage_cache$cache_delete_all} 16 | does not accept any names, but deletes all files. For deleting many specific 17 | files, use \code{manage_cache$cache_delete} in an \link[base:lapply]{base::lapply} type call. 18 | } 19 | \section{Useful user functions}{ 20 | 21 | \describe{ 22 | \item{\code{manage_cache$cache_path_get()}}{get cache path} 23 | \item{\code{manage_cache$cache_path_set()}}{set cache path} 24 | \item{\code{manage_cache$list()}}{returns a character vector of full 25 | path file names} 26 | \item{\code{manage_cache$files()}}{returns file objects with metadata} 27 | \item{\code{manage_cache$details()}}{returns files with details} 28 | \item{\code{manage_cache$delete()}}{delete specific files} 29 | \item{\code{manage_cache$delete_all()}}{delete all files, returns 30 | nothing} 31 | } 32 | } 33 | 34 | \examples{ 35 | \dontrun{ 36 | 37 | # list files in cache 38 | manage_cache$list() 39 | 40 | # delete certain database files 41 | manage_cache$delete("file path") 42 | manage_cache$list() 43 | 44 | # delete all files in cache 45 | manage_cache$delete_all() 46 | manage_cache$list() 47 | 48 | # set a different cache path from the default 49 | manage_cache$cache_path_set("~/tmp") 50 | } 51 | } 52 | \author{ 53 | Adam H. Sparks, \email{adamhsparks@gmail.com} 54 | } 55 | -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if (requireNamespace("spelling", quietly = TRUE)) { 2 | spelling::spell_check_test( 3 | vignettes = TRUE, 4 | error = FALSE, 5 | skip_on_cran = TRUE 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(getCRUCLdata) 3 | 4 | test_check("getCRUCLdata") 5 | -------------------------------------------------------------------------------- /tests/testthat/test-get_CRU.R: -------------------------------------------------------------------------------- 1 | # Test that get_CRU will retrieve only precipitation file when pre_cv TRUE ----- 2 | test_that("get_CRU will retrieve only precipitation file when pre_cv TRUE", { 3 | unlink(list.files( 4 | path = tempdir(), 5 | pattern = ".dat.gz$", 6 | full.names = TRUE 7 | )) 8 | 9 | skip_if_offline() 10 | 11 | .get_CRU( 12 | pre = FALSE, 13 | pre_cv = TRUE, 14 | rd0 = FALSE, 15 | tmp = FALSE, 16 | dtr = FALSE, 17 | reh = FALSE, 18 | tmn = FALSE, 19 | tmx = FALSE, 20 | sunp = FALSE, 21 | frs = FALSE, 22 | wnd = FALSE, 23 | elv = FALSE, 24 | cache_dir = tempdir() 25 | ) 26 | files <- list.files(tempdir(), pattern = ".dat.gz$") 27 | expect_identical(files, "grid_10min_pre.dat.gz") 28 | unlink(list.files( 29 | path = tempdir(), 30 | pattern = ".dat.gz$", 31 | full.names = TRUE 32 | )) 33 | }) 34 | 35 | # Test that get_CRU will retrieve diurnal tmp & tmp files when tmn TRUE -------- 36 | 37 | test_that("get_CRU will retrieve diurnal tmp range & tmp files when tmn TRUE", { 38 | skip_if_offline() 39 | 40 | .get_CRU( 41 | pre = FALSE, 42 | pre_cv = FALSE, 43 | rd0 = FALSE, 44 | tmp = FALSE, 45 | dtr = FALSE, 46 | reh = FALSE, 47 | tmn = TRUE, 48 | tmx = FALSE, 49 | sunp = FALSE, 50 | frs = FALSE, 51 | wnd = FALSE, 52 | elv = FALSE, 53 | cache_dir = tempdir() 54 | ) 55 | files <- list.files(tempdir(), pattern = ".dat.gz$") 56 | expect_identical(files, c("grid_10min_dtr.dat.gz", "grid_10min_tmp.dat.gz")) 57 | unlink(list.files( 58 | path = tempdir(), 59 | pattern = ".dat.gz$", 60 | full.names = TRUE 61 | )) 62 | }) 63 | 64 | # Test that get_CRU will retrieve diurnal tmp & tmp files when tmx TRUE -------- 65 | test_that("get_CRU will retrieve diurnal tmp range & tmp files when tmx TRUE", { 66 | skip_if_offline() 67 | 68 | unlink(list.files( 69 | path = tempdir(), 70 | pattern = ".dat.gz$", 71 | full.names = TRUE 72 | )) 73 | 74 | .get_CRU( 75 | pre = FALSE, 76 | pre_cv = FALSE, 77 | rd0 = FALSE, 78 | tmp = FALSE, 79 | dtr = FALSE, 80 | reh = FALSE, 81 | tmn = FALSE, 82 | tmx = TRUE, 83 | sunp = FALSE, 84 | frs = FALSE, 85 | wnd = FALSE, 86 | elv = FALSE, 87 | cache_dir = tempdir() 88 | ) 89 | files <- list.files(tempdir(), pattern = ".dat.gz$") 90 | expect_identical(files, c("grid_10min_dtr.dat.gz", "grid_10min_tmp.dat.gz")) 91 | unlink(list.files( 92 | path = tempdir(), 93 | pattern = ".dat.gz$", 94 | full.names = TRUE 95 | )) 96 | }) 97 | 98 | # Test that get_CRU will set pre to TRUE if pre_cv is TRUE and pre is FALSE ---- 99 | 100 | test_that("get_CRU will set pre to TRUE if pre_cv is TRUE and pre is FALSE", { 101 | skip_if_offline() 102 | 103 | pre <- FALSE 104 | pre_cv <- TRUE 105 | 106 | expect_false(pre) 107 | 108 | if (pre_cv) { 109 | pre <- TRUE 110 | } 111 | expect_true(pre) 112 | }) 113 | 114 | # Test that get_CRU will set tmp and dtr to TRUE if tmn or tmx is TRUE and ----- 115 | # either/both are false 116 | 117 | test_that("get_CRU will set tmp and dtr to TRUE if tmn or tmx 118 | is TRUE and either/both are false", { 119 | skip_if_offline() 120 | 121 | tmp <- FALSE 122 | dtr <- FALSE 123 | tmn <- TRUE 124 | tmx <- TRUE 125 | expect_false(isTRUE(tmp)) 126 | expect_false(isTRUE(dtr)) 127 | 128 | if (any(tmn, tmx)) { 129 | dtr <- tmp <- TRUE 130 | } 131 | 132 | expect_true(tmp) 133 | expect_true(dtr) 134 | }) 135 | -------------------------------------------------------------------------------- /tests/testthat/test-validate_dsn.R: -------------------------------------------------------------------------------- 1 | # Test that .validate_dsn stops if the dsn is not provided --------------------- 2 | 3 | test_that(".validate_dsn stops if the dsn is not provided", { 4 | skip_if_offline() 5 | dsn <- NULL 6 | expect_error(.validate_dsn(dsn)) 7 | }) 8 | -------------------------------------------------------------------------------- /tests/testthat/test_caching.R: -------------------------------------------------------------------------------- 1 | # test that .set_cache creates a cache directory if none exists ---------------- 2 | test_that("test that set_cache creates a cache directory if none exists", { 3 | skip_if_offline() 4 | withr::local_envvar(R_USER_CACHE_DIR = tempdir()) 5 | temp_cache <- file.path(tempdir(), "R/getCRUCLdata/") 6 | manage_cache <- hoardr::hoard() 7 | manage_cache$cache_path_set( 8 | path = "getCRUCLdata", 9 | prefix = "org.R-project.R/R", 10 | type = "user_cache_dir" 11 | ) 12 | cache <- TRUE 13 | .set_cache(cache) 14 | expect_true(file.exists(manage_cache$cache_path_get())) 15 | withr::deferred_run() 16 | }) 17 | 18 | # test that .set_cache does a cache directory if cache is FALSE ---------------- 19 | 20 | test_that("test that set_cache does not create a dir if cache == FALSE", { 21 | cache_dir <- .set_cache(cache = FALSE) 22 | expect_true(cache_dir == tempdir()) 23 | }) 24 | 25 | test_that("cache directory is created if necessary", { 26 | # if cache directory exists during testing, remove it 27 | unlink(manage_cache$cache_path_get(), recursive = TRUE) 28 | cache <- TRUE 29 | cache_dir <- .set_cache(cache) 30 | expect_true(dir.exists(manage_cache$cache_path_get())) 31 | }) 32 | 33 | test_that("caching utils list files in cache and delete when asked", { 34 | skip_if_offline() 35 | withr::local_envvar(R_USER_CACHE_DIR = tempdir()) 36 | temp_cache <- file.path(tempdir(), "R/getCRUCLdata/") 37 | manage_cache <- hoardr::hoard() 38 | manage_cache$cache_path_set( 39 | path = "getCRUCLdata", 40 | prefix = "org.R-project.R/R", 41 | type = "user_cache_dir" 42 | ) 43 | cache <- TRUE 44 | cache_dir <- .set_cache(cache) 45 | f <- 46 | terra::rast(system.file("ex/test.grd", package = "terra")) 47 | cache_dir <- manage_cache$cache_path_get() 48 | manage_cache$delete_all() 49 | 50 | terra::writeRaster(f, file.path(cache_dir, "file1.grd")) 51 | terra::writeRaster(f, file.path(cache_dir, "file2.grd")) 52 | 53 | # test getCRUCLdata cache list 54 | k <- list.files(manage_cache$cache_path_get()) 55 | expect_identical(basename(manage_cache$list()), k) 56 | 57 | # test delete one file 58 | expect_error(manage_cache$delete("file1.tif")) 59 | 60 | manage_cache$delete("file1.grd") 61 | l <- list.files(manage_cache$cache_path_get()) 62 | expect_identical(basename(manage_cache$list()), l) 63 | 64 | # test delete all 65 | manage_cache$delete_all() 66 | expect_identical(list.files(manage_cache$list()), character(0)) 67 | withr::deferred_run() 68 | }) 69 | -------------------------------------------------------------------------------- /tests/testthat/test_set_cache.R: -------------------------------------------------------------------------------- 1 | # test that .set_cache creates a cache directory if none exists ---------------- 2 | 3 | test_that("test that .set_cache creates a cache directory if none exists", { 4 | skip_if_offline() 5 | unlink(manage_cache$cache_path_get(), recursive = TRUE) 6 | cache <- TRUE 7 | .set_cache(cache) 8 | expect_true(file.exists(manage_cache$cache_path_get())) 9 | # cleanup 10 | unlink(manage_cache$cache_path_get(), recursive = TRUE) 11 | }) 12 | 13 | # test that .set_cache does a cache directory if cache is FALSE ---------------- 14 | 15 | test_that("test that set_cache does not create a dir if cache == FALSE", { 16 | cache <- FALSE 17 | cache_dir <- .set_cache(cache) 18 | expect_true(cache_dir == tempdir()) 19 | }) 20 | -------------------------------------------------------------------------------- /vignettes/Advanced_use.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Advanced usage of getCRUCLdata" 3 | author: "Adam H. Sparks" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Advanced usage of getCRUCLdata} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r setup, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | ``` 18 | 19 | ## Caching files for later use 20 | 21 | When using the `get_CRU_df()` or `get_CRU_stack()` functions, files may be cached in the users' local space for later use (optional) or stored in a temporary directory and deleted when the R session is closed and not saved (this is the default behaviour already illustrated above). 22 | Illustrated here, create a data.table of all CRU CL v. 2.0 climatology elements available and cache them to save time in the future. 23 | *In order to take advantage of the cached data, you must use the `get_CRU_df()` function again in the future*. 24 | This functionality is somewhat modelled after the raster package's `raster::getData()` that will not download files that already exist in the working directory, however in this case the function is portable and it will work for any working directory. 25 | That is, if you have cached the data and you use `get_CRU_df()` again, it will use the cached data no matter what working directory you are in. 26 | This functionality will be most useful for writing scripts that may be used several times rather than just once off or if you frequently use the data in multiple analyses the data will not be downloaded again if they have been cached. 27 | 28 | Create a list of raster stacks of maximum and minimum temperature. 29 | To take advantage of the previously cached files and save time by not downloading files, specify `cache = TRUE`. 30 | 31 | ```{r, eval=FALSE} 32 | tmn_tmx <- get_CRU_stack( 33 | tmn = TRUE, 34 | tmx = TRUE, 35 | cache = TRUE 36 | ) 37 | ``` 38 | 39 | ## Handling files downloaded outside of R 40 | 41 | A second set of functions, `create_CRU_df()` and `create_CRU_stack()`, is provided for users that may have connectivity issues or simply wish to use something other than R to download the data files. 42 | You may also wish to use these if you want to download the data and specify where it is stored rather than using the `cache` functionality of `get_CRU_df()` and `get_CRU_stack()`. 43 | 44 | The `create_CRU_df()` and `create_CRU_stack()` functions work in the same way as `get_CRU_df()` and `get_CRU_stack()` functions with only one major difference. 45 | You must supply the location of the files on the local disk (`dsn`) that you wish to import. 46 | That is, the CRU CL v. 2.0 data files *must* be downloaded prior to the use of these functions using a program external to R. 47 | 48 | ```{r, eval=FALSE} 49 | t <- create_CRU_df(tmp = TRUE, dsn = "~/Downloads") 50 | ``` 51 | -------------------------------------------------------------------------------- /vignettes/plot_t-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci/getCRUCLdata/1555031f739979bed196c923e2a3a261ea98c1ff/vignettes/plot_t-1.png -------------------------------------------------------------------------------- /vignettes/precompile.R: -------------------------------------------------------------------------------- 1 | # vignettes that depend on internet access need to be precompiled and take a 2 | # while to run 3 | knitr::knit("vignettes/getCRUCLdata.Rmd.orig", "vignettes/getCRUCLdata.Rmd") 4 | 5 | # remove file path such that vignettes will build with figures 6 | replace <- readLines("vignettes/getCRUCLdata.Rmd") 7 | replace <- gsub("